Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 49d66a133e | |||
| c559f26d0e | |||
| bbe9f1bad2 | |||
| 7e1fb6472d | |||
| 0ff8d20573 | |||
| 9c1f9448dc | |||
| 43a6081dd6 | |||
| 985e961876 | |||
| 098f6de047 | |||
| 1b0f90fd68 | |||
| 12252f407b | |||
| 3b6e3f47ab | |||
| 6a9ac9b025 | |||
| ae6aa4088b | |||
| c08f431180 | |||
| 123c1f56e9 | |||
| 35ac65a864 | |||
| e9f362cc87 | |||
| 65685c23e1 | |||
| 2f74748cea | |||
| f477bd66f3 | |||
| d7d77ae8f0 | |||
| 31110a740d | |||
| b64d8b1d7f | |||
| c46006aacc | |||
| 92f81b1493 | |||
| 70213cfc8f | |||
| 8a82bf5c50 | |||
| 37405384a2 | |||
| 54ea6cc53b | |||
| 339c00d815 | |||
| ea6b4dcde2 | |||
| 2b84623d1e | |||
| c8b3afa56b | |||
| 1348f3c24c | |||
| 62208ce3e1 | |||
| 813b2481de | |||
| 27b924ba61 | |||
| b40170b8ce | |||
| 8bfa9d2734 | |||
| c7cf76d4a8 | |||
| dfd2969b3e | |||
| 0e1866fe1d | |||
| b9ae46b913 | |||
| 06e7284055 | |||
| 93289e8fca | |||
| 130d5057d4 | |||
| be492d5084 | |||
| e0bf1d736f | |||
| 5a6b71cbeb | |||
| e6934cd5e2 | |||
| b5aada0792 | |||
| 165ea83ac3 | |||
| 440a82dee4 | |||
| 9c2d3e5e26 |
@@ -1,2 +0,0 @@
|
|||||||
[build]
|
|
||||||
rustflags = ["-C", "target-cpu=native"]
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
# Builds
|
# Builds
|
||||||
target
|
target
|
||||||
|
dist
|
||||||
|
|
||||||
# Copies
|
# Copies
|
||||||
*\ copy*
|
*\ copy*
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ members = ["crates/*"]
|
|||||||
package.description = "The Bitcoin Research Kit is a suite of tools designed to extract, compute and display data stored on a Bitcoin Core node"
|
package.description = "The Bitcoin Research Kit is a suite of tools designed to extract, compute and display data stored on a Bitcoin Core node"
|
||||||
package.license = "MIT"
|
package.license = "MIT"
|
||||||
package.edition = "2024"
|
package.edition = "2024"
|
||||||
package.version = "0.0.44"
|
package.version = "0.0.64"
|
||||||
package.homepage = "https://bitcoinresearchkit.org"
|
package.homepage = "https://bitcoinresearchkit.org"
|
||||||
package.repository = "https://github.com/bitcoinresearchkit/brk"
|
package.repository = "https://github.com/bitcoinresearchkit/brk"
|
||||||
|
|
||||||
@@ -22,26 +22,27 @@ axum = "0.8.4"
|
|||||||
bincode = { version = "2.0.1", features = ["serde"] }
|
bincode = { version = "2.0.1", features = ["serde"] }
|
||||||
bitcoin = { version = "0.32.6", features = ["serde"] }
|
bitcoin = { version = "0.32.6", features = ["serde"] }
|
||||||
bitcoincore-rpc = "0.19.0"
|
bitcoincore-rpc = "0.19.0"
|
||||||
brk_cli = { version = "0", path = "crates/brk_cli" }
|
brk_bundler = { version = "0.0.64", path = "crates/brk_bundler" }
|
||||||
brk_computer = { version = "0", path = "crates/brk_computer" }
|
brk_cli = { version = "0.0.64", path = "crates/brk_cli" }
|
||||||
brk_core = { version = "0", path = "crates/brk_core" }
|
brk_computer = { version = "0.0.64", path = "crates/brk_computer" }
|
||||||
brk_exit = { version = "0", path = "crates/brk_exit" }
|
brk_core = { version = "0.0.64", path = "crates/brk_core" }
|
||||||
brk_fetcher = { version = "0", path = "crates/brk_fetcher" }
|
brk_exit = { version = "0.0.64", path = "crates/brk_exit" }
|
||||||
brk_indexer = { version = "0", path = "crates/brk_indexer" }
|
brk_fetcher = { version = "0.0.64", path = "crates/brk_fetcher" }
|
||||||
brk_logger = { version = "0", path = "crates/brk_logger" }
|
brk_indexer = { version = "0.0.64", path = "crates/brk_indexer" }
|
||||||
brk_parser = { version = "0", path = "crates/brk_parser" }
|
brk_logger = { version = "0.0.64", path = "crates/brk_logger" }
|
||||||
brk_query = { version = "0", path = "crates/brk_query" }
|
brk_parser = { version = "0.0.64", path = "crates/brk_parser" }
|
||||||
brk_server = { version = "0", path = "crates/brk_server" }
|
brk_query = { version = "0.0.64", path = "crates/brk_query" }
|
||||||
brk_state = { version = "0", path = "crates/brk_state" }
|
brk_server = { version = "0.0.64", path = "crates/brk_server" }
|
||||||
brk_store = { version = "0", path = "crates/brk_store" }
|
brk_state = { version = "0.0.64", path = "crates/brk_state" }
|
||||||
brk_vec = { version = "0", path = "crates/brk_vec" }
|
brk_store = { version = "0.0.64", path = "crates/brk_store" }
|
||||||
|
brk_vec = { version = "0.0.64", path = "crates/brk_vec" }
|
||||||
byteview = "=0.6.1"
|
byteview = "=0.6.1"
|
||||||
clap = { version = "4.5.39", features = ["string"] }
|
clap = { version = "4.5.40", features = ["string"] }
|
||||||
clap_derive = "4.5.32"
|
clap_derive = "4.5.40"
|
||||||
color-eyre = "0.6.5"
|
color-eyre = "0.6.5"
|
||||||
derive_deref = "1.1.1"
|
derive_deref = "1.1.1"
|
||||||
fjall = "2.11.0"
|
fjall = "2.11.0"
|
||||||
jiff = "0.2.14"
|
jiff = "0.2.15"
|
||||||
log = { version = "0.4.27" }
|
log = { version = "0.4.27" }
|
||||||
minreq = { version = "2.13.4", features = ["https", "serde_json"] }
|
minreq = { version = "2.13.4", features = ["https", "serde_json"] }
|
||||||
rayon = "1.10.0"
|
rayon = "1.10.0"
|
||||||
|
|||||||
@@ -31,28 +31,22 @@
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
> **WARNING**
|
The Bitcoin Research Kit is a high-performance toolchain designed to parse, index, compute, serve and visualize data from a Bitcoin node, enabling users to gain deeper insights into the Bitcoin network.
|
||||||
>
|
|
||||||
> This project is still a work in progress and while it's much better in many ways than its previous version ([kibo v0.5](https://github.com/kibo-money/kibo)), it doesn't yet include all of those datasets. If you're interested in having everything right now, please use the latter until feature parity is achieved.
|
|
||||||
>
|
|
||||||
> The explorer part (mempool.space/electrs) is also not viable just yet.
|
|
||||||
>
|
|
||||||
> Stay tuned and please be patient, it's a lot of work !
|
|
||||||
|
|
||||||
The Bitcoin Research Kit is a high-performance toolchain designed to parse, index, compute, serve and visualize data from a Bitcoin Core node, enabling users to gain deeper insights into the Bitcoin network.
|
In other words it's an alternative to [Glassnode](https://glassnode.com), [mempool.space](https://mempool.space/) (soon) and [electrs](https://github.com/romanz/electrs) (soon) all in one package with a particular focus on simplicity and ease of use.
|
||||||
|
|
||||||
In other words it's an alternative to [Glassnode](https://glassnode.com), [mempool.space](https://mempool.space/) and [electrs](https://github.com/romanz/electrs) all in one package with a particular focus on simplicity and the self-hosting experience.
|
|
||||||
|
|
||||||
The toolkit can be used in various ways to accommodate as many needs as possible:
|
The toolkit can be used in various ways to accommodate as many needs as possible:
|
||||||
|
|
||||||
- **[Website](https://kibo.money)** \
|
- **[Website](https://bitcoinresearchkit.org)** \
|
||||||
Everyone is welcome to visit [kibo.money](https://kibo.money) which is the official showcase of the suite's capabilities and served by default when running BRK. \
|
Everyone is welcome to visit the official instance and showcase of the suite's capabilities. \
|
||||||
Researchers and developers are free to use the API which endpoints documentation can be found [here](https://github.com/bitcoinresearchkit/brk/tree/main/crates/brk_server#endpoints). \
|
It has a wide range of functionalities including charts, tables and simulations which you can visit for free and without the need for an account. \
|
||||||
As a token of gratitude to the community and to stimulate curiosity, both the website and the API are entirely free, allowing anyone to use them.
|
Also available at: [kibo.money](https://kibo.money) // [satonomics.xyz](https://satonomics.xyz)
|
||||||
|
- **[API](https://github.com/bitcoinresearchkit/brk/tree/main/crates/brk_server#endpoints)** \
|
||||||
|
Researchers and developers are free to use BRK's public API with  dataset variants at your disposal. \
|
||||||
|
Just like the website, it's entirely free, with no authentication or rate-limiting.
|
||||||
- **[CLI](https://crates.io/crates/brk_cli)** \
|
- **[CLI](https://crates.io/crates/brk_cli)** \
|
||||||
Node runners are strongly encouraged to try out and self-host their own instance. \
|
Node runners are strongly encouraged to try out and self-host their own instance using BRK's command line interface. \
|
||||||
A lot of effort has gone into making this as easy as possible. \
|
The CLI has multiple cogs available for users to tweak to adapt to all situations with even the possibility for web developers to create their own custom website which could later on be added as an alternative front-end.
|
||||||
For more information visit: [`brk_cli`](https://crates.io/crates/brk_cli)
|
|
||||||
- **[Crates](https://crates.io/crates/brk)** \
|
- **[Crates](https://crates.io/crates/brk)** \
|
||||||
Rust developers have access to a wide range crates, each built upon one another with its own specific purpose, enabling independent use and offering great flexibility.
|
Rust developers have access to a wide range crates, each built upon one another with its own specific purpose, enabling independent use and offering great flexibility.
|
||||||
PRs are welcome, especially if their goal is to introduce additional datasets.
|
PRs are welcome, especially if their goal is to introduce additional datasets.
|
||||||
@@ -77,22 +71,15 @@ In contrast, existing alternatives tend to be either [very costly](https://studi
|
|||||||
- [`brk_state`](https://crates.io/crates/brk_state): Various states used mainly by the computer
|
- [`brk_state`](https://crates.io/crates/brk_state): Various states used mainly by the computer
|
||||||
- [`brk_store`](https://crates.io/crates/brk_store): A thin wrapper around [`fjall`](https://crates.io/crates/fjall)
|
- [`brk_store`](https://crates.io/crates/brk_store): A thin wrapper around [`fjall`](https://crates.io/crates/fjall)
|
||||||
- [`brk_vec`](https://crates.io/crates/brk_vec): A push-only, truncable, compressable, saveable Vec
|
- [`brk_vec`](https://crates.io/crates/brk_vec): A push-only, truncable, compressable, saveable Vec
|
||||||
|
- [`brk_bundler`](https://crates.io/crates/brk_bundler): A thin wrapper around [`rolldown`](https://rolldown.rs/)
|
||||||
## Acknowledgments
|
|
||||||
|
|
||||||
Deepest gratitude to the [Open Sats](https://opensats.org/) public charity. Their grant — from December 2024 to the present — has been critical in sustaining this project.
|
|
||||||
|
|
||||||
Heartfelt thanks go out to every donor on [Nostr](https://primal.net/p/npub1jagmm3x39lmwfnrtvxcs9ac7g300y3dusv9lgzhk2e4x5frpxlrqa73v44) and [Geyser.fund](https://geyser.fund/project/brk) whose support has ensured the availability of the [kibo.money](https://kibo.money) public instance.
|
|
||||||
|
|
||||||
## Hosting as a service
|
## Hosting as a service
|
||||||
|
|
||||||
*Soon™*
|
|
||||||
|
|
||||||
If you'd like to have your own instance hosted for you please contact [hosting@bitcoinresearchkit.org](mailto:hosting@bitcoinresearchkit.org).
|
If you'd like to have your own instance hosted for you please contact [hosting@bitcoinresearchkit.org](mailto:hosting@bitcoinresearchkit.org).
|
||||||
|
|
||||||
- 2 separate dedicated servers (1 GB/s each) with different ISPs and Cloudflare integration for enhanced performance and optimal availability
|
- 2 separate dedicated servers (1 GB/s each) with different ISPs and Cloudflare integration for enhanced performance and optimal availability
|
||||||
- 99.99% SLA
|
- 99.99% SLA
|
||||||
- Configurated for speed (`raw + eager`)
|
- Configured for speed
|
||||||
- Updates delivered at your convenience
|
- Updates delivered at your convenience
|
||||||
- Direct communication for feature requests and support
|
- Direct communication for feature requests and support
|
||||||
- Bitcoin Core or Knots with desired version
|
- Bitcoin Core or Knots with desired version
|
||||||
@@ -101,6 +88,12 @@ If you'd like to have your own instance hosted for you please contact [hosting@b
|
|||||||
|
|
||||||
Pricing: `0.01 BTC / month` *or* `0.1 BTC / year`
|
Pricing: `0.01 BTC / month` *or* `0.1 BTC / year`
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
Deepest gratitude to the [Open Sats](https://opensats.org/) public charity. Their grant — from December 2024 to the present — has been critical in sustaining this project.
|
||||||
|
|
||||||
|
Heartfelt thanks go out to every donor on [Nostr](https://primal.net/p/npub1jagmm3x39lmwfnrtvxcs9ac7g300y3dusv9lgzhk2e4x5frpxlrqa73v44) and [Geyser.fund](https://geyser.fund/project/brk) whose support has ensured the availability of the [kibo.money](https://kibo.money) public instance.
|
||||||
|
|
||||||
## Donate
|
## Donate
|
||||||
|
|
||||||
[`bc1q09 8zsm89 m7kgyz e338vf ejhpdt 92ua9p 3peuve`](bitcoin:bc1q098zsm89m7kgyze338vfejhpdt92ua9p3peuve)
|
[`bc1q09 8zsm89 m7kgyz e338vf ejhpdt 92ua9p 3peuve`](bitcoin:bc1q098zsm89m7kgyze338vfejhpdt92ua9p3peuve)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ version.workspace = true
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
full = [
|
full = [
|
||||||
|
"bundler",
|
||||||
"core",
|
"core",
|
||||||
"computer",
|
"computer",
|
||||||
"exit",
|
"exit",
|
||||||
@@ -23,6 +24,7 @@ full = [
|
|||||||
"store",
|
"store",
|
||||||
"vec",
|
"vec",
|
||||||
]
|
]
|
||||||
|
bundler = ["brk_bundler"]
|
||||||
core = ["brk_core"]
|
core = ["brk_core"]
|
||||||
computer = ["brk_computer"]
|
computer = ["brk_computer"]
|
||||||
exit = ["brk_exit"]
|
exit = ["brk_exit"]
|
||||||
@@ -37,6 +39,7 @@ store = ["brk_store"]
|
|||||||
vec = ["brk_vec"]
|
vec = ["brk_vec"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
brk_bundler = { workspace = true, optional = true }
|
||||||
brk_cli = { workspace = true }
|
brk_cli = { workspace = true }
|
||||||
brk_core = { workspace = true, optional = true }
|
brk_core = { workspace = true, optional = true }
|
||||||
brk_computer = { workspace = true, optional = true }
|
brk_computer = { workspace = true, optional = true }
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
fn main() {}
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
|
#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
|
||||||
|
|
||||||
|
#[cfg(feature = "bundler")]
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use brk_bundler as bundler;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use brk_cli as cli;
|
||||||
|
|
||||||
#[cfg(feature = "core")]
|
#[cfg(feature = "core")]
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use brk_core as core;
|
pub use brk_core as core;
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "brk_bundler"
|
||||||
|
description = "A thin wrapper around rolldown"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = { workspace = true }
|
||||||
|
notify = "8.0.0"
|
||||||
|
brk_rolldown = "0.0.1"
|
||||||
|
sugar_path = "1.2.0"
|
||||||
|
tokio = { workspace = true }
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
use std::{fs, io, path::Path, sync::Arc};
|
||||||
|
|
||||||
|
use brk_rolldown::{Bundler, BundlerOptions, RawMinifyOptions, SourceMapType};
|
||||||
|
use log::error;
|
||||||
|
use notify::{EventKind, RecursiveMode, Watcher};
|
||||||
|
use sugar_path::SugarPath;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
pub async fn bundle(websites_path: &Path, source_folder: &str, watch: bool) -> io::Result<()> {
|
||||||
|
let source_path = websites_path.join(source_folder);
|
||||||
|
let dist_path = websites_path.join("dist");
|
||||||
|
|
||||||
|
let _ = fs::remove_dir_all(&dist_path);
|
||||||
|
copy_dir_all(&source_path, &dist_path)?;
|
||||||
|
|
||||||
|
let source_scripts = format!("./{source_folder}/scripts");
|
||||||
|
let source_entry = format!("{source_scripts}/entry.js");
|
||||||
|
|
||||||
|
let absolute_websites_path = websites_path.absolutize();
|
||||||
|
|
||||||
|
let mut bundler = Bundler::new(BundlerOptions {
|
||||||
|
input: Some(vec![source_entry.into()]),
|
||||||
|
dir: Some("./dist/scripts".to_string()),
|
||||||
|
cwd: Some(absolute_websites_path),
|
||||||
|
minify: Some(RawMinifyOptions::Bool(true)),
|
||||||
|
sourcemap: Some(SourceMapType::File),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
bundler.write().await.unwrap();
|
||||||
|
|
||||||
|
let absolute_source_index_path = source_path.join("index.html").absolutize();
|
||||||
|
let absolute_source_index_path_clone = absolute_source_index_path.clone();
|
||||||
|
let absolute_source_path = source_path.absolutize();
|
||||||
|
let absolute_source_path_clone = absolute_source_path.clone();
|
||||||
|
let absolute_source_scripts_path = websites_path.join(source_scripts).absolutize();
|
||||||
|
let absolute_source_sw_path = source_path.join("service-worker.js").absolutize();
|
||||||
|
let absolute_source_sw_path_clone = absolute_source_sw_path.clone();
|
||||||
|
|
||||||
|
let absolute_dist_entry_path = dist_path.join("scripts/entry.js").absolutize();
|
||||||
|
let absolute_dist_index_path = dist_path.join("index.html").absolutize();
|
||||||
|
let absolute_dist_path = dist_path.absolutize();
|
||||||
|
let absolute_dist_path_clone = absolute_dist_path.clone();
|
||||||
|
let absolute_dist_sw_path = dist_path.join("service-worker.js").absolutize();
|
||||||
|
|
||||||
|
let write_index = move || {
|
||||||
|
let mut contents = fs::read_to_string(&absolute_source_index_path).unwrap();
|
||||||
|
|
||||||
|
if let Ok(entry) = fs::read_to_string(absolute_dist_path_clone.join("scripts/entry.js")) {
|
||||||
|
if let Some(start) = entry.find("main") {
|
||||||
|
if let Some(end) = entry.find(".js") {
|
||||||
|
let main_hashed = &entry[start..end];
|
||||||
|
contents =
|
||||||
|
contents.replace("/scripts/main.js", &format!("/scripts/{main_hashed}.js"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = fs::write(&absolute_dist_index_path, contents);
|
||||||
|
};
|
||||||
|
|
||||||
|
let write_sw = move || {
|
||||||
|
let contents = fs::read_to_string(&absolute_source_sw_path)
|
||||||
|
.unwrap()
|
||||||
|
.replace("__VERSION__", &format!("v{VERSION}"));
|
||||||
|
let _ = fs::write(&absolute_dist_sw_path, contents);
|
||||||
|
};
|
||||||
|
|
||||||
|
write_index();
|
||||||
|
write_sw();
|
||||||
|
|
||||||
|
if !watch {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let write_index_clone = write_index.clone();
|
||||||
|
|
||||||
|
let mut entry_watcher = notify::recommended_watcher(
|
||||||
|
move |res: Result<notify::Event, notify::Error>| match res {
|
||||||
|
Ok(_) => write_index_clone(),
|
||||||
|
Err(e) => error!("watch error: {:?}", e),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
entry_watcher
|
||||||
|
.watch(&absolute_dist_entry_path, RecursiveMode::Recursive)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut source_watcher = notify::recommended_watcher(
|
||||||
|
move |res: Result<notify::Event, notify::Error>| match res {
|
||||||
|
Ok(event) => match event.kind {
|
||||||
|
EventKind::Create(_) => event.paths,
|
||||||
|
EventKind::Modify(_) => event.paths,
|
||||||
|
_ => vec![],
|
||||||
|
}
|
||||||
|
.into_iter()
|
||||||
|
.filter(|path| path.starts_with(&absolute_source_path))
|
||||||
|
.filter(|path| !path.starts_with(&absolute_source_scripts_path))
|
||||||
|
.for_each(|source_path| {
|
||||||
|
let suffix = source_path.strip_prefix(&absolute_source_path).unwrap();
|
||||||
|
let dist_path = absolute_dist_path.join(suffix);
|
||||||
|
|
||||||
|
if source_path == absolute_source_index_path_clone {
|
||||||
|
write_index();
|
||||||
|
} else if source_path == absolute_source_sw_path_clone {
|
||||||
|
write_sw();
|
||||||
|
} else {
|
||||||
|
let _ = fs::copy(&source_path, &dist_path);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Err(e) => error!("watch error: {:?}", e),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
source_watcher
|
||||||
|
.watch(&absolute_source_path_clone, RecursiveMode::Recursive)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let watcher =
|
||||||
|
brk_rolldown::Watcher::new(vec![Arc::new(Mutex::new(bundler))], None).unwrap();
|
||||||
|
|
||||||
|
watcher.start().await;
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||||
|
fs::create_dir_all(&dst)?;
|
||||||
|
for entry in fs::read_dir(src)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let ty = entry.file_type()?;
|
||||||
|
if ty.is_dir() {
|
||||||
|
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||||
|
} else {
|
||||||
|
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::fs;
|
use std::{fs, thread};
|
||||||
|
|
||||||
use brk_core::{dot_brk_log_path, dot_brk_path};
|
use brk_core::{dot_brk_log_path, dot_brk_path};
|
||||||
use brk_query::Params as QueryArgs;
|
use brk_query::Params as QueryArgs;
|
||||||
@@ -20,9 +20,9 @@ struct Cli {
|
|||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
enum Commands {
|
enum Commands {
|
||||||
/// Run the indexer, computer and server
|
/// Run the indexer, computer and server, use `run -h` for more information
|
||||||
Run(RunConfig),
|
Run(RunConfig),
|
||||||
/// Query generated datasets via the `run` command in a similar fashion as the server's API
|
/// Query generated datasets via the `run` command in a similar fashion as the server's API, use `query -h` for more information
|
||||||
Query(QueryArgs),
|
Query(QueryArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,8 +35,12 @@ pub fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
match cli.command {
|
thread::Builder::new()
|
||||||
Commands::Run(args) => run(args),
|
.stack_size(128 * 1024 * 1024)
|
||||||
Commands::Query(args) => query(args),
|
.spawn(|| match cli.command {
|
||||||
}
|
Commands::Run(args) => run(args),
|
||||||
|
Commands::Query(args) => query(args),
|
||||||
|
})?
|
||||||
|
.join()
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ pub fn query(params: QueryParams) -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let format = config.format();
|
let format = config.format();
|
||||||
|
|
||||||
let mut indexer = Indexer::new(&config.outputsdir(), format, config.check_collisions())?;
|
let mut indexer = Indexer::new(&config.outputsdir(), config.check_collisions())?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|
||||||
let mut computer = Computer::new(&config.outputsdir(), config.fetcher(), format);
|
let mut computer = Computer::new(&config.outputsdir(), config.fetcher(), format);
|
||||||
@@ -19,12 +19,14 @@ pub fn query(params: QueryParams) -> color_eyre::Result<()> {
|
|||||||
let query = Query::build(&indexer, &computer);
|
let query = Query::build(&indexer, &computer);
|
||||||
|
|
||||||
let index = Index::try_from(params.index.as_str())?;
|
let index = Index::try_from(params.index.as_str())?;
|
||||||
|
|
||||||
let ids = params.values.iter().map(|s| s.as_str()).collect::<Vec<_>>();
|
let ids = params.values.iter().map(|s| s.as_str()).collect::<Vec<_>>();
|
||||||
|
let from = params.from();
|
||||||
|
let to = params.to();
|
||||||
|
let format = params.format();
|
||||||
|
|
||||||
let res = query.search_and_format(index, &ids, params.from, params.to, params.format)?;
|
let res = query.search_and_format(index, &ids, from, to, format)?;
|
||||||
|
|
||||||
if params.format.is_some() {
|
if format.is_some() {
|
||||||
println!("{}", res);
|
println!("{}", res);
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
thread::{self, sleep},
|
thread::sleep,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bitcoincore_rpc::{self, Auth, Client, RpcApi};
|
use bitcoincore_rpc::{self, Auth, Client, RpcApi};
|
||||||
use brk_computer::Computer;
|
use brk_computer::Computer;
|
||||||
use brk_core::{default_bitcoin_path, default_brk_path, dot_brk_path};
|
use brk_core::{default_bitcoin_path, default_brk_path, default_on_error, dot_brk_path};
|
||||||
use brk_exit::Exit;
|
use brk_exit::Exit;
|
||||||
use brk_fetcher::Fetcher;
|
use brk_fetcher::Fetcher;
|
||||||
use brk_indexer::Indexer;
|
use brk_indexer::Indexer;
|
||||||
@@ -29,7 +29,7 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let format = config.format();
|
let format = config.format();
|
||||||
|
|
||||||
let mut indexer = Indexer::new(&config.outputsdir(), format, config.check_collisions())?;
|
let mut indexer = Indexer::new(&config.outputsdir(), config.check_collisions())?;
|
||||||
indexer.import_stores()?;
|
indexer.import_stores()?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|
||||||
@@ -49,130 +49,143 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
let f = move || -> color_eyre::Result<()> {
|
let mut computer = Computer::new(&config.outputsdir(), config.fetcher(), format);
|
||||||
let mut computer = Computer::new(&config.outputsdir(), config.fetcher(), format);
|
computer.import_stores(&indexer)?;
|
||||||
computer.import_stores(&indexer)?;
|
computer.import_vecs(&indexer, config.computation())?;
|
||||||
computer.import_vecs(&indexer, config.computation())?;
|
|
||||||
|
|
||||||
tokio::runtime::Builder::new_multi_thread()
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
.enable_all()
|
.enable_all()
|
||||||
.build()?
|
.build()?
|
||||||
.block_on(async {
|
.block_on(async {
|
||||||
let server = if config.serve() {
|
let server = if config.serve() {
|
||||||
let served_indexer = indexer.clone();
|
let served_indexer = indexer.clone();
|
||||||
let served_computer = computer.clone();
|
let served_computer = computer.clone();
|
||||||
|
|
||||||
let server = Server::new(served_indexer, served_computer, config.website())?;
|
let server = Server::new(served_indexer, served_computer, config.website())?;
|
||||||
|
|
||||||
let opt = Some(tokio::spawn(async move {
|
let watch = config.watch();
|
||||||
server.serve().await.unwrap();
|
let opt = Some(tokio::spawn(async move {
|
||||||
}));
|
server.serve(watch).await.unwrap();
|
||||||
|
}));
|
||||||
|
|
||||||
sleep(Duration::from_secs(1));
|
sleep(Duration::from_secs(1));
|
||||||
|
|
||||||
opt
|
opt
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if config.process() {
|
if config.process() {
|
||||||
loop {
|
loop {
|
||||||
wait_for_synced_node()?;
|
wait_for_synced_node()?;
|
||||||
|
|
||||||
let block_count = rpc.get_block_count()?;
|
let block_count = rpc.get_block_count()?;
|
||||||
|
|
||||||
info!("{} blocks found.", block_count + 1);
|
info!("{} blocks found.", block_count + 1);
|
||||||
|
|
||||||
let starting_indexes = indexer.index(&parser, rpc, &exit)?;
|
let starting_indexes = indexer.index(&parser, rpc, &exit)?;
|
||||||
|
|
||||||
computer.compute(&mut indexer, starting_indexes, &exit)?;
|
computer.compute(&mut indexer, starting_indexes, &exit)?;
|
||||||
|
|
||||||
if let Some(delay) = config.delay() {
|
if let Some(delay) = config.delay() {
|
||||||
sleep(Duration::from_secs(delay))
|
sleep(Duration::from_secs(delay))
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Waiting for new blocks...");
|
info!("Waiting for new blocks...");
|
||||||
|
|
||||||
while block_count == rpc.get_block_count()? {
|
while block_count == rpc.get_block_count()? {
|
||||||
sleep(Duration::from_secs(1))
|
sleep(Duration::from_secs(1))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(handle) = server {
|
if let Some(handle) = server {
|
||||||
handle.await.unwrap();
|
handle.await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
};
|
|
||||||
|
|
||||||
thread::Builder::new()
|
|
||||||
.stack_size(128 * 1024 * 1024)
|
|
||||||
.spawn(f)?
|
|
||||||
.join()
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
#[derive(Parser, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||||
pub struct RunConfig {
|
pub struct RunConfig {
|
||||||
/// Bitcoin main directory path, defaults: ~/.bitcoin, ~/Library/Application\ Support/Bitcoin, saved
|
/// Bitcoin main directory path, defaults: ~/.bitcoin, ~/Library/Application\ Support/Bitcoin, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long, value_name = "PATH")]
|
||||||
bitcoindir: Option<String>,
|
bitcoindir: Option<String>,
|
||||||
|
|
||||||
/// Bitcoin blocks directory path, default: --bitcoindir/blocks, saved
|
/// Bitcoin blocks directory path, default: --bitcoindir/blocks, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long, value_name = "PATH")]
|
||||||
blocksdir: Option<String>,
|
blocksdir: Option<String>,
|
||||||
|
|
||||||
/// Bitcoin Research Kit outputs directory path, default: ~/.brk, saved
|
/// Bitcoin Research Kit outputs directory path, default: ~/.brk, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long, value_name = "PATH")]
|
||||||
brkdir: Option<String>,
|
brkdir: Option<String>,
|
||||||
|
|
||||||
/// Executed by the runner, default: all, saved
|
/// Activated services, default: all, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
mode: Option<Mode>,
|
services: Option<Services>,
|
||||||
|
|
||||||
/// Computation mode for compatible datasets, `lazy` computes data whenever requested without saving it, `eager` computes the data once and saves it to disk, default: Lazy, saved
|
/// Computation of computed datasets, `lazy` computes data whenever requested without saving it, `eager` computes the data once and saves it to disk, default: `lazy`, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
computation: Option<Computation>,
|
computation: Option<Computation>,
|
||||||
|
|
||||||
/// Activate compression of datasets, set to true to save disk space or false if prioritize speed, default: true, saved
|
/// Format of computed datasets, `compressed` to save disk space (experimental), `raw` to prioritize speed, default: `raw`, saved
|
||||||
#[arg(short, long, value_name = "FORMAT")]
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
|
#[arg(short, long)]
|
||||||
format: Option<Format>,
|
format: Option<Format>,
|
||||||
|
|
||||||
/// Activate fetching prices from exchanges APIs and the computation of all related datasets, default: true, saved
|
/// Activate fetching prices from exchanges APIs and the computation of all related datasets, default: true, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(short = 'F', long, value_name = "BOOL")]
|
#[arg(short = 'F', long, value_name = "BOOL")]
|
||||||
fetch: Option<bool>,
|
fetch: Option<bool>,
|
||||||
|
|
||||||
/// Website served by the server (if active), default: default, saved
|
/// Website served by the server (if active), default: default, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
website: Option<Website>,
|
website: Option<Website>,
|
||||||
|
|
||||||
/// Bitcoin RPC ip, default: localhost, saved
|
/// Bitcoin RPC ip, default: localhost, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "IP")]
|
#[arg(long, value_name = "IP")]
|
||||||
rpcconnect: Option<String>,
|
rpcconnect: Option<String>,
|
||||||
|
|
||||||
/// Bitcoin RPC port, default: 8332, saved
|
/// Bitcoin RPC port, default: 8332, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PORT")]
|
#[arg(long, value_name = "PORT")]
|
||||||
rpcport: Option<u16>,
|
rpcport: Option<u16>,
|
||||||
|
|
||||||
/// Bitcoin RPC cookie file, default: --bitcoindir/.cookie, saved
|
/// Bitcoin RPC cookie file, default: --bitcoindir/.cookie, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long, value_name = "PATH")]
|
||||||
rpccookiefile: Option<String>,
|
rpccookiefile: Option<String>,
|
||||||
|
|
||||||
/// Bitcoin RPC username, saved
|
/// Bitcoin RPC username, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "USERNAME")]
|
#[arg(long, value_name = "USERNAME")]
|
||||||
rpcuser: Option<String>,
|
rpcuser: Option<String>,
|
||||||
|
|
||||||
/// Bitcoin RPC password, saved
|
/// Bitcoin RPC password, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "PASSWORD")]
|
#[arg(long, value_name = "PASSWORD")]
|
||||||
rpcpassword: Option<String>,
|
rpcpassword: Option<String>,
|
||||||
|
|
||||||
/// Delay between runs, default: 0, saved
|
/// Delay between runs, default: 0, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "SECONDS")]
|
#[arg(long, value_name = "SECONDS")]
|
||||||
delay: Option<u64>,
|
delay: Option<u64>,
|
||||||
|
|
||||||
|
/// DEV: Activate to watch the selected website's folder for changes, default: false, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
|
#[arg(long, value_name = "BOOL")]
|
||||||
|
watch: Option<bool>,
|
||||||
|
|
||||||
/// DEV: Activate checking address hashes for collisions when indexing, default: false, saved
|
/// DEV: Activate checking address hashes for collisions when indexing, default: false, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "BOOL")]
|
#[arg(long, value_name = "BOOL")]
|
||||||
check_collisions: Option<bool>,
|
check_collisions: Option<bool>,
|
||||||
}
|
}
|
||||||
@@ -200,8 +213,8 @@ impl RunConfig {
|
|||||||
config_saved.brkdir = Some(brkdir);
|
config_saved.brkdir = Some(brkdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mode) = config_args.mode.take() {
|
if let Some(services) = config_args.services.take() {
|
||||||
config_saved.mode = Some(mode);
|
config_saved.services = Some(services);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(computation) = config_args.computation.take() {
|
if let Some(computation) = config_args.computation.take() {
|
||||||
@@ -248,6 +261,10 @@ impl RunConfig {
|
|||||||
config_saved.check_collisions = Some(check_collisions);
|
config_saved.check_collisions = Some(check_collisions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(watch) = config_args.watch.take() {
|
||||||
|
config_saved.watch = Some(watch);
|
||||||
|
}
|
||||||
|
|
||||||
if config_args != RunConfig::default() {
|
if config_args != RunConfig::default() {
|
||||||
dbg!(config_args);
|
dbg!(config_args);
|
||||||
panic!("Didn't consume the full config")
|
panic!("Didn't consume the full config")
|
||||||
@@ -260,19 +277,6 @@ impl RunConfig {
|
|||||||
|
|
||||||
config.write(&path)?;
|
config.write(&path)?;
|
||||||
|
|
||||||
// info!("Configuration {{");
|
|
||||||
// info!(" bitcoindir: {:?}", config.bitcoindir);
|
|
||||||
// info!(" brkdir: {:?}", config.brkdir);
|
|
||||||
// info!(" mode: {:?}", config.mode);
|
|
||||||
// info!(" website: {:?}", config.website);
|
|
||||||
// info!(" rpcconnect: {:?}", config.rpcconnect);
|
|
||||||
// info!(" rpcport: {:?}", config.rpcport);
|
|
||||||
// info!(" rpccookiefile: {:?}", config.rpccookiefile);
|
|
||||||
// info!(" rpcuser: {:?}", config.rpcuser);
|
|
||||||
// info!(" rpcpassword: {:?}", config.rpcpassword);
|
|
||||||
// info!(" delay: {:?}", config.delay);
|
|
||||||
// info!("}}");
|
|
||||||
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,13 +387,13 @@ impl RunConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&self) -> bool {
|
pub fn process(&self) -> bool {
|
||||||
self.mode
|
self.services
|
||||||
.is_none_or(|m| m == Mode::All || m == Mode::Processor)
|
.is_none_or(|m| m == Services::All || m == Services::Processor)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve(&self) -> bool {
|
pub fn serve(&self) -> bool {
|
||||||
self.mode
|
self.services
|
||||||
.is_none_or(|m| m == Mode::All || m == Mode::Server)
|
.is_none_or(|m| m == Services::All || m == Services::Server)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_cookiefile(&self) -> PathBuf {
|
fn path_cookiefile(&self) -> PathBuf {
|
||||||
@@ -441,6 +445,10 @@ impl RunConfig {
|
|||||||
pub fn check_collisions(&self) -> bool {
|
pub fn check_collisions(&self) -> bool {
|
||||||
self.check_collisions.is_some_and(|b| b)
|
self.check_collisions.is_some_and(|b| b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn watch(&self) -> bool {
|
||||||
|
self.watch.is_some_and(|b| b)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
@@ -457,7 +465,7 @@ impl RunConfig {
|
|||||||
PartialOrd,
|
PartialOrd,
|
||||||
Ord,
|
Ord,
|
||||||
)]
|
)]
|
||||||
pub enum Mode {
|
pub enum Services {
|
||||||
#[default]
|
#[default]
|
||||||
All,
|
All,
|
||||||
Processor,
|
Processor,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ pub fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let format = Format::Raw;
|
let format = Format::Raw;
|
||||||
|
|
||||||
let mut indexer = Indexer::new(outputs_dir, format, true)?;
|
let mut indexer = Indexer::new(outputs_dir, true)?;
|
||||||
indexer.import_stores()?;
|
indexer.import_stores()?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use brk_core::{
|
|||||||
use brk_exit::Exit;
|
use brk_exit::Exit;
|
||||||
use brk_fetcher::Fetcher;
|
use brk_fetcher::Fetcher;
|
||||||
use brk_indexer::Indexer;
|
use brk_indexer::Indexer;
|
||||||
use brk_vec::{AnyCollectableVec, AnyIterableVec, Computation, EagerVec, Format};
|
use brk_vec::{AnyCollectableVec, AnyIterableVec, Computation, EagerVec, Format, StoredIndex};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Indexes,
|
Indexes,
|
||||||
@@ -429,8 +429,18 @@ impl Vecs {
|
|||||||
self.dateindex_to_ohlc_in_cents.compute_transform(
|
self.dateindex_to_ohlc_in_cents.compute_transform(
|
||||||
starting_indexes.dateindex,
|
starting_indexes.dateindex,
|
||||||
&indexes.dateindex_to_date,
|
&indexes.dateindex_to_date,
|
||||||
|(di, d, ..)| {
|
|(di, d, this)| {
|
||||||
let ohlc = fetcher.get_date(d).unwrap();
|
let mut ohlc = fetcher.get_date(d).unwrap();
|
||||||
|
if let Some(prev) = di.decremented() {
|
||||||
|
let prev_open = *this
|
||||||
|
.get_or_read(prev, &this.mmap().load())
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.close;
|
||||||
|
*ohlc.open = prev_open;
|
||||||
|
*ohlc.high = (*ohlc.high).max(prev_open);
|
||||||
|
*ohlc.low = (*ohlc.low).min(prev_open);
|
||||||
|
}
|
||||||
(di, ohlc)
|
(di, ohlc)
|
||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
|
|||||||
@@ -130,7 +130,11 @@ where
|
|||||||
Box::new(
|
Box::new(
|
||||||
EagerVec::forced_import(
|
EagerVec::forced_import(
|
||||||
path,
|
path,
|
||||||
&maybe_suffix("sum"),
|
&(if !options.last {
|
||||||
|
name.to_string()
|
||||||
|
} else {
|
||||||
|
maybe_suffix("sum")
|
||||||
|
}),
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
format,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,8 +24,10 @@ pub struct ComputedRatioVecsFromDateIndex {
|
|||||||
pub ratio_1w_sma: ComputedVecsFromDateIndex<StoredF32>,
|
pub ratio_1w_sma: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
pub ratio_1m_sma: ComputedVecsFromDateIndex<StoredF32>,
|
pub ratio_1m_sma: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
pub ratio_1y_sma: ComputedVecsFromDateIndex<StoredF32>,
|
pub ratio_1y_sma: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
|
pub ratio_4y_sma: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
pub ratio_1y_sma_momentum_oscillator: ComputedVecsFromDateIndex<StoredF32>,
|
pub ratio_1y_sma_momentum_oscillator: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
pub ratio_standard_deviation: ComputedVecsFromDateIndex<StoredF32>,
|
pub ratio_sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
|
pub ratio_4y_sd: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
pub ratio_p99_9: ComputedVecsFromDateIndex<StoredF32>,
|
pub ratio_p99_9: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
pub ratio_p99_5: ComputedVecsFromDateIndex<StoredF32>,
|
pub ratio_p99_5: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
pub ratio_p99: ComputedVecsFromDateIndex<StoredF32>,
|
pub ratio_p99: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
@@ -51,6 +53,7 @@ pub struct ComputedRatioVecsFromDateIndex {
|
|||||||
pub ratio_m2sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
pub ratio_m2sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||||
pub ratio_m3sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
pub ratio_m3sd_as_price: ComputedVecsFromDateIndex<Dollars>,
|
||||||
pub ratio_zscore: ComputedVecsFromDateIndex<StoredF32>,
|
pub ratio_zscore: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
|
pub ratio_4y_zscore: ComputedVecsFromDateIndex<StoredF32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const VERSION: Version = Version::ZERO;
|
const VERSION: Version = Version::ZERO;
|
||||||
@@ -116,6 +119,14 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
format,
|
format,
|
||||||
options,
|
options,
|
||||||
)?,
|
)?,
|
||||||
|
ratio_4y_sma: ComputedVecsFromDateIndex::forced_import(
|
||||||
|
path,
|
||||||
|
&format!("{name}_ratio_4y_sma"),
|
||||||
|
true,
|
||||||
|
version + VERSION + Version::ZERO,
|
||||||
|
format,
|
||||||
|
options,
|
||||||
|
)?,
|
||||||
ratio_1y_sma_momentum_oscillator: ComputedVecsFromDateIndex::forced_import(
|
ratio_1y_sma_momentum_oscillator: ComputedVecsFromDateIndex::forced_import(
|
||||||
path,
|
path,
|
||||||
&format!("{name}_ratio_1y_sma_momentum_oscillator"),
|
&format!("{name}_ratio_1y_sma_momentum_oscillator"),
|
||||||
@@ -124,9 +135,17 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
format,
|
format,
|
||||||
options,
|
options,
|
||||||
)?,
|
)?,
|
||||||
ratio_standard_deviation: ComputedVecsFromDateIndex::forced_import(
|
ratio_sd: ComputedVecsFromDateIndex::forced_import(
|
||||||
path,
|
path,
|
||||||
&format!("{name}_ratio_standard_deviation"),
|
&format!("{name}_ratio_sd"),
|
||||||
|
true,
|
||||||
|
version + VERSION + Version::ZERO,
|
||||||
|
format,
|
||||||
|
options,
|
||||||
|
)?,
|
||||||
|
ratio_4y_sd: ComputedVecsFromDateIndex::forced_import(
|
||||||
|
path,
|
||||||
|
&format!("{name}_ratio_4y_sd"),
|
||||||
true,
|
true,
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
format,
|
||||||
@@ -332,6 +351,14 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
format,
|
format,
|
||||||
options,
|
options,
|
||||||
)?,
|
)?,
|
||||||
|
ratio_4y_zscore: ComputedVecsFromDateIndex::forced_import(
|
||||||
|
path,
|
||||||
|
&format!("{name}_ratio_4y_zscore"),
|
||||||
|
true,
|
||||||
|
version + VERSION + Version::ZERO,
|
||||||
|
format,
|
||||||
|
options,
|
||||||
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,6 +503,22 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
self.ratio_4y_sma.compute_all(
|
||||||
|
indexer,
|
||||||
|
indexes,
|
||||||
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
|v, _, _, starting_indexes, exit| {
|
||||||
|
v.compute_sma_(
|
||||||
|
starting_indexes.dateindex,
|
||||||
|
self.ratio.dateindex.as_ref().unwrap(),
|
||||||
|
4 * 365,
|
||||||
|
exit,
|
||||||
|
Some(min_ratio_date),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
self.ratio_1y_sma_momentum_oscillator.compute_all(
|
self.ratio_1y_sma_momentum_oscillator.compute_all(
|
||||||
indexer,
|
indexer,
|
||||||
indexes,
|
indexes,
|
||||||
@@ -528,6 +571,8 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
|
|
||||||
let mut sma_iter = self.ratio_sma.dateindex.as_ref().unwrap().into_iter();
|
let mut sma_iter = self.ratio_sma.dateindex.as_ref().unwrap().into_iter();
|
||||||
|
|
||||||
|
let mut _4y_sma_iter = self.ratio_4y_sma.dateindex.as_ref().unwrap().into_iter();
|
||||||
|
|
||||||
let nan = StoredF32::from(f32::NAN);
|
let nan = StoredF32::from(f32::NAN);
|
||||||
self.ratio
|
self.ratio
|
||||||
.dateindex
|
.dateindex
|
||||||
@@ -566,7 +611,12 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.forced_push_at(index, nan, exit)?;
|
.forced_push_at(index, nan, exit)?;
|
||||||
self.ratio_standard_deviation
|
self.ratio_sd
|
||||||
|
.dateindex
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.forced_push_at(index, nan, exit)?;
|
||||||
|
self.ratio_4y_sd
|
||||||
.dateindex
|
.dateindex
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -645,12 +695,26 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
.sqrt(),
|
.sqrt(),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.ratio_standard_deviation
|
self.ratio_sd
|
||||||
.dateindex
|
.dateindex
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.forced_push_at(index, sd, exit)?;
|
.forced_push_at(index, sd, exit)?;
|
||||||
|
|
||||||
|
let _4y_avg = _4y_sma_iter.unwrap_get_inner(index);
|
||||||
|
|
||||||
|
let _4y_sd = StoredF32::from(
|
||||||
|
(sorted.iter().map(|v| (**v - *_4y_avg).powi(2)).sum::<f32>()
|
||||||
|
/ (index.unwrap_to_usize() + 1) as f32)
|
||||||
|
.sqrt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.ratio_4y_sd
|
||||||
|
.dateindex
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.forced_push_at(index, _4y_sd, exit)?;
|
||||||
|
|
||||||
self.ratio_p1sd.dateindex.as_mut().unwrap().forced_push_at(
|
self.ratio_p1sd.dateindex.as_mut().unwrap().forced_push_at(
|
||||||
index,
|
index,
|
||||||
avg + sd,
|
avg + sd,
|
||||||
@@ -726,7 +790,13 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
exit,
|
exit,
|
||||||
None as Option<&EagerVec<_, _>>,
|
None as Option<&EagerVec<_, _>>,
|
||||||
)?;
|
)?;
|
||||||
self.ratio_standard_deviation.compute_rest(
|
self.ratio_sd.compute_rest(
|
||||||
|
indexes,
|
||||||
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
None as Option<&EagerVec<_, _>>,
|
||||||
|
)?;
|
||||||
|
self.ratio_4y_sd.compute_rest(
|
||||||
indexes,
|
indexes,
|
||||||
starting_indexes,
|
starting_indexes,
|
||||||
exit,
|
exit,
|
||||||
@@ -1007,21 +1077,27 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
starting_indexes,
|
starting_indexes,
|
||||||
exit,
|
exit,
|
||||||
|vec, _, _, starting_indexes, exit| {
|
|vec, _, _, starting_indexes, exit| {
|
||||||
let mut sma_iter = self.ratio_sma.dateindex.as_ref().unwrap().into_iter();
|
vec.compute_zscore(
|
||||||
let mut sd_iter = self
|
|
||||||
.ratio_standard_deviation
|
|
||||||
.dateindex
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.into_iter();
|
|
||||||
vec.compute_transform(
|
|
||||||
starting_indexes.dateindex,
|
starting_indexes.dateindex,
|
||||||
self.ratio.dateindex.as_ref().unwrap(),
|
self.ratio.dateindex.as_ref().unwrap(),
|
||||||
|(i, ratio, ..)| {
|
self.ratio_sma.dateindex.as_ref().unwrap(),
|
||||||
let sma = sma_iter.unwrap_get_inner(i);
|
self.ratio_sd.dateindex.as_ref().unwrap(),
|
||||||
let sd = sd_iter.unwrap_get_inner(i);
|
exit,
|
||||||
(i, (ratio - sma) / sd)
|
)
|
||||||
},
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.ratio_4y_zscore.compute_all(
|
||||||
|
indexer,
|
||||||
|
indexes,
|
||||||
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
|vec, _, _, starting_indexes, exit| {
|
||||||
|
vec.compute_zscore(
|
||||||
|
starting_indexes.dateindex,
|
||||||
|
self.ratio.dateindex.as_ref().unwrap(),
|
||||||
|
self.ratio_4y_sma.dateindex.as_ref().unwrap(),
|
||||||
|
self.ratio_4y_sd.dateindex.as_ref().unwrap(),
|
||||||
exit,
|
exit,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -1032,7 +1108,8 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
|
|
||||||
fn mut_ratio_vecs(&mut self) -> Vec<&mut EagerVec<DateIndex, StoredF32>> {
|
fn mut_ratio_vecs(&mut self) -> Vec<&mut EagerVec<DateIndex, StoredF32>> {
|
||||||
vec![
|
vec![
|
||||||
self.ratio_standard_deviation.dateindex.as_mut().unwrap(),
|
self.ratio_sd.dateindex.as_mut().unwrap(),
|
||||||
|
self.ratio_4y_sd.dateindex.as_mut().unwrap(),
|
||||||
self.ratio_p99_9.dateindex.as_mut().unwrap(),
|
self.ratio_p99_9.dateindex.as_mut().unwrap(),
|
||||||
self.ratio_p99_5.dateindex.as_mut().unwrap(),
|
self.ratio_p99_5.dateindex.as_mut().unwrap(),
|
||||||
self.ratio_p99.dateindex.as_mut().unwrap(),
|
self.ratio_p99.dateindex.as_mut().unwrap(),
|
||||||
@@ -1056,8 +1133,10 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
self.ratio_1w_sma.vecs(),
|
self.ratio_1w_sma.vecs(),
|
||||||
self.ratio_1m_sma.vecs(),
|
self.ratio_1m_sma.vecs(),
|
||||||
self.ratio_1y_sma.vecs(),
|
self.ratio_1y_sma.vecs(),
|
||||||
|
self.ratio_4y_sma.vecs(),
|
||||||
self.ratio_1y_sma_momentum_oscillator.vecs(),
|
self.ratio_1y_sma_momentum_oscillator.vecs(),
|
||||||
self.ratio_standard_deviation.vecs(),
|
self.ratio_sd.vecs(),
|
||||||
|
self.ratio_4y_sd.vecs(),
|
||||||
self.ratio_p99_9.vecs(),
|
self.ratio_p99_9.vecs(),
|
||||||
self.ratio_p99_5.vecs(),
|
self.ratio_p99_5.vecs(),
|
||||||
self.ratio_p99.vecs(),
|
self.ratio_p99.vecs(),
|
||||||
@@ -1083,6 +1162,7 @@ impl ComputedRatioVecsFromDateIndex {
|
|||||||
self.ratio_m2sd_as_price.vecs(),
|
self.ratio_m2sd_as_price.vecs(),
|
||||||
self.ratio_m3sd_as_price.vecs(),
|
self.ratio_m3sd_as_price.vecs(),
|
||||||
self.ratio_zscore.vecs(),
|
self.ratio_zscore.vecs(),
|
||||||
|
self.ratio_4y_zscore.vecs(),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
|
|||||||
@@ -226,7 +226,11 @@ impl ComputedValueVecsFromTxindex {
|
|||||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||||
[
|
[
|
||||||
self.sats.vecs(),
|
self.sats.vecs(),
|
||||||
|
vec![&self.bitcoin_txindex as &dyn AnyCollectableVec],
|
||||||
self.bitcoin.vecs(),
|
self.bitcoin.vecs(),
|
||||||
|
self.dollars_txindex
|
||||||
|
.as_ref()
|
||||||
|
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
|
||||||
self.dollars.as_ref().map_or(vec![], |v| v.vecs()),
|
self.dollars.as_ref().map_or(vec![], |v| v.vecs()),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
@@ -36,11 +36,15 @@ pub struct Vecs {
|
|||||||
pub indexes_to_55d_sma: ComputedRatioVecsFromDateIndex,
|
pub indexes_to_55d_sma: ComputedRatioVecsFromDateIndex,
|
||||||
pub indexes_to_89d_sma: ComputedRatioVecsFromDateIndex,
|
pub indexes_to_89d_sma: ComputedRatioVecsFromDateIndex,
|
||||||
pub indexes_to_144d_sma: ComputedRatioVecsFromDateIndex,
|
pub indexes_to_144d_sma: ComputedRatioVecsFromDateIndex,
|
||||||
|
pub indexes_to_200d_sma: ComputedRatioVecsFromDateIndex,
|
||||||
pub indexes_to_1y_sma: ComputedRatioVecsFromDateIndex,
|
pub indexes_to_1y_sma: ComputedRatioVecsFromDateIndex,
|
||||||
pub indexes_to_2y_sma: ComputedRatioVecsFromDateIndex,
|
pub indexes_to_2y_sma: ComputedRatioVecsFromDateIndex,
|
||||||
pub indexes_to_200w_sma: ComputedRatioVecsFromDateIndex,
|
pub indexes_to_200w_sma: ComputedRatioVecsFromDateIndex,
|
||||||
pub indexes_to_4y_sma: ComputedRatioVecsFromDateIndex,
|
pub indexes_to_4y_sma: ComputedRatioVecsFromDateIndex,
|
||||||
|
|
||||||
|
pub indexes_to_200d_sma_x2_4: ComputedVecsFromDateIndex<Dollars>,
|
||||||
|
pub indexes_to_200d_sma_x0_8: ComputedVecsFromDateIndex<Dollars>,
|
||||||
|
|
||||||
pub price_1d_ago: ComputedVecsFromDateIndex<Dollars>,
|
pub price_1d_ago: ComputedVecsFromDateIndex<Dollars>,
|
||||||
pub price_1w_ago: ComputedVecsFromDateIndex<Dollars>,
|
pub price_1w_ago: ComputedVecsFromDateIndex<Dollars>,
|
||||||
pub price_1m_ago: ComputedVecsFromDateIndex<Dollars>,
|
pub price_1m_ago: ComputedVecsFromDateIndex<Dollars>,
|
||||||
@@ -306,6 +310,14 @@ impl Vecs {
|
|||||||
format,
|
format,
|
||||||
StorableVecGeneatorOptions::default().add_last(),
|
StorableVecGeneatorOptions::default().add_last(),
|
||||||
)?,
|
)?,
|
||||||
|
indexes_to_200d_sma: ComputedRatioVecsFromDateIndex::forced_import(
|
||||||
|
path,
|
||||||
|
"200d_sma",
|
||||||
|
true,
|
||||||
|
version + VERSION + Version::ZERO,
|
||||||
|
format,
|
||||||
|
StorableVecGeneatorOptions::default().add_last(),
|
||||||
|
)?,
|
||||||
indexes_to_1y_sma: ComputedRatioVecsFromDateIndex::forced_import(
|
indexes_to_1y_sma: ComputedRatioVecsFromDateIndex::forced_import(
|
||||||
path,
|
path,
|
||||||
"1y_sma",
|
"1y_sma",
|
||||||
@@ -1215,6 +1227,23 @@ impl Vecs {
|
|||||||
format,
|
format,
|
||||||
StorableVecGeneatorOptions::default().add_last(),
|
StorableVecGeneatorOptions::default().add_last(),
|
||||||
)?,
|
)?,
|
||||||
|
|
||||||
|
indexes_to_200d_sma_x2_4: ComputedVecsFromDateIndex::forced_import(
|
||||||
|
path,
|
||||||
|
"200d_sma_x2_4",
|
||||||
|
true,
|
||||||
|
version + VERSION + Version::ZERO,
|
||||||
|
format,
|
||||||
|
StorableVecGeneatorOptions::default().add_last(),
|
||||||
|
)?,
|
||||||
|
indexes_to_200d_sma_x0_8: ComputedVecsFromDateIndex::forced_import(
|
||||||
|
path,
|
||||||
|
"200d_sma_x0_8",
|
||||||
|
true,
|
||||||
|
version + VERSION + Version::ZERO,
|
||||||
|
format,
|
||||||
|
StorableVecGeneatorOptions::default().add_last(),
|
||||||
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1771,6 +1800,7 @@ impl Vecs {
|
|||||||
(&mut self.indexes_to_55d_sma, 55),
|
(&mut self.indexes_to_55d_sma, 55),
|
||||||
(&mut self.indexes_to_89d_sma, 89),
|
(&mut self.indexes_to_89d_sma, 89),
|
||||||
(&mut self.indexes_to_144d_sma, 144),
|
(&mut self.indexes_to_144d_sma, 144),
|
||||||
|
(&mut self.indexes_to_200d_sma, 200),
|
||||||
(&mut self.indexes_to_1y_sma, 365),
|
(&mut self.indexes_to_1y_sma, 365),
|
||||||
(&mut self.indexes_to_2y_sma, 2 * 365),
|
(&mut self.indexes_to_2y_sma, 2 * 365),
|
||||||
(&mut self.indexes_to_200w_sma, 200 * 7),
|
(&mut self.indexes_to_200w_sma, 200 * 7),
|
||||||
@@ -1797,7 +1827,51 @@ impl Vecs {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})?;
|
||||||
|
|
||||||
|
self.indexes_to_200d_sma_x0_8.compute_all(
|
||||||
|
indexer,
|
||||||
|
indexes,
|
||||||
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
|v, _, _, starting_indexes, exit| {
|
||||||
|
v.compute_transform(
|
||||||
|
starting_indexes.dateindex,
|
||||||
|
self.indexes_to_200d_sma
|
||||||
|
.price
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.dateindex
|
||||||
|
.as_ref()
|
||||||
|
.unwrap(),
|
||||||
|
|(i, v, ..)| (i, v * 0.8),
|
||||||
|
exit,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.indexes_to_200d_sma_x2_4.compute_all(
|
||||||
|
indexer,
|
||||||
|
indexes,
|
||||||
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
|v, _, _, starting_indexes, exit| {
|
||||||
|
v.compute_transform(
|
||||||
|
starting_indexes.dateindex,
|
||||||
|
self.indexes_to_200d_sma
|
||||||
|
.price
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.dateindex
|
||||||
|
.as_ref()
|
||||||
|
.unwrap(),
|
||||||
|
|(i, v, ..)| (i, v * 2.4),
|
||||||
|
exit,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
|
||||||
@@ -1817,10 +1891,13 @@ impl Vecs {
|
|||||||
self.indexes_to_55d_sma.vecs(),
|
self.indexes_to_55d_sma.vecs(),
|
||||||
self.indexes_to_89d_sma.vecs(),
|
self.indexes_to_89d_sma.vecs(),
|
||||||
self.indexes_to_144d_sma.vecs(),
|
self.indexes_to_144d_sma.vecs(),
|
||||||
|
self.indexes_to_200d_sma.vecs(),
|
||||||
self.indexes_to_1y_sma.vecs(),
|
self.indexes_to_1y_sma.vecs(),
|
||||||
self.indexes_to_2y_sma.vecs(),
|
self.indexes_to_2y_sma.vecs(),
|
||||||
self.indexes_to_200w_sma.vecs(),
|
self.indexes_to_200w_sma.vecs(),
|
||||||
self.indexes_to_4y_sma.vecs(),
|
self.indexes_to_4y_sma.vecs(),
|
||||||
|
self.indexes_to_200d_sma_x0_8.vecs(),
|
||||||
|
self.indexes_to_200d_sma_x2_4.vecs(),
|
||||||
self.price_1d_ago.vecs(),
|
self.price_1d_ago.vecs(),
|
||||||
self.price_1w_ago.vecs(),
|
self.price_1w_ago.vecs(),
|
||||||
self.price_1m_ago.vecs(),
|
self.price_1m_ago.vecs(),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::{fs, path::Path};
|
use std::{fs, path::Path, thread};
|
||||||
|
|
||||||
use brk_core::Version;
|
use brk_core::Version;
|
||||||
use brk_exit::Exit;
|
use brk_exit::Exit;
|
||||||
@@ -44,22 +44,31 @@ impl Vecs {
|
|||||||
) -> color_eyre::Result<Self> {
|
) -> color_eyre::Result<Self> {
|
||||||
fs::create_dir_all(path)?;
|
fs::create_dir_all(path)?;
|
||||||
|
|
||||||
let indexes = indexes::Vecs::forced_import(
|
let (indexes, fetched) = thread::scope(|s| {
|
||||||
path,
|
let indexes_handle = s.spawn(|| {
|
||||||
version + VERSION + Version::ZERO,
|
indexes::Vecs::forced_import(
|
||||||
indexer,
|
path,
|
||||||
computation,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
indexer,
|
||||||
)?;
|
computation,
|
||||||
|
format,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
let fetched = fetch.then(|| {
|
let fetch_handle = s.spawn(|| {
|
||||||
fetched::Vecs::forced_import(
|
fetch.then(|| {
|
||||||
path,
|
fetched::Vecs::forced_import(
|
||||||
version + VERSION + Version::ZERO,
|
path,
|
||||||
computation,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
computation,
|
||||||
)
|
format,
|
||||||
.unwrap()
|
)
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
(indexes_handle.join().unwrap(), fetch_handle.join().unwrap())
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -115,6 +124,7 @@ impl Vecs {
|
|||||||
fetcher: Option<&mut Fetcher>,
|
fetcher: Option<&mut Fetcher>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> color_eyre::Result<()> {
|
) -> color_eyre::Result<()> {
|
||||||
|
info!("Computing indexes...");
|
||||||
let starting_indexes = self.indexes.compute(indexer, starting_indexes, exit)?;
|
let starting_indexes = self.indexes.compute(indexer, starting_indexes, exit)?;
|
||||||
|
|
||||||
info!("Computing constants...");
|
info!("Computing constants...");
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ pub struct Vecs {
|
|||||||
pub indexes_to_coinblocks_destroyed: ComputedVecsFromHeight<StoredF64>,
|
pub indexes_to_coinblocks_destroyed: ComputedVecsFromHeight<StoredF64>,
|
||||||
pub indexes_to_coindays_destroyed: ComputedVecsFromHeight<StoredF64>,
|
pub indexes_to_coindays_destroyed: ComputedVecsFromHeight<StoredF64>,
|
||||||
pub dateindex_to_adjusted_spent_output_profit_ratio: Option<EagerVec<DateIndex, StoredF32>>,
|
pub dateindex_to_adjusted_spent_output_profit_ratio: Option<EagerVec<DateIndex, StoredF32>>,
|
||||||
pub dateindex_to_realized_cap_30d_change: Option<EagerVec<DateIndex, Dollars>>,
|
pub indexes_to_realized_cap_30d_change: Option<ComputedVecsFromDateIndex<Dollars>>,
|
||||||
pub dateindex_to_sell_side_risk_ratio: Option<EagerVec<DateIndex, StoredF32>>,
|
pub dateindex_to_sell_side_risk_ratio: Option<EagerVec<DateIndex, StoredF32>>,
|
||||||
pub dateindex_to_spent_output_profit_ratio: Option<EagerVec<DateIndex, StoredF32>>,
|
pub dateindex_to_spent_output_profit_ratio: Option<EagerVec<DateIndex, StoredF32>>,
|
||||||
pub indexes_to_adjusted_value_created: Option<ComputedVecsFromHeight<Dollars>>,
|
pub indexes_to_adjusted_value_created: Option<ComputedVecsFromHeight<Dollars>>,
|
||||||
@@ -89,6 +89,10 @@ pub struct Vecs {
|
|||||||
Option<EagerVec<Height, StoredF32>>,
|
Option<EagerVec<Height, StoredF32>>,
|
||||||
pub indexes_to_net_unrealized_profit_and_loss_relative_to_market_cap:
|
pub indexes_to_net_unrealized_profit_and_loss_relative_to_market_cap:
|
||||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||||
|
pub indexes_to_realized_profit_relative_to_realized_cap:
|
||||||
|
Option<ComputedVecsFromHeight<StoredF32>>,
|
||||||
|
pub indexes_to_realized_loss_relative_to_realized_cap:
|
||||||
|
Option<ComputedVecsFromHeight<StoredF32>>,
|
||||||
pub indexes_to_net_realized_profit_and_loss_relative_to_realized_cap:
|
pub indexes_to_net_realized_profit_and_loss_relative_to_realized_cap:
|
||||||
Option<ComputedVecsFromHeight<StoredF32>>,
|
Option<ComputedVecsFromHeight<StoredF32>>,
|
||||||
pub height_to_supply_even_value: Option<ComputedHeightValueVecs>,
|
pub height_to_supply_even_value: Option<ComputedHeightValueVecs>,
|
||||||
@@ -117,6 +121,12 @@ pub struct Vecs {
|
|||||||
Option<ComputedVecsFromDateIndex<StoredF64>>,
|
Option<ComputedVecsFromDateIndex<StoredF64>>,
|
||||||
pub indexes_to_supply_in_profit_relative_to_circulating_supply:
|
pub indexes_to_supply_in_profit_relative_to_circulating_supply:
|
||||||
Option<ComputedVecsFromDateIndex<StoredF64>>,
|
Option<ComputedVecsFromDateIndex<StoredF64>>,
|
||||||
|
pub indexes_to_cumulative_net_realized_profit_and_loss_30d_change:
|
||||||
|
Option<ComputedVecsFromDateIndex<Dollars>>,
|
||||||
|
pub indexes_to_cumulative_net_realized_profit_and_loss_30d_change_relative_to_realized_cap:
|
||||||
|
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||||
|
pub indexes_to_cumulative_net_realized_profit_and_loss_30d_change_relative_to_market_cap:
|
||||||
|
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vecs {
|
impl Vecs {
|
||||||
@@ -432,7 +442,9 @@ impl Vecs {
|
|||||||
false,
|
false,
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
format,
|
||||||
StorableVecGeneatorOptions::default().add_sum(),
|
StorableVecGeneatorOptions::default()
|
||||||
|
.add_sum()
|
||||||
|
.add_cumulative(),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}),
|
}),
|
||||||
@@ -452,7 +464,9 @@ impl Vecs {
|
|||||||
false,
|
false,
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
format,
|
||||||
StorableVecGeneatorOptions::default().add_sum(),
|
StorableVecGeneatorOptions::default()
|
||||||
|
.add_sum()
|
||||||
|
.add_cumulative(),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}),
|
}),
|
||||||
@@ -463,7 +477,7 @@ impl Vecs {
|
|||||||
true,
|
true,
|
||||||
version + VERSION + Version::ONE,
|
version + VERSION + Version::ONE,
|
||||||
format,
|
format,
|
||||||
StorableVecGeneatorOptions::default().add_sum(),
|
StorableVecGeneatorOptions::default().add_sum().add_cumulative(),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}),
|
}),
|
||||||
@@ -558,12 +572,14 @@ impl Vecs {
|
|||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}),
|
}),
|
||||||
dateindex_to_realized_cap_30d_change: compute_dollars.then(|| {
|
indexes_to_realized_cap_30d_change: compute_dollars.then(|| {
|
||||||
EagerVec::forced_import(
|
ComputedVecsFromDateIndex::forced_import(
|
||||||
path,
|
path,
|
||||||
&suffix("realized_cap_30d_change"),
|
&suffix("realized_cap_30d_change"),
|
||||||
|
true,
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
format,
|
||||||
|
StorableVecGeneatorOptions::default().add_last(),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}),
|
}),
|
||||||
@@ -574,7 +590,9 @@ impl Vecs {
|
|||||||
true,
|
true,
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
format,
|
||||||
StorableVecGeneatorOptions::default().add_sum(),
|
StorableVecGeneatorOptions::default()
|
||||||
|
.add_sum()
|
||||||
|
.add_cumulative(),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}),
|
}),
|
||||||
@@ -686,15 +704,37 @@ impl Vecs {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
indexes_to_realized_profit_relative_to_realized_cap: compute_dollars.then(|| {
|
||||||
|
ComputedVecsFromHeight::forced_import(
|
||||||
|
path,
|
||||||
|
&suffix("realized_profit_relative_to_realized_cap"),
|
||||||
|
true,
|
||||||
|
version + VERSION + Version::ZERO,
|
||||||
|
format,
|
||||||
|
StorableVecGeneatorOptions::default().add_sum(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}),
|
||||||
|
indexes_to_realized_loss_relative_to_realized_cap: compute_dollars.then(|| {
|
||||||
|
ComputedVecsFromHeight::forced_import(
|
||||||
|
path,
|
||||||
|
&suffix("realized_loss_relative_to_realized_cap"),
|
||||||
|
true,
|
||||||
|
version + VERSION + Version::ZERO,
|
||||||
|
format,
|
||||||
|
StorableVecGeneatorOptions::default().add_sum(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}),
|
||||||
indexes_to_net_realized_profit_and_loss_relative_to_realized_cap: compute_dollars.then(
|
indexes_to_net_realized_profit_and_loss_relative_to_realized_cap: compute_dollars.then(
|
||||||
|| {
|
|| {
|
||||||
ComputedVecsFromHeight::forced_import(
|
ComputedVecsFromHeight::forced_import(
|
||||||
path,
|
path,
|
||||||
&suffix("net_realized_profit_and_loss_relative_to_realized_cap"),
|
&suffix("net_realized_profit_and_loss_relative_to_realized_cap"),
|
||||||
true,
|
true,
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ONE,
|
||||||
format,
|
format,
|
||||||
StorableVecGeneatorOptions::default().add_last(),
|
StorableVecGeneatorOptions::default().add_sum(),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
},
|
},
|
||||||
@@ -903,6 +943,39 @@ impl Vecs {
|
|||||||
format,
|
format,
|
||||||
StorableVecGeneatorOptions::default().add_sum(),
|
StorableVecGeneatorOptions::default().add_sum(),
|
||||||
)?,
|
)?,
|
||||||
|
indexes_to_cumulative_net_realized_profit_and_loss_30d_change: compute_dollars.then(|| {
|
||||||
|
ComputedVecsFromDateIndex::forced_import(
|
||||||
|
path,
|
||||||
|
&format!("cumulative_{}", suffix("net_realized_profit_and_loss_30d_change")),
|
||||||
|
true,
|
||||||
|
version + VERSION + Version::TWO,
|
||||||
|
format,
|
||||||
|
StorableVecGeneatorOptions::default().add_last()
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}),
|
||||||
|
indexes_to_cumulative_net_realized_profit_and_loss_30d_change_relative_to_realized_cap: compute_dollars.then(|| {
|
||||||
|
ComputedVecsFromDateIndex::forced_import(
|
||||||
|
path,
|
||||||
|
&format!("cumulative_{}", suffix("net_realized_profit_and_loss_30d_change_relative_to_realized_cap")),
|
||||||
|
true,
|
||||||
|
version + VERSION + Version::TWO,
|
||||||
|
format,
|
||||||
|
StorableVecGeneatorOptions::default().add_last()
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}),
|
||||||
|
indexes_to_cumulative_net_realized_profit_and_loss_30d_change_relative_to_market_cap: compute_dollars.then(|| {
|
||||||
|
ComputedVecsFromDateIndex::forced_import(
|
||||||
|
path,
|
||||||
|
&format!("cumulative_{}", suffix("net_realized_profit_and_loss_30d_change_relative_to_market_cap")),
|
||||||
|
true,
|
||||||
|
version + VERSION + Version::TWO,
|
||||||
|
format,
|
||||||
|
StorableVecGeneatorOptions::default().add_last()
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1877,6 +1950,7 @@ impl Vecs {
|
|||||||
height_to_supply: &impl AnyIterableVec<Height, Bitcoin>,
|
height_to_supply: &impl AnyIterableVec<Height, Bitcoin>,
|
||||||
dateindex_to_supply: &impl AnyIterableVec<DateIndex, Bitcoin>,
|
dateindex_to_supply: &impl AnyIterableVec<DateIndex, Bitcoin>,
|
||||||
height_to_realized_cap: Option<&impl AnyIterableVec<Height, Dollars>>,
|
height_to_realized_cap: Option<&impl AnyIterableVec<Height, Dollars>>,
|
||||||
|
dateindex_to_realized_cap: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> color_eyre::Result<()> {
|
) -> color_eyre::Result<()> {
|
||||||
if let Some(v) = self
|
if let Some(v) = self
|
||||||
@@ -2021,18 +2095,26 @@ impl Vecs {
|
|||||||
Some(self.height_to_adjusted_value_destroyed.as_ref().unwrap()),
|
Some(self.height_to_adjusted_value_destroyed.as_ref().unwrap()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.dateindex_to_realized_cap_30d_change
|
self.indexes_to_realized_cap_30d_change
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.compute_change(
|
.compute_all(
|
||||||
starting_indexes.dateindex,
|
indexer,
|
||||||
self.indexes_to_realized_cap
|
indexes,
|
||||||
.as_ref()
|
starting_indexes,
|
||||||
.unwrap()
|
|
||||||
.dateindex
|
|
||||||
.unwrap_last(),
|
|
||||||
30,
|
|
||||||
exit,
|
exit,
|
||||||
|
|vec, _, _, starting_indexes, exit| {
|
||||||
|
vec.compute_change(
|
||||||
|
starting_indexes.dateindex,
|
||||||
|
self.indexes_to_realized_cap
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.dateindex
|
||||||
|
.unwrap_last(),
|
||||||
|
30,
|
||||||
|
exit,
|
||||||
|
)
|
||||||
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.indexes_to_net_realized_profit_and_loss
|
self.indexes_to_net_realized_profit_and_loss
|
||||||
@@ -2280,6 +2362,42 @@ impl Vecs {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
self.indexes_to_realized_profit_relative_to_realized_cap
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.compute_all(
|
||||||
|
indexer,
|
||||||
|
indexes,
|
||||||
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
|vec, _, _, starting_indexes, exit| {
|
||||||
|
vec.compute_percentage(
|
||||||
|
starting_indexes.height,
|
||||||
|
self.height_to_realized_profit.as_ref().unwrap(),
|
||||||
|
*height_to_realized_cap.as_ref().unwrap(),
|
||||||
|
exit,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.indexes_to_realized_loss_relative_to_realized_cap
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.compute_all(
|
||||||
|
indexer,
|
||||||
|
indexes,
|
||||||
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
|vec, _, _, starting_indexes, exit| {
|
||||||
|
vec.compute_percentage(
|
||||||
|
starting_indexes.height,
|
||||||
|
self.height_to_realized_loss.as_ref().unwrap(),
|
||||||
|
*height_to_realized_cap.as_ref().unwrap(),
|
||||||
|
exit,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
self.indexes_to_net_realized_profit_and_loss_relative_to_realized_cap
|
self.indexes_to_net_realized_profit_and_loss_relative_to_realized_cap
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -2435,6 +2553,64 @@ impl Vecs {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
self.indexes_to_cumulative_net_realized_profit_and_loss_30d_change
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.compute_all(
|
||||||
|
indexer,
|
||||||
|
indexes,
|
||||||
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
|v, _, _, starting_indexes, exit| {
|
||||||
|
v.compute_change(
|
||||||
|
starting_indexes.dateindex,
|
||||||
|
self.indexes_to_net_realized_profit_and_loss
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.dateindex
|
||||||
|
.unwrap_cumulative(),
|
||||||
|
30,
|
||||||
|
exit,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.indexes_to_cumulative_net_realized_profit_and_loss_30d_change_relative_to_realized_cap.
|
||||||
|
as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.compute_all(
|
||||||
|
indexer,
|
||||||
|
indexes,
|
||||||
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
|v, _, _, starting_indexes, exit| {
|
||||||
|
v.compute_percentage(
|
||||||
|
starting_indexes.dateindex,
|
||||||
|
self.indexes_to_cumulative_net_realized_profit_and_loss_30d_change.as_ref().unwrap().dateindex.as_ref().unwrap(),
|
||||||
|
*dateindex_to_realized_cap.as_ref().unwrap(),
|
||||||
|
exit,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.indexes_to_cumulative_net_realized_profit_and_loss_30d_change_relative_to_market_cap.
|
||||||
|
as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.compute_all(
|
||||||
|
indexer,
|
||||||
|
indexes,
|
||||||
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
|v, _, _, starting_indexes, exit| {
|
||||||
|
v.compute_percentage(
|
||||||
|
starting_indexes.dateindex,
|
||||||
|
self.indexes_to_cumulative_net_realized_profit_and_loss_30d_change.as_ref().unwrap().dateindex.as_ref().unwrap(),
|
||||||
|
market.indexes_to_marketcap.dateindex.as_ref().unwrap(),
|
||||||
|
exit,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
if let Some(height_to_supply_even_relative_to_circulating_supply) = self
|
if let Some(height_to_supply_even_relative_to_circulating_supply) = self
|
||||||
.height_to_supply_even_relative_to_circulating_supply
|
.height_to_supply_even_relative_to_circulating_supply
|
||||||
.as_mut()
|
.as_mut()
|
||||||
@@ -2618,9 +2794,9 @@ impl Vecs {
|
|||||||
self.indexes_to_adjusted_value_destroyed
|
self.indexes_to_adjusted_value_destroyed
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(vec![], |v| v.vecs()),
|
.map_or(vec![], |v| v.vecs()),
|
||||||
self.dateindex_to_realized_cap_30d_change
|
self.indexes_to_realized_cap_30d_change
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(vec![], |v| vec![v]),
|
.map_or(vec![], |v| v.vecs()),
|
||||||
self.indexes_to_net_realized_profit_and_loss
|
self.indexes_to_net_realized_profit_and_loss
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(vec![], |v| v.vecs()),
|
.map_or(vec![], |v| v.vecs()),
|
||||||
@@ -2703,6 +2879,12 @@ impl Vecs {
|
|||||||
self.indexes_to_net_unrealized_profit_and_loss_relative_to_market_cap
|
self.indexes_to_net_unrealized_profit_and_loss_relative_to_market_cap
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(vec![], |v| v.vecs()),
|
.map_or(vec![], |v| v.vecs()),
|
||||||
|
self.indexes_to_realized_profit_relative_to_realized_cap
|
||||||
|
.as_ref()
|
||||||
|
.map_or(vec![], |v| v.vecs()),
|
||||||
|
self.indexes_to_realized_loss_relative_to_realized_cap
|
||||||
|
.as_ref()
|
||||||
|
.map_or(vec![], |v| v.vecs()),
|
||||||
self.indexes_to_net_realized_profit_and_loss_relative_to_realized_cap
|
self.indexes_to_net_realized_profit_and_loss_relative_to_realized_cap
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(vec![], |v| v.vecs()),
|
.map_or(vec![], |v| v.vecs()),
|
||||||
@@ -2756,6 +2938,12 @@ impl Vecs {
|
|||||||
.map_or(vec![], |v| v.vecs()),
|
.map_or(vec![], |v| v.vecs()),
|
||||||
self.indexes_to_coinblocks_destroyed.vecs(),
|
self.indexes_to_coinblocks_destroyed.vecs(),
|
||||||
self.indexes_to_coindays_destroyed.vecs(),
|
self.indexes_to_coindays_destroyed.vecs(),
|
||||||
|
self.indexes_to_cumulative_net_realized_profit_and_loss_30d_change.as_ref()
|
||||||
|
.map_or(vec![], |v| v.vecs()),
|
||||||
|
self.indexes_to_cumulative_net_realized_profit_and_loss_30d_change_relative_to_realized_cap.as_ref()
|
||||||
|
.map_or(vec![], |v| v.vecs()),
|
||||||
|
self.indexes_to_cumulative_net_realized_profit_and_loss_30d_change_relative_to_market_cap.as_ref()
|
||||||
|
.map_or(vec![], |v| v.vecs()),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use brk_core::{DateIndex, Height, InputIndex, OutputIndex, OutputType, Result, S
|
|||||||
use brk_exit::Exit;
|
use brk_exit::Exit;
|
||||||
use brk_indexer::Indexer;
|
use brk_indexer::Indexer;
|
||||||
use brk_vec::{
|
use brk_vec::{
|
||||||
AnyCollectableVec, AnyVec, BaseVecIterator, CollectableVec, Computation, EagerVec, Format,
|
AnyCollectableVec, AnyVec, CollectableVec, Computation, EagerVec, Format, GenericStoredVec,
|
||||||
GenericStoredVec, StoredIndex, StoredVec, UnsafeSlice, VecIterator,
|
StoredIndex, StoredVec, UnsafeSlice, VecIterator,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use outputs::OutputCohorts;
|
use outputs::OutputCohorts;
|
||||||
@@ -1289,7 +1289,7 @@ impl Vecs {
|
|||||||
base_version + self.height_to_opreturn_supply.inner_version(),
|
base_version + self.height_to_opreturn_supply.inner_version(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut chain_state: Vec<BlockState>;
|
let mut chain_state: Vec<BlockState> = vec![];
|
||||||
let mut chain_state_starting_height = Height::from(self.chain_state.len());
|
let mut chain_state_starting_height = Height::from(self.chain_state.len());
|
||||||
|
|
||||||
let stateful_starting_height = match separate_utxo_vecs
|
let stateful_starting_height = match separate_utxo_vecs
|
||||||
@@ -1322,287 +1322,286 @@ impl Vecs {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
chain_state_starting_height
|
chain_state_starting_height
|
||||||
}
|
}
|
||||||
Ordering::Less => {
|
Ordering::Less => Height::ZERO,
|
||||||
// todo!("rollback instead");
|
|
||||||
chain_state = vec![];
|
|
||||||
chain_state_starting_height = Height::ZERO;
|
|
||||||
Height::ZERO
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if stateful_starting_height.is_zero() {
|
|
||||||
info!("Starting processing utxos from the start");
|
|
||||||
separate_utxo_vecs
|
|
||||||
.par_iter_mut()
|
|
||||||
.try_for_each(|(_, v)| v.state.price_to_amount.reset())?;
|
|
||||||
}
|
|
||||||
let starting_height = starting_indexes
|
let starting_height = starting_indexes
|
||||||
.height
|
.height
|
||||||
.min(stateful_starting_height)
|
.min(stateful_starting_height)
|
||||||
.min(Height::from(self.height_to_unspendable_supply.len()))
|
.min(Height::from(self.height_to_unspendable_supply.len()))
|
||||||
.min(Height::from(self.height_to_opreturn_supply.len()));
|
.min(Height::from(self.height_to_opreturn_supply.len()));
|
||||||
|
|
||||||
if starting_height == Height::from(height_to_date_fixed.len()) {
|
if starting_height.is_zero() {
|
||||||
return Ok(());
|
info!("Starting processing utxos from the start");
|
||||||
|
|
||||||
|
// todo!("rollback instead");
|
||||||
|
chain_state = vec![];
|
||||||
|
chain_state_starting_height = Height::ZERO;
|
||||||
|
|
||||||
|
separate_utxo_vecs
|
||||||
|
.par_iter_mut()
|
||||||
|
.try_for_each(|(_, v)| v.state.price_to_amount.reset())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---
|
if starting_height < Height::from(height_to_date_fixed.len()) {
|
||||||
// INIT
|
starting_indexes.update_from_height(starting_height, indexes);
|
||||||
// ---
|
|
||||||
|
|
||||||
separate_utxo_vecs
|
separate_utxo_vecs
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.for_each(|(_, v)| v.init(starting_height));
|
.for_each(|(_, v)| v.init(starting_height));
|
||||||
|
|
||||||
let mut unspendable_supply = if let Some(prev_height) = starting_height.decremented() {
|
let mut unspendable_supply = if let Some(prev_height) = starting_height.decremented() {
|
||||||
self.height_to_unspendable_supply
|
self.height_to_unspendable_supply
|
||||||
.into_iter()
|
|
||||||
.unwrap_get_inner(prev_height)
|
|
||||||
} else {
|
|
||||||
Sats::ZERO
|
|
||||||
};
|
|
||||||
let mut opreturn_supply = if let Some(prev_height) = starting_height.decremented() {
|
|
||||||
self.height_to_opreturn_supply
|
|
||||||
.into_iter()
|
|
||||||
.unwrap_get_inner(prev_height)
|
|
||||||
} else {
|
|
||||||
Sats::ZERO
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut height = starting_height;
|
|
||||||
starting_indexes.update_from_height(height, indexes);
|
|
||||||
|
|
||||||
(height.unwrap_to_usize()..height_to_first_outputindex_iter.len())
|
|
||||||
.map(Height::from)
|
|
||||||
.try_for_each(|_height| -> color_eyre::Result<()> {
|
|
||||||
height = _height;
|
|
||||||
|
|
||||||
self.utxos_vecs
|
|
||||||
.as_mut_separate_vecs()
|
|
||||||
.iter_mut()
|
|
||||||
.for_each(|(_, v)| v.state.reset_single_iteration_values());
|
|
||||||
|
|
||||||
info!("Processing chain at {height}...");
|
|
||||||
|
|
||||||
let timestamp = height_to_timestamp_fixed_iter.unwrap_get_inner(height);
|
|
||||||
let price = height_to_close_iter
|
|
||||||
.as_mut()
|
|
||||||
.map(|i| *i.unwrap_get_inner(height));
|
|
||||||
let first_outputindex = height_to_first_outputindex_iter
|
|
||||||
.unwrap_get_inner(height)
|
|
||||||
.unwrap_to_usize();
|
|
||||||
let first_inputindex = height_to_first_inputindex_iter
|
|
||||||
.unwrap_get_inner(height)
|
|
||||||
.unwrap_to_usize();
|
|
||||||
let output_count = height_to_output_count_iter.unwrap_get_inner(height);
|
|
||||||
let input_count = height_to_input_count_iter.unwrap_get_inner(height);
|
|
||||||
|
|
||||||
let (mut height_to_sent, mut received) = thread::scope(|s| {
|
|
||||||
if chain_state_starting_height <= height {
|
|
||||||
s.spawn(|| {
|
|
||||||
self.utxos_vecs
|
|
||||||
.tick_tock_next_block(&chain_state, timestamp);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let sent_handle = s.spawn(|| {
|
|
||||||
// Skip coinbase
|
|
||||||
(first_inputindex + 1..first_inputindex + *input_count)
|
|
||||||
.into_par_iter()
|
|
||||||
.map(InputIndex::from)
|
|
||||||
.map(|inputindex| {
|
|
||||||
let outputindex = inputindex_to_outputindex
|
|
||||||
.get_or_read(inputindex, &inputindex_to_outputindex_mmap)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.into_inner();
|
|
||||||
|
|
||||||
let value = outputindex_to_value
|
|
||||||
.get_or_read(outputindex, &outputindex_to_value_mmap)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.into_inner();
|
|
||||||
|
|
||||||
let input_type = outputindex_to_outputtype
|
|
||||||
.get_or_read(outputindex, &outputindex_to_outputtype_mmap)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.into_inner();
|
|
||||||
|
|
||||||
// dbg!(input_type);
|
|
||||||
|
|
||||||
if input_type.is_unspendable() {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
|
|
||||||
let input_txindex = outputindex_to_txindex
|
|
||||||
.get_or_read(outputindex, &outputindex_to_txindex_mmap)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.into_inner();
|
|
||||||
|
|
||||||
let height = txindex_to_height
|
|
||||||
.get_or_read(input_txindex, &txindex_to_height_mmap)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.into_inner();
|
|
||||||
|
|
||||||
(height, value, input_type)
|
|
||||||
})
|
|
||||||
.fold(
|
|
||||||
BTreeMap::<Height, Transacted>::default,
|
|
||||||
|mut tree, (height, value, input_type)| {
|
|
||||||
tree.entry(height).or_default().iterate(value, input_type);
|
|
||||||
tree
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.reduce(BTreeMap::<Height, Transacted>::default, |first, second| {
|
|
||||||
let (mut source, to_consume) = if first.len() > second.len() {
|
|
||||||
(first, second)
|
|
||||||
} else {
|
|
||||||
(second, first)
|
|
||||||
};
|
|
||||||
to_consume.into_iter().for_each(|(k, v)| {
|
|
||||||
*source.entry(k).or_default() += v;
|
|
||||||
});
|
|
||||||
source
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
let received_handle = s.spawn(|| {
|
|
||||||
(first_outputindex..first_outputindex + *output_count)
|
|
||||||
.into_par_iter()
|
|
||||||
.map(OutputIndex::from)
|
|
||||||
.map(|outputindex| {
|
|
||||||
let value = outputindex_to_value
|
|
||||||
.get_or_read(outputindex, &outputindex_to_value_mmap)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.into_inner();
|
|
||||||
|
|
||||||
let output_type = outputindex_to_outputtype
|
|
||||||
.get_or_read(outputindex, &outputindex_to_outputtype_mmap)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.into_inner();
|
|
||||||
|
|
||||||
(value, output_type)
|
|
||||||
})
|
|
||||||
.fold(
|
|
||||||
Transacted::default,
|
|
||||||
|mut transacted, (value, output_type)| {
|
|
||||||
transacted.iterate(value, output_type);
|
|
||||||
transacted
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.reduce(Transacted::default, |acc, transacted| acc + transacted)
|
|
||||||
});
|
|
||||||
|
|
||||||
(sent_handle.join().unwrap(), received_handle.join().unwrap())
|
|
||||||
});
|
|
||||||
|
|
||||||
unspendable_supply += received
|
|
||||||
.by_type
|
|
||||||
.unspendable
|
|
||||||
.as_vec()
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|state| state.value)
|
.unwrap_get_inner(prev_height)
|
||||||
.sum::<Sats>()
|
} else {
|
||||||
+ height_to_unclaimed_rewards_iter.unwrap_get_inner(height);
|
Sats::ZERO
|
||||||
|
};
|
||||||
opreturn_supply += received.by_type.unspendable.opreturn.value;
|
let mut opreturn_supply = if let Some(prev_height) = starting_height.decremented() {
|
||||||
|
|
||||||
if height == Height::new(0) {
|
|
||||||
received = Transacted::default();
|
|
||||||
unspendable_supply += Sats::FIFTY_BTC;
|
|
||||||
} else if height == Height::new(91_842) || height == Height::new(91_880) {
|
|
||||||
// Need to destroy invalid coinbases due to duplicate txids
|
|
||||||
if height == Height::new(91_842) {
|
|
||||||
height_to_sent.entry(Height::new(91_812)).or_default()
|
|
||||||
} else {
|
|
||||||
height_to_sent.entry(Height::new(91_722)).or_default()
|
|
||||||
}
|
|
||||||
.iterate(Sats::FIFTY_BTC, OutputType::P2PK65);
|
|
||||||
};
|
|
||||||
|
|
||||||
if chain_state_starting_height <= height {
|
|
||||||
// Push current block state before processing sends and receives
|
|
||||||
chain_state.push(BlockState {
|
|
||||||
supply: received.spendable_supply.clone(),
|
|
||||||
price,
|
|
||||||
timestamp,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.utxos_vecs.receive(received, height, price);
|
|
||||||
|
|
||||||
let unsafe_chain_state = UnsafeSlice::new(&mut chain_state);
|
|
||||||
|
|
||||||
height_to_sent.par_iter().for_each(|(height, sent)| unsafe {
|
|
||||||
(*unsafe_chain_state.get(height.unwrap_to_usize())).supply -=
|
|
||||||
&sent.spendable_supply;
|
|
||||||
});
|
|
||||||
|
|
||||||
self.utxos_vecs.send(height_to_sent, chain_state.as_slice());
|
|
||||||
} else {
|
|
||||||
dbg!(chain_state_starting_height, height);
|
|
||||||
panic!("temp, just making sure")
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut separate_utxo_vecs = self.utxos_vecs.as_mut_separate_vecs();
|
|
||||||
|
|
||||||
separate_utxo_vecs
|
|
||||||
.iter_mut()
|
|
||||||
.try_for_each(|(_, v)| v.forced_pushed_at(height, exit))?;
|
|
||||||
|
|
||||||
self.height_to_unspendable_supply.forced_push_at(
|
|
||||||
height,
|
|
||||||
unspendable_supply,
|
|
||||||
exit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.height_to_opreturn_supply
|
self.height_to_opreturn_supply
|
||||||
.forced_push_at(height, opreturn_supply, exit)?;
|
.into_iter()
|
||||||
|
.unwrap_get_inner(prev_height)
|
||||||
|
} else {
|
||||||
|
Sats::ZERO
|
||||||
|
};
|
||||||
|
|
||||||
let date = height_to_date_fixed_iter.unwrap_get_inner(height);
|
let mut height = starting_height;
|
||||||
let dateindex = DateIndex::try_from(date).unwrap();
|
|
||||||
let date_first_height = dateindex_to_first_height_iter.unwrap_get_inner(dateindex);
|
|
||||||
let date_height_count = dateindex_to_height_count_iter.unwrap_get_inner(dateindex);
|
|
||||||
let is_date_last_height = date_first_height
|
|
||||||
+ Height::from(date_height_count).decremented().unwrap()
|
|
||||||
== height;
|
|
||||||
let date_price = dateindex_to_close_iter
|
|
||||||
.as_mut()
|
|
||||||
.map(|v| is_date_last_height.then(|| *v.unwrap_get_inner(dateindex)));
|
|
||||||
|
|
||||||
separate_utxo_vecs.par_iter_mut().try_for_each(|(_, v)| {
|
(height.unwrap_to_usize()..height_to_date_fixed.len())
|
||||||
v.compute_then_force_push_unrealized_states(
|
.map(Height::from)
|
||||||
|
.try_for_each(|_height| -> color_eyre::Result<()> {
|
||||||
|
height = _height;
|
||||||
|
|
||||||
|
self.utxos_vecs
|
||||||
|
.as_mut_separate_vecs()
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|(_, v)| v.state.reset_single_iteration_values());
|
||||||
|
|
||||||
|
info!("Processing chain at {height}...");
|
||||||
|
|
||||||
|
let timestamp = height_to_timestamp_fixed_iter.unwrap_get_inner(height);
|
||||||
|
let price = height_to_close_iter
|
||||||
|
.as_mut()
|
||||||
|
.map(|i| *i.unwrap_get_inner(height));
|
||||||
|
let first_outputindex = height_to_first_outputindex_iter
|
||||||
|
.unwrap_get_inner(height)
|
||||||
|
.unwrap_to_usize();
|
||||||
|
let first_inputindex = height_to_first_inputindex_iter
|
||||||
|
.unwrap_get_inner(height)
|
||||||
|
.unwrap_to_usize();
|
||||||
|
let output_count = height_to_output_count_iter.unwrap_get_inner(height);
|
||||||
|
let input_count = height_to_input_count_iter.unwrap_get_inner(height);
|
||||||
|
|
||||||
|
let (mut height_to_sent, mut received) = thread::scope(|s| {
|
||||||
|
if chain_state_starting_height <= height {
|
||||||
|
s.spawn(|| {
|
||||||
|
self.utxos_vecs
|
||||||
|
.tick_tock_next_block(&chain_state, timestamp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let sent_handle = s.spawn(|| {
|
||||||
|
// Skip coinbase
|
||||||
|
(first_inputindex + 1..first_inputindex + *input_count)
|
||||||
|
.into_par_iter()
|
||||||
|
.map(InputIndex::from)
|
||||||
|
.map(|inputindex| {
|
||||||
|
let outputindex = inputindex_to_outputindex
|
||||||
|
.get_or_read(inputindex, &inputindex_to_outputindex_mmap)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
let value = outputindex_to_value
|
||||||
|
.get_or_read(outputindex, &outputindex_to_value_mmap)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
let input_type = outputindex_to_outputtype
|
||||||
|
.get_or_read(outputindex, &outputindex_to_outputtype_mmap)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
// dbg!(input_type);
|
||||||
|
|
||||||
|
if input_type.is_unspendable() {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_txindex = outputindex_to_txindex
|
||||||
|
.get_or_read(outputindex, &outputindex_to_txindex_mmap)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
let height = txindex_to_height
|
||||||
|
.get_or_read(input_txindex, &txindex_to_height_mmap)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
(height, value, input_type)
|
||||||
|
})
|
||||||
|
.fold(
|
||||||
|
BTreeMap::<Height, Transacted>::default,
|
||||||
|
|mut tree, (height, value, input_type)| {
|
||||||
|
tree.entry(height).or_default().iterate(value, input_type);
|
||||||
|
tree
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.reduce(BTreeMap::<Height, Transacted>::default, |first, second| {
|
||||||
|
let (mut source, to_consume) = if first.len() > second.len() {
|
||||||
|
(first, second)
|
||||||
|
} else {
|
||||||
|
(second, first)
|
||||||
|
};
|
||||||
|
to_consume.into_iter().for_each(|(k, v)| {
|
||||||
|
*source.entry(k).or_default() += v;
|
||||||
|
});
|
||||||
|
source
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let received_handle = s.spawn(|| {
|
||||||
|
(first_outputindex..first_outputindex + *output_count)
|
||||||
|
.into_par_iter()
|
||||||
|
.map(OutputIndex::from)
|
||||||
|
.map(|outputindex| {
|
||||||
|
let value = outputindex_to_value
|
||||||
|
.get_or_read(outputindex, &outputindex_to_value_mmap)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
let output_type = outputindex_to_outputtype
|
||||||
|
.get_or_read(outputindex, &outputindex_to_outputtype_mmap)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
(value, output_type)
|
||||||
|
})
|
||||||
|
.fold(
|
||||||
|
Transacted::default,
|
||||||
|
|mut transacted, (value, output_type)| {
|
||||||
|
transacted.iterate(value, output_type);
|
||||||
|
transacted
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.reduce(Transacted::default, |acc, transacted| acc + transacted)
|
||||||
|
});
|
||||||
|
|
||||||
|
(sent_handle.join().unwrap(), received_handle.join().unwrap())
|
||||||
|
});
|
||||||
|
|
||||||
|
unspendable_supply += received
|
||||||
|
.by_type
|
||||||
|
.unspendable
|
||||||
|
.as_vec()
|
||||||
|
.into_iter()
|
||||||
|
.map(|state| state.value)
|
||||||
|
.sum::<Sats>()
|
||||||
|
+ height_to_unclaimed_rewards_iter.unwrap_get_inner(height);
|
||||||
|
|
||||||
|
opreturn_supply += received.by_type.unspendable.opreturn.value;
|
||||||
|
|
||||||
|
if height == Height::new(0) {
|
||||||
|
received = Transacted::default();
|
||||||
|
unspendable_supply += Sats::FIFTY_BTC;
|
||||||
|
} else if height == Height::new(91_842) || height == Height::new(91_880) {
|
||||||
|
// Need to destroy invalid coinbases due to duplicate txids
|
||||||
|
if height == Height::new(91_842) {
|
||||||
|
height_to_sent.entry(Height::new(91_812)).or_default()
|
||||||
|
} else {
|
||||||
|
height_to_sent.entry(Height::new(91_722)).or_default()
|
||||||
|
}
|
||||||
|
.iterate(Sats::FIFTY_BTC, OutputType::P2PK65);
|
||||||
|
};
|
||||||
|
|
||||||
|
if chain_state_starting_height <= height {
|
||||||
|
// Push current block state before processing sends and receives
|
||||||
|
chain_state.push(BlockState {
|
||||||
|
supply: received.spendable_supply.clone(),
|
||||||
|
price,
|
||||||
|
timestamp,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.utxos_vecs.receive(received, height, price);
|
||||||
|
|
||||||
|
let unsafe_chain_state = UnsafeSlice::new(&mut chain_state);
|
||||||
|
|
||||||
|
height_to_sent.par_iter().for_each(|(height, sent)| unsafe {
|
||||||
|
(*unsafe_chain_state.get(height.unwrap_to_usize())).supply -=
|
||||||
|
&sent.spendable_supply;
|
||||||
|
});
|
||||||
|
|
||||||
|
self.utxos_vecs.send(height_to_sent, chain_state.as_slice());
|
||||||
|
} else {
|
||||||
|
dbg!(chain_state_starting_height, height);
|
||||||
|
panic!("temp, just making sure")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut separate_utxo_vecs = self.utxos_vecs.as_mut_separate_vecs();
|
||||||
|
|
||||||
|
separate_utxo_vecs
|
||||||
|
.iter_mut()
|
||||||
|
.try_for_each(|(_, v)| v.forced_pushed_at(height, exit))?;
|
||||||
|
|
||||||
|
self.height_to_unspendable_supply.forced_push_at(
|
||||||
height,
|
height,
|
||||||
price,
|
unspendable_supply,
|
||||||
is_date_last_height.then_some(dateindex),
|
|
||||||
date_price,
|
|
||||||
exit,
|
exit,
|
||||||
)
|
)?;
|
||||||
|
|
||||||
|
self.height_to_opreturn_supply
|
||||||
|
.forced_push_at(height, opreturn_supply, exit)?;
|
||||||
|
|
||||||
|
let date = height_to_date_fixed_iter.unwrap_get_inner(height);
|
||||||
|
let dateindex = DateIndex::try_from(date).unwrap();
|
||||||
|
let date_first_height =
|
||||||
|
dateindex_to_first_height_iter.unwrap_get_inner(dateindex);
|
||||||
|
let date_height_count =
|
||||||
|
dateindex_to_height_count_iter.unwrap_get_inner(dateindex);
|
||||||
|
let is_date_last_height = date_first_height
|
||||||
|
+ Height::from(date_height_count).decremented().unwrap()
|
||||||
|
== height;
|
||||||
|
let date_price = dateindex_to_close_iter
|
||||||
|
.as_mut()
|
||||||
|
.map(|v| is_date_last_height.then(|| *v.unwrap_get_inner(dateindex)));
|
||||||
|
|
||||||
|
separate_utxo_vecs.par_iter_mut().try_for_each(|(_, v)| {
|
||||||
|
v.compute_then_force_push_unrealized_states(
|
||||||
|
height,
|
||||||
|
price,
|
||||||
|
is_date_last_height.then_some(dateindex),
|
||||||
|
date_price,
|
||||||
|
exit,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if height != Height::ZERO && height.unwrap_to_usize() % 20_000 == 0 {
|
||||||
|
info!("Flushing...");
|
||||||
|
exit.block();
|
||||||
|
self.flush_states(height, &chain_state, exit)?;
|
||||||
|
exit.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if height != Height::ZERO && height.unwrap_to_usize() % 20_000 == 0 {
|
exit.block();
|
||||||
info!("Flushing...");
|
|
||||||
exit.block();
|
|
||||||
self.flush_states(height, &chain_state, exit)?;
|
|
||||||
exit.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
info!("Flushing...");
|
||||||
})?;
|
|
||||||
|
|
||||||
exit.block();
|
self.flush_states(height, &chain_state, exit)?;
|
||||||
|
}
|
||||||
|
|
||||||
info!("Flushing...");
|
info!("Computing overlapping...");
|
||||||
|
|
||||||
self.flush_states(height, &chain_state, exit)?;
|
|
||||||
|
|
||||||
info!("Computing overlaping...");
|
|
||||||
|
|
||||||
self.utxos_vecs
|
self.utxos_vecs
|
||||||
.compute_overlaping_vecs(&starting_indexes, exit)?;
|
.compute_overlapping_vecs(&starting_indexes, exit)?;
|
||||||
|
|
||||||
info!("Computing rest part 1...");
|
info!("Computing rest part 1...");
|
||||||
|
|
||||||
@@ -1625,6 +1624,13 @@ impl Vecs {
|
|||||||
.dateindex
|
.dateindex
|
||||||
.clone();
|
.clone();
|
||||||
let height_to_realized_cap = self.utxos_vecs.all.1.height_to_realized_cap.clone();
|
let height_to_realized_cap = self.utxos_vecs.all.1.height_to_realized_cap.clone();
|
||||||
|
let dateindex_to_realized_cap = self
|
||||||
|
.utxos_vecs
|
||||||
|
.all
|
||||||
|
.1
|
||||||
|
.indexes_to_realized_cap
|
||||||
|
.as_ref()
|
||||||
|
.map(|v| v.dateindex.unwrap_last().clone());
|
||||||
|
|
||||||
self.utxos_vecs
|
self.utxos_vecs
|
||||||
.as_mut_vecs()
|
.as_mut_vecs()
|
||||||
@@ -1639,6 +1645,7 @@ impl Vecs {
|
|||||||
&height_to_supply,
|
&height_to_supply,
|
||||||
dateindex_to_supply.as_ref().unwrap(),
|
dateindex_to_supply.as_ref().unwrap(),
|
||||||
height_to_realized_cap.as_ref(),
|
height_to_realized_cap.as_ref(),
|
||||||
|
dateindex_to_realized_cap.as_ref(),
|
||||||
exit,
|
exit,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ pub trait OutputCohorts {
|
|||||||
fn tick_tock_next_block(&mut self, chain_state: &[BlockState], timestamp: Timestamp);
|
fn tick_tock_next_block(&mut self, chain_state: &[BlockState], timestamp: Timestamp);
|
||||||
fn send(&mut self, height_to_sent: BTreeMap<Height, Transacted>, chain_state: &[BlockState]);
|
fn send(&mut self, height_to_sent: BTreeMap<Height, Transacted>, chain_state: &[BlockState]);
|
||||||
fn receive(&mut self, received: Transacted, height: Height, price: Option<Dollars>);
|
fn receive(&mut self, received: Transacted, height: Height, price: Option<Dollars>);
|
||||||
fn compute_overlaping_vecs(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()>;
|
fn compute_overlapping_vecs(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputCohorts for Outputs<(OutputFilter, cohort::Vecs)> {
|
impl OutputCohorts for Outputs<(OutputFilter, cohort::Vecs)> {
|
||||||
@@ -172,7 +172,7 @@ impl OutputCohorts for Outputs<(OutputFilter, cohort::Vecs)> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_overlaping_vecs(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
fn compute_overlapping_vecs(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||||
let by_date_range = self.by_date_range.as_vec();
|
let by_date_range = self.by_date_range.as_vec();
|
||||||
let by_size_range = self.by_size_range.as_vec();
|
let by_size_range = self.by_size_range.as_vec();
|
||||||
|
|
||||||
|
|||||||
@@ -178,6 +178,17 @@ impl Mul<usize> for Close<Dollars> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mul<f64> for Dollars {
|
||||||
|
type Output = Dollars;
|
||||||
|
fn mul(self, rhs: f64) -> Self::Output {
|
||||||
|
if rhs.fract() != 0.0 {
|
||||||
|
Self::from(self.0 * rhs)
|
||||||
|
} else {
|
||||||
|
self * rhs as i64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Mul<Bitcoin> for Dollars {
|
impl Mul<Bitcoin> for Dollars {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
fn mul(self, rhs: Bitcoin) -> Self::Output {
|
fn mul(self, rhs: Bitcoin) -> Self::Output {
|
||||||
@@ -208,11 +219,14 @@ impl Mul<Sats> for Dollars {
|
|||||||
impl Mul<StoredF32> for Dollars {
|
impl Mul<StoredF32> for Dollars {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
fn mul(self, rhs: StoredF32) -> Self::Output {
|
fn mul(self, rhs: StoredF32) -> Self::Output {
|
||||||
if rhs.fract() != 0.0 {
|
self * *rhs as f64
|
||||||
Self::from(self.0 * *rhs as f64)
|
}
|
||||||
} else {
|
}
|
||||||
self * *rhs as i64
|
|
||||||
}
|
impl Mul<StoredF64> for Dollars {
|
||||||
|
type Output = Self;
|
||||||
|
fn mul(self, rhs: StoredF64) -> Self::Output {
|
||||||
|
self * *rhs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::ops::{Add, Div};
|
use std::ops::{Add, Div};
|
||||||
|
|
||||||
use derive_deref::Deref;
|
use derive_deref::{Deref, DerefMut};
|
||||||
use serde::{Serialize, Serializer, ser::SerializeTuple};
|
use serde::{Serialize, Serializer, ser::SerializeTuple};
|
||||||
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
|
||||||
|
|
||||||
@@ -172,6 +172,7 @@ impl From<Close<Sats>> for OHLCSats {
|
|||||||
IntoBytes,
|
IntoBytes,
|
||||||
KnownLayout,
|
KnownLayout,
|
||||||
Deref,
|
Deref,
|
||||||
|
DerefMut,
|
||||||
Serialize,
|
Serialize,
|
||||||
)]
|
)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@@ -259,6 +260,7 @@ where
|
|||||||
IntoBytes,
|
IntoBytes,
|
||||||
KnownLayout,
|
KnownLayout,
|
||||||
Deref,
|
Deref,
|
||||||
|
DerefMut,
|
||||||
Serialize,
|
Serialize,
|
||||||
)]
|
)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@@ -346,6 +348,7 @@ where
|
|||||||
IntoBytes,
|
IntoBytes,
|
||||||
KnownLayout,
|
KnownLayout,
|
||||||
Deref,
|
Deref,
|
||||||
|
DerefMut,
|
||||||
Serialize,
|
Serialize,
|
||||||
)]
|
)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@@ -433,6 +436,7 @@ where
|
|||||||
IntoBytes,
|
IntoBytes,
|
||||||
KnownLayout,
|
KnownLayout,
|
||||||
Deref,
|
Deref,
|
||||||
|
DerefMut,
|
||||||
Serialize,
|
Serialize,
|
||||||
)]
|
)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ impl Version {
|
|||||||
Self(self.0.swap_bytes())
|
Self(self.0.swap_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(&self, path: &Path) -> Result<()> {
|
/// Ok(true) if existed and is same
|
||||||
|
/// Ok(false) if didn't exist
|
||||||
|
pub fn validate(&self, path: &Path) -> Result<bool> {
|
||||||
if let Ok(prev_version) = Version::try_from(path) {
|
if let Ok(prev_version) = Version::try_from(path) {
|
||||||
if prev_version != *self {
|
if prev_version != *self {
|
||||||
if prev_version.swap_bytes() == *self {
|
if prev_version.swap_bytes() == *self {
|
||||||
@@ -44,9 +46,11 @@ impl Version {
|
|||||||
expected: *self,
|
expected: *self,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ mod checked_sub;
|
|||||||
mod paths;
|
mod paths;
|
||||||
mod pause;
|
mod pause;
|
||||||
mod rlimit;
|
mod rlimit;
|
||||||
|
mod serde;
|
||||||
|
|
||||||
pub use bytes::*;
|
pub use bytes::*;
|
||||||
pub use checked_sub::*;
|
pub use checked_sub::*;
|
||||||
pub use paths::*;
|
pub use paths::*;
|
||||||
pub use pause::*;
|
pub use pause::*;
|
||||||
pub use rlimit::*;
|
pub use rlimit::*;
|
||||||
|
pub use serde::*;
|
||||||
|
|||||||
@@ -3,5 +3,7 @@ use log::info;
|
|||||||
pub fn pause() {
|
pub fn pause() {
|
||||||
info!("Press enter to continue...");
|
info!("Press enter to continue...");
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
std::io::stdin().read_line(&mut buffer).expect("Failed to read line");
|
std::io::stdin()
|
||||||
|
.read_line(&mut buffer)
|
||||||
|
.expect("Failed to read line");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
|
|
||||||
|
pub fn default_on_error<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
T: Deserialize<'de> + Default,
|
||||||
|
{
|
||||||
|
match T::deserialize(deserializer) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(_) => Ok(T::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use brk_core::{Date, Height};
|
use brk_core::{Date, Height};
|
||||||
use brk_fetcher::{BRK, Fetcher};
|
use brk_fetcher::{BRK, Binance, Fetcher, Kraken};
|
||||||
|
|
||||||
fn main() -> color_eyre::Result<()> {
|
fn main() -> color_eyre::Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
@@ -12,6 +12,19 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let mut fetcher = Fetcher::import(None)?;
|
let mut fetcher = Fetcher::import(None)?;
|
||||||
|
|
||||||
|
Binance::fetch_1d().map(|b| {
|
||||||
|
dbg!(b.last_key_value());
|
||||||
|
})?;
|
||||||
|
Kraken::fetch_1d().map(|b| {
|
||||||
|
dbg!(b.last_key_value());
|
||||||
|
})?;
|
||||||
|
Binance::fetch_1mn().map(|b| {
|
||||||
|
dbg!(b.last_key_value());
|
||||||
|
})?;
|
||||||
|
Kraken::fetch_1mn().map(|b| {
|
||||||
|
dbg!(b.last_key_value());
|
||||||
|
})?;
|
||||||
|
|
||||||
dbg!(fetcher.get_date(Date::new(2025, 6, 5))?);
|
dbg!(fetcher.get_date(Date::new(2025, 6, 5))?);
|
||||||
dbg!(fetcher.get_height(
|
dbg!(fetcher.get_height(
|
||||||
899911_u32.into(),
|
899911_u32.into(),
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ impl Kraken {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_1mn() -> color_eyre::Result<BTreeMap<Timestamp, OHLCCents>> {
|
pub fn fetch_1mn() -> color_eyre::Result<BTreeMap<Timestamp, OHLCCents>> {
|
||||||
info!("Fetching 1mn prices from Kraken...");
|
info!("Fetching 1mn prices from Kraken...");
|
||||||
|
|
||||||
retry(
|
retry(
|
||||||
@@ -54,7 +54,7 @@ impl Kraken {
|
|||||||
.ok_or(color_eyre::eyre::Error::msg("Couldn't find date"))
|
.ok_or(color_eyre::eyre::Error::msg("Couldn't find date"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_1d() -> color_eyre::Result<BTreeMap<Date, OHLCCents>> {
|
pub fn fetch_1d() -> color_eyre::Result<BTreeMap<Date, OHLCCents>> {
|
||||||
info!("Fetching daily prices from Kraken...");
|
info!("Fetching daily prices from Kraken...");
|
||||||
|
|
||||||
retry(
|
retry(
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ impl Fetcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_date_(&mut self, date: Date, tries: usize) -> color_eyre::Result<OHLCCents> {
|
fn get_date_(&mut self, date: Date, tries: usize) -> color_eyre::Result<OHLCCents> {
|
||||||
self.binance
|
self.kraken
|
||||||
.get_from_1d(&date)
|
.get_from_1d(&date)
|
||||||
.or_else(|_| {
|
.or_else(|_| {
|
||||||
// eprintln!("{e}");
|
// eprintln!("{e}");
|
||||||
self.kraken.get_from_1d(&date)
|
self.binance.get_from_1d(&date)
|
||||||
})
|
})
|
||||||
.or_else(|_| {
|
.or_else(|_| {
|
||||||
// eprintln!("{e}");
|
// eprintln!("{e}");
|
||||||
@@ -90,11 +90,11 @@ impl Fetcher {
|
|||||||
let previous_timestamp = previous_timestamp.map(|t| t.floor_seconds());
|
let previous_timestamp = previous_timestamp.map(|t| t.floor_seconds());
|
||||||
|
|
||||||
let ohlc = self
|
let ohlc = self
|
||||||
.binance
|
.kraken
|
||||||
.get_from_1mn(timestamp, previous_timestamp)
|
.get_from_1mn(timestamp, previous_timestamp)
|
||||||
.unwrap_or_else(|_report| {
|
.unwrap_or_else(|_report| {
|
||||||
// eprintln!("{_report}");
|
// eprintln!("{_report}");
|
||||||
self.kraken
|
self.binance
|
||||||
.get_from_1mn(timestamp, previous_timestamp)
|
.get_from_1mn(timestamp, previous_timestamp)
|
||||||
.unwrap_or_else(|_report| {
|
.unwrap_or_else(|_report| {
|
||||||
// // eprintln!("{_report}");
|
// // eprintln!("{_report}");
|
||||||
@@ -185,8 +185,8 @@ How to fix this:
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
|
self.kraken.clear();
|
||||||
self.binance.clear();
|
self.binance.clear();
|
||||||
self.brk.clear();
|
self.brk.clear();
|
||||||
self.kraken.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use brk_core::default_bitcoin_path;
|
|||||||
use brk_exit::Exit;
|
use brk_exit::Exit;
|
||||||
use brk_indexer::Indexer;
|
use brk_indexer::Indexer;
|
||||||
use brk_parser::Parser;
|
use brk_parser::Parser;
|
||||||
use brk_vec::Format;
|
|
||||||
|
|
||||||
fn main() -> color_eyre::Result<()> {
|
fn main() -> color_eyre::Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
@@ -25,7 +24,7 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let outputs = Path::new("../../_outputs");
|
let outputs = Path::new("../../_outputs");
|
||||||
|
|
||||||
let mut indexer = Indexer::new(outputs, Format::Raw, false)?;
|
let mut indexer = Indexer::new(outputs, false)?;
|
||||||
|
|
||||||
indexer.import_stores()?;
|
indexer.import_stores()?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|||||||
@@ -110,13 +110,7 @@ impl TryFrom<(&mut Vecs, &Stores, &Client)> for Indexes {
|
|||||||
vecs.height_to_blockhash
|
vecs.height_to_blockhash
|
||||||
.iter()
|
.iter()
|
||||||
.get(*height)
|
.get(*height)
|
||||||
.is_none_or(|saved_blockhash| {
|
.is_none_or(|saved_blockhash| &rpc_blockhash != saved_blockhash.as_ref())
|
||||||
let b = &rpc_blockhash != saved_blockhash.as_ref();
|
|
||||||
if b {
|
|
||||||
dbg!(rpc_blockhash, saved_blockhash.as_ref());
|
|
||||||
}
|
|
||||||
b
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.unwrap_or(starting_height);
|
.unwrap_or(starting_height);
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use brk_core::{
|
|||||||
use bitcoin::{Transaction, TxIn, TxOut};
|
use bitcoin::{Transaction, TxIn, TxOut};
|
||||||
use brk_exit::Exit;
|
use brk_exit::Exit;
|
||||||
use brk_parser::Parser;
|
use brk_parser::Parser;
|
||||||
use brk_vec::{AnyVec, Format, VecIterator};
|
use brk_vec::{AnyVec, VecIterator};
|
||||||
use color_eyre::eyre::{ContextCompat, eyre};
|
use color_eyre::eyre::{ContextCompat, eyre};
|
||||||
use fjall::TransactionalKeyspace;
|
use fjall::TransactionalKeyspace;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
@@ -42,21 +42,15 @@ pub struct Indexer {
|
|||||||
vecs: Option<Vecs>,
|
vecs: Option<Vecs>,
|
||||||
stores: Option<Stores>,
|
stores: Option<Stores>,
|
||||||
check_collisions: bool,
|
check_collisions: bool,
|
||||||
format: Format,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Indexer {
|
impl Indexer {
|
||||||
pub fn new(
|
pub fn new(outputs_dir: &Path, check_collisions: bool) -> color_eyre::Result<Self> {
|
||||||
outputs_dir: &Path,
|
|
||||||
format: Format,
|
|
||||||
check_collisions: bool,
|
|
||||||
) -> color_eyre::Result<Self> {
|
|
||||||
setrlimit()?;
|
setrlimit()?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path: outputs_dir.to_owned(),
|
path: outputs_dir.to_owned(),
|
||||||
vecs: None,
|
vecs: None,
|
||||||
stores: None,
|
stores: None,
|
||||||
format,
|
|
||||||
check_collisions,
|
check_collisions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -65,7 +59,6 @@ impl Indexer {
|
|||||||
self.vecs = Some(Vecs::forced_import(
|
self.vecs = Some(Vecs::forced_import(
|
||||||
&self.path.join("vecs/indexed"),
|
&self.path.join("vecs/indexed"),
|
||||||
VERSION + Version::ZERO,
|
VERSION + Version::ZERO,
|
||||||
self.format,
|
|
||||||
)?);
|
)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,11 +66,7 @@ pub struct Vecs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Vecs {
|
impl Vecs {
|
||||||
pub fn forced_import(
|
pub fn forced_import(path: &Path, version: Version) -> color_eyre::Result<Self> {
|
||||||
path: &Path,
|
|
||||||
version: Version,
|
|
||||||
format: Format,
|
|
||||||
) -> color_eyre::Result<Self> {
|
|
||||||
fs::create_dir_all(path)?;
|
fs::create_dir_all(path)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -78,7 +74,7 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"txindex",
|
"txindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_blockhash: IndexedVec::forced_import(
|
height_to_blockhash: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
@@ -90,145 +86,145 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"difficulty",
|
"difficulty",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_emptyoutputindex: IndexedVec::forced_import(
|
height_to_first_emptyoutputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_emptyoutputindex",
|
"first_emptyoutputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_inputindex: IndexedVec::forced_import(
|
height_to_first_inputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_inputindex",
|
"first_inputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_opreturnindex: IndexedVec::forced_import(
|
height_to_first_opreturnindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_opreturnindex",
|
"first_opreturnindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_outputindex: IndexedVec::forced_import(
|
height_to_first_outputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_outputindex",
|
"first_outputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2aindex: IndexedVec::forced_import(
|
height_to_first_p2aindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2aindex",
|
"first_p2aindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2msindex: IndexedVec::forced_import(
|
height_to_first_p2msindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2msindex",
|
"first_p2msindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2pk33index: IndexedVec::forced_import(
|
height_to_first_p2pk33index: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2pk33index",
|
"first_p2pk33index",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2pk65index: IndexedVec::forced_import(
|
height_to_first_p2pk65index: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2pk65index",
|
"first_p2pk65index",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2pkhindex: IndexedVec::forced_import(
|
height_to_first_p2pkhindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2pkhindex",
|
"first_p2pkhindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2shindex: IndexedVec::forced_import(
|
height_to_first_p2shindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2shindex",
|
"first_p2shindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2trindex: IndexedVec::forced_import(
|
height_to_first_p2trindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2trindex",
|
"first_p2trindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2wpkhindex: IndexedVec::forced_import(
|
height_to_first_p2wpkhindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2wpkhindex",
|
"first_p2wpkhindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_p2wshindex: IndexedVec::forced_import(
|
height_to_first_p2wshindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_p2wshindex",
|
"first_p2wshindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_txindex: IndexedVec::forced_import(
|
height_to_first_txindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_txindex",
|
"first_txindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_first_unknownoutputindex: IndexedVec::forced_import(
|
height_to_first_unknownoutputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_unknownoutputindex",
|
"first_unknownoutputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_timestamp: IndexedVec::forced_import(
|
height_to_timestamp: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"timestamp",
|
"timestamp",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_total_size: IndexedVec::forced_import(
|
height_to_total_size: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"total_size",
|
"total_size",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
height_to_weight: IndexedVec::forced_import(
|
height_to_weight: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"weight",
|
"weight",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
inputindex_to_outputindex: IndexedVec::forced_import(
|
inputindex_to_outputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"outputindex",
|
"outputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
opreturnindex_to_txindex: IndexedVec::forced_import(
|
opreturnindex_to_txindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"txindex",
|
"txindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
outputindex_to_outputtype: IndexedVec::forced_import(
|
outputindex_to_outputtype: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"outputtype",
|
"outputtype",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
outputindex_to_outputtypeindex: IndexedVec::forced_import(
|
outputindex_to_outputtypeindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"outputtypeindex",
|
"outputtypeindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
outputindex_to_value: IndexedVec::forced_import(
|
outputindex_to_value: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"value",
|
"value",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
p2aindex_to_p2abytes: IndexedVec::forced_import(
|
p2aindex_to_p2abytes: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
@@ -240,7 +236,7 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"txindex",
|
"txindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
p2pk33index_to_p2pk33bytes: IndexedVec::forced_import(
|
p2pk33index_to_p2pk33bytes: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
@@ -288,13 +284,13 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"base_size",
|
"base_size",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
txindex_to_first_inputindex: IndexedVec::forced_import(
|
txindex_to_first_inputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"first_inputindex",
|
"first_inputindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
txindex_to_first_outputindex: IndexedVec::forced_import(
|
txindex_to_first_outputindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
@@ -306,19 +302,19 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"is_explicitly_rbf",
|
"is_explicitly_rbf",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
txindex_to_rawlocktime: IndexedVec::forced_import(
|
txindex_to_rawlocktime: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"rawlocktime",
|
"rawlocktime",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
txindex_to_total_size: IndexedVec::forced_import(
|
txindex_to_total_size: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"total_size",
|
"total_size",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
txindex_to_txid: IndexedVec::forced_import(
|
txindex_to_txid: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
@@ -330,13 +326,13 @@ impl Vecs {
|
|||||||
path,
|
path,
|
||||||
"txversion",
|
"txversion",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
unknownoutputindex_to_txindex: IndexedVec::forced_import(
|
unknownoutputindex_to_txindex: IndexedVec::forced_import(
|
||||||
path,
|
path,
|
||||||
"txindex",
|
"txindex",
|
||||||
version + VERSION + Version::ZERO,
|
version + VERSION + Version::ZERO,
|
||||||
format,
|
Format::Raw,
|
||||||
)?,
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,50 +25,53 @@ pub fn init(path: Option<&Path>) {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
Builder::from_env(Env::default().default_filter_or("info,fjall=off,lsm_tree=off"))
|
Builder::from_env(
|
||||||
.format(move |buf, record| {
|
Env::default()
|
||||||
let date_time = Timestamp::now()
|
.default_filter_or("info,fjall=off,lsm_tree=off,rolldown=off,brk_rolldown=off"),
|
||||||
.to_zoned(tz::TimeZone::system())
|
)
|
||||||
.strftime("%Y-%m-%d %H:%M:%S")
|
.format(move |buf, record| {
|
||||||
.to_string();
|
let date_time = Timestamp::now()
|
||||||
let level = record.level().as_str().to_lowercase();
|
.to_zoned(tz::TimeZone::system())
|
||||||
let level = format!("{:5}", level);
|
.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
let target = record.target();
|
.to_string();
|
||||||
let dash = "-";
|
let level = record.level().as_str().to_lowercase();
|
||||||
let args = record.args();
|
let level = format!("{:5}", level);
|
||||||
|
let target = record.target();
|
||||||
|
let dash = "-";
|
||||||
|
let args = record.args();
|
||||||
|
|
||||||
if let Some(file) = file.as_ref() {
|
if let Some(file) = file.as_ref() {
|
||||||
let _ = write(
|
let _ = write(
|
||||||
file.try_clone().unwrap(),
|
file.try_clone().unwrap(),
|
||||||
&date_time,
|
&date_time,
|
||||||
target,
|
|
||||||
&level,
|
|
||||||
dash,
|
|
||||||
args,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let colored_date_time = date_time.bright_black();
|
|
||||||
let colored_level = match level.chars().next().unwrap() {
|
|
||||||
'e' => level.red().to_string(),
|
|
||||||
'w' => level.yellow().to_string(),
|
|
||||||
'i' => level.green().to_string(),
|
|
||||||
'd' => level.blue().to_string(),
|
|
||||||
't' => level.cyan().to_string(),
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
let colored_dash = dash.bright_black();
|
|
||||||
|
|
||||||
write(
|
|
||||||
buf,
|
|
||||||
colored_date_time,
|
|
||||||
target,
|
target,
|
||||||
colored_level,
|
&level,
|
||||||
colored_dash,
|
dash,
|
||||||
args,
|
args,
|
||||||
)
|
);
|
||||||
})
|
}
|
||||||
.init();
|
|
||||||
|
let colored_date_time = date_time.bright_black();
|
||||||
|
let colored_level = match level.chars().next().unwrap() {
|
||||||
|
'e' => level.red().to_string(),
|
||||||
|
'w' => level.yellow().to_string(),
|
||||||
|
'i' => level.green().to_string(),
|
||||||
|
'd' => level.blue().to_string(),
|
||||||
|
't' => level.cyan().to_string(),
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
let colored_dash = dash.bright_black();
|
||||||
|
|
||||||
|
write(
|
||||||
|
buf,
|
||||||
|
colored_date_time,
|
||||||
|
target,
|
||||||
|
colored_level,
|
||||||
|
colored_dash,
|
||||||
|
args,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(
|
fn write(
|
||||||
@@ -80,5 +83,9 @@ fn write(
|
|||||||
args: impl Display,
|
args: impl Display,
|
||||||
) -> Result<(), std::io::Error> {
|
) -> Result<(), std::io::Error> {
|
||||||
writeln!(buf, "{} {} {} {}", date_time, dash, level, args)
|
writeln!(buf, "{} {} {} {}", date_time, dash, level, args)
|
||||||
// writeln!(buf, "{} {} {} {} {}", date_time, _target, level, dash, args)
|
// writeln!(
|
||||||
|
// buf,
|
||||||
|
// "{} {} {} {} {}",
|
||||||
|
// date_time, _target, level, dash, args
|
||||||
|
// )
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,9 +96,10 @@ impl BlkIndexToBlkRecap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn export(&self) {
|
pub fn export(&self) {
|
||||||
let file = File::create(&self.path).unwrap_or_else(|_| {
|
let file = File::create(&self.path).unwrap_or_else(|e| {
|
||||||
|
dbg!(e);
|
||||||
dbg!(&self.path);
|
dbg!(&self.path);
|
||||||
panic!("No such file or directory")
|
panic!("Cannot write file");
|
||||||
});
|
});
|
||||||
|
|
||||||
serde_json::to_writer(&mut BufWriter::new(file), &self.tree).unwrap();
|
serde_json::to_writer(&mut BufWriter::new(file), &self.tree).unwrap();
|
||||||
|
|||||||
@@ -18,5 +18,5 @@ color-eyre = { workspace = true }
|
|||||||
derive_deref = { workspace = true }
|
derive_deref = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
serde_with = "3.12.0"
|
serde_with = "3.13.0"
|
||||||
tabled = { workspace = true }
|
tabled = { workspace = true }
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let format = Format::Compressed;
|
let format = Format::Compressed;
|
||||||
|
|
||||||
let mut indexer = Indexer::new(outputs_dir, format, true)?;
|
let mut indexer = Indexer::new(outputs_dir, true)?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|
||||||
let mut computer = Computer::new(outputs_dir, None, format);
|
let mut computer = Computer::new(outputs_dir, None, format);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ mod vec_trees;
|
|||||||
pub use format::Format;
|
pub use format::Format;
|
||||||
pub use index::Index;
|
pub use index::Index;
|
||||||
pub use output::{Output, Value};
|
pub use output::{Output, Value};
|
||||||
pub use params::Params;
|
pub use params::{Params, ParamsOpt};
|
||||||
pub use table::Tabled;
|
pub use table::Tabled;
|
||||||
use vec_trees::VecTrees;
|
use vec_trees::VecTrees;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
use std::{fmt::Display, ops::Deref, str::FromStr};
|
||||||
|
|
||||||
use clap::builder::PossibleValuesParser;
|
use clap::builder::PossibleValuesParser;
|
||||||
use clap_derive::Parser;
|
use clap_derive::Parser;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Deserializer};
|
||||||
use serde_with::{OneOrMany, formats::PreferOne, serde_as};
|
use serde_with::{OneOrMany, formats::PreferOne, serde_as};
|
||||||
|
|
||||||
use crate::{Format, Index};
|
use crate::{Format, Index};
|
||||||
@@ -17,15 +19,111 @@ pub struct Params {
|
|||||||
#[serde_as(as = "OneOrMany<_, PreferOne>")]
|
#[serde_as(as = "OneOrMany<_, PreferOne>")]
|
||||||
/// Names of the values requested
|
/// Names of the values requested
|
||||||
pub values: Vec<String>,
|
pub values: Vec<String>,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub rest: ParamsOpt,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The macro creates custom deserialization code.
|
||||||
|
// You need to specify a function name and the field name of the flattened field.
|
||||||
|
serde_with::flattened_maybe!(deserialize_rest, "rest");
|
||||||
|
|
||||||
|
impl Deref for Params {
|
||||||
|
type Target = ParamsOpt;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.rest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<((String, String), ParamsOpt)> for Params {
|
||||||
|
fn from(((index, id), rest): ((String, String), ParamsOpt)) -> Self {
|
||||||
|
Self {
|
||||||
|
index,
|
||||||
|
values: vec![id],
|
||||||
|
rest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[derive(Debug, Deserialize, Parser)]
|
||||||
|
pub struct ParamsOpt {
|
||||||
#[clap(short, long, allow_hyphen_values = true)]
|
#[clap(short, long, allow_hyphen_values = true)]
|
||||||
#[serde(alias = "f")]
|
#[serde(default, alias = "f", deserialize_with = "de_unquote_i64")]
|
||||||
/// Inclusive starting index, if negative will be from the end
|
/// Inclusive starting index, if negative will be from the end
|
||||||
pub from: Option<i64>,
|
from: Option<i64>,
|
||||||
#[clap(short, long, allow_hyphen_values = true)]
|
#[clap(short, long, allow_hyphen_values = true)]
|
||||||
#[serde(default, alias = "t")]
|
#[serde(default, alias = "t", deserialize_with = "de_unquote_i64")]
|
||||||
/// Inclusive ending index, if negative will be from the end
|
/// Exclusive ending index, if negative will be from the end, overrides 'count'
|
||||||
pub to: Option<i64>,
|
to: Option<i64>,
|
||||||
|
#[clap(short, long, allow_hyphen_values = true)]
|
||||||
|
#[serde(default, alias = "c", deserialize_with = "de_unquote_usize")]
|
||||||
|
/// Number of values
|
||||||
|
count: Option<usize>,
|
||||||
#[clap(short = 'F', long)]
|
#[clap(short = 'F', long)]
|
||||||
/// Format of the output
|
/// Format of the output
|
||||||
pub format: Option<Format>,
|
format: Option<Format>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParamsOpt {
|
||||||
|
pub fn from(&self) -> Option<i64> {
|
||||||
|
self.from
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to(&self) -> Option<i64> {
|
||||||
|
if self.to.is_none() {
|
||||||
|
if let Some(c) = self.count {
|
||||||
|
let c = c as i64;
|
||||||
|
if let Some(f) = self.from {
|
||||||
|
if f.is_positive() || f.abs() > c {
|
||||||
|
return Some(f + c);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Some(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.to
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(&self) -> Option<Format> {
|
||||||
|
self.format
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn de_unquote_i64<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
de_unquote(deserializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn de_unquote_usize<'de, D>(deserializer: D) -> Result<Option<usize>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
de_unquote(deserializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn de_unquote<'de, D, F>(deserializer: D) -> Result<Option<F>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
F: FromStr + Display,
|
||||||
|
<F as std::str::FromStr>::Err: Display,
|
||||||
|
{
|
||||||
|
let opt: Option<String> = Option::deserialize(deserializer)?;
|
||||||
|
let s = match opt {
|
||||||
|
None => return Ok(None),
|
||||||
|
Some(mut s) => {
|
||||||
|
// strip any leading/trailing quotes
|
||||||
|
if s.starts_with('"') && s.ends_with('"') && s.len() >= 2 {
|
||||||
|
s = s[1..s.len() - 1].to_string();
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
};
|
||||||
|
s.parse::<F>()
|
||||||
|
.map(Some)
|
||||||
|
.map_err(|e| serde::de::Error::custom(format!("cannot parse `{}` as type: {}", s, e)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,14 @@ impl<'a> VecTrees<'a> {
|
|||||||
|| s.starts_with("cumulative_from")
|
|| s.starts_with("cumulative_from")
|
||||||
}))
|
}))
|
||||||
&& !(split.len() == 4
|
&& !(split.len() == 4
|
||||||
&& split
|
&& split.get(1).is_some_and(|s| {
|
||||||
.get(1)
|
s == &"up"
|
||||||
.is_some_and(|s| s == &"up" || s == &"start" || s.starts_with("from"))
|
|| s == &"start"
|
||||||
|
|| s.starts_with("from")
|
||||||
|
|| s == &"cumulative_up"
|
||||||
|
|| s == &"cumulative_start"
|
||||||
|
|| s.starts_with("cumulative_from")
|
||||||
|
})
|
||||||
&& split.get(2).is_some_and(|s| s.ends_with("relative")))
|
&& split.get(2).is_some_and(|s| s.ends_with("relative")))
|
||||||
{
|
{
|
||||||
dbg!(&name, &split);
|
dbg!(&name, &split);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ repository.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
axum = { workspace = true }
|
axum = { workspace = true }
|
||||||
bitcoincore-rpc = { workspace = true }
|
bitcoincore-rpc = { workspace = true }
|
||||||
|
brk_bundler = { workspace = true }
|
||||||
brk_computer = { workspace = true }
|
brk_computer = { workspace = true }
|
||||||
brk_exit = { workspace = true }
|
brk_exit = { workspace = true }
|
||||||
brk_core = { workspace = true }
|
brk_core = { workspace = true }
|
||||||
@@ -25,11 +26,11 @@ color-eyre = { workspace = true }
|
|||||||
jiff = { workspace = true }
|
jiff = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
minreq = { workspace = true }
|
minreq = { workspace = true }
|
||||||
oxc = { version = "0.72.3", features = ["codegen", "minifier"] }
|
oxc = { version = "0.73.0", features = ["codegen", "minifier"] }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tower-http = { version = "0.6.6", features = ["compression-full", "trace"] }
|
tower-http = { version = "0.6.6", features = ["compression-full", "trace"] }
|
||||||
zip = "4.0.0"
|
zip = "4.1.0"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
[package.metadata.cargo-machete]
|
||||||
|
|||||||
@@ -41,22 +41,59 @@ The API uses `brk_query` and so inherites all of its features including formats.
|
|||||||
|
|
||||||
### API
|
### API
|
||||||
|
|
||||||
#### `GET /api/vecs/indexes`
|
#### [`GET /api/vecs/index-count`](https://bitcoinresearchkit.org/api/vecs/index-count)
|
||||||
|
|
||||||
|
Count of all possible indexes
|
||||||
|
|
||||||
|
#### [`GET /api/vecs/id-count`](https://bitcoinresearchkit.org/api/vecs/id-count)
|
||||||
|
|
||||||
|
Count of all possible ids
|
||||||
|
|
||||||
|
#### [`GET /api/vecs/variant-count`](https://bitcoinresearchkit.org/api/vecs/variant-count)
|
||||||
|
|
||||||
|
Count of all possible variants
|
||||||
|
|
||||||
|
#### [`GET /api/vecs/indexes`](https://bitcoinresearchkit.org/api/vecs/indexes)
|
||||||
|
|
||||||
A list of all possible vec indexes and their accepted variants
|
A list of all possible vec indexes and their accepted variants
|
||||||
|
|
||||||
#### `GET /api/vecs/ids`
|
#### [`GET /api/vecs/ids`](https://bitcoinresearchkit.org/api/vecs/ids)
|
||||||
|
|
||||||
A list of all possible vec ids
|
A list of all possible vec ids
|
||||||
|
|
||||||
#### `GET /api/vecs/id-to-indexes`
|
#### [`GET /api/vecs/variants`](https://bitcoinresearchkit.org/api/vecs/variants)
|
||||||
|
|
||||||
|
A list of all possible variants
|
||||||
|
|
||||||
|
#### [`GET /api/vecs/id-to-indexes`](https://bitcoinresearchkit.org/api/vecs/id-to-indexes)
|
||||||
|
|
||||||
A list of all possible vec ids and their supported vec indexes
|
A list of all possible vec ids and their supported vec indexes
|
||||||
|
|
||||||
#### `GET /api/vecs/index-to-ids`
|
#### [`GET /api/vecs/index-to-ids`](https://bitcoinresearchkit.org/api/vecs/index-to-ids)
|
||||||
|
|
||||||
A list of all possible vec indexes and their supported vec ids
|
A list of all possible vec indexes and their supported vec ids
|
||||||
|
|
||||||
|
#### `GET /api/{INDEX}-to-{ID}`
|
||||||
|
|
||||||
|
This endpoint retrieves data based on the specified vector index and id.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
| Parameter | Type | Required | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `from` | `signed int` | No | Inclusive starting index for pagination (default is 0). |
|
||||||
|
| `to` | `signed int` | No | Exclusive ending index for pagination (default is the total number of results). Overrides `count` |
|
||||||
|
| `count` | `unsigned int` | No | The number of values requested |
|
||||||
|
| `format` | `string` | No | The format of the response. Options include `json`, `csv`, `tsv`, or `md` (default is `json`). |
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/date-to-close
|
||||||
|
GET /date-to-close?from=-100
|
||||||
|
GET /date-to-close?count=100&format=csv
|
||||||
|
```
|
||||||
|
|
||||||
#### `GET /api/query`
|
#### `GET /api/query`
|
||||||
|
|
||||||
This endpoint retrieves data based on the specified vector index and values.
|
This endpoint retrieves data based on the specified vector index and values.
|
||||||
@@ -67,8 +104,9 @@ This endpoint retrieves data based on the specified vector index and values.
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `index` | `VecIndex` | Yes | The vector index to query. |
|
| `index` | `VecIndex` | Yes | The vector index to query. |
|
||||||
| `values` | `VecId[]` | Yes | A comma or space-separated list of vector IDs to retrieve. |
|
| `values` | `VecId[]` | Yes | A comma or space-separated list of vector IDs to retrieve. |
|
||||||
| `from` | `unsigned int` | No | The starting index for pagination (default is 0). |
|
| `from` | `signed int` | No | Inclusive starting index for pagination (default is 0). |
|
||||||
| `to` | `unsigned int` | No | The ending index for pagination (default is the total number of results). |
|
| `to` | `signed int` | No | Exclusive ending index for pagination (default is the total number of results). Overrides `count` |
|
||||||
|
| `count` | `unsigned int` | No | The number of values requested |
|
||||||
| `format` | `string` | No | The format of the response. Options include `json`, `csv`, `tsv`, or `md` (default is `json`). |
|
| `format` | `string` | No | The format of the response. Options include `json`, `csv`, `tsv`, or `md` (default is `json`). |
|
||||||
|
|
||||||
**Examples:**
|
**Examples:**
|
||||||
@@ -80,7 +118,7 @@ GET /api/query?index=week&values=ohlc,block-interval-average&from=0&to=20&format
|
|||||||
|
|
||||||
### Meta
|
### Meta
|
||||||
|
|
||||||
#### `GET /version`
|
#### [`GET /version`](https://bitcoinresearchkit.org/version)
|
||||||
|
|
||||||
The version of the server and thus BRK.
|
The version of the server and thus BRK.
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ pub fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
let format = Format::Compressed;
|
let format = Format::Compressed;
|
||||||
|
|
||||||
let mut indexer = Indexer::new(outputs_dir, format, true)?;
|
let mut indexer = Indexer::new(outputs_dir, true)?;
|
||||||
indexer.import_stores()?;
|
indexer.import_stores()?;
|
||||||
indexer.import_vecs()?;
|
indexer.import_vecs()?;
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ pub fn main() -> color_eyre::Result<()> {
|
|||||||
let server = Server::new(served_indexer, served_computer, Website::Default)?;
|
let server = Server::new(served_indexer, served_computer, Website::Default)?;
|
||||||
|
|
||||||
let server = tokio::spawn(async move {
|
let server = tokio::spawn(async move {
|
||||||
server.serve().await.unwrap();
|
server.serve(true).await.unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
if process {
|
if process {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
Router,
|
Json, Router,
|
||||||
extract::State,
|
extract::{Path, Query, State},
|
||||||
|
http::HeaderMap,
|
||||||
response::{IntoResponse, Redirect, Response},
|
response::{IntoResponse, Redirect, Response},
|
||||||
routing::get,
|
routing::get,
|
||||||
};
|
};
|
||||||
|
use brk_query::{Params, ParamsOpt};
|
||||||
|
|
||||||
use super::AppState;
|
use super::AppState;
|
||||||
|
|
||||||
mod explorer;
|
mod explorer;
|
||||||
mod query;
|
mod query;
|
||||||
|
|
||||||
pub use query::DTS;
|
pub use query::Bridge;
|
||||||
|
|
||||||
pub trait ApiRoutes {
|
pub trait ApiRoutes {
|
||||||
fn add_api_routes(self) -> Self;
|
fn add_api_routes(self) -> Self;
|
||||||
@@ -20,24 +22,29 @@ pub trait ApiRoutes {
|
|||||||
|
|
||||||
impl ApiRoutes for Router<AppState> {
|
impl ApiRoutes for Router<AppState> {
|
||||||
fn add_api_routes(self) -> Self {
|
fn add_api_routes(self) -> Self {
|
||||||
self.route(
|
self.route("/api/query", get(query::handler))
|
||||||
"/api",
|
.route("/api/vecs/id-count", get(id_count_handler))
|
||||||
get(|| async {
|
.route("/api/vecs/index-count", get(index_count_handler))
|
||||||
Redirect::permanent(
|
.route("/api/vecs/variant-count", get(variant_count_handler))
|
||||||
"https://github.com/bitcoinresearchkit/brk/tree/main/crates/brk_server#api",
|
.route("/api/vecs/ids", get(ids_handler))
|
||||||
)
|
.route("/api/vecs/indexes", get(indexes_handler))
|
||||||
}),
|
.route("/api/vecs/variants", get(variants_handler))
|
||||||
)
|
.route("/api/vecs/id-to-indexes", get(id_to_indexes_handler))
|
||||||
.route("/api/query", get(query::handler))
|
.route("/api/vecs/index-to-ids", get(index_to_ids_handler))
|
||||||
.route("/api/vecs/ids", get(vecids_handler))
|
.route("/api/{variant}", get(variant_handler))
|
||||||
.route("/api/vecs/indexes", get(vecindexes_handler))
|
.route(
|
||||||
.route("/api/vecs/id-to-indexes", get(vecid_to_vecindexes_handler))
|
"/api",
|
||||||
.route("/api/vecs/index-to-ids", get(vecindex_to_vecids_handler))
|
get(|| async {
|
||||||
|
Redirect::temporary(
|
||||||
|
"https://github.com/bitcoinresearchkit/brk/tree/main/crates/brk_server#api",
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn vecids_handler(State(app_state): State<AppState>) -> Response {
|
pub async fn ids_handler(State(app_state): State<AppState>) -> Response {
|
||||||
axum::Json(
|
Json(
|
||||||
app_state
|
app_state
|
||||||
.query
|
.query
|
||||||
.vec_trees
|
.vec_trees
|
||||||
@@ -48,8 +55,29 @@ pub async fn vecids_handler(State(app_state): State<AppState>) -> Response {
|
|||||||
.into_response()
|
.into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn vecindexes_handler(State(app_state): State<AppState>) -> Response {
|
pub async fn variant_count_handler(State(app_state): State<AppState>) -> Response {
|
||||||
axum::Json(
|
Json(
|
||||||
|
app_state
|
||||||
|
.query
|
||||||
|
.vec_trees
|
||||||
|
.index_to_id_to_vec
|
||||||
|
.values()
|
||||||
|
.map(|tree| tree.len())
|
||||||
|
.sum::<usize>(),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn id_count_handler(State(app_state): State<AppState>) -> Response {
|
||||||
|
Json(app_state.query.vec_trees.id_to_index_to_vec.keys().count()).into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn index_count_handler(State(app_state): State<AppState>) -> Response {
|
||||||
|
Json(app_state.query.vec_trees.index_to_id_to_vec.keys().count()).into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn indexes_handler(State(app_state): State<AppState>) -> Response {
|
||||||
|
Json(
|
||||||
app_state
|
app_state
|
||||||
.query
|
.query
|
||||||
.vec_trees
|
.vec_trees
|
||||||
@@ -61,10 +89,49 @@ pub async fn vecindexes_handler(State(app_state): State<AppState>) -> Response {
|
|||||||
.into_response()
|
.into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn vecid_to_vecindexes_handler(State(app_state): State<AppState>) -> Response {
|
pub async fn variants_handler(State(app_state): State<AppState>) -> Response {
|
||||||
axum::Json(app_state.query.vec_trees.serialize_id_to_index_to_vec()).into_response()
|
Json(
|
||||||
|
app_state
|
||||||
|
.query
|
||||||
|
.vec_trees
|
||||||
|
.index_to_id_to_vec
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(index, id_to_vec)| {
|
||||||
|
let index_ser = index.serialize_long();
|
||||||
|
id_to_vec
|
||||||
|
.keys()
|
||||||
|
.map(|id| format!("{}-to-{}", index_ser, id))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn vecindex_to_vecids_handler(State(app_state): State<AppState>) -> Response {
|
pub async fn id_to_indexes_handler(State(app_state): State<AppState>) -> Response {
|
||||||
axum::Json(app_state.query.vec_trees.serialize_index_to_id_to_vec()).into_response()
|
Json(app_state.query.vec_trees.serialize_id_to_index_to_vec()).into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn index_to_ids_handler(State(app_state): State<AppState>) -> Response {
|
||||||
|
Json(app_state.query.vec_trees.serialize_index_to_id_to_vec()).into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
const TO_SEPARATOR: &str = "-to-";
|
||||||
|
|
||||||
|
pub async fn variant_handler(
|
||||||
|
headers: HeaderMap,
|
||||||
|
Path(variant): Path<String>,
|
||||||
|
Query(params_opt): Query<ParamsOpt>,
|
||||||
|
state: State<AppState>,
|
||||||
|
) -> Response {
|
||||||
|
let variant = variant.replace("_", "-");
|
||||||
|
let mut split = variant.split(TO_SEPARATOR);
|
||||||
|
let params = Params::from((
|
||||||
|
(
|
||||||
|
split.next().unwrap().to_string(),
|
||||||
|
split.collect::<Vec<_>>().join(TO_SEPARATOR),
|
||||||
|
),
|
||||||
|
params_opt,
|
||||||
|
));
|
||||||
|
query::handler(headers, Query(params), state).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ use std::{fs, io, path::Path};
|
|||||||
|
|
||||||
use brk_query::{Index, Query};
|
use brk_query::{Index, Query};
|
||||||
|
|
||||||
use crate::Website;
|
use crate::{VERSION, Website};
|
||||||
|
|
||||||
const SCRIPTS: &str = "scripts";
|
const SCRIPTS: &str = "scripts";
|
||||||
|
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
pub trait DTS {
|
pub trait Bridge {
|
||||||
fn generate_dts_file(&self, website: Website, websites_path: &Path) -> io::Result<()>;
|
fn generate_bridge_file(&self, website: Website, websites_path: &Path) -> io::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DTS for Query<'static> {
|
impl Bridge for Query<'static> {
|
||||||
fn generate_dts_file(&self, website: Website, websites_path: &Path) -> io::Result<()> {
|
fn generate_bridge_file(&self, website: Website, websites_path: &Path) -> io::Result<()> {
|
||||||
if website.is_none() {
|
if website.is_none() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -31,10 +31,16 @@ impl DTS for Query<'static> {
|
|||||||
|
|
||||||
let indexes = Index::all();
|
let indexes = Index::all();
|
||||||
|
|
||||||
let mut contents = "//
|
let mut contents = format!(
|
||||||
|
"//
|
||||||
// File auto-generated, any modifications will be overwritten
|
// File auto-generated, any modifications will be overwritten
|
||||||
//\n\n"
|
//
|
||||||
.to_string();
|
|
||||||
|
export const VERSION = \"v{}\";
|
||||||
|
|
||||||
|
",
|
||||||
|
VERSION
|
||||||
|
);
|
||||||
|
|
||||||
contents += &indexes
|
contents += &indexes
|
||||||
.iter()
|
.iter()
|
||||||
@@ -55,9 +61,12 @@ impl DTS for Query<'static> {
|
|||||||
.join(" | ")
|
.join(" | ")
|
||||||
);
|
);
|
||||||
|
|
||||||
contents += "\n\nexport function createVecIdToIndexes() {\n";
|
contents += "\n\n/** @typedef {ReturnType<typeof createVecIdToIndexes>} VecIdToIndexes */";
|
||||||
|
contents += "\n/** @typedef {keyof VecIdToIndexes} VecId */\n";
|
||||||
|
|
||||||
contents += "\n\n return /** @type {const} */ ({\n";
|
contents += "\nexport function createVecIdToIndexes() {\n";
|
||||||
|
|
||||||
|
contents += " return {\n";
|
||||||
|
|
||||||
self.vec_trees
|
self.vec_trees
|
||||||
.id_to_index_to_vec
|
.id_to_index_to_vec
|
||||||
@@ -73,11 +82,7 @@ impl DTS for Query<'static> {
|
|||||||
contents += &format!(" \"{id}\": [{indexes}],\n");
|
contents += &format!(" \"{id}\": [{indexes}],\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
contents += " });\n";
|
contents += " };\n}\n";
|
||||||
contents.push('}');
|
|
||||||
|
|
||||||
contents += "\n/** @typedef {ReturnType<typeof createVecIdToIndexes>} VecIdToIndexes */";
|
|
||||||
contents += "\n/** @typedef {keyof VecIdToIndexes} VecId */\n";
|
|
||||||
|
|
||||||
fs::write(path, contents)
|
fs::write(path, contents)
|
||||||
}
|
}
|
||||||
@@ -5,14 +5,18 @@ use axum::{
|
|||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use brk_query::{Format, Index, Output, Params};
|
use brk_query::{Format, Index, Output, Params};
|
||||||
|
use brk_vec::{CollectableVec, StoredVec};
|
||||||
|
use color_eyre::eyre::eyre;
|
||||||
|
|
||||||
use crate::traits::{HeaderMapExtended, ModifiedState, ResponseExtended};
|
use crate::traits::{HeaderMapExtended, ModifiedState, ResponseExtended};
|
||||||
|
|
||||||
use super::AppState;
|
use super::AppState;
|
||||||
|
|
||||||
mod dts;
|
mod bridge;
|
||||||
|
|
||||||
pub use dts::*;
|
pub use bridge::*;
|
||||||
|
|
||||||
|
const MAX_WEIGHT: usize = 320_000;
|
||||||
|
|
||||||
pub async fn handler(
|
pub async fn handler(
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
@@ -33,11 +37,9 @@ pub async fn handler(
|
|||||||
fn req_to_response_res(
|
fn req_to_response_res(
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
AxumQuery(Params {
|
AxumQuery(Params {
|
||||||
format,
|
|
||||||
from,
|
|
||||||
index,
|
index,
|
||||||
to,
|
|
||||||
values,
|
values,
|
||||||
|
rest,
|
||||||
}): AxumQuery<Params>,
|
}): AxumQuery<Params>,
|
||||||
AppState { query, .. }: AppState,
|
AppState { query, .. }: AppState,
|
||||||
) -> color_eyre::Result<Response> {
|
) -> color_eyre::Result<Response> {
|
||||||
@@ -48,6 +50,27 @@ fn req_to_response_res(
|
|||||||
&values.iter().map(|v| v.as_str()).collect::<Vec<_>>(),
|
&values.iter().map(|v| v.as_str()).collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if vecs.is_empty() {
|
||||||
|
return Ok(Json(vec![] as Vec<usize>).into_response());
|
||||||
|
}
|
||||||
|
|
||||||
|
let from = rest.from();
|
||||||
|
let to = rest.to();
|
||||||
|
let format = rest.format();
|
||||||
|
|
||||||
|
let weight = vecs
|
||||||
|
.iter()
|
||||||
|
.map(|(_, v)| {
|
||||||
|
let len = v.len();
|
||||||
|
let count = StoredVec::<usize, usize>::range_count(from, to, len);
|
||||||
|
count * v.value_type_to_size_of()
|
||||||
|
})
|
||||||
|
.sum::<usize>();
|
||||||
|
|
||||||
|
if weight > MAX_WEIGHT {
|
||||||
|
return Err(eyre!("Request is too heavy, max weight is {MAX_WEIGHT}"));
|
||||||
|
}
|
||||||
|
|
||||||
let mut date_modified_opt = None;
|
let mut date_modified_opt = None;
|
||||||
|
|
||||||
if to.is_none() {
|
if to.is_none() {
|
||||||
@@ -75,8 +98,8 @@ fn req_to_response_res(
|
|||||||
Output::TSV(s) => s.into_response(),
|
Output::TSV(s) => s.into_response(),
|
||||||
Output::Json(v) => match v {
|
Output::Json(v) => match v {
|
||||||
brk_query::Value::Single(v) => Json(v).into_response(),
|
brk_query::Value::Single(v) => Json(v).into_response(),
|
||||||
brk_query::Value::List(l) => Json(l).into_response(),
|
brk_query::Value::List(v) => Json(v).into_response(),
|
||||||
brk_query::Value::Matrix(m) => Json(m).into_response(),
|
brk_query::Value::Matrix(v) => Json(v).into_response(),
|
||||||
},
|
},
|
||||||
Output::MD(s) => s.into_response(),
|
Output::MD(s) => s.into_response(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ use crate::{
|
|||||||
traits::{HeaderMapExtended, ModifiedState, ResponseExtended},
|
traits::{HeaderMapExtended, ModifiedState, ResponseExtended},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::minify::minify_js;
|
|
||||||
|
|
||||||
pub async fn file_handler(
|
pub async fn file_handler(
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
State(app_state): State<AppState>,
|
State(app_state): State<AppState>,
|
||||||
@@ -32,18 +30,14 @@ fn any_handler(
|
|||||||
app_state: AppState,
|
app_state: AppState,
|
||||||
path: Option<extract::Path<String>>,
|
path: Option<extract::Path<String>>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
let website_path = app_state
|
let dist_path = app_state.dist_path();
|
||||||
.websites_path
|
|
||||||
.as_ref()
|
|
||||||
.expect("Should never reach here is websites_path is None")
|
|
||||||
.join(app_state.website.to_folder_name());
|
|
||||||
|
|
||||||
if let Some(path) = path.as_ref() {
|
if let Some(path) = path.as_ref() {
|
||||||
let path = path.0.replace("..", "").replace("\\", "");
|
let path = path.0.replace("..", "").replace("\\", "");
|
||||||
|
|
||||||
let mut path = website_path.join(&path);
|
let mut path = dist_path.join(&path);
|
||||||
|
|
||||||
if !path.exists() {
|
if !path.exists() || path.is_dir() {
|
||||||
if path.extension().is_some() {
|
if path.extension().is_some() {
|
||||||
let mut response: Response<Body> = (
|
let mut response: Response<Body> = (
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
@@ -55,13 +49,13 @@ fn any_handler(
|
|||||||
|
|
||||||
return response;
|
return response;
|
||||||
} else {
|
} else {
|
||||||
path = website_path.join("index.html");
|
path = dist_path.join("index.html");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
path_to_response(&headers, &path)
|
path_to_response(&headers, &path)
|
||||||
} else {
|
} else {
|
||||||
path_to_response(&headers, &website_path.join("index.html"))
|
path_to_response(&headers, &dist_path.join("index.html"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,49 +79,35 @@ fn path_to_response_(headers: &HeaderMap, path: &Path) -> color_eyre::Result<Res
|
|||||||
return Ok(Response::new_not_modified());
|
return Ok(Response::new_not_modified());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut response;
|
let content = fs::read(path).unwrap_or_else(|error| {
|
||||||
|
error!("{error}");
|
||||||
|
let path = path.to_str().unwrap();
|
||||||
|
info!("Can't read file {path}");
|
||||||
|
panic!("")
|
||||||
|
});
|
||||||
|
|
||||||
let is_localhost = headers.check_if_host_is_localhost();
|
let mut response = Response::new(content.into());
|
||||||
|
|
||||||
if !is_localhost
|
|
||||||
&& path.extension().unwrap_or_else(|| {
|
|
||||||
dbg!(path);
|
|
||||||
panic!();
|
|
||||||
}) == "js"
|
|
||||||
{
|
|
||||||
let content = minify_js(path);
|
|
||||||
|
|
||||||
response = Response::new(content.into());
|
|
||||||
} else {
|
|
||||||
let content = fs::read(path).unwrap_or_else(|error| {
|
|
||||||
error!("{error}");
|
|
||||||
let path = path.to_str().unwrap();
|
|
||||||
info!("Can't read file {path}");
|
|
||||||
panic!("")
|
|
||||||
});
|
|
||||||
|
|
||||||
response = Response::new(content.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let headers = response.headers_mut();
|
let headers = response.headers_mut();
|
||||||
headers.insert_cors();
|
headers.insert_cors();
|
||||||
headers.insert_content_type(path);
|
headers.insert_content_type(path);
|
||||||
|
|
||||||
if !is_localhost {
|
let serialized_path = path.to_str().unwrap();
|
||||||
let serialized_path = path.to_str().unwrap();
|
|
||||||
|
|
||||||
if serialized_path.contains("fonts/")
|
if path
|
||||||
|| serialized_path.contains("assets/")
|
.extension()
|
||||||
|| serialized_path.contains("packages/")
|
.is_some_and(|extension| extension == "html")
|
||||||
|| path.extension().is_some_and(|extension| {
|
|| serialized_path.ends_with("service-worker.js")
|
||||||
extension == "pdf"
|
{
|
||||||
|| extension == "jpg"
|
headers.insert_cache_control_must_revalidate();
|
||||||
|| extension == "png"
|
} else if path.extension().is_some_and(|extension| {
|
||||||
|| extension == "woff2"
|
extension == "jpg"
|
||||||
})
|
|| extension == "png"
|
||||||
{
|
|| extension == "woff2"
|
||||||
headers.insert_cache_control_immutable();
|
|| extension == "js"
|
||||||
}
|
|| extension == "map"
|
||||||
|
}) {
|
||||||
|
headers.insert_cache_control_immutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.insert_last_modified(date);
|
headers.insert_last_modified(date);
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
// Source: https://github.com/oxc-project/oxc/blob/main/crates/oxc_minifier/examples/minifier.rs
|
|
||||||
|
|
||||||
use std::{fs, path::Path};
|
|
||||||
|
|
||||||
use oxc::{
|
|
||||||
allocator::Allocator,
|
|
||||||
codegen::{Codegen, CodegenOptions, LegalComment},
|
|
||||||
minifier::{CompressOptions, MangleOptions, Minifier, MinifierOptions},
|
|
||||||
parser::Parser,
|
|
||||||
span::SourceType,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn minify_js(path: &Path) -> String {
|
|
||||||
let source_text = fs::read_to_string(path).unwrap();
|
|
||||||
let source_type = SourceType::from_path(path).unwrap();
|
|
||||||
|
|
||||||
let allocator = Allocator::default();
|
|
||||||
|
|
||||||
let parser_return = Parser::new(&allocator, &source_text, source_type).parse();
|
|
||||||
|
|
||||||
let mut program = parser_return.program;
|
|
||||||
|
|
||||||
let minifier_return = Minifier::new(MinifierOptions {
|
|
||||||
mangle: Some(MangleOptions::default()),
|
|
||||||
compress: Some(CompressOptions::default()),
|
|
||||||
})
|
|
||||||
.build(&allocator, &mut program);
|
|
||||||
|
|
||||||
Codegen::new()
|
|
||||||
.with_options(CodegenOptions {
|
|
||||||
minify: true,
|
|
||||||
single_quote: false,
|
|
||||||
comments: false,
|
|
||||||
annotation_comments: false,
|
|
||||||
source_map_path: None,
|
|
||||||
legal_comments: LegalComment::None,
|
|
||||||
})
|
|
||||||
.with_scoping(minifier_return.scoping)
|
|
||||||
.build(&program)
|
|
||||||
.code
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ use axum::{Router, routing::get};
|
|||||||
use super::AppState;
|
use super::AppState;
|
||||||
|
|
||||||
mod file;
|
mod file;
|
||||||
mod minify;
|
|
||||||
mod website;
|
mod website;
|
||||||
|
|
||||||
use file::{file_handler, index_handler};
|
use file::{file_handler, index_handler};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use api::{ApiRoutes, DTS};
|
use api::{ApiRoutes, Bridge};
|
||||||
use axum::{
|
use axum::{
|
||||||
Json, Router,
|
Json, Router,
|
||||||
body::Body,
|
body::Body,
|
||||||
@@ -19,6 +19,7 @@ use axum::{
|
|||||||
routing::get,
|
routing::get,
|
||||||
serve,
|
serve,
|
||||||
};
|
};
|
||||||
|
use brk_bundler::bundle;
|
||||||
use brk_computer::Computer;
|
use brk_computer::Computer;
|
||||||
use brk_core::dot_brk_path;
|
use brk_core::dot_brk_path;
|
||||||
use brk_indexer::Indexer;
|
use brk_indexer::Indexer;
|
||||||
@@ -45,10 +46,20 @@ pub struct AppState {
|
|||||||
websites_path: Option<PathBuf>,
|
websites_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AppState {
|
||||||
|
pub fn dist_path(&self) -> PathBuf {
|
||||||
|
self.websites_path
|
||||||
|
.as_ref()
|
||||||
|
.expect("Should never reach here is websites_path is None")
|
||||||
|
.join("dist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
const DEV_PATH: &str = "../..";
|
const DEV_PATH: &str = "../..";
|
||||||
const DOWNLOADS: &str = "downloads";
|
const DOWNLOADS: &str = "downloads";
|
||||||
const WEBSITES: &str = "websites";
|
const WEBSITES: &str = "websites";
|
||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
||||||
|
|
||||||
pub struct Server(AppState);
|
pub struct Server(AppState);
|
||||||
|
|
||||||
@@ -88,7 +99,7 @@ impl Server {
|
|||||||
downloaded_websites_path
|
downloaded_websites_path
|
||||||
};
|
};
|
||||||
|
|
||||||
query.generate_dts_file(website, websites_path.as_path())?;
|
query.generate_bridge_file(website, websites_path.as_path())?;
|
||||||
|
|
||||||
Some(websites_path)
|
Some(websites_path)
|
||||||
} else {
|
} else {
|
||||||
@@ -102,9 +113,13 @@ impl Server {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn serve(self) -> color_eyre::Result<()> {
|
pub async fn serve(self, watch: bool) -> color_eyre::Result<()> {
|
||||||
let state = self.0;
|
let state = self.0;
|
||||||
|
|
||||||
|
if let Some(websites_path) = state.websites_path.clone() {
|
||||||
|
bundle(&websites_path, state.website.to_folder_name(), watch).await?;
|
||||||
|
}
|
||||||
|
|
||||||
let compression_layer = CompressionLayer::new()
|
let compression_layer = CompressionLayer::new()
|
||||||
.br(true)
|
.br(true)
|
||||||
.deflate(true)
|
.deflate(true)
|
||||||
|
|||||||
@@ -5,12 +5,11 @@ use std::{
|
|||||||
|
|
||||||
use axum::http::{
|
use axum::http::{
|
||||||
HeaderMap,
|
HeaderMap,
|
||||||
header::{self, HOST, IF_MODIFIED_SINCE},
|
header::{self, IF_MODIFIED_SINCE},
|
||||||
};
|
};
|
||||||
use jiff::{Timestamp, civil::DateTime, fmt::strtime, tz::TimeZone};
|
use jiff::{Timestamp, civil::DateTime, fmt::strtime, tz::TimeZone};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
const STALE_IF_ERROR: u64 = 30_000_000; // 1 Year ish
|
|
||||||
const MODIFIED_SINCE_FORMAT: &str = "%a, %d %b %Y %H:%M:%S GMT";
|
const MODIFIED_SINCE_FORMAT: &str = "%a, %d %b %Y %H:%M:%S GMT";
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
@@ -20,12 +19,6 @@ pub enum ModifiedState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait HeaderMapExtended {
|
pub trait HeaderMapExtended {
|
||||||
fn _get_scheme(&self) -> &str;
|
|
||||||
fn get_host(&self) -> &str;
|
|
||||||
fn check_if_host_is_local(&self) -> bool;
|
|
||||||
fn check_if_host_is_0000(&self) -> bool;
|
|
||||||
fn check_if_host_is_localhost(&self) -> bool;
|
|
||||||
|
|
||||||
fn insert_cors(&mut self);
|
fn insert_cors(&mut self);
|
||||||
|
|
||||||
fn get_if_modified_since(&self) -> Option<DateTime>;
|
fn get_if_modified_since(&self) -> Option<DateTime>;
|
||||||
@@ -36,8 +29,8 @@ pub trait HeaderMapExtended {
|
|||||||
duration: Duration,
|
duration: Duration,
|
||||||
) -> color_eyre::Result<(ModifiedState, DateTime)>;
|
) -> color_eyre::Result<(ModifiedState, DateTime)>;
|
||||||
|
|
||||||
|
fn insert_cache_control_must_revalidate(&mut self);
|
||||||
fn insert_cache_control_immutable(&mut self);
|
fn insert_cache_control_immutable(&mut self);
|
||||||
fn _insert_cache_control_revalidate(&mut self, max_age: u64, stale_while_revalidate: u64);
|
|
||||||
fn insert_last_modified(&mut self, date: DateTime);
|
fn insert_last_modified(&mut self, date: DateTime);
|
||||||
|
|
||||||
fn insert_content_disposition_attachment(&mut self);
|
fn insert_content_disposition_attachment(&mut self);
|
||||||
@@ -59,41 +52,22 @@ pub trait HeaderMapExtended {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HeaderMapExtended for HeaderMap {
|
impl HeaderMapExtended for HeaderMap {
|
||||||
fn _get_scheme(&self) -> &str {
|
|
||||||
if self.check_if_host_is_local() {
|
|
||||||
"http"
|
|
||||||
} else {
|
|
||||||
"https"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_host(&self) -> &str {
|
|
||||||
self[HOST].to_str().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_if_host_is_local(&self) -> bool {
|
|
||||||
self.check_if_host_is_localhost() || self.check_if_host_is_0000()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_if_host_is_0000(&self) -> bool {
|
|
||||||
self.get_host().contains("0.0.0.0")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_if_host_is_localhost(&self) -> bool {
|
|
||||||
self.get_host().contains("localhost")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_cors(&mut self) {
|
fn insert_cors(&mut self) {
|
||||||
self.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*".parse().unwrap());
|
self.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*".parse().unwrap());
|
||||||
self.insert(header::ACCESS_CONTROL_ALLOW_HEADERS, "*".parse().unwrap());
|
self.insert(header::ACCESS_CONTROL_ALLOW_HEADERS, "*".parse().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn insert_cache_control_must_revalidate(&mut self) {
|
||||||
|
self.insert(
|
||||||
|
header::CACHE_CONTROL,
|
||||||
|
"public, max-age=0, must-revalidate".parse().unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn insert_cache_control_immutable(&mut self) {
|
fn insert_cache_control_immutable(&mut self) {
|
||||||
self.insert(
|
self.insert(
|
||||||
header::CACHE_CONTROL,
|
header::CACHE_CONTROL,
|
||||||
format!("public, max-age=604800, immutable, stale-if-error={STALE_IF_ERROR}")
|
"public, max-age=31536000, immutable".parse().unwrap(),
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,16 +75,6 @@ impl HeaderMapExtended for HeaderMap {
|
|||||||
self.insert(header::CONTENT_DISPOSITION, "attachment".parse().unwrap());
|
self.insert(header::CONTENT_DISPOSITION, "attachment".parse().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _insert_cache_control_revalidate(&mut self, max_age: u64, stale_while_revalidate: u64) {
|
|
||||||
self.insert(
|
|
||||||
header::CACHE_CONTROL,
|
|
||||||
format!(
|
|
||||||
"public, max-age={max_age}, stale-while-revalidate={stale_while_revalidate}, stale-if-error={STALE_IF_ERROR}")
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_last_modified(&mut self, date: DateTime) {
|
fn insert_last_modified(&mut self, date: DateTime) {
|
||||||
let formatted = date
|
let formatted = date
|
||||||
.to_zoned(TimeZone::system())
|
.to_zoned(TimeZone::system())
|
||||||
@@ -167,7 +131,7 @@ impl HeaderMapExtended for HeaderMap {
|
|||||||
fn insert_content_type(&mut self, path: &Path) {
|
fn insert_content_type(&mut self, path: &Path) {
|
||||||
match path.extension().unwrap().to_str().unwrap() {
|
match path.extension().unwrap().to_str().unwrap() {
|
||||||
"js" => self.insert_content_type_application_javascript(),
|
"js" => self.insert_content_type_application_javascript(),
|
||||||
"json" => self.insert_content_type_application_json(),
|
"json" | "map" => self.insert_content_type_application_json(),
|
||||||
"html" => self.insert_content_type_text_html(),
|
"html" => self.insert_content_type_text_html(),
|
||||||
"css" => self.insert_content_type_text_css(),
|
"css" => self.insert_content_type_text_css(),
|
||||||
"toml" | "txt" => self.insert_content_type_text_plain(),
|
"toml" | "txt" => self.insert_content_type_text_plain(),
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ impl<T> Outputs<T> {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_mut_overlaping_vecs(&mut self) -> Vec<&mut T> {
|
pub fn as_mut_overlapping_vecs(&mut self) -> Vec<&mut T> {
|
||||||
[&mut self.all]
|
[&mut self.all]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(self.by_term.as_mut_vec())
|
.chain(self.by_term.as_mut_vec())
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, ValueEnum,
|
Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, ValueEnum,
|
||||||
)]
|
)]
|
||||||
pub enum Format {
|
pub enum Format {
|
||||||
#[default]
|
|
||||||
Compressed,
|
Compressed,
|
||||||
|
#[default]
|
||||||
Raw,
|
Raw,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ pub trait AnyVec: Send + Sync {
|
|||||||
}
|
}
|
||||||
fn modified_time(&self) -> Result<Duration>;
|
fn modified_time(&self) -> Result<Duration>;
|
||||||
fn index_type_to_string(&self) -> String;
|
fn index_type_to_string(&self) -> String;
|
||||||
|
fn value_type_to_size_of(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AnyIterableVec<I, T>: AnyVec {
|
pub trait AnyIterableVec<I, T>: AnyVec {
|
||||||
|
|||||||
@@ -27,13 +27,19 @@ where
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn i64_to_usize(i: i64, len: usize) -> usize {
|
fn i64_to_usize(i: i64, len: usize) -> usize {
|
||||||
if i >= 0 {
|
if i >= 0 {
|
||||||
i as usize
|
(i as usize).min(len)
|
||||||
} else {
|
} else {
|
||||||
let v = len as i64 + i;
|
let v = len as i64 + i;
|
||||||
if v < 0 { 0 } else { v as usize }
|
if v < 0 { 0 } else { v as usize }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn range_count(from: Option<i64>, to: Option<i64>, len: usize) -> usize {
|
||||||
|
let from = from.map(|i| Self::i64_to_usize(i, len));
|
||||||
|
let to = to.map(|i| Self::i64_to_usize(i, len));
|
||||||
|
(from.unwrap_or_default()..to.unwrap_or(len)).count()
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn collect_signed_range(&self, from: Option<i64>, to: Option<i64>) -> Result<Vec<T>> {
|
fn collect_signed_range(&self, from: Option<i64>, to: Option<i64>) -> Result<Vec<T>> {
|
||||||
let len = self.len();
|
let len = self.len();
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ where
|
|||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_or_read_(&self, index: usize, mmap: &Mmap) -> Result<Option<Value<T>>> {
|
fn get_or_read_(&self, index: usize, mmap: &Mmap) -> Result<Option<Value<T>>> {
|
||||||
let stored_len = mmap.len() / Self::SIZE_OF_T;
|
let stored_len = self.stored_len_(mmap);
|
||||||
|
|
||||||
if index >= stored_len {
|
if index >= stored_len {
|
||||||
let pushed = self.pushed();
|
let pushed = self.pushed();
|
||||||
@@ -53,6 +53,7 @@ where
|
|||||||
fn mmap(&self) -> &ArcSwap<Mmap>;
|
fn mmap(&self) -> &ArcSwap<Mmap>;
|
||||||
|
|
||||||
fn stored_len(&self) -> usize;
|
fn stored_len(&self) -> usize;
|
||||||
|
fn stored_len_(&self, mmap: &Mmap) -> usize;
|
||||||
|
|
||||||
fn pushed(&self) -> &[T];
|
fn pushed(&self) -> &[T];
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ use crate::{
|
|||||||
const ONE_KIB: usize = 1024;
|
const ONE_KIB: usize = 1024;
|
||||||
const ONE_MIB: usize = ONE_KIB * ONE_KIB;
|
const ONE_MIB: usize = ONE_KIB * ONE_KIB;
|
||||||
pub const MAX_CACHE_SIZE: usize = 100 * ONE_MIB;
|
pub const MAX_CACHE_SIZE: usize = 100 * ONE_MIB;
|
||||||
pub const MAX_PAGE_SIZE: usize = 16 * ONE_KIB;
|
pub const MAX_PAGE_SIZE: usize = 64 * ONE_KIB;
|
||||||
|
|
||||||
|
const VERSION: Version = Version::ONE;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CompressedVec<I, T> {
|
pub struct CompressedVec<I, T> {
|
||||||
@@ -39,7 +41,9 @@ where
|
|||||||
pub const CACHE_LENGTH: usize = MAX_CACHE_SIZE / Self::PAGE_SIZE;
|
pub const CACHE_LENGTH: usize = MAX_CACHE_SIZE / Self::PAGE_SIZE;
|
||||||
|
|
||||||
/// Same as import but will reset the folder under certain errors, so be careful !
|
/// Same as import but will reset the folder under certain errors, so be careful !
|
||||||
pub fn forced_import(path: &Path, version: Version) -> Result<Self> {
|
pub fn forced_import(path: &Path, mut version: Version) -> Result<Self> {
|
||||||
|
version = version + VERSION;
|
||||||
|
|
||||||
let res = Self::import(path, version);
|
let res = Self::import(path, version);
|
||||||
match res {
|
match res {
|
||||||
Err(Error::WrongEndian)
|
Err(Error::WrongEndian)
|
||||||
@@ -129,7 +133,7 @@ where
|
|||||||
page_index * Self::PER_PAGE
|
page_index * Self::PER_PAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stored_len_(pages_meta: &Guard<Arc<CompressedPagesMetadata>>) -> usize {
|
fn stored_len__(pages_meta: &Guard<Arc<CompressedPagesMetadata>>) -> usize {
|
||||||
if let Some(last) = pages_meta.last() {
|
if let Some(last) = pages_meta.last() {
|
||||||
(pages_meta.len() - 1) * Self::PER_PAGE + last.values_len as usize
|
(pages_meta.len() - 1) * Self::PER_PAGE + last.values_len as usize
|
||||||
} else {
|
} else {
|
||||||
@@ -178,7 +182,11 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn stored_len(&self) -> usize {
|
fn stored_len(&self) -> usize {
|
||||||
Self::stored_len_(&self.pages_meta.load())
|
Self::stored_len__(&self.pages_meta.load())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn stored_len_(&self, _: &Mmap) -> usize {
|
||||||
|
self.stored_len()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -361,6 +369,11 @@ where
|
|||||||
fn index_type_to_string(&self) -> String {
|
fn index_type_to_string(&self) -> String {
|
||||||
I::to_string()
|
I::to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn value_type_to_size_of(&self) -> usize {
|
||||||
|
size_of::<T>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, T> Clone for CompressedVec<I, T> {
|
impl<I, T> Clone for CompressedVec<I, T> {
|
||||||
@@ -472,7 +485,7 @@ where
|
|||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
let pages_meta = self.pages_meta.load();
|
let pages_meta = self.pages_meta.load();
|
||||||
let stored_len = CompressedVec::<I, T>::stored_len_(&pages_meta);
|
let stored_len = CompressedVec::<I, T>::stored_len__(&pages_meta);
|
||||||
CompressedVecIterator {
|
CompressedVecIterator {
|
||||||
vec: self,
|
vec: self,
|
||||||
guard: self.mmap().load(),
|
guard: self.mmap().load(),
|
||||||
|
|||||||
@@ -255,6 +255,11 @@ where
|
|||||||
ComputedVec::LazyFrom3(v) => v.modified_time(),
|
ComputedVec::LazyFrom3(v) => v.modified_time(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn value_type_to_size_of(&self) -> usize {
|
||||||
|
size_of::<T>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ComputedVecIterator<'a, I, T, S1I, S1T, S2I, S2T, S3I, S3T> {
|
pub enum ComputedVecIterator<'a, I, T, S1I, S1T, S2I, S2T, S3I, S3T> {
|
||||||
|
|||||||
@@ -1015,6 +1015,32 @@ where
|
|||||||
|
|
||||||
self.safe_flush(exit)
|
self.safe_flush(exit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn compute_zscore(
|
||||||
|
&mut self,
|
||||||
|
max_from: I,
|
||||||
|
ratio: &impl AnyIterableVec<I, StoredF32>,
|
||||||
|
sma: &impl AnyIterableVec<I, StoredF32>,
|
||||||
|
sd: &impl AnyIterableVec<I, StoredF32>,
|
||||||
|
exit: &Exit,
|
||||||
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
T: From<StoredF32>,
|
||||||
|
{
|
||||||
|
let mut sma_iter = sma.iter();
|
||||||
|
let mut sd_iter = sd.iter();
|
||||||
|
|
||||||
|
self.compute_transform(
|
||||||
|
max_from,
|
||||||
|
ratio,
|
||||||
|
|(i, ratio, ..)| {
|
||||||
|
let sma = sma_iter.unwrap_get_inner(i);
|
||||||
|
let sd = sd_iter.unwrap_get_inner(i);
|
||||||
|
(i, T::from((ratio - sma) / sd))
|
||||||
|
},
|
||||||
|
exit,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EagerVec<DateIndex, Sats> {
|
impl EagerVec<DateIndex, Sats> {
|
||||||
@@ -1116,12 +1142,12 @@ impl EagerVec<DateIndex, Dollars> {
|
|||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.validate_computed_version_or_reset_file(
|
self.validate_computed_version_or_reset_file(
|
||||||
Version::ZERO + self.inner.version() + stacks.version(),
|
Version::ONE + self.inner.version() + stacks.version(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let index = max_from.min(DateIndex::from(self.len()));
|
let index = max_from.min(DateIndex::from(self.len()));
|
||||||
|
|
||||||
let first_price_date = DateIndex::try_from(Date::new(2010, 8, 16)).unwrap();
|
let first_price_date = DateIndex::try_from(Date::new(2010, 7, 12)).unwrap();
|
||||||
|
|
||||||
stacks.iter_at(index).try_for_each(|(i, stack)| {
|
stacks.iter_at(index).try_for_each(|(i, stack)| {
|
||||||
let stack = stack.into_inner();
|
let stack = stack.into_inner();
|
||||||
@@ -1298,6 +1324,11 @@ where
|
|||||||
fn index_type_to_string(&self) -> String {
|
fn index_type_to_string(&self) -> String {
|
||||||
I::to_string()
|
I::to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn value_type_to_size_of(&self) -> usize {
|
||||||
|
size_of::<T>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, T> AnyIterableVec<I, T> for EagerVec<I, T>
|
impl<I, T> AnyIterableVec<I, T> for EagerVec<I, T>
|
||||||
|
|||||||
@@ -127,6 +127,11 @@ where
|
|||||||
fn index_type_to_string(&self) -> String {
|
fn index_type_to_string(&self) -> String {
|
||||||
I::to_string()
|
I::to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn value_type_to_size_of(&self) -> usize {
|
||||||
|
size_of::<T>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AnyIndexedVec: AnyVec {
|
pub trait AnyIndexedVec: AnyVec {
|
||||||
|
|||||||
@@ -146,6 +146,11 @@ where
|
|||||||
fn modified_time(&self) -> Result<std::time::Duration> {
|
fn modified_time(&self) -> Result<std::time::Duration> {
|
||||||
self.source.modified_time()
|
self.source.modified_time()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn value_type_to_size_of(&self) -> usize {
|
||||||
|
size_of::<T>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, T, S1I, S1T> AnyIterableVec<I, T> for LazyVecFrom1<I, T, S1I, S1T>
|
impl<I, T, S1I, S1T> AnyIterableVec<I, T> for LazyVecFrom1<I, T, S1I, S1T>
|
||||||
|
|||||||
@@ -194,6 +194,11 @@ where
|
|||||||
.modified_time()?
|
.modified_time()?
|
||||||
.min(self.source2.modified_time()?))
|
.min(self.source2.modified_time()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn value_type_to_size_of(&self) -> usize {
|
||||||
|
size_of::<T>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, T, S1I, S1T, S2I, S2T> AnyIterableVec<I, T> for LazyVecFrom2<I, T, S1I, S1T, S2I, S2T>
|
impl<I, T, S1I, S1T, S2I, S2T> AnyIterableVec<I, T> for LazyVecFrom2<I, T, S1I, S1T, S2I, S2T>
|
||||||
|
|||||||
@@ -229,6 +229,11 @@ where
|
|||||||
.min(self.source2.modified_time()?)
|
.min(self.source2.modified_time()?)
|
||||||
.min(self.source3.modified_time()?))
|
.min(self.source3.modified_time()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn value_type_to_size_of(&self) -> usize {
|
||||||
|
size_of::<T>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, T, S1I, S1T, S2I, S2T, S3I, S3T> AnyIterableVec<I, T>
|
impl<I, T, S1I, S1T, S2I, S2T, S3I, S3T> AnyIterableVec<I, T>
|
||||||
|
|||||||
@@ -48,8 +48,10 @@ where
|
|||||||
fs::create_dir_all(path)?;
|
fs::create_dir_all(path)?;
|
||||||
|
|
||||||
let version_path = Self::path_version_(path);
|
let version_path = Self::path_version_(path);
|
||||||
version.validate(version_path.as_ref())?;
|
|
||||||
version.write(version_path.as_ref())?;
|
if !version.validate(version_path.as_ref())? {
|
||||||
|
version.write(version_path.as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
let file = Self::open_file_(Self::path_vec_(path).as_path())?;
|
let file = Self::open_file_(Self::path_vec_(path).as_path())?;
|
||||||
let mmap = Arc::new(ArcSwap::new(Self::new_mmap(file)?));
|
let mmap = Arc::new(ArcSwap::new(Self::new_mmap(file)?));
|
||||||
@@ -102,7 +104,11 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn stored_len(&self) -> usize {
|
fn stored_len(&self) -> usize {
|
||||||
self.mmap.load().len() / Self::SIZE_OF_T
|
self.stored_len_(&self.mmap.load())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn stored_len_(&self, mmap: &Mmap) -> usize {
|
||||||
|
mmap.len() / Self::SIZE_OF_T
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -195,6 +201,11 @@ where
|
|||||||
fn index_type_to_string(&self) -> String {
|
fn index_type_to_string(&self) -> String {
|
||||||
I::to_string()
|
I::to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn value_type_to_size_of(&self) -> usize {
|
||||||
|
size_of::<T>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, T> Clone for RawVec<I, T> {
|
impl<I, T> Clone for RawVec<I, T> {
|
||||||
|
|||||||
@@ -73,6 +73,13 @@ where
|
|||||||
StoredVec::Compressed(v) => v.stored_len(),
|
StoredVec::Compressed(v) => v.stored_len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
|
fn stored_len_(&self, mmap: &Mmap) -> usize {
|
||||||
|
match self {
|
||||||
|
StoredVec::Raw(v) => v.stored_len_(mmap),
|
||||||
|
StoredVec::Compressed(v) => v.stored_len_(mmap),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pushed(&self) -> &[T] {
|
fn pushed(&self) -> &[T] {
|
||||||
@@ -149,6 +156,11 @@ where
|
|||||||
StoredVec::Compressed(v) => v.name(),
|
StoredVec::Compressed(v) => v.name(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn value_type_to_size_of(&self) -> usize {
|
||||||
|
size_of::<T>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 34 KiB |