computer: snapshot

This commit is contained in:
nym21
2025-12-28 14:57:25 +01:00
parent e77d338357
commit f08ac7f916
81 changed files with 1394 additions and 930 deletions

26
Cargo.lock generated
View File

@@ -546,6 +546,15 @@ dependencies = [
"brk_types",
]
[[package]]
name = "brk_alloc"
version = "0.1.0-alpha.1"
dependencies = [
"libmimalloc-sys",
"log",
"mimalloc",
]
[[package]]
name = "brk_bencher"
version = "0.1.0-alpha.1"
@@ -590,6 +599,7 @@ dependencies = [
name = "brk_cli"
version = "0.1.0-alpha.1"
dependencies = [
"brk_alloc",
"brk_binder",
"brk_bundler",
"brk_computer",
@@ -606,7 +616,6 @@ dependencies = [
"clap",
"color-eyre",
"log",
"mimalloc",
"minreq",
"serde",
"tokio",
@@ -629,6 +638,7 @@ name = "brk_computer"
version = "0.1.0-alpha.1"
dependencies = [
"bitcoin",
"brk_alloc",
"brk_bencher",
"brk_error",
"brk_fetcher",
@@ -644,7 +654,6 @@ dependencies = [
"color-eyre",
"derive_deref",
"log",
"mimalloc",
"pco",
"rayon",
"rustc-hash",
@@ -697,6 +706,7 @@ name = "brk_indexer"
version = "0.1.0-alpha.1"
dependencies = [
"bitcoin",
"brk_alloc",
"brk_bencher",
"brk_error",
"brk_grouper",
@@ -710,7 +720,6 @@ dependencies = [
"color-eyre",
"fjall",
"log",
"mimalloc",
"rayon",
"rlimit",
"rustc-hash",
@@ -1790,6 +1799,12 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "cty"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]]
name = "darling"
version = "0.21.3"
@@ -2935,6 +2950,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870"
dependencies = [
"cc",
"cty",
"libc",
]
@@ -4086,9 +4102,9 @@ dependencies = [
[[package]]
name = "portable-atomic"
version = "1.12.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd"
checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950"
[[package]]
name = "portable-atomic-util"

View File

@@ -40,6 +40,7 @@ aide = { version = "0.16.0-alpha.1", features = ["axum-json", "axum-query"] }
axum = "0.8.8"
bitcoin = { version = "0.32.8", features = ["serde"] }
bitcoincore-rpc = "0.19.0"
brk_alloc = { version = "0.1.0-alpha.1", path = "crates/brk_alloc" }
brk_bencher = { version = "0.1.0-alpha.1", path = "crates/brk_bencher" }
brk_binder = { version = "0.1.0-alpha.1", path = "crates/brk_binder" }
brk_bundler = { version = "0.1.0-alpha.1", path = "crates/brk_bundler" }
@@ -69,7 +70,6 @@ derive_deref = "1.1.1"
fjall = { path = "../fjall" }
jiff = "0.2.17"
log = "0.4.29"
mimalloc = { version = "0.1.48", features = ["v3"] }
minreq = { version = "2.14.1", features = ["https", "serde_json"] }
parking_lot = "0.12.5"
rayon = "1.11.0"

View File

@@ -0,0 +1,13 @@
[package]
name = "brk_alloc"
description = "Global allocator and memory utilities for brk"
version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
[dependencies]
libmimalloc-sys = { version = "0.1.44", features = ["extended"] }
log = { workspace = true }
mimalloc = { version = "0.1.48", features = ["v3"] }

146
crates/brk_alloc/src/lib.rs Normal file
View File

@@ -0,0 +1,146 @@
//! Global allocator and memory utilities for brk.
//!
//! This crate sets mimalloc as the global allocator and provides
//! utilities for monitoring and managing memory.
//! ```
use std::{fmt, mem::MaybeUninit};
use log::info;
use mimalloc::MiMalloc as Allocator;
#[global_allocator]
static GLOBAL: Allocator = Allocator;
/// Mimalloc allocator utilities
pub struct Mimalloc;
impl Mimalloc {
/// Get current mimalloc memory statistics.
/// Very fast (~100-500ns) - uses getrusage/mach syscalls, no file I/O.
#[inline]
pub fn stats() -> Stats {
let mut elapsed_msecs = MaybeUninit::uninit();
let mut user_msecs = MaybeUninit::uninit();
let mut system_msecs = MaybeUninit::uninit();
let mut current_rss = MaybeUninit::uninit();
let mut peak_rss = MaybeUninit::uninit();
let mut current_commit = MaybeUninit::uninit();
let mut peak_commit = MaybeUninit::uninit();
let mut page_faults = MaybeUninit::uninit();
unsafe {
libmimalloc_sys::mi_process_info(
elapsed_msecs.as_mut_ptr(),
user_msecs.as_mut_ptr(),
system_msecs.as_mut_ptr(),
current_rss.as_mut_ptr(),
peak_rss.as_mut_ptr(),
current_commit.as_mut_ptr(),
peak_commit.as_mut_ptr(),
page_faults.as_mut_ptr(),
);
Stats {
current_rss: current_rss.assume_init(),
peak_rss: peak_rss.assume_init(),
current_commit: current_commit.assume_init(),
peak_commit: peak_commit.assume_init(),
}
}
}
/// Eagerly free memory back to OS.
/// This is expensive - only call at natural pause points.
#[inline]
pub fn collect() {
unsafe { libmimalloc_sys::mi_collect(true) }
}
/// Collect if wasted memory exceeds threshold (in MB).
/// Returns true if collection was triggered.
pub fn collect_if_wasted_above(threshold_mb: usize) -> bool {
let stats = Self::stats();
info!("Mimalloc stats: {:?}", stats);
if stats.wasted_mb() > threshold_mb {
info!(
"Mimalloc wasted {} MB (commit: {} MB, rss: {} MB), collecting...",
stats.wasted_mb(),
stats.commit_mb(),
stats.rss_mb(),
);
Self::collect();
true
} else {
false
}
}
/// Force collection and return stats before/after.
pub fn force_collect() -> (Stats, Stats) {
let before = Self::stats();
Self::collect();
let after = Self::stats();
info!(
"Mimalloc collected: {} MB -> {} MB (freed {} MB)",
before.commit_mb(),
after.commit_mb(),
before.commit_mb().saturating_sub(after.commit_mb()),
);
(before, after)
}
}
/// Memory stats from mimalloc
#[derive(Debug, Clone, Copy)]
pub struct Stats {
/// Resident set size (physical memory used)
pub current_rss: usize,
pub peak_rss: usize,
/// Committed memory (virtual memory reserved)
pub current_commit: usize,
pub peak_commit: usize,
}
impl Stats {
/// Returns wasted memory in bytes (commit - rss).
/// High values suggest fragmentation.
#[inline]
pub fn wasted(&self) -> usize {
self.current_commit.saturating_sub(self.current_rss)
}
/// Returns wasted memory in MB.
#[inline]
pub fn wasted_mb(&self) -> usize {
self.wasted() / 1024 / 1024
}
/// Returns current RSS in MB.
#[inline]
pub fn rss_mb(&self) -> usize {
self.current_rss / 1024 / 1024
}
/// Returns current commit in MB.
#[inline]
pub fn commit_mb(&self) -> usize {
self.current_commit / 1024 / 1024
}
}
impl fmt::Display for Stats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"rss: {} MB, commit: {} MB, wasted: {} MB",
self.rss_mb(),
self.commit_mb(),
self.wasted_mb()
)
}
}

View File

@@ -579,6 +579,9 @@ fn field_to_js_type_with_generic_value(
return format!("{}<{}>", field.rust_type, type_param);
}
field.rust_type.clone()
} else if field.is_branch() {
// Non-pattern branch struct
field.rust_type.clone()
} else if let Some(accessor) = metadata.find_index_set_pattern(&field.indexes) {
format!("{}<{}>", accessor.name, value_type)
} else {

View File

@@ -625,6 +625,9 @@ fn field_to_python_type_with_generic_value(
return format!("{}[{}]", field.rust_type, type_param);
}
field.rust_type.clone()
} else if field.is_branch() {
// Non-pattern branch struct
field.rust_type.clone()
} else if let Some(accessor) = metadata.find_index_set_pattern(&field.indexes) {
// Leaf with accessor - use value_type as the generic
format!("{}[{}]", accessor.name, value_type)
@@ -744,7 +747,16 @@ fn generate_tree_class(
)
.unwrap();
}
} else if field.is_branch() {
// Non-pattern branch - instantiate the nested class
writeln!(
output,
" self.{}: {} = {}(client, f'{{base_path}}/{}')",
field_name_py, py_type, field.rust_type, field.name
)
.unwrap();
} else {
// Leaf - use MetricNode
let metric_path = if let TreeNode::Leaf(leaf) = child_node {
format!("/{}", leaf.name())
} else {

View File

@@ -417,6 +417,9 @@ fn field_to_type_annotation_with_generic(
return format!("{}<{}>", field.rust_type, type_param);
}
field.rust_type.clone()
} else if field.is_branch() {
// Non-pattern branch struct
field.rust_type.clone()
} else if let Some(accessor) = metadata.find_index_set_pattern(&field.indexes) {
format!("{}<{}>", accessor.name, value_type)
} else {
@@ -537,7 +540,16 @@ fn generate_tree_node(
)
.unwrap();
}
} else if field.is_branch() {
// Non-pattern branch - instantiate the nested struct
writeln!(
output,
" {}: {}::new(client.clone(), &format!(\"{{base_path}}/{}\")),",
field_name, field.rust_type, field.name
)
.unwrap();
} else {
// Leaf - use MetricNode
let metric_path = if let TreeNode::Leaf(leaf) = child_node {
format!("/{}", leaf.name())
} else {

View File

@@ -24,8 +24,8 @@ brk_rpc = { workspace = true }
brk_server = { workspace = true }
clap = { version = "4.5.53", features = ["derive", "string"] }
color-eyre = { workspace = true }
brk_alloc = { workspace = true }
log = { workspace = true }
mimalloc = { workspace = true }
minreq = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }

View File

@@ -17,9 +17,9 @@ use brk_iterator::Blocks;
use brk_mempool::Mempool;
use brk_query::AsyncQuery;
use brk_reader::Reader;
use brk_alloc::Mimalloc;
use brk_server::{Server, VERSION};
use log::info;
use mimalloc::MiMalloc;
use vecdb::Exit;
mod config;
@@ -28,9 +28,6 @@ mod website;
use crate::{config::Config, paths::*};
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
pub fn main() -> color_eyre::Result<()> {
// Can't increase main thread's stack size, thus we need to use another thread
thread::Builder::new()
@@ -60,6 +57,16 @@ pub fn run() -> color_eyre::Result<()> {
let mut indexer = Indexer::forced_import(&config.brkdir())?;
// Pre-run indexer if too far behind, then drop and reimport to reduce memory
let chain_height = client.get_last_height()?;
let indexed_height = indexer.vecs.starting_height();
if u32::from(chain_height) - u32::from(indexed_height) > 1000 {
indexer.index(&blocks, &client, &exit)?;
drop(indexer);
Mimalloc::collect();
indexer = Indexer::forced_import(&config.brkdir())?;
}
let mut computer = Computer::forced_import(&config.brkdir(), &indexer, config.fetcher())?;
let mempool = Mempool::new(&client);
@@ -155,6 +162,8 @@ pub fn run() -> color_eyre::Result<()> {
indexer.index(&blocks, &client, &exit)?
};
Mimalloc::collect_if_wasted_above(500);
computer.compute(&indexer, starting_indexes, &reader, &exit)?;
info!("Waiting for new blocks...");

View File

@@ -32,6 +32,6 @@ smallvec = { workspace = true }
vecdb = { workspace = true }
[dev-dependencies]
brk_alloc = { workspace = true }
brk_bencher = { workspace = true }
color-eyre = { workspace = true }
mimalloc = { workspace = true }

View File

@@ -5,6 +5,7 @@ use std::{
time::{Duration, Instant},
};
use brk_alloc::Mimalloc;
use brk_computer::Computer;
use brk_error::Result;
use brk_fetcher::Fetcher;
@@ -12,12 +13,8 @@ use brk_indexer::Indexer;
use brk_iterator::Blocks;
use brk_reader::Reader;
use brk_rpc::{Auth, Client};
use mimalloc::MiMalloc;
use vecdb::Exit;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
pub fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
@@ -56,11 +53,24 @@ fn run() -> Result<()> {
let exit = Exit::new();
exit.set_ctrlc_handler();
// Pre-run indexer if too far behind, then drop and reimport to reduce memory
let chain_height = client.get_last_height()?;
let indexed_height = indexer.vecs.starting_height();
if u32::from(chain_height) - u32::from(indexed_height) > 1000 {
indexer.checked_index(&blocks, &client, &exit)?;
drop(indexer);
Mimalloc::collect();
indexer = Indexer::forced_import(&outputs_dir)?;
}
let mut computer = Computer::forced_import(&outputs_dir, &indexer, Some(fetcher))?;
loop {
let i = Instant::now();
let starting_indexes = indexer.checked_index(&blocks, &client, &exit)?;
Mimalloc::collect_if_wasted_above(500);
computer.compute(&indexer, starting_indexes, &reader, &exit)?;
dbg!(i.elapsed());
sleep(Duration::from_secs(10));

View File

@@ -1,5 +1,6 @@
use std::{env, path::Path, thread, time::Instant};
use brk_alloc::Mimalloc;
use brk_bencher::Bencher;
use brk_computer::Computer;
use brk_error::Result;
@@ -9,12 +10,8 @@ use brk_iterator::Blocks;
use brk_reader::Reader;
use brk_rpc::{Auth, Client};
use log::{debug, info};
use mimalloc::MiMalloc;
use vecdb::Exit;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
pub fn main() -> Result<()> {
// Can't increase main thread's stack size, thus we need to use another thread
thread::Builder::new()
@@ -65,6 +62,8 @@ fn run() -> Result<()> {
let starting_indexes = indexer.index(&blocks, &client, &exit)?;
info!("Done in {:?}", i.elapsed());
Mimalloc::collect_if_wasted_above(500);
let i = Instant::now();
computer.compute(&indexer, starting_indexes, &reader, &exit)?;
info!("Done in {:?}", i.elapsed());

View File

@@ -4,12 +4,8 @@ use brk_computer::Computer;
use brk_error::Result;
use brk_fetcher::Fetcher;
use brk_indexer::Indexer;
use mimalloc::MiMalloc;
use vecdb::{AnyStoredVec, Exit};
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
pub fn main() -> Result<()> {
// Can't increase main thread's stack size, thus we need to use another thread
thread::Builder::new()

View File

@@ -5,6 +5,7 @@ use std::{
time::{Duration, Instant},
};
use brk_alloc::Mimalloc;
use brk_bencher::Bencher;
use brk_computer::Computer;
use brk_error::Result;
@@ -14,12 +15,8 @@ use brk_iterator::Blocks;
use brk_reader::Reader;
use brk_rpc::{Auth, Client};
use log::{debug, info};
use mimalloc::MiMalloc;
use vecdb::Exit;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
pub fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
@@ -76,6 +73,8 @@ fn run() -> Result<()> {
let starting_indexes = indexer.index(&blocks, &client, &exit)?;
info!("Done in {:?}", i.elapsed());
Mimalloc::collect_if_wasted_above(500);
let i = Instant::now();
computer.compute(&indexer, starting_indexes, &reader, &exit)?;
info!("Done in {:?}", i.elapsed());

View File

@@ -1,258 +1,48 @@
use std::path::Path;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{StoredF32, StoredI16, StoredU16, Version};
use vecdb::{AnyVec, Database, Exit, PAGE_SIZE};
use crate::grouped::Source;
use super::{
Indexes,
grouped::{ComputedVecsFromHeight, VecBuilderOptions},
grouped::{ConstantVecs, ReturnF32Tenths, ReturnI16, ReturnU16},
indexes,
};
pub const DB_NAME: &str = "constants";
#[derive(Clone, Traversable)]
pub struct Vecs {
db: Database,
pub constant_0: ComputedVecsFromHeight<StoredU16>,
pub constant_1: ComputedVecsFromHeight<StoredU16>,
pub constant_2: ComputedVecsFromHeight<StoredU16>,
pub constant_3: ComputedVecsFromHeight<StoredU16>,
pub constant_4: ComputedVecsFromHeight<StoredU16>,
pub constant_38_2: ComputedVecsFromHeight<StoredF32>,
pub constant_50: ComputedVecsFromHeight<StoredU16>,
pub constant_61_8: ComputedVecsFromHeight<StoredF32>,
pub constant_100: ComputedVecsFromHeight<StoredU16>,
pub constant_600: ComputedVecsFromHeight<StoredU16>,
pub constant_minus_1: ComputedVecsFromHeight<StoredI16>,
pub constant_minus_2: ComputedVecsFromHeight<StoredI16>,
pub constant_minus_3: ComputedVecsFromHeight<StoredI16>,
pub constant_minus_4: ComputedVecsFromHeight<StoredI16>,
pub constant_0: ConstantVecs<StoredU16>,
pub constant_1: ConstantVecs<StoredU16>,
pub constant_2: ConstantVecs<StoredU16>,
pub constant_3: ConstantVecs<StoredU16>,
pub constant_4: ConstantVecs<StoredU16>,
pub constant_38_2: ConstantVecs<StoredF32>,
pub constant_50: ConstantVecs<StoredU16>,
pub constant_61_8: ConstantVecs<StoredF32>,
pub constant_100: ConstantVecs<StoredU16>,
pub constant_600: ConstantVecs<StoredU16>,
pub constant_minus_1: ConstantVecs<StoredI16>,
pub constant_minus_2: ConstantVecs<StoredI16>,
pub constant_minus_3: ConstantVecs<StoredI16>,
pub constant_minus_4: ConstantVecs<StoredI16>,
}
impl Vecs {
pub fn forced_import(
parent_path: &Path,
parent_version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let db = Database::open(&parent_path.join(DB_NAME))?;
db.set_min_len(PAGE_SIZE * 1_000_000)?;
pub fn new(version: Version, indexes: &indexes::Vecs) -> Self {
let v = version + Version::ZERO;
let version = parent_version + Version::ZERO;
let this = Self {
constant_0: ComputedVecsFromHeight::forced_import(
&db,
"constant_0",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_1: ComputedVecsFromHeight::forced_import(
&db,
"constant_1",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_2: ComputedVecsFromHeight::forced_import(
&db,
"constant_2",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_3: ComputedVecsFromHeight::forced_import(
&db,
"constant_3",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_4: ComputedVecsFromHeight::forced_import(
&db,
"constant_4",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_38_2: ComputedVecsFromHeight::forced_import(
&db,
"constant_38_2",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_50: ComputedVecsFromHeight::forced_import(
&db,
"constant_50",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_61_8: ComputedVecsFromHeight::forced_import(
&db,
"constant_61_8",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_100: ComputedVecsFromHeight::forced_import(
&db,
"constant_100",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_600: ComputedVecsFromHeight::forced_import(
&db,
"constant_600",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_minus_1: ComputedVecsFromHeight::forced_import(
&db,
"constant_minus_1",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_minus_2: ComputedVecsFromHeight::forced_import(
&db,
"constant_minus_2",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_minus_3: ComputedVecsFromHeight::forced_import(
&db,
"constant_minus_3",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_minus_4: ComputedVecsFromHeight::forced_import(
&db,
"constant_minus_4",
Source::Compute,
version + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
db,
};
this.db.retain_regions(
this.iter_any_exportable()
.flat_map(|v| v.region_names())
.collect(),
)?;
this.db.compact()?;
Ok(this)
}
pub fn compute(
&mut self,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.compute_(indexes, starting_indexes, exit)?;
let _lock = exit.lock();
self.db.compact()?;
Ok(())
}
fn compute_(
&mut self,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
[
(&mut self.constant_0, 0),
(&mut self.constant_1, 1),
(&mut self.constant_2, 2),
(&mut self.constant_3, 3),
(&mut self.constant_4, 4),
(&mut self.constant_50, 50),
(&mut self.constant_100, 100),
(&mut self.constant_600, 600),
]
.into_iter()
.try_for_each(|(vec, value)| {
vec.compute_all(indexes, starting_indexes, exit, |vec| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU16::new(value)),
exit,
)?;
Ok(())
})
})?;
[
(&mut self.constant_minus_1, -1),
(&mut self.constant_minus_2, -2),
(&mut self.constant_minus_3, 3),
(&mut self.constant_minus_4, 4),
]
.into_iter()
.try_for_each(|(vec, value)| {
vec.compute_all(indexes, starting_indexes, exit, |vec| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredI16::new(value)),
exit,
)?;
Ok(())
})
})?;
[
(&mut self.constant_38_2, 38.2),
(&mut self.constant_61_8, 61.8),
]
.into_iter()
.try_for_each(|(vec, value)| {
vec.compute_all(indexes, starting_indexes, exit, |vec| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredF32::from(value)),
exit,
)?;
Ok(())
})
})?;
Ok(())
Self {
constant_0: ConstantVecs::new::<ReturnU16<0>>("constant_0", v, indexes),
constant_1: ConstantVecs::new::<ReturnU16<1>>("constant_1", v, indexes),
constant_2: ConstantVecs::new::<ReturnU16<2>>("constant_2", v, indexes),
constant_3: ConstantVecs::new::<ReturnU16<3>>("constant_3", v, indexes),
constant_4: ConstantVecs::new::<ReturnU16<4>>("constant_4", v, indexes),
constant_38_2: ConstantVecs::new::<ReturnF32Tenths<382>>("constant_38_2", v, indexes),
constant_50: ConstantVecs::new::<ReturnU16<50>>("constant_50", v, indexes),
constant_61_8: ConstantVecs::new::<ReturnF32Tenths<618>>("constant_61_8", v, indexes),
constant_100: ConstantVecs::new::<ReturnU16<100>>("constant_100", v, indexes),
constant_600: ConstantVecs::new::<ReturnU16<600>>("constant_600", v, indexes),
constant_minus_1: ConstantVecs::new::<ReturnI16<-1>>("constant_minus_1", v, indexes),
constant_minus_2: ConstantVecs::new::<ReturnI16<-2>>("constant_minus_2", v, indexes),
constant_minus_3: ConstantVecs::new::<ReturnI16<-3>>("constant_minus_3", v, indexes),
constant_minus_4: ConstantVecs::new::<ReturnI16<-4>>("constant_minus_4", v, indexes),
}
}
}

View File

@@ -0,0 +1,85 @@
use brk_traversable::Traversable;
use brk_types::{
DateIndex, DecadeIndex, Height, MonthIndex, QuarterIndex, SemesterIndex, Version, WeekIndex,
YearIndex,
};
use schemars::JsonSchema;
use serde::Serialize;
use vecdb::{Formattable, IterableCloneableVec, LazyVecFrom1, UnaryTransform, VecValue};
use crate::indexes;
/// Lazy constant vecs for all index levels.
/// Uses const generic transforms to return the same value for every index.
#[derive(Clone, Traversable)]
pub struct ConstantVecs<T>
where
T: VecValue + Formattable + Serialize + JsonSchema,
{
pub height: LazyVecFrom1<Height, T, Height, Height>,
pub dateindex: LazyVecFrom1<DateIndex, T, DateIndex, DateIndex>,
pub weekindex: LazyVecFrom1<WeekIndex, T, WeekIndex, WeekIndex>,
pub monthindex: LazyVecFrom1<MonthIndex, T, MonthIndex, MonthIndex>,
pub quarterindex: LazyVecFrom1<QuarterIndex, T, QuarterIndex, QuarterIndex>,
pub semesterindex: LazyVecFrom1<SemesterIndex, T, SemesterIndex, SemesterIndex>,
pub yearindex: LazyVecFrom1<YearIndex, T, YearIndex, YearIndex>,
pub decadeindex: LazyVecFrom1<DecadeIndex, T, DecadeIndex, DecadeIndex>,
}
impl<T: VecValue + Formattable + Serialize + JsonSchema> ConstantVecs<T> {
/// Create constant vecs using a transform that ignores input and returns a constant.
pub fn new<F>(name: &str, version: Version, indexes: &indexes::Vecs) -> Self
where
F: UnaryTransform<Height, T>
+ UnaryTransform<DateIndex, T>
+ UnaryTransform<WeekIndex, T>
+ UnaryTransform<MonthIndex, T>
+ UnaryTransform<QuarterIndex, T>
+ UnaryTransform<SemesterIndex, T>
+ UnaryTransform<YearIndex, T>
+ UnaryTransform<DecadeIndex, T>,
{
Self {
height: LazyVecFrom1::transformed::<F>(
name,
version,
indexes.height_to_height.boxed_clone(),
),
dateindex: LazyVecFrom1::transformed::<F>(
name,
version,
indexes.dateindex_to_dateindex.boxed_clone(),
),
weekindex: LazyVecFrom1::transformed::<F>(
name,
version,
indexes.weekindex_to_weekindex.boxed_clone(),
),
monthindex: LazyVecFrom1::transformed::<F>(
name,
version,
indexes.monthindex_to_monthindex.boxed_clone(),
),
quarterindex: LazyVecFrom1::transformed::<F>(
name,
version,
indexes.quarterindex_to_quarterindex.boxed_clone(),
),
semesterindex: LazyVecFrom1::transformed::<F>(
name,
version,
indexes.semesterindex_to_semesterindex.boxed_clone(),
),
yearindex: LazyVecFrom1::transformed::<F>(
name,
version,
indexes.yearindex_to_yearindex.boxed_clone(),
),
decadeindex: LazyVecFrom1::transformed::<F>(
name,
version,
indexes.decadeindex_to_decadeindex.boxed_clone(),
),
}
}
}

View File

@@ -9,7 +9,6 @@ use super::{ComputedVecValue, ComputedVecsFromDateIndex, LazyTransformBuilder};
const VERSION: Version = Version::ZERO;
/// Fully lazy version of `ComputedVecsFromDateIndex` where all vecs are lazy transforms.
#[derive(Clone)]
pub struct LazyVecsFromDateIndex<T, S1T = T>
where
@@ -42,11 +41,19 @@ where
let v = version + VERSION;
Self {
dateindex: dateindex_source.map(|s| LazyVecFrom1::transformed::<F>(name, v, s)),
dateindex_extra: LazyTransformBuilder::from_eager::<F>(name, v, &source.dateindex_extra),
dateindex_extra: LazyTransformBuilder::from_eager::<F>(
name,
v,
&source.dateindex_extra,
),
weekindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.weekindex),
monthindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.monthindex),
quarterindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.quarterindex),
semesterindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.semesterindex),
semesterindex: LazyTransformBuilder::from_lazy::<F, _, _>(
name,
v,
&source.semesterindex,
),
yearindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.yearindex),
decadeindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.decadeindex),
}

View File

@@ -0,0 +1,65 @@
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Sats, Version};
use vecdb::{IterableCloneableVec, UnaryTransform};
use super::{ComputedValueVecsFromDateIndex, LazyVecsFromDateIndex};
const VERSION: Version = Version::ZERO;
/// Fully lazy version of `ComputedValueVecsFromDateIndex` where all fields are lazy transforms.
/// Used for computed values like supply_half where sources are stored sats and dollars vecs.
#[derive(Clone, Traversable)]
pub struct LazyValueVecsFromDateIndex {
pub sats: LazyVecsFromDateIndex<Sats, Sats>,
pub bitcoin: LazyVecsFromDateIndex<Bitcoin, Sats>,
pub dollars: Option<LazyVecsFromDateIndex<Dollars, Dollars>>,
}
impl LazyValueVecsFromDateIndex {
/// Create lazy dateindex value vecs from source vecs via transforms.
///
/// - `SatsTransform`: Transform from Sats -> Sats (e.g., HalveSats)
/// - `BitcoinTransform`: Transform from Sats -> Bitcoin (e.g., HalveSatsToBitcoin)
/// - `DollarsTransform`: Transform from Dollars -> Dollars (e.g., HalveDollars)
pub fn from_source<SatsTransform, BitcoinTransform, DollarsTransform>(
name: &str,
source: &ComputedValueVecsFromDateIndex,
version: Version,
) -> Self
where
SatsTransform: UnaryTransform<Sats, Sats>,
BitcoinTransform: UnaryTransform<Sats, Bitcoin>,
DollarsTransform: UnaryTransform<Dollars, Dollars>,
{
let v = version + VERSION;
let sats = LazyVecsFromDateIndex::from_computed::<SatsTransform>(
name,
v + Version::ZERO,
source.sats.dateindex.as_ref().map(|v| v.boxed_clone()),
&source.sats,
);
let bitcoin = LazyVecsFromDateIndex::from_computed::<BitcoinTransform>(
&format!("{name}_btc"),
v + Version::ZERO,
source.sats.dateindex.as_ref().map(|v| v.boxed_clone()),
&source.sats,
);
let dollars = source.dollars.as_ref().map(|dollars_source| {
LazyVecsFromDateIndex::from_computed::<DollarsTransform>(
&format!("{name}_usd"),
v + Version::ZERO,
dollars_source.dateindex.as_ref().map(|v| v.boxed_clone()),
dollars_source,
)
});
Self {
sats,
bitcoin,
dollars,
}
}
}

View File

@@ -0,0 +1,63 @@
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Close, Dollars, Height, Sats, Version};
use vecdb::{BinaryTransform, IterableBoxedVec, LazyVecFrom1, LazyVecFrom2, UnaryTransform};
const VERSION: Version = Version::ZERO;
/// Fully lazy version of `ComputedHeightValueVecs` where all fields are lazy transforms.
/// Used for computed values like supply_half where sources are stored sats and dollars vecs.
#[derive(Clone, Traversable)]
pub struct LazyHeightValueVecs {
pub sats: LazyVecFrom1<Height, Sats, Height, Sats>,
pub bitcoin: LazyVecFrom1<Height, Bitcoin, Height, Sats>,
pub dollars: Option<LazyVecFrom2<Height, Dollars, Height, Close<Dollars>, Height, Sats>>,
}
impl LazyHeightValueVecs {
/// Create lazy height value vecs from sats and price sources via transforms.
///
/// - `SatsTransform`: Transform from Sats -> Sats (e.g., HalveSats)
/// - `BitcoinTransform`: Transform from Sats -> Bitcoin (e.g., HalveSatsToBitcoin)
/// - `DollarsTransform`: Binary transform from (Close<Dollars>, Sats) -> Dollars (e.g., HalfClosePriceTimesSats)
pub fn from_sources<SatsTransform, BitcoinTransform, DollarsTransform>(
name: &str,
sats_source: IterableBoxedVec<Height, Sats>,
price_source: Option<IterableBoxedVec<Height, Close<Dollars>>>,
version: Version,
) -> Self
where
SatsTransform: UnaryTransform<Sats, Sats>,
BitcoinTransform: UnaryTransform<Sats, Bitcoin>,
DollarsTransform: BinaryTransform<Close<Dollars>, Sats, Dollars>,
{
let v = version + VERSION;
let sats = LazyVecFrom1::transformed::<SatsTransform>(
name,
v + Version::ZERO,
sats_source.clone(),
);
let bitcoin = LazyVecFrom1::transformed::<BitcoinTransform>(
&format!("{name}_btc"),
v + Version::ZERO,
sats_source.clone(),
);
// dollars is binary transform: price × sats (with optional halving etc)
let dollars = price_source.map(|price| {
LazyVecFrom2::transformed::<DollarsTransform>(
&format!("{name}_usd"),
v + Version::ZERO,
price,
sats_source,
)
});
Self {
sats,
bitcoin,
dollars,
}
}
}

View File

@@ -3,6 +3,7 @@ mod builder_lazy;
mod builder_transform;
mod builder_transform2;
mod computed;
mod constant;
mod computed_from_dateindex;
mod computed_from_height;
mod computed_from_height_strict;
@@ -10,6 +11,8 @@ mod computed_from_txindex;
mod lazy2_from_dateindex;
mod lazy_from_dateindex;
mod lazy_from_height;
mod lazy_value_from_dateindex;
mod lazy_value_height;
// mod lazy_from_height_strict;
// mod lazy_from_txindex;
mod price_percentiles;
@@ -27,12 +30,15 @@ pub use builder_lazy::*;
pub use builder_transform::*;
pub use builder_transform2::*;
use computed::*;
pub use constant::*;
pub use computed_from_dateindex::*;
pub use computed_from_height::*;
pub use computed_from_height_strict::*;
pub use computed_from_txindex::*;
pub use lazy_from_dateindex::*;
pub use lazy_from_height::*;
pub use lazy_value_from_dateindex::*;
pub use lazy_value_height::*;
pub use lazy2_from_dateindex::*;
// pub use lazy_from_height_strict::*;
// pub use lazy_from_txindex::*;

View File

@@ -1,18 +1,19 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Date, DateIndex, Dollars, StoredF32, Version};
use vecdb::{PcoVec,
use vecdb::{
AnyStoredVec, AnyVec, CollectableVec, Database, EagerVec, Exit, GenericStoredVec, IterableVec,
TypedVecIterator, VecIndex,
PcoVec, VecIndex,
};
use crate::{
Indexes,
grouped::{
ComputedStandardDeviationVecsFromDateIndex, StandardDeviationVecsOptions, source::Source,
ComputedStandardDeviationVecsFromDateIndex, LazyVecsFrom2FromDateIndex, PriceTimesRatio,
StandardDeviationVecsOptions, source::Source,
},
indexes, price,
utils::{get_percentile, OptionExt},
utils::{OptionExt, get_percentile},
};
use super::{ComputedVecsFromDateIndex, VecBuilderOptions};
@@ -30,12 +31,12 @@ pub struct ComputedRatioVecsFromDateIndex {
pub ratio_pct5: Option<ComputedVecsFromDateIndex<StoredF32>>,
pub ratio_pct2: Option<ComputedVecsFromDateIndex<StoredF32>>,
pub ratio_pct1: Option<ComputedVecsFromDateIndex<StoredF32>>,
pub ratio_pct99_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub ratio_pct98_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub ratio_pct95_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub ratio_pct5_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub ratio_pct2_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub ratio_pct1_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub ratio_pct99_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub ratio_pct98_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub ratio_pct95_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub ratio_pct5_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub ratio_pct2_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub ratio_pct1_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub ratio_sd: Option<ComputedStandardDeviationVecsFromDateIndex>,
pub ratio_4y_sd: Option<ComputedStandardDeviationVecsFromDateIndex>,
@@ -61,23 +62,60 @@ impl ComputedRatioVecsFromDateIndex {
macro_rules! import {
($suffix:expr) => {
ComputedVecsFromDateIndex::forced_import(
db, &format!("{name}_{}", $suffix), Source::Compute, v, indexes, opts,
).unwrap()
db,
&format!("{name}_{}", $suffix),
Source::Compute,
v,
indexes,
opts,
)
.unwrap()
};
}
// Create sources first so lazy vecs can reference them
let price = source.is_compute().then(|| {
ComputedVecsFromDateIndex::forced_import(db, name, Source::Compute, v, indexes, opts)
.unwrap()
});
macro_rules! import_sd {
($suffix:expr, $days:expr) => {
ComputedStandardDeviationVecsFromDateIndex::forced_import(
db, &format!("{name}_{}", $suffix), $days, Source::Compute, v, indexes,
db,
&format!("{name}_{}", $suffix),
$days,
Source::Compute,
v,
indexes,
StandardDeviationVecsOptions::default().add_all(),
).unwrap()
price.as_ref(),
)
.unwrap()
};
}
let ratio_pct99 = extended.then(|| import!("ratio_pct99"));
let ratio_pct98 = extended.then(|| import!("ratio_pct98"));
let ratio_pct95 = extended.then(|| import!("ratio_pct95"));
let ratio_pct5 = extended.then(|| import!("ratio_pct5"));
let ratio_pct2 = extended.then(|| import!("ratio_pct2"));
let ratio_pct1 = extended.then(|| import!("ratio_pct1"));
// Create lazy usd vecs from price and ratio sources
macro_rules! lazy_usd {
($ratio:expr, $suffix:expr) => {
price.as_ref().zip($ratio.as_ref()).map(|(p, r)| {
LazyVecsFrom2FromDateIndex::from_computed::<PriceTimesRatio>(
&format!("{name}_{}", $suffix),
v,
p,
r,
)
})
};
}
Ok(Self {
price: source.is_compute().then(|| {
ComputedVecsFromDateIndex::forced_import(db, name, Source::Compute, v, indexes, opts).unwrap()
}),
ratio: import!("ratio"),
ratio_1w_sma: extended.then(|| import!("ratio_1w_sma")),
ratio_1m_sma: extended.then(|| import!("ratio_1m_sma")),
@@ -85,18 +123,19 @@ impl ComputedRatioVecsFromDateIndex {
ratio_1y_sd: extended.then(|| import_sd!("ratio_1y", 365)),
ratio_2y_sd: extended.then(|| import_sd!("ratio_2y", 2 * 365)),
ratio_4y_sd: extended.then(|| import_sd!("ratio_4y", 4 * 365)),
ratio_pct99: extended.then(|| import!("ratio_pct99")),
ratio_pct98: extended.then(|| import!("ratio_pct98")),
ratio_pct95: extended.then(|| import!("ratio_pct95")),
ratio_pct5: extended.then(|| import!("ratio_pct5")),
ratio_pct2: extended.then(|| import!("ratio_pct2")),
ratio_pct1: extended.then(|| import!("ratio_pct1")),
ratio_pct99_usd: extended.then(|| import!("ratio_pct99_usd")),
ratio_pct98_usd: extended.then(|| import!("ratio_pct98_usd")),
ratio_pct95_usd: extended.then(|| import!("ratio_pct95_usd")),
ratio_pct5_usd: extended.then(|| import!("ratio_pct5_usd")),
ratio_pct2_usd: extended.then(|| import!("ratio_pct2_usd")),
ratio_pct1_usd: extended.then(|| import!("ratio_pct1_usd")),
ratio_pct99_usd: lazy_usd!(&ratio_pct99, "ratio_pct99_usd"),
ratio_pct98_usd: lazy_usd!(&ratio_pct98, "ratio_pct98_usd"),
ratio_pct95_usd: lazy_usd!(&ratio_pct95, "ratio_pct95_usd"),
ratio_pct5_usd: lazy_usd!(&ratio_pct5, "ratio_pct5_usd"),
ratio_pct2_usd: lazy_usd!(&ratio_pct2, "ratio_pct2_usd"),
ratio_pct1_usd: lazy_usd!(&ratio_pct1, "ratio_pct1_usd"),
price,
ratio_pct99,
ratio_pct98,
ratio_pct95,
ratio_pct5,
ratio_pct2,
ratio_pct1,
})
}
@@ -128,9 +167,8 @@ impl ComputedRatioVecsFromDateIndex {
) -> Result<()> {
let closes = price.timeindexes_to_price_close.dateindex.u();
let price = price_opt.unwrap_or_else(|| unsafe {
std::mem::transmute(&self.price.u().dateindex)
});
let price =
price_opt.unwrap_or_else(|| unsafe { std::mem::transmute(&self.price.u().dateindex) });
self.ratio.compute_all(starting_indexes, exit, |v| {
v.compute_transform2(
@@ -283,83 +321,18 @@ impl ComputedRatioVecsFromDateIndex {
None as Option<&EagerVec<PcoVec<_, _>>>,
)?;
let date_to_price = price_opt.unwrap_or_else(|| unsafe {
std::mem::transmute(&self.price.u().dateindex)
});
self.ratio_pct99_usd
.as_mut()
.unwrap()
.compute_all(starting_indexes, exit, |vec| {
let mut iter = self
.ratio_pct99
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.into_iter();
vec.compute_transform(
starting_indexes.dateindex,
date_to_price,
|(i, price, ..)| {
let multiplier = iter.get_unwrap(i);
(i, price * multiplier)
},
exit,
)?;
Ok(())
})?;
let compute_usd =
|usd: Option<&mut ComputedVecsFromDateIndex<Dollars>>,
source: Option<&ComputedVecsFromDateIndex<StoredF32>>| {
usd.unwrap().compute_all(starting_indexes, exit, |vec| {
let mut iter = source.unwrap().dateindex.u().into_iter();
vec.compute_transform(
starting_indexes.dateindex,
date_to_price,
|(i, price, ..)| {
let multiplier = iter.get_unwrap(i);
(i, price * multiplier)
},
exit,
)?;
Ok(())
})
};
compute_usd(self.ratio_pct1_usd.as_mut(), self.ratio_pct1.as_ref())?;
compute_usd(self.ratio_pct2_usd.as_mut(), self.ratio_pct2.as_ref())?;
compute_usd(self.ratio_pct5_usd.as_mut(), self.ratio_pct5.as_ref())?;
compute_usd(self.ratio_pct95_usd.as_mut(), self.ratio_pct95.as_ref())?;
compute_usd(self.ratio_pct98_usd.as_mut(), self.ratio_pct98.as_ref())?;
compute_usd(self.ratio_pct99_usd.as_mut(), self.ratio_pct99.as_ref())?;
self.ratio_sd.um().compute_all(
starting_indexes,
exit,
self.ratio.dateindex.u(),
Some(date_to_price),
)?;
self.ratio_4y_sd.um().compute_all(
starting_indexes,
exit,
self.ratio.dateindex.u(),
Some(date_to_price),
)?;
self.ratio_2y_sd.um().compute_all(
starting_indexes,
exit,
self.ratio.dateindex.u(),
Some(date_to_price),
)?;
self.ratio_1y_sd.um().compute_all(
starting_indexes,
exit,
self.ratio.dateindex.u(),
Some(date_to_price),
)?;
self.ratio_sd
.um()
.compute_all(starting_indexes, exit, self.ratio.dateindex.u())?;
self.ratio_4y_sd
.um()
.compute_all(starting_indexes, exit, self.ratio.dateindex.u())?;
self.ratio_2y_sd
.um()
.compute_all(starting_indexes, exit, self.ratio.dateindex.u())?;
self.ratio_1y_sd
.um()
.compute_all(starting_indexes, exit, self.ratio.dateindex.u())?;
Ok(())
}

View File

@@ -4,13 +4,15 @@ use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Date, DateIndex, Dollars, StoredF32, Version};
use vecdb::{
AnyStoredVec, AnyVec, BoxedVecIterator, CollectableVec, Database, EagerVec, Exit,
GenericStoredVec, IterableVec, PcoVec, VecIndex,
AnyStoredVec, AnyVec, CollectableVec, Database, EagerVec, Exit, GenericStoredVec, IterableVec,
PcoVec, VecIndex,
};
use crate::{Indexes, grouped::source::Source, indexes, utils::OptionExt};
use super::{ComputedVecsFromDateIndex, VecBuilderOptions};
use super::{
ComputedVecsFromDateIndex, LazyVecsFrom2FromDateIndex, PriceTimesRatio, VecBuilderOptions,
};
#[derive(Clone, Traversable)]
pub struct ComputedStandardDeviationVecsFromDateIndex {
@@ -35,19 +37,19 @@ pub struct ComputedStandardDeviationVecsFromDateIndex {
pub m2_5sd: Option<ComputedVecsFromDateIndex<StoredF32>>,
pub m3sd: Option<ComputedVecsFromDateIndex<StoredF32>>,
pub _0sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub p0_5sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub p1sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub p1_5sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub p2sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub p2_5sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub p3sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub m0_5sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub m1sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub m1_5sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub m2sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub m2_5sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub m3sd_usd: Option<ComputedVecsFromDateIndex<Dollars>>,
pub _0sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub p0_5sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub p1sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub p1_5sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub p2sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub p2_5sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub p3sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub m0_5sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub m1sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub m1_5sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub m2sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub m2_5sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
pub m3sd_usd: Option<LazyVecsFrom2FromDateIndex<Dollars, Dollars, StoredF32>>,
}
#[derive(Debug, Default)]
@@ -104,6 +106,7 @@ impl ComputedStandardDeviationVecsFromDateIndex {
parent_version: Version,
indexes: &indexes::Vecs,
options: StandardDeviationVecsOptions,
price: Option<&ComputedVecsFromDateIndex<Dollars>>,
) -> Result<Self> {
let opts = VecBuilderOptions::default().add_last();
let version = parent_version + Version::ONE;
@@ -122,36 +125,70 @@ impl ComputedStandardDeviationVecsFromDateIndex {
};
}
// Create sources first so lazy vecs can reference them
let sma_vec = sma.is_compute().then(|| import!("sma"));
let p0_5sd = options.bands().then(|| import!("p0_5sd"));
let p1sd = options.bands().then(|| import!("p1sd"));
let p1_5sd = options.bands().then(|| import!("p1_5sd"));
let p2sd = options.bands().then(|| import!("p2sd"));
let p2_5sd = options.bands().then(|| import!("p2_5sd"));
let p3sd = options.bands().then(|| import!("p3sd"));
let m0_5sd = options.bands().then(|| import!("m0_5sd"));
let m1sd = options.bands().then(|| import!("m1sd"));
let m1_5sd = options.bands().then(|| import!("m1_5sd"));
let m2sd = options.bands().then(|| import!("m2sd"));
let m2_5sd = options.bands().then(|| import!("m2_5sd"));
let m3sd = options.bands().then(|| import!("m3sd"));
// Create lazy USD vecs from price and band sources
macro_rules! lazy_usd {
($band:expr, $suffix:expr) => {
price
.zip($band.as_ref())
.filter(|_| options.price_bands())
.map(|(p, b)| {
LazyVecsFrom2FromDateIndex::from_computed::<PriceTimesRatio>(
&format!("{name}_{}", $suffix),
version,
p,
b,
)
})
};
}
Ok(Self {
days,
sma: sma.is_compute().then(|| import!("sma")),
sd: import!("sd"),
p0_5sd: options.bands().then(|| import!("p0_5sd")),
p1sd: options.bands().then(|| import!("p1sd")),
p1_5sd: options.bands().then(|| import!("p1_5sd")),
p2sd: options.bands().then(|| import!("p2sd")),
p2_5sd: options.bands().then(|| import!("p2_5sd")),
p3sd: options.bands().then(|| import!("p3sd")),
m0_5sd: options.bands().then(|| import!("m0_5sd")),
m1sd: options.bands().then(|| import!("m1sd")),
m1_5sd: options.bands().then(|| import!("m1_5sd")),
m2sd: options.bands().then(|| import!("m2sd")),
m2_5sd: options.bands().then(|| import!("m2_5sd")),
m3sd: options.bands().then(|| import!("m3sd")),
_0sd_usd: options.price_bands().then(|| import!("0sd_usd")),
p0_5sd_usd: options.price_bands().then(|| import!("p0_5sd_usd")),
p1sd_usd: options.price_bands().then(|| import!("p1sd_usd")),
p1_5sd_usd: options.price_bands().then(|| import!("p1_5sd_usd")),
p2sd_usd: options.price_bands().then(|| import!("p2sd_usd")),
p2_5sd_usd: options.price_bands().then(|| import!("p2_5sd_usd")),
p3sd_usd: options.price_bands().then(|| import!("p3sd_usd")),
m0_5sd_usd: options.price_bands().then(|| import!("m0_5sd_usd")),
m1sd_usd: options.price_bands().then(|| import!("m1sd_usd")),
m1_5sd_usd: options.price_bands().then(|| import!("m1_5sd_usd")),
m2sd_usd: options.price_bands().then(|| import!("m2sd_usd")),
m2_5sd_usd: options.price_bands().then(|| import!("m2_5sd_usd")),
m3sd_usd: options.price_bands().then(|| import!("m3sd_usd")),
zscore: options.zscore().then(|| import!("zscore")),
// Lazy USD vecs
_0sd_usd: lazy_usd!(&sma_vec, "0sd_usd"),
p0_5sd_usd: lazy_usd!(&p0_5sd, "p0_5sd_usd"),
p1sd_usd: lazy_usd!(&p1sd, "p1sd_usd"),
p1_5sd_usd: lazy_usd!(&p1_5sd, "p1_5sd_usd"),
p2sd_usd: lazy_usd!(&p2sd, "p2sd_usd"),
p2_5sd_usd: lazy_usd!(&p2_5sd, "p2_5sd_usd"),
p3sd_usd: lazy_usd!(&p3sd, "p3sd_usd"),
m0_5sd_usd: lazy_usd!(&m0_5sd, "m0_5sd_usd"),
m1sd_usd: lazy_usd!(&m1sd, "m1sd_usd"),
m1_5sd_usd: lazy_usd!(&m1_5sd, "m1_5sd_usd"),
m2sd_usd: lazy_usd!(&m2sd, "m2sd_usd"),
m2_5sd_usd: lazy_usd!(&m2_5sd, "m2_5sd_usd"),
m3sd_usd: lazy_usd!(&m3sd, "m3sd_usd"),
// Stored band sources
sma: sma_vec,
p0_5sd,
p1sd,
p1_5sd,
p2sd,
p2_5sd,
p3sd,
m0_5sd,
m1sd,
m1_5sd,
m2sd,
m2_5sd,
m3sd,
})
}
@@ -160,7 +197,6 @@ impl ComputedStandardDeviationVecsFromDateIndex {
starting_indexes: &Indexes,
exit: &Exit,
source: &impl CollectableVec<DateIndex, StoredF32>,
price_opt: Option<&impl IterableVec<DateIndex, Dollars>>,
) -> Result<()> {
let min_date = DateIndex::try_from(Date::MIN_RATIO).unwrap();
@@ -179,17 +215,15 @@ impl ComputedStandardDeviationVecsFromDateIndex {
})?;
let sma_opt: Option<&EagerVec<PcoVec<DateIndex, StoredF32>>> = None;
self.compute_rest(starting_indexes, exit, sma_opt, source, price_opt)
self.compute_rest(starting_indexes, exit, sma_opt, source)
}
#[allow(clippy::too_many_arguments)]
pub fn compute_rest(
&mut self,
starting_indexes: &Indexes,
exit: &Exit,
sma_opt: Option<&impl IterableVec<DateIndex, StoredF32>>,
source: &impl CollectableVec<DateIndex, StoredF32>,
price_opt: Option<&impl IterableVec<DateIndex, Dollars>>,
) -> Result<()> {
let sma = sma_opt.unwrap_or_else(|| unsafe { mem::transmute(&self.sma.u().dateindex) });
@@ -371,153 +405,6 @@ impl ComputedStandardDeviationVecsFromDateIndex {
})?;
}
let Some(price) = price_opt else {
return Ok(());
};
let compute_usd =
|usd: &mut ComputedVecsFromDateIndex<Dollars>,
mut iter: BoxedVecIterator<DateIndex, StoredF32>| {
usd.compute_all(starting_indexes, exit, |vec| {
vec.compute_transform(
starting_indexes.dateindex,
price,
|(i, price, ..)| {
let multiplier = iter.get_unwrap(i);
(i, price * multiplier)
},
exit,
)?;
Ok(())
})
};
if self._0sd_usd.is_none() {
return Ok(());
}
compute_usd(self._0sd_usd.um(), sma.iter())?;
compute_usd(
self.p0_5sd_usd.um(),
self.p0_5sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
compute_usd(
self.p1sd_usd.um(),
self.p1sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
compute_usd(
self.p1_5sd_usd.um(),
self.p1_5sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
compute_usd(
self.p2sd_usd.um(),
self.p2sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
compute_usd(
self.p2_5sd_usd.um(),
self.p2_5sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
compute_usd(
self.p3sd_usd.um(),
self.p3sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
compute_usd(
self.m0_5sd_usd.um(),
self.m0_5sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
compute_usd(
self.m1sd_usd.um(),
self.m1sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
compute_usd(
self.m1_5sd_usd.um(),
self.m1_5sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
compute_usd(
self.m2sd_usd.um(),
self.m2sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
compute_usd(
self.m2_5sd_usd.um(),
self.m2_5sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
compute_usd(
self.m3sd_usd.um(),
self.m3sd
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap()
.iter(),
)?;
Ok(())
}

View File

@@ -1,4 +1,4 @@
use brk_types::{Bitcoin, Dollars, Sats, StoredF32, StoredF64};
use brk_types::{Bitcoin, Close, Dollars, Sats, StoredF32};
use vecdb::{BinaryTransform, UnaryTransform};
/// (Dollars, Dollars) -> Dollars addition
@@ -57,16 +57,6 @@ impl UnaryTransform<Sats, Bitcoin> for SatsToBitcoin {
}
}
/// Sats -> StoredF64 via Bitcoin (for coinblocks/coindays)
pub struct SatsToStoredF64;
impl UnaryTransform<Sats, StoredF64> for SatsToStoredF64 {
#[inline(always)]
fn apply(sats: Sats) -> StoredF64 {
StoredF64::from(Bitcoin::from(sats))
}
}
/// Sats -> Sats/2 (for supply_half)
pub struct HalveSats;
@@ -76,3 +66,100 @@ impl UnaryTransform<Sats, Sats> for HalveSats {
sats / 2
}
}
/// Sats -> Bitcoin/2 (halve then convert to bitcoin)
/// Avoids lazy-from-lazy by combining both transforms
pub struct HalveSatsToBitcoin;
impl UnaryTransform<Sats, Bitcoin> for HalveSatsToBitcoin {
#[inline(always)]
fn apply(sats: Sats) -> Bitcoin {
Bitcoin::from(sats / 2)
}
}
/// Dollars -> Dollars/2 (for supply_half_usd)
pub struct HalveDollars;
impl UnaryTransform<Dollars, Dollars> for HalveDollars {
#[inline(always)]
fn apply(dollars: Dollars) -> Dollars {
dollars.halved()
}
}
/// Dollars * StoredF32 -> Dollars (price × ratio)
pub struct PriceTimesRatio;
impl BinaryTransform<Dollars, StoredF32, Dollars> for PriceTimesRatio {
#[inline(always)]
fn apply(price: Dollars, ratio: StoredF32) -> Dollars {
price * ratio
}
}
/// Close<Dollars> * Sats -> Dollars (price × sats / 1e8)
/// Same as PriceTimesSats but accepts Close<Dollars> price source.
pub struct ClosePriceTimesSats;
impl BinaryTransform<Close<Dollars>, Sats, Dollars> for ClosePriceTimesSats {
#[inline(always)]
fn apply(price: Close<Dollars>, sats: Sats) -> Dollars {
*price * Bitcoin::from(sats)
}
}
/// Close<Dollars> * Sats -> Dollars/2 (price × sats / 1e8 / 2)
/// Computes halved dollars directly from sats, avoiding lazy-from-lazy chains.
pub struct HalfClosePriceTimesSats;
impl BinaryTransform<Close<Dollars>, Sats, Dollars> for HalfClosePriceTimesSats {
#[inline(always)]
fn apply(price: Close<Dollars>, sats: Sats) -> Dollars {
(*price * Bitcoin::from(sats)).halved()
}
}
// === Constant Transforms (using const generics) ===
use brk_types::{StoredI16, StoredU16};
/// Returns a constant u16 value, ignoring the input.
pub struct ReturnU16<const V: u16>;
impl<S, const V: u16> UnaryTransform<S, StoredU16> for ReturnU16<V> {
#[inline(always)]
fn apply(_: S) -> StoredU16 {
StoredU16::new(V)
}
}
/// Returns a constant i16 value, ignoring the input.
pub struct ReturnI16<const V: i16>;
impl<S, const V: i16> UnaryTransform<S, StoredI16> for ReturnI16<V> {
#[inline(always)]
fn apply(_: S) -> StoredI16 {
StoredI16::new(V)
}
}
/// Returns a constant f32 value from tenths (V=382 -> 38.2), ignoring the input.
pub struct ReturnF32Tenths<const V: u16>;
impl<S, const V: u16> UnaryTransform<S, StoredF32> for ReturnF32Tenths<V> {
#[inline(always)]
fn apply(_: S) -> StoredF32 {
StoredF32::from(V as f32 / 10.0)
}
}
/// Dollars * (V/10) -> Dollars (e.g., V=8 -> * 0.8, V=24 -> * 2.4)
pub struct DollarsTimesTenths<const V: u16>;
impl<const V: u16> UnaryTransform<Dollars, Dollars> for DollarsTimesTenths<V> {
#[inline(always)]
fn apply(d: Dollars) -> Dollars {
d * (V as f64 / 10.0)
}
}

View File

@@ -111,45 +111,6 @@ impl ComputedValueVecsFromTxindex {
})
}
// pub fn compute_all<F>(
// &mut self,
// indexer: &Indexer,
// indexes: &indexes::Vecs,
// price: Option<&marketprice::Vecs>,
// starting_indexes: &Indexes,
// exit: &Exit,
// mut compute: F,
// ) -> Result<()>
// where
// F: FnMut(
// &mut EagerVec<PcoVec<TxIndex, Sats>>,
// &Indexer,
// &indexes::Vecs,
// &Indexes,
// &Exit,
// ) -> Result<()>,
// {
// compute(
// self.sats.txindex.um(),
// indexer,
// indexes,
// starting_indexes,
// exit,
// )?;
// let txindex: Option<&PcoVec<TxIndex, Sats>> = None;
// self.compute_rest(
// indexer,
// indexes,
// fetched,
// starting_indexes,
// exit,
// txindex,
// )?;
// Ok(())
// }
pub fn compute_rest(
&mut self,
indexer: &Indexer,

View File

@@ -1,21 +1,18 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
use vecdb::{Database, EagerVec, Exit, ImportableVec, IterableCloneableVec, LazyVecFrom1, PcoVec};
use crate::{
Indexes,
grouped::{SatsToBitcoin, Source},
price,
traits::ComputeFromBitcoin,
utils::OptionExt,
use brk_types::{Bitcoin, Close, Dollars, Height, Sats, Version};
use vecdb::{
Database, EagerVec, ImportableVec, IterableBoxedVec, IterableCloneableVec, LazyVecFrom1,
LazyVecFrom2, PcoVec,
};
use crate::grouped::{ClosePriceTimesSats, SatsToBitcoin, Source};
#[derive(Clone, Traversable)]
pub struct ComputedHeightValueVecs {
pub sats: Option<EagerVec<PcoVec<Height, Sats>>>,
pub bitcoin: LazyVecFrom1<Height, Bitcoin, Height, Sats>,
pub dollars: Option<EagerVec<PcoVec<Height, Dollars>>>,
pub dollars: Option<LazyVecFrom2<Height, Dollars, Height, Close<Dollars>, Height, Sats>>,
}
const VERSION: Version = Version::ZERO;
@@ -26,74 +23,35 @@ impl ComputedHeightValueVecs {
name: &str,
source: Source<Height, Sats>,
version: Version,
compute_dollars: bool,
price_source: Option<IterableBoxedVec<Height, Close<Dollars>>>,
) -> Result<Self> {
let sats = source
.is_compute()
.then(|| EagerVec::forced_import(db, name, version + VERSION + Version::ZERO).unwrap());
let bitcoin = match &source {
Source::Compute => LazyVecFrom1::transformed::<SatsToBitcoin>(
&format!("{name}_btc"),
let sats_source: IterableBoxedVec<Height, Sats> = source
.vec()
.unwrap_or_else(|| sats.as_ref().unwrap().boxed_clone());
let bitcoin = LazyVecFrom1::transformed::<SatsToBitcoin>(
&format!("{name}_btc"),
version + VERSION + Version::ZERO,
sats_source.clone(),
);
let dollars = price_source.map(|price| {
LazyVecFrom2::transformed::<ClosePriceTimesSats>(
&format!("{name}_usd"),
version + VERSION + Version::ZERO,
sats.as_ref().unwrap().boxed_clone(),
),
Source::Vec(boxed) => LazyVecFrom1::transformed::<SatsToBitcoin>(
&format!("{name}_btc"),
version + VERSION + Version::ZERO,
boxed.clone(),
),
Source::None => {
panic!("Source::None not supported for lazy bitcoin - use Source::Vec instead")
}
};
price,
sats_source.clone(),
)
});
Ok(Self {
sats,
bitcoin,
dollars: compute_dollars.then(|| {
EagerVec::forced_import(
db,
&format!("{name}_usd"),
version + VERSION + Version::ZERO,
)
.unwrap()
}),
dollars,
})
}
pub fn compute_all<F>(
&mut self,
price: Option<&price::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
mut compute: F,
) -> Result<()>
where
F: FnMut(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
{
compute(self.sats.um())?;
self.compute_rest(price, starting_indexes, exit)?;
Ok(())
}
pub fn compute_rest(
&mut self,
price: Option<&price::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
if let Some(dollars) = self.dollars.as_mut() {
dollars.compute_from_bitcoin(
starting_indexes.height,
&self.bitcoin,
&price.u().chainindexes_to_price_close.height,
exit,
)?;
}
Ok(())
}
}

View File

@@ -96,11 +96,8 @@ impl Computer {
);
let i = Instant::now();
let (price, constants, market) = thread::scope(|s| -> Result<_> {
let constants_handle = big_thread().spawn_scoped(s, || {
constants::Vecs::forced_import(&computed_path, VERSION, &indexes)
})?;
let constants = constants::Vecs::new(VERSION, &indexes);
let (price, market) = thread::scope(|s| -> Result<_> {
let market_handle = big_thread().spawn_scoped(s, || {
market::Vecs::forced_import(&computed_path, VERSION, &indexes)
})?;
@@ -109,10 +106,9 @@ impl Computer {
.is_some()
.then(|| price::Vecs::forced_import(&computed_path, VERSION, &indexes).unwrap());
let constants = constants_handle.join().unwrap()?;
let market = market_handle.join().unwrap()?;
Ok((price, constants, market))
Ok((price, market))
})?;
info!("Imported price/constants/market in {:?}", i.elapsed());
@@ -176,7 +172,6 @@ impl Computer {
blks::DB_NAME,
chain::DB_NAME,
cointime::DB_NAME,
constants::DB_NAME,
indexes::DB_NAME,
market::DB_NAME,
pools::DB_NAME,
@@ -246,15 +241,6 @@ impl Computer {
Ok(())
});
let constants = scope.spawn(|| -> Result<()> {
info!("Computing constants...");
let i = Instant::now();
self.constants
.compute(&self.indexes, &starting_indexes, exit)?;
info!("Computed constants in {:?}", i.elapsed());
Ok(())
});
// Txins must complete before txouts (txouts needs txinindex_to_txoutindex)
// and before chain (chain needs txinindex_to_value)
info!("Computing txins...");
@@ -291,7 +277,6 @@ impl Computer {
}
blks.join().unwrap()?;
constants.join().unwrap()?;
txouts.join().unwrap()?;
Ok(())
})?;

View File

@@ -1,14 +1,13 @@
use std::thread;
use brk_error::Result;
use brk_types::{Date, DateIndex, Dollars, StoredF32, StoredU16};
use vecdb::{EagerVec, Exit, GenericStoredVec, PcoVec, TypedVecIterator, VecIndex};
use brk_types::{Date, DateIndex, StoredF32, StoredU16};
use vecdb::{Exit, GenericStoredVec, TypedVecIterator, VecIndex};
use crate::{
price,
Indexes, price,
traits::{ComputeDCAAveragePriceViaLen, ComputeDCAStackViaLen, ComputeDrawdown},
utils::OptionExt,
Indexes,
};
use super::Vecs;
@@ -576,57 +575,20 @@ impl Vecs {
Ok(())
})?;
self.indexes_to_price_200d_sma_x0_8
.compute_all(starting_indexes, exit, |v| {
v.compute_transform(
starting_indexes.dateindex,
self.indexes_to_price_200d_sma
.price
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap(),
|(i, v, ..)| (i, v * 0.8),
exit,
)?;
Ok(())
})?;
self.indexes_to_price_200d_sma_x2_4
.compute_all(starting_indexes, exit, |v| {
v.compute_transform(
starting_indexes.dateindex,
self.indexes_to_price_200d_sma
.price
.as_ref()
.unwrap()
.dateindex
.as_ref()
.unwrap(),
|(i, v, ..)| (i, v * 2.4),
exit,
)?;
Ok(())
})?;
self.indexes_to_1d_returns_1w_sd.compute_all(
starting_indexes,
exit,
self._1d_price_returns.dateindex.u(),
None as Option<&EagerVec<PcoVec<DateIndex, Dollars>>>,
)?;
self.indexes_to_1d_returns_1m_sd.compute_all(
starting_indexes,
exit,
self._1d_price_returns.dateindex.u(),
None as Option<&EagerVec<PcoVec<DateIndex, Dollars>>>,
)?;
self.indexes_to_1d_returns_1y_sd.compute_all(
starting_indexes,
exit,
self._1d_price_returns.dateindex.u(),
None as Option<&EagerVec<PcoVec<DateIndex, Dollars>>>,
)?;
self.indexes_to_price_1w_volatility

View File

@@ -3,12 +3,13 @@ use std::path::Path;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::Version;
use vecdb::{Database, EagerVec, ImportableVec, PAGE_SIZE};
use vecdb::{Database, EagerVec, ImportableVec, IterableCloneableVec, PAGE_SIZE};
use crate::{
grouped::{
ComputedRatioVecsFromDateIndex, ComputedStandardDeviationVecsFromDateIndex,
ComputedVecsFromDateIndex, Source, StandardDeviationVecsOptions, VecBuilderOptions,
ComputedVecsFromDateIndex, DollarsTimesTenths, LazyVecsFromDateIndex, Source,
StandardDeviationVecsOptions, VecBuilderOptions,
},
indexes,
};
@@ -74,6 +75,7 @@ impl Vecs {
version + $v,
indexes,
StandardDeviationVecsOptions::default(),
None, // No USD conversion for market returns
)?
};
}
@@ -88,6 +90,25 @@ impl Vecs {
};
}
let indexes_to_price_200d_sma = ratio_di!("price_200d_sma");
let price_200d_sma_source = indexes_to_price_200d_sma.price.as_ref().unwrap();
let indexes_to_price_200d_sma_x2_4 = LazyVecsFromDateIndex::from_computed::<
DollarsTimesTenths<24>,
>(
"price_200d_sma_x2_4",
version + v0,
price_200d_sma_source.dateindex.as_ref().map(|v| v.boxed_clone()),
price_200d_sma_source,
);
let indexes_to_price_200d_sma_x0_8 = LazyVecsFromDateIndex::from_computed::<
DollarsTimesTenths<8>,
>(
"price_200d_sma_x0_8",
version + v0,
price_200d_sma_source.dateindex.as_ref().map(|v| v.boxed_clone()),
price_200d_sma_source,
);
let this = Self {
height_to_price_ath: eager_h!("price_ath", v0),
height_to_price_drawdown: eager_h!("price_drawdown", v0),
@@ -112,7 +133,7 @@ impl Vecs {
indexes_to_price_55d_sma: ratio_di!("price_55d_sma"),
indexes_to_price_89d_sma: ratio_di!("price_89d_sma"),
indexes_to_price_144d_sma: ratio_di!("price_144d_sma"),
indexes_to_price_200d_sma: ratio_di!("price_200d_sma"),
indexes_to_price_200d_sma,
indexes_to_price_1y_sma: ratio_di!("price_1y_sma"),
indexes_to_price_2y_sma: ratio_di!("price_2y_sma"),
indexes_to_price_200w_sma: ratio_di!("price_200w_sma"),
@@ -247,8 +268,8 @@ impl Vecs {
dca_class_2016_returns: computed_di!("dca_class_2016_returns"),
dca_class_2015_returns: computed_di!("dca_class_2015_returns"),
indexes_to_price_200d_sma_x2_4: computed_di!("price_200d_sma_x2_4"),
indexes_to_price_200d_sma_x0_8: computed_di!("price_200d_sma_x0_8"),
indexes_to_price_200d_sma_x2_4,
indexes_to_price_200d_sma_x0_8,
dateindex_to_price_true_range: eager_di!("price_true_range", v0),
dateindex_to_price_true_range_2w_sum: eager_di!("price_true_range_2w_sum", v0),
indexes_to_price_1w_min: computed_di!("price_1w_min", v1),

View File

@@ -7,7 +7,7 @@ use vecdb::{Database, EagerVec, PcoVec};
use crate::grouped::{
ComputedRatioVecsFromDateIndex, ComputedStandardDeviationVecsFromDateIndex,
ComputedVecsFromDateIndex,
ComputedVecsFromDateIndex, LazyVecsFromDateIndex,
};
pub const DB_NAME: &str = "market";
@@ -74,8 +74,8 @@ pub struct Vecs {
pub indexes_to_price_200w_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_price_4y_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_price_200d_sma_x2_4: ComputedVecsFromDateIndex<Dollars>,
pub indexes_to_price_200d_sma_x0_8: ComputedVecsFromDateIndex<Dollars>,
pub indexes_to_price_200d_sma_x2_4: LazyVecsFromDateIndex<Dollars>,
pub indexes_to_price_200d_sma_x0_8: LazyVecsFromDateIndex<Dollars>,
pub price_1d_ago: ComputedVecsFromDateIndex<Dollars>,
pub price_1w_ago: ComputedVecsFromDateIndex<Dollars>,

View File

@@ -10,8 +10,9 @@ use vecdb::{
use crate::{
Indexes,
grouped::{
ComputedHeightValueVecs, ComputedValueVecsFromDateIndex, ComputedVecsFromHeight, Source,
VecBuilderOptions,
ComputedHeightValueVecs, ComputedValueVecsFromDateIndex, ComputedVecsFromHeight,
HalfClosePriceTimesSats, HalveDollars, HalveSats, HalveSatsToBitcoin, LazyHeightValueVecs,
LazyValueVecsFromDateIndex, Source, VecBuilderOptions,
},
indexes, price,
stateful::states::SupplyState,
@@ -37,11 +38,11 @@ pub struct SupplyMetrics {
/// UTXO count indexed by various dimensions
pub indexes_to_utxo_count: ComputedVecsFromHeight<StoredU64>,
/// Half of supply value (used for computing median)
pub height_to_supply_half_value: ComputedHeightValueVecs,
/// Half of supply value (used for computing median) - lazy from supply_value
pub height_to_supply_half_value: LazyHeightValueVecs,
/// Half of supply indexed by date
pub indexes_to_supply_half: ComputedValueVecsFromDateIndex,
/// Half of supply indexed by date - lazy from indexes_to_supply
pub indexes_to_supply_half: LazyValueVecsFromDateIndex,
}
impl SupplyMetrics {
@@ -55,27 +56,48 @@ impl SupplyMetrics {
let height_to_supply: EagerVec<PcoVec<Height, Sats>> =
EagerVec::forced_import(cfg.db, &cfg.name("supply"), cfg.version + v0)?;
let price_source = cfg
.price
.map(|p| p.chainindexes_to_price_close.height.boxed_clone());
let height_to_supply_value = ComputedHeightValueVecs::forced_import(
cfg.db,
&cfg.name("supply"),
Source::Vec(height_to_supply.boxed_clone()),
cfg.version + v0,
compute_dollars,
price_source.clone(),
)?;
let indexes_to_supply = ComputedValueVecsFromDateIndex::forced_import(
cfg.db,
&cfg.name("supply"),
Source::Compute,
cfg.version + v1,
last,
compute_dollars,
cfg.indexes,
)?;
// Create lazy supply_half from supply sources
let height_to_supply_half_value =
LazyHeightValueVecs::from_sources::<HalveSats, HalveSatsToBitcoin, HalfClosePriceTimesSats>(
&cfg.name("supply_half"),
height_to_supply.boxed_clone(),
price_source,
cfg.version + v0,
);
let indexes_to_supply_half =
LazyValueVecsFromDateIndex::from_source::<HalveSats, HalveSatsToBitcoin, HalveDollars>(
&cfg.name("supply_half"),
&indexes_to_supply,
cfg.version + v0,
);
Ok(Self {
height_to_supply,
height_to_supply_value,
indexes_to_supply: ComputedValueVecsFromDateIndex::forced_import(
cfg.db,
&cfg.name("supply"),
Source::Compute,
cfg.version + v1,
last,
compute_dollars,
cfg.indexes,
)?,
indexes_to_supply,
height_to_utxo_count: EagerVec::forced_import(
cfg.db,
@@ -92,23 +114,8 @@ impl SupplyMetrics {
last,
)?,
height_to_supply_half_value: ComputedHeightValueVecs::forced_import(
cfg.db,
&cfg.name("supply_half"),
Source::Compute,
cfg.version + v0,
compute_dollars,
)?,
indexes_to_supply_half: ComputedValueVecsFromDateIndex::forced_import(
cfg.db,
&cfg.name("supply_half"),
Source::Compute,
cfg.version + v0,
last,
compute_dollars,
cfg.indexes,
)?,
height_to_supply_half_value,
indexes_to_supply_half,
})
}
@@ -183,9 +190,6 @@ impl SupplyMetrics {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.height_to_supply_value
.compute_rest(price, starting_indexes, exit)?;
self.indexes_to_supply
.compute_all(price, starting_indexes, exit, |v| {
let mut dateindex_to_height_count_iter =
@@ -214,28 +218,6 @@ impl SupplyMetrics {
Some(&self.height_to_utxo_count),
)?;
self.height_to_supply_half_value
.compute_all(price, starting_indexes, exit, |v| {
v.compute_transform(
starting_indexes.height,
&self.height_to_supply,
|(h, v, ..)| (h, v / 2),
exit,
)?;
Ok(())
})?;
self.indexes_to_supply_half
.compute_all(price, starting_indexes, exit, |v| {
v.compute_transform(
starting_indexes.dateindex,
self.indexes_to_supply.sats.dateindex.as_ref().unwrap(),
|(i, sats, ..)| (i, sats / 2),
exit,
)?;
Ok(())
})?;
Ok(())
}
}

View File

@@ -142,19 +142,23 @@ impl UnrealizedMetrics {
let height_to_supply_in_loss: EagerVec<PcoVec<Height, Sats>> =
EagerVec::forced_import(cfg.db, &cfg.name("supply_in_loss"), cfg.version + v0)?;
let price_source = cfg
.price
.map(|p| p.chainindexes_to_price_close.height.boxed_clone());
let height_to_supply_in_profit_value = ComputedHeightValueVecs::forced_import(
cfg.db,
&cfg.name("supply_in_profit"),
Source::Vec(height_to_supply_in_profit.boxed_clone()),
cfg.version + v0,
compute_dollars,
price_source.clone(),
)?;
let height_to_supply_in_loss_value = ComputedHeightValueVecs::forced_import(
cfg.db,
&cfg.name("supply_in_loss"),
Source::Vec(height_to_supply_in_loss.boxed_clone()),
cfg.version + v0,
compute_dollars,
price_source,
)?;
Ok(Self {
@@ -341,13 +345,6 @@ impl UnrealizedMetrics {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
// Compute supply value from sats
self.height_to_supply_in_profit_value
.compute_rest(price, starting_indexes, exit)?;
self.height_to_supply_in_loss_value
.compute_rest(price, starting_indexes, exit)?;
// Compute indexes from dateindex sources
self.indexes_to_supply_in_profit.compute_rest(
price,
starting_indexes,

View File

@@ -2,11 +2,11 @@ use std::ops::{Add, AddAssign, SubAssign};
use brk_types::{CheckedSub, LoadedAddressData, Sats};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{Bytes, Formattable};
/// Current supply state tracking UTXO count and total value
#[derive(Debug, Default, Clone, Serialize, JsonSchema)]
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SupplyState {
/// Number of unspent transaction outputs
pub utxo_count: u64,

View File

@@ -27,6 +27,6 @@ rustc-hash = { workspace = true }
vecdb = { workspace = true }
[dev-dependencies]
brk_alloc = { workspace = true }
brk_bencher = { workspace = true }
color-eyre = { workspace = true }
mimalloc = { workspace = true }

View File

@@ -5,17 +5,14 @@ use std::{
time::{Duration, Instant},
};
use brk_alloc::Mimalloc;
use brk_indexer::Indexer;
use brk_iterator::Blocks;
use brk_reader::Reader;
use brk_rpc::{Auth, Client};
use log::{debug, info};
use mimalloc::MiMalloc;
use vecdb::Exit;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
@@ -52,6 +49,8 @@ fn main() -> color_eyre::Result<()> {
indexer.checked_index(&blocks, &client, &exit)?;
info!("Done in {:?}", i.elapsed());
Mimalloc::collect_if_wasted_above(500);
sleep(Duration::from_secs(60));
}
}

View File

@@ -5,6 +5,7 @@ use std::{
time::{Duration, Instant},
};
use brk_alloc::Mimalloc;
use brk_bencher::Bencher;
use brk_error::Result;
use brk_indexer::Indexer;
@@ -12,12 +13,8 @@ use brk_iterator::Blocks;
use brk_reader::Reader;
use brk_rpc::{Auth, Client};
use log::{debug, info};
use mimalloc::MiMalloc;
use vecdb::Exit;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
fn main() -> Result<()> {
brk_logger::init(None)?;
@@ -61,5 +58,9 @@ fn main() -> Result<()> {
sleep(Duration::from_secs(10));
Mimalloc::collect_if_wasted_above(500);
sleep(Duration::from_secs(10));
Ok(())
}

View File

@@ -5,6 +5,7 @@ use std::{
time::{Duration, Instant},
};
use brk_alloc::Mimalloc;
use brk_bencher::Bencher;
use brk_error::Result;
use brk_indexer::Indexer;
@@ -12,12 +13,8 @@ use brk_iterator::Blocks;
use brk_reader::Reader;
use brk_rpc::{Auth, Client};
use log::{debug, info};
use mimalloc::MiMalloc;
use vecdb::Exit;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
fn main() -> Result<()> {
brk_logger::init(None)?;
@@ -59,6 +56,8 @@ fn main() -> Result<()> {
indexer.index(&blocks, &client, &exit)?;
info!("Done in {:?}", i.elapsed());
Mimalloc::collect_if_wasted_above(500);
sleep(Duration::from_secs(60));
}
}

View File

@@ -1,12 +1,8 @@
use brk_error::Result;
use brk_indexer::Indexer;
use mimalloc::MiMalloc;
// use brk_types::Sats;
use std::{fs, path::Path};
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
fn main() -> Result<()> {
brk_logger::init(Some(Path::new(".log")))?;

View File

@@ -3,10 +3,6 @@ use std::{fs, path::Path, time::Instant};
use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::Sats;
use mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
fn run_benchmark(indexer: &Indexer) -> (Sats, std::time::Duration, usize) {
let start = Instant::now();

View File

@@ -236,9 +236,13 @@ impl Query {
}
pub fn metric_count(&self) -> MetricCount {
let total = self.total_metric_count();
let lazy = self.lazy_metric_count();
MetricCount {
distinct_metrics: self.distinct_metric_count(),
total_endpoints: self.total_metric_count(),
total_endpoints: total,
lazy_endpoints: lazy,
stored_endpoints: total - lazy,
}
}
@@ -250,6 +254,10 @@ impl Query {
self.vecs().total_metric_count
}
pub fn lazy_metric_count(&self) -> usize {
self.vecs().lazy_metric_count
}
pub fn indexes(&self) -> &[IndexInfo] {
&self.vecs().indexes
}

View File

@@ -16,6 +16,7 @@ pub struct Vecs<'a> {
pub indexes: Vec<IndexInfo>,
pub distinct_metric_count: usize,
pub total_metric_count: usize,
pub lazy_metric_count: usize,
catalog: Option<TreeNode>,
matcher: Option<QuickMatch<'a>>,
metric_to_indexes: BTreeMap<&'a str, Vec<Index>>,
@@ -61,6 +62,12 @@ impl<'a> Vecs<'a> {
.values()
.map(|tree| tree.len())
.sum::<usize>();
this.lazy_metric_count = this
.index_to_metric_to_vec
.values()
.flat_map(|tree| tree.values())
.filter(|vec| vec.region_names().is_empty())
.count();
this.indexes = this
.index_to_metric_to_vec
.keys()

View File

@@ -1,5 +1,7 @@
use std::fmt;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{Bytes, Formattable};
use crate::{EmptyAddressIndex, LoadedAddressIndex, TypeIndex};
@@ -42,8 +44,18 @@ impl Serialize for AnyAddressIndex {
}
}
impl std::fmt::Display for AnyAddressIndex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<'de> Deserialize<'de> for AnyAddressIndex {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let variant = AnyAddressDataIndexEnum::deserialize(deserializer)?;
Ok(Self::from(variant))
}
}
impl fmt::Display for AnyAddressIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
@@ -55,7 +67,7 @@ impl Formattable for AnyAddressIndex {
}
}
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AnyAddressDataIndexEnum {
Loaded(LoadedAddressIndex),
@@ -73,3 +85,13 @@ impl From<AnyAddressIndex> for AnyAddressDataIndexEnum {
}
}
}
impl From<AnyAddressDataIndexEnum> for AnyAddressIndex {
#[inline]
fn from(value: AnyAddressDataIndexEnum) -> Self {
match value {
AnyAddressDataIndexEnum::Loaded(idx) => Self::from(idx),
AnyAddressDataIndexEnum::Empty(idx) => Self::from(idx),
}
}
}

View File

@@ -1,8 +1,8 @@
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
/// A single block rewards data point.
#[derive(Debug, Serialize, JsonSchema)]
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct BlockRewardsEntry {
pub avg_height: u32,

View File

@@ -1,8 +1,8 @@
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
/// A single block size data point.
#[derive(Debug, Serialize, JsonSchema)]
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct BlockSizeEntry {
pub avg_height: u32,

View File

@@ -1,10 +1,10 @@
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use super::{BlockSizeEntry, BlockWeightEntry};
/// Combined block sizes and weights response.
#[derive(Debug, Serialize, JsonSchema)]
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct BlockSizesWeights {
pub sizes: Vec<BlockSizeEntry>,
pub weights: Vec<BlockWeightEntry>,

View File

@@ -1,10 +1,10 @@
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use crate::{BlockHash, Height};
/// Block information returned for timestamp queries
#[derive(Debug, Clone, Serialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct BlockTimestamp {
/// Block height
pub height: Height,

View File

@@ -1,8 +1,8 @@
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
/// A single block weight data point.
#[derive(Debug, Serialize, JsonSchema)]
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct BlockWeightEntry {
pub avg_height: u32,

View File

@@ -1,6 +1,6 @@
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::Bytes;
#[derive(
@@ -13,6 +13,7 @@ use vecdb::Bytes;
PartialOrd,
Ord,
Serialize,
Deserialize,
Bytes,
Hash,
JsonSchema,
@@ -37,6 +38,7 @@ impl From<&[u8]> for U8x2 {
PartialOrd,
Ord,
Serialize,
Deserialize,
Bytes,
Hash,
JsonSchema,
@@ -61,6 +63,7 @@ impl From<&[u8]> for U8x20 {
PartialOrd,
Ord,
Serialize,
Deserialize,
Bytes,
Hash,
JsonSchema,
@@ -75,7 +78,20 @@ impl From<&[u8]> for U8x32 {
}
}
#[derive(Debug, Clone, Deref, DerefMut, PartialEq, Eq, PartialOrd, Ord, Bytes, Hash, Serialize)]
#[derive(
Debug,
Clone,
Deref,
DerefMut,
PartialEq,
Eq,
PartialOrd,
Ord,
Bytes,
Hash,
Serialize,
Deserialize,
)]
pub struct U8x33(#[serde(with = "serde_bytes")] [u8; 33]);
impl JsonSchema for U8x33 {
@@ -98,7 +114,20 @@ impl From<&[u8]> for U8x33 {
}
}
#[derive(Debug, Clone, Deref, DerefMut, PartialEq, Eq, PartialOrd, Ord, Bytes, Hash, Serialize)]
#[derive(
Debug,
Clone,
Deref,
DerefMut,
PartialEq,
Eq,
PartialOrd,
Ord,
Bytes,
Hash,
Serialize,
Deserialize,
)]
pub struct U8x65(#[serde(with = "serde_bytes")] [u8; 65]);
impl JsonSchema for U8x65 {

View File

@@ -1,13 +1,24 @@
use std::ops::{Add, Div, Mul, Sub};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco};
use super::Dollars;
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Pco, JsonSchema,
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]
pub struct Cents(i64);

View File

@@ -1,6 +1,8 @@
use std::fmt;
use jiff::{Span, Zoned, civil::Date as Date_, tz::TimeZone};
use schemars::JsonSchema;
use serde::{Serialize, Serializer};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor};
use vecdb::{Formattable, Pco};
use crate::ONE_DAY_IN_SEC_F64;
@@ -118,8 +120,49 @@ impl Serialize for Date {
}
}
impl std::fmt::Display for Date {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<'de> Deserialize<'de> for Date {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct DateVisitor;
impl Visitor<'_> for DateVisitor {
type Value = Date;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a date string in YYYY-MM-DD format")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
// Parse YYYY-MM-DD format
if v.len() != 10 {
return Err(E::invalid_length(v.len(), &self));
}
let year: u16 = v[0..4]
.parse()
.map_err(|_| E::invalid_value(serde::de::Unexpected::Str(v), &self))?;
let month: u8 = v[5..7]
.parse()
.map_err(|_| E::invalid_value(serde::de::Unexpected::Str(v), &self))?;
let day: u8 = v[8..10]
.parse()
.map_err(|_| E::invalid_value(serde::de::Unexpected::Str(v), &self))?;
Ok(Date::new(year, month, day))
}
}
deserializer.deserialize_str(DateVisitor)
}
}
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buf = itoa::Buffer::new();
f.write_str(buf.format(self.year()))?;

View File

@@ -1,9 +1,12 @@
use std::ops::{Add, Rem};
use std::{
fmt,
ops::{Add, Rem},
};
use brk_error::Error;
use jiff::Span;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, FromCoarserIndex, Pco, PrintableIndex};
use crate::{DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, WeekIndex, YearIndex};
@@ -11,7 +14,18 @@ use crate::{DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, WeekIndex, Yea
use super::Date;
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Pco, JsonSchema,
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]
pub struct DateIndex(u16);
@@ -227,8 +241,8 @@ impl PrintableIndex for DateIndex {
}
}
impl std::fmt::Display for DateIndex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl fmt::Display for DateIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buf = itoa::Buffer::new();
let str = buf.format(self.0);
f.write_str(str)

View File

@@ -48,6 +48,10 @@ impl Dollars {
pub fn is_zero(&self) -> bool {
self.0 == 0.0
}
pub fn halved(self) -> Self {
Self(self.0 / 2.0)
}
}
impl From<f32> for Dollars {

View File

@@ -2,13 +2,25 @@ use std::ops::Add;
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deref, Serialize, Pco, JsonSchema,
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Deref,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]
pub struct EmptyAddressIndex(TypeIndex);

View File

@@ -1,14 +1,27 @@
use std::ops::Add;
use std::{fmt, ops::Add};
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
#[derive(
Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deref, DerefMut, Default, Serialize, Pco, JsonSchema,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]
pub struct EmptyOutputIndex(TypeIndex);
impl From<TypeIndex> for EmptyOutputIndex {
@@ -58,8 +71,8 @@ impl PrintableIndex for EmptyOutputIndex {
}
}
impl std::fmt::Display for EmptyOutputIndex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl fmt::Display for EmptyOutputIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

View File

@@ -4,13 +4,13 @@ use std::{
};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{Formattable, Pco};
use super::{Sats, VSize};
/// Fee rate in sats/vB
#[derive(Debug, Default, Clone, Copy, Serialize, Pco, JsonSchema)]
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, Pco, JsonSchema)]
pub struct FeeRate(f64);
impl FeeRate {

View File

@@ -2,13 +2,25 @@ use std::ops::Add;
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
#[derive(
Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deref, Default, Serialize, Pco, JsonSchema,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]
pub struct LoadedAddressIndex(TypeIndex);

View File

@@ -10,4 +10,10 @@ pub struct MetricCount {
/// Total number of metric-index combinations across all timeframes
#[schemars(example = 21000)]
pub total_endpoints: usize,
/// Number of lazy (computed on-the-fly) metric-index combinations
#[schemars(example = 5000)]
pub lazy_endpoints: usize,
/// Number of eager (stored on disk) metric-index combinations
#[schemars(example = 16000)]
pub stored_endpoints: usize,
}

View File

@@ -6,7 +6,11 @@ use std::{
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::{Serialize, Serializer, ser::SerializeTuple};
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{SeqAccess, Visitor},
ser::SerializeTuple,
};
use vecdb::{Bytes, Formattable, Pco, TransparentPco};
use crate::StoredF64;
@@ -61,6 +65,58 @@ impl Serialize for OHLCCents {
}
}
macro_rules! impl_ohlc_deserialize {
($ohlc_type:ty, $inner_type:ty) => {
impl<'de> Deserialize<'de> for $ohlc_type {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct OHLCVisitor;
impl<'de> Visitor<'de> for OHLCVisitor {
type Value = $ohlc_type;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a tuple of 4 elements (open, high, low, close)")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let open = seq
.next_element::<$inner_type>()?
.ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
let high = seq
.next_element::<$inner_type>()?
.ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
let low = seq
.next_element::<$inner_type>()?
.ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
let close = seq
.next_element::<$inner_type>()?
.ok_or_else(|| serde::de::Error::invalid_length(3, &self))?;
Ok(Self::Value {
open: Open::new(open),
high: High::new(high),
low: Low::new(low),
close: Close::new(close),
})
}
}
deserializer.deserialize_tuple(4, OHLCVisitor)
}
}
};
}
impl_ohlc_deserialize!(OHLCCents, Cents);
impl_ohlc_deserialize!(OHLCDollars, Dollars);
impl_ohlc_deserialize!(OHLCSats, Sats);
impl Display for OHLCCents {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(

View File

@@ -2,7 +2,7 @@ use std::ops::Add;
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
@@ -19,6 +19,7 @@ use crate::TypeIndex;
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]

View File

@@ -1,12 +1,10 @@
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use vecdb::{Formattable, Pco};
use crate::{TxIndex, Vout};
#[derive(
Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Serialize, JsonSchema, Hash, Pco,
)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, JsonSchema, Hash, Pco)]
pub struct OutPoint(u64);
impl OutPoint {
@@ -54,3 +52,31 @@ impl Formattable for OutPoint {
true
}
}
impl Serialize for OutPoint {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use serde::ser::SerializeStruct;
let mut state = serializer.serialize_struct("OutPoint", 2)?;
state.serialize_field("txindex", &self.txindex())?;
state.serialize_field("vout", &self.vout())?;
state.end()
}
}
impl<'de> Deserialize<'de> for OutPoint {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Helper {
txindex: TxIndex,
vout: Vout,
}
let h = Helper::deserialize(deserializer)?;
Ok(Self::new(h.txindex, h.vout))
}
}

View File

@@ -1,13 +1,26 @@
use bitcoin::{AddressType, ScriptBuf, opcodes::all::OP_PUSHBYTES_2};
use brk_error::Error;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use strum::Display;
use vecdb::{Bytes, Formattable, Pco, TransparentPco};
use crate::AddressBytes;
#[derive(Debug, Clone, Copy, Display, PartialEq, Eq, PartialOrd, Ord, Serialize, JsonSchema, Hash)]
#[derive(
Debug,
Clone,
Copy,
Display,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
JsonSchema,
Hash,
)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
#[repr(u16)]

View File

@@ -2,7 +2,7 @@ use std::ops::Add;
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
@@ -19,6 +19,7 @@ use crate::TypeIndex;
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]

View File

@@ -2,12 +2,25 @@ use std::fmt;
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{Bytes, Formattable};
use crate::U8x2;
#[derive(Debug, Clone, Deref, PartialEq, Eq, PartialOrd, Ord, Serialize, Bytes, Hash, JsonSchema)]
#[derive(
Debug,
Clone,
Deref,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Bytes,
Hash,
JsonSchema,
)]
pub struct P2ABytes(U8x2);
impl From<&[u8]> for P2ABytes {

View File

@@ -2,7 +2,7 @@ use std::ops::Add;
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
@@ -19,6 +19,7 @@ use crate::TypeIndex;
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]

View File

@@ -2,7 +2,7 @@ use std::ops::Add;
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
@@ -19,6 +19,7 @@ use crate::TypeIndex;
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]

View File

@@ -2,12 +2,25 @@ use std::fmt;
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{Bytes, Formattable};
use crate::U8x33;
#[derive(Debug, Clone, Deref, PartialEq, Eq, PartialOrd, Ord, Serialize, Bytes, Hash, JsonSchema)]
#[derive(
Debug,
Clone,
Deref,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Bytes,
Hash,
JsonSchema,
)]
pub struct P2PK33Bytes(U8x33);
impl From<&[u8]> for P2PK33Bytes {

View File

@@ -2,7 +2,7 @@ use std::ops::Add;
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
@@ -19,6 +19,7 @@ use crate::TypeIndex;
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]

View File

@@ -2,12 +2,25 @@ use std::fmt;
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{Bytes, Formattable};
use crate::U8x65;
#[derive(Debug, Clone, Deref, PartialEq, Eq, PartialOrd, Ord, Serialize, Bytes, Hash, JsonSchema)]
#[derive(
Debug,
Clone,
Deref,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Bytes,
Hash,
JsonSchema,
)]
pub struct P2PK65Bytes(U8x65);
impl From<&[u8]> for P2PK65Bytes {

View File

@@ -2,7 +2,7 @@ use std::ops::Add;
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
@@ -19,6 +19,7 @@ use crate::TypeIndex;
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]

View File

@@ -2,12 +2,25 @@ use std::fmt;
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{Bytes, Formattable};
use crate::U8x20;
#[derive(Debug, Clone, Deref, PartialEq, Eq, PartialOrd, Ord, Serialize, Bytes, Hash, JsonSchema)]
#[derive(
Debug,
Clone,
Deref,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Bytes,
Hash,
JsonSchema,
)]
pub struct P2PKHBytes(U8x20);
impl From<&[u8]> for P2PKHBytes {

View File

@@ -2,7 +2,7 @@ use std::ops::Add;
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
@@ -19,6 +19,7 @@ use crate::TypeIndex;
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]

View File

@@ -2,12 +2,25 @@ use std::fmt;
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{Bytes, Formattable};
use crate::U8x20;
#[derive(Debug, Clone, Deref, PartialEq, Eq, PartialOrd, Ord, Serialize, Bytes, Hash, JsonSchema)]
#[derive(
Debug,
Clone,
Deref,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Bytes,
Hash,
JsonSchema,
)]
pub struct P2SHBytes(U8x20);
impl From<&[u8]> for P2SHBytes {

View File

@@ -2,7 +2,7 @@ use std::ops::Add;
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
@@ -19,6 +19,7 @@ use crate::TypeIndex;
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]

View File

@@ -2,12 +2,25 @@ use std::fmt;
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{Bytes, Formattable};
use crate::U8x32;
#[derive(Debug, Clone, Deref, PartialEq, Eq, PartialOrd, Ord, Serialize, Bytes, Hash, JsonSchema)]
#[derive(
Debug,
Clone,
Deref,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Bytes,
Hash,
JsonSchema,
)]
pub struct P2TRBytes(U8x32);
impl From<&[u8]> for P2TRBytes {

View File

@@ -2,7 +2,7 @@ use std::ops::Add;
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
@@ -19,6 +19,7 @@ use crate::TypeIndex;
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]

View File

@@ -2,12 +2,25 @@ use std::fmt;
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{Bytes, Formattable};
use crate::U8x20;
#[derive(Debug, Clone, Deref, PartialEq, Eq, PartialOrd, Ord, Serialize, Bytes, Hash, JsonSchema)]
#[derive(
Debug,
Clone,
Deref,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Bytes,
Hash,
JsonSchema,
)]
pub struct P2WPKHBytes(U8x20);
impl From<&[u8]> for P2WPKHBytes {

View File

@@ -2,7 +2,7 @@ use std::ops::Add;
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
@@ -19,6 +19,7 @@ use crate::TypeIndex;
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]

View File

@@ -2,12 +2,25 @@ use std::fmt;
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{Bytes, Formattable};
use crate::U8x32;
#[derive(Debug, Clone, Deref, PartialEq, Eq, PartialOrd, Ord, Serialize, Bytes, Hash, JsonSchema)]
#[derive(
Debug,
Clone,
Deref,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Bytes,
Hash,
JsonSchema,
)]
pub struct P2WSHBytes(U8x32);
impl From<&[u8]> for P2WSHBytes {

View File

@@ -1,3 +1,5 @@
use std::borrow::Cow;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

View File

@@ -2,11 +2,23 @@ use std::ops::{Add, AddAssign, Div};
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
#[derive(
Debug, Deref, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Pco, JsonSchema,
Debug,
Deref,
Clone,
Default,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]
pub struct StoredI16(i16);

View File

@@ -2,7 +2,7 @@ use std::ops::{Add, AddAssign, Div};
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use super::{
@@ -12,7 +12,19 @@ use super::{
};
#[derive(
Debug, Deref, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Pco, JsonSchema,
Debug,
Deref,
Clone,
Default,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]
pub struct StoredU16(u16);

View File

@@ -2,13 +2,26 @@ use std::ops::Add;
use derive_deref::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
use crate::TypeIndex;
#[derive(
Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deref, DerefMut, Default, Serialize, Pco, JsonSchema,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Copy,
Deref,
DerefMut,
Default,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]
pub struct UnknownOutputIndex(TypeIndex);