diff --git a/Cargo.lock b/Cargo.lock index 85a7e9ecc..b420a311e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -336,15 +336,6 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" -[[package]] -name = "block2" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" -dependencies = [ - "objc2", -] - [[package]] name = "brk" version = "0.1.9" @@ -826,17 +817,11 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -1078,17 +1063,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" -[[package]] -name = "ctrlc" -version = "3.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" -dependencies = [ - "dispatch2", - "nix", - "windows-sys 0.61.2", -] - [[package]] name = "cty" version = "0.2.2" @@ -1153,18 +1127,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "dispatch2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" -dependencies = [ - "bitflags 2.11.0", - "block2", - "libc", - "objc2", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -1935,9 +1897,9 @@ checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" [[package]] name = "js-sys" -version = "0.3.88" +version = "0.3.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e709f3e3d22866f9c25b3aff01af289b18422cc8b4262fb19103ee80fe513d" +checksum = "f4eacb0641a310445a4c513f2a5e23e19952e269c6a38887254d5f837a305506" dependencies = [ "once_cell", "wasm-bindgen", @@ -2170,18 +2132,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "nix" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225e7cfe711e0ba79a68baeddb2982723e4235247aefce1482f2f16c27865b66" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nom" version = "7.1.3" @@ -2218,21 +2168,6 @@ dependencies = [ "url", ] -[[package]] -name = "objc2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" -dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - [[package]] name = "object" version = "0.37.3" @@ -3351,7 +3286,7 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23" name = "vecdb" version = "0.6.8" dependencies = [ - "ctrlc", + "libc", "log", "lz4_flex 0.12.0", "parking_lot", @@ -3416,9 +3351,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.111" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1adf1535672f5b7824f817792b1afd731d7e843d2d04ec8f27e8cb51edd8ac" +checksum = "05d7d0fce354c88b7982aec4400b3e7fcf723c32737cef571bd165f7613557ee" dependencies = [ "cfg-if", "once_cell", @@ -3429,9 +3364,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.111" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e638317c08b21663aed4d2b9a2091450548954695ff4efa75bff5fa546b3b1" +checksum = "55839b71ba921e4f75b674cb16f843f4b1f3b26ddfcb3454de1cf65cc021ec0f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3439,9 +3374,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.111" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c64760850114d03d5f65457e96fc988f11f01d38fbaa51b254e4ab5809102af" +checksum = "caf2e969c2d60ff52e7e98b7392ff1588bffdd1ccd4769eba27222fd3d621571" dependencies = [ "bumpalo", "proc-macro2", @@ -3452,9 +3387,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.111" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60eecd4fe26177cfa3339eb00b4a36445889ba3ad37080c2429879718e20ca41" +checksum = "0861f0dcdf46ea819407495634953cdcc8a8c7215ab799a7a7ce366be71c7b30" dependencies = [ "unicode-ident", ] @@ -3495,9 +3430,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.88" +version = "0.3.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6bb20ed2d9572df8584f6dc81d68a41a625cadc6f15999d649a70ce7e3597a" +checksum = "10053fbf9a374174094915bbce141e87a6bf32ecd9a002980db4b638405e8962" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/crates/brk_computer/src/blocks/compute.rs b/crates/brk_computer/src/blocks/compute.rs index 6a07e2bb0..293f13714 100644 --- a/crates/brk_computer/src/blocks/compute.rs +++ b/crates/brk_computer/src/blocks/compute.rs @@ -14,11 +14,19 @@ impl Vecs { starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.interval.compute(indexer, starting_indexes, exit)?; + self.time.timestamp.compute_first( + starting_indexes, + &indexer.vecs.blocks.timestamp, + indexes, + exit, + )?; self.count .compute(indexer, &self.time, starting_indexes, exit)?; - self.size.compute(indexer, starting_indexes, exit)?; - self.weight.compute(indexer, starting_indexes, exit)?; + self.interval + .compute(indexer, &self.count, starting_indexes, exit)?; + self.size.compute(indexer, &self.count, starting_indexes, exit)?; + self.weight + .compute(indexer, &self.count, starting_indexes, exit)?; self.difficulty .compute(indexer, indexes, starting_indexes, exit)?; self.halving.compute(indexes, starting_indexes, exit)?; diff --git a/crates/brk_computer/src/blocks/count/compute.rs b/crates/brk_computer/src/blocks/count/compute.rs index 505310305..3abb1aa91 100644 --- a/crates/brk_computer/src/blocks/count/compute.rs +++ b/crates/brk_computer/src/blocks/count/compute.rs @@ -158,28 +158,16 @@ impl Vecs { )?; // Compute rolling window block counts - self.block_count_24h_sum.height.compute_transform( + let ws = crate::internal::WindowStarts { + _24h: &self.height_24h_ago, + _7d: &self.height_1w_ago, + _30d: &self.height_1m_ago, + _1y: &self.height_1y_ago, + }; + self.block_count_sum.compute_rolling_sum( starting_indexes.height, - &self.height_24h_ago, - |(h, start, ..)| (h, StoredU32::from(*h + 1 - *start)), - exit, - )?; - self.block_count_1w_sum.height.compute_transform( - starting_indexes.height, - &self.height_1w_ago, - |(h, start, ..)| (h, StoredU32::from(*h + 1 - *start)), - exit, - )?; - self.block_count_1m_sum.height.compute_transform( - starting_indexes.height, - &self.height_1m_ago, - |(h, start, ..)| (h, StoredU32::from(*h + 1 - *start)), - exit, - )?; - self.block_count_1y_sum.height.compute_transform( - starting_indexes.height, - &self.height_1y_ago, - |(h, start, ..)| (h, StoredU32::from(*h + 1 - *start)), + &ws, + &self.block_count.height, exit, )?; diff --git a/crates/brk_computer/src/blocks/count/import.rs b/crates/brk_computer/src/blocks/count/import.rs index d40ede602..ac36c0ec9 100644 --- a/crates/brk_computer/src/blocks/count/import.rs +++ b/crates/brk_computer/src/blocks/count/import.rs @@ -5,7 +5,7 @@ use vecdb::{Database, ImportableVec}; use super::Vecs; use crate::{ indexes, - internal::{BlockCountTarget, ComputedFromHeightLast, ComputedFromHeightSumCum, ConstantVecs}, + internal::{BlockCountTarget, ComputedFromHeightSumCum, ConstantVecs, RollingWindows}, }; impl Vecs { @@ -52,27 +52,9 @@ impl Vecs { height_6y_ago: ImportableVec::forced_import(db, "height_6y_ago", version)?, height_8y_ago: ImportableVec::forced_import(db, "height_8y_ago", version)?, height_10y_ago: ImportableVec::forced_import(db, "height_10y_ago", version)?, - block_count_24h_sum: ComputedFromHeightLast::forced_import( + block_count_sum: RollingWindows::forced_import( db, - "block_count_24h_sum", - version, - indexes, - )?, - block_count_1w_sum: ComputedFromHeightLast::forced_import( - db, - "block_count_1w_sum", - version, - indexes, - )?, - block_count_1m_sum: ComputedFromHeightLast::forced_import( - db, - "block_count_1m_sum", - version, - indexes, - )?, - block_count_1y_sum: ComputedFromHeightLast::forced_import( - db, - "block_count_1y_sum", + "block_count_sum", version, indexes, )?, diff --git a/crates/brk_computer/src/blocks/count/vecs.rs b/crates/brk_computer/src/blocks/count/vecs.rs index 8ffdb0dfb..4f75cf2d6 100644 --- a/crates/brk_computer/src/blocks/count/vecs.rs +++ b/crates/brk_computer/src/blocks/count/vecs.rs @@ -2,7 +2,7 @@ use brk_traversable::Traversable; use brk_types::{Height, StoredU32, StoredU64}; use vecdb::{EagerVec, PcoVec, Rw, StorageMode}; -use crate::internal::{ComputedFromHeightLast, ComputedFromHeightSumCum, ConstantVecs}; +use crate::internal::{ComputedFromHeightSumCum, ConstantVecs, RollingWindows, WindowStarts}; #[derive(Traversable)] pub struct Vecs { @@ -40,13 +40,20 @@ pub struct Vecs { pub height_8y_ago: M::Stored>>, pub height_10y_ago: M::Stored>>, // Rolling window block counts - pub block_count_24h_sum: ComputedFromHeightLast, - pub block_count_1w_sum: ComputedFromHeightLast, - pub block_count_1m_sum: ComputedFromHeightLast, - pub block_count_1y_sum: ComputedFromHeightLast, + pub block_count_sum: RollingWindows, } impl Vecs { + /// Get the standard 4 rolling window start heights (24h, 7d, 30d, 1y). + pub fn window_starts(&self) -> WindowStarts<'_> { + WindowStarts { + _24h: &self.height_24h_ago, + _7d: &self.height_1w_ago, + _30d: &self.height_1m_ago, + _1y: &self.height_1y_ago, + } + } + pub fn start_vec(&self, days: usize) -> &EagerVec> { match days { 1 => &self.height_24h_ago, diff --git a/crates/brk_computer/src/blocks/difficulty/import.rs b/crates/brk_computer/src/blocks/difficulty/import.rs index e33ede1dd..71dad4baf 100644 --- a/crates/brk_computer/src/blocks/difficulty/import.rs +++ b/crates/brk_computer/src/blocks/difficulty/import.rs @@ -6,7 +6,7 @@ use vecdb::{Database, ReadableCloneableVec}; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightLast, ComputedFromHeightSum, ComputedHeightDerivedLast}, + internal::{ComputedFromHeightLast, ComputedHeightDerivedLast}, }; impl Vecs { @@ -26,7 +26,7 @@ impl Vecs { indexes, ), as_hash: ComputedFromHeightLast::forced_import(db, "difficulty_as_hash", version, indexes)?, - adjustment: ComputedFromHeightSum::forced_import(db, "difficulty_adjustment", version, indexes)?, + adjustment: ComputedFromHeightLast::forced_import(db, "difficulty_adjustment", version, indexes)?, epoch: ComputedFromHeightLast::forced_import(db, "difficulty_epoch", version, indexes)?, blocks_before_next_adjustment: ComputedFromHeightLast::forced_import( db, diff --git a/crates/brk_computer/src/blocks/difficulty/vecs.rs b/crates/brk_computer/src/blocks/difficulty/vecs.rs index 820dd3f2e..ef269eeca 100644 --- a/crates/brk_computer/src/blocks/difficulty/vecs.rs +++ b/crates/brk_computer/src/blocks/difficulty/vecs.rs @@ -2,7 +2,7 @@ use brk_traversable::Traversable; use brk_types::{DifficultyEpoch, StoredF32, StoredF64, StoredU32}; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightLast, ComputedFromHeightSum, ComputedHeightDerivedLast}; +use crate::internal::{ComputedFromHeightLast, ComputedHeightDerivedLast}; /// Difficulty metrics: raw difficulty, derived stats, adjustment, and countdown #[derive(Traversable)] @@ -10,7 +10,7 @@ pub struct Vecs { /// Raw difficulty with day1/period stats - merges with indexer's raw pub raw: ComputedHeightDerivedLast, pub as_hash: ComputedFromHeightLast, - pub adjustment: ComputedFromHeightSum, + pub adjustment: ComputedFromHeightLast, pub epoch: ComputedFromHeightLast, pub blocks_before_next_adjustment: ComputedFromHeightLast, pub days_before_next_adjustment: ComputedFromHeightLast, diff --git a/crates/brk_computer/src/blocks/import.rs b/crates/brk_computer/src/blocks/import.rs index 0bcf9cee0..b7ba09a51 100644 --- a/crates/brk_computer/src/blocks/import.rs +++ b/crates/brk_computer/src/blocks/import.rs @@ -29,7 +29,7 @@ impl Vecs { let interval = IntervalVecs::forced_import(&db, version, indexes)?; let size = SizeVecs::forced_import(&db, version, indexer, indexes)?; let weight = WeightVecs::forced_import(&db, version, indexer, indexes)?; - let time = TimeVecs::forced_import(&db, version, indexer, indexes)?; + let time = TimeVecs::forced_import(&db, version)?; let difficulty = DifficultyVecs::forced_import(&db, version, indexer, indexes)?; let halving = HalvingVecs::forced_import(&db, version, indexes)?; diff --git a/crates/brk_computer/src/blocks/interval/compute.rs b/crates/brk_computer/src/blocks/interval/compute.rs index e2a8bbc9a..20af1a0c3 100644 --- a/crates/brk_computer/src/blocks/interval/compute.rs +++ b/crates/brk_computer/src/blocks/interval/compute.rs @@ -4,12 +4,13 @@ use brk_types::{CheckedSub, Timestamp}; use vecdb::{Exit, ReadableVec}; use super::Vecs; -use crate::ComputeIndexes; +use crate::{blocks, ComputeIndexes}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, + count_vecs: &blocks::CountVecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { @@ -31,6 +32,15 @@ impl Vecs { }, exit, )?; + + let window_starts = count_vecs.window_starts(); + self.interval_rolling.compute_distribution( + starting_indexes.height, + &window_starts, + &self.interval.height, + exit, + )?; + Ok(()) } } diff --git a/crates/brk_computer/src/blocks/interval/import.rs b/crates/brk_computer/src/blocks/interval/import.rs index 08f9c6eea..ec0344970 100644 --- a/crates/brk_computer/src/blocks/interval/import.rs +++ b/crates/brk_computer/src/blocks/interval/import.rs @@ -3,7 +3,10 @@ use brk_types::Version; use vecdb::Database; use super::Vecs; -use crate::{indexes, internal::ComputedFromHeightDistribution}; +use crate::{ + indexes, + internal::{ComputedFromHeightLast, RollingDistribution}, +}; impl Vecs { pub(crate) fn forced_import( @@ -11,13 +14,15 @@ impl Vecs { version: Version, indexes: &indexes::Vecs, ) -> Result { - let interval = ComputedFromHeightDistribution::forced_import( - db, - "block_interval", - version, - indexes, - )?; + let interval = + ComputedFromHeightLast::forced_import(db, "block_interval", version, indexes)?; - Ok(Self { interval }) + let interval_rolling = + RollingDistribution::forced_import(db, "block_interval", version, indexes)?; + + Ok(Self { + interval, + interval_rolling, + }) } } diff --git a/crates/brk_computer/src/blocks/interval/vecs.rs b/crates/brk_computer/src/blocks/interval/vecs.rs index 81a501962..55e47a4ef 100644 --- a/crates/brk_computer/src/blocks/interval/vecs.rs +++ b/crates/brk_computer/src/blocks/interval/vecs.rs @@ -2,10 +2,11 @@ use brk_traversable::Traversable; use brk_types::Timestamp; use vecdb::{Rw, StorageMode}; -use crate::internal::ComputedFromHeightDistribution; +use crate::internal::{ComputedFromHeightLast, RollingDistribution}; #[derive(Traversable)] pub struct Vecs { #[traversable(flatten)] - pub interval: ComputedFromHeightDistribution, + pub interval: ComputedFromHeightLast, + pub interval_rolling: RollingDistribution, } diff --git a/crates/brk_computer/src/blocks/size/compute.rs b/crates/brk_computer/src/blocks/size/compute.rs index 09784a50e..39f74ebf9 100644 --- a/crates/brk_computer/src/blocks/size/compute.rs +++ b/crates/brk_computer/src/blocks/size/compute.rs @@ -1,21 +1,43 @@ use brk_error::Result; use brk_indexer::Indexer; +use brk_types::StoredU64; use vecdb::Exit; use super::Vecs; -use crate::ComputeIndexes; +use crate::{blocks, ComputeIndexes}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, + count_vecs: &blocks::CountVecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.size - .compute_cumulative(starting_indexes, &indexer.vecs.blocks.total_size, exit)?; + let window_starts = count_vecs.window_starts(); - self.vbytes.compute_cumulative(starting_indexes, exit)?; + // vbytes = ceil(weight / 4), stored at height level + self.vbytes.compute( + starting_indexes.height, + &window_starts, + exit, + |height| { + Ok(height.compute_transform( + starting_indexes.height, + &indexer.vecs.blocks.weight, + |(h, weight, ..)| (h, StoredU64::from(weight.to_vbytes_floor())), + exit, + )?) + }, + )?; + + // size from indexer total_size + self.size.compute( + starting_indexes.height, + &window_starts, + &indexer.vecs.blocks.total_size, + exit, + )?; Ok(()) } diff --git a/crates/brk_computer/src/blocks/size/import.rs b/crates/brk_computer/src/blocks/size/import.rs index b98c5c157..656d1eb9f 100644 --- a/crates/brk_computer/src/blocks/size/import.rs +++ b/crates/brk_computer/src/blocks/size/import.rs @@ -4,7 +4,10 @@ use brk_types::Version; use vecdb::{Database, ReadableCloneableVec}; use super::Vecs; -use crate::{indexes, internal::{ComputedHeightDerivedFull, LazyComputedFromHeightFull, WeightToVbytes}}; +use crate::{ + indexes, + internal::{ComputedFromHeightCumFull, ComputedHeightDerivedCumFull}, +}; impl Vecs { pub(crate) fn forced_import( @@ -14,14 +17,13 @@ impl Vecs { indexes: &indexes::Vecs, ) -> Result { Ok(Self { - vbytes: LazyComputedFromHeightFull::forced_import::( + vbytes: ComputedFromHeightCumFull::forced_import( db, "block_vbytes", version, - &indexer.vecs.blocks.weight, indexes, )?, - size: ComputedHeightDerivedFull::forced_import( + size: ComputedHeightDerivedCumFull::forced_import( db, "block_size", indexer.vecs.blocks.total_size.read_only_boxed_clone(), diff --git a/crates/brk_computer/src/blocks/size/vecs.rs b/crates/brk_computer/src/blocks/size/vecs.rs index d2f01bc06..d31d8a69d 100644 --- a/crates/brk_computer/src/blocks/size/vecs.rs +++ b/crates/brk_computer/src/blocks/size/vecs.rs @@ -1,11 +1,11 @@ use brk_traversable::Traversable; -use brk_types::{StoredU64, Weight}; +use brk_types::StoredU64; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedHeightDerivedFull, LazyComputedFromHeightFull}; +use crate::internal::{ComputedFromHeightCumFull, ComputedHeightDerivedCumFull}; #[derive(Traversable)] pub struct Vecs { - pub vbytes: LazyComputedFromHeightFull, - pub size: ComputedHeightDerivedFull, + pub vbytes: ComputedFromHeightCumFull, + pub size: ComputedHeightDerivedCumFull, } diff --git a/crates/brk_computer/src/blocks/time/import.rs b/crates/brk_computer/src/blocks/time/import.rs index 101dafc58..e009a9187 100644 --- a/crates/brk_computer/src/blocks/time/import.rs +++ b/crates/brk_computer/src/blocks/time/import.rs @@ -1,18 +1,12 @@ use brk_error::Result; -use brk_indexer::Indexer; use brk_types::{Date, Height, Version}; -use vecdb::{Database, EagerVec, ImportableVec, ReadableCloneableVec, LazyVecFrom1}; +use vecdb::{Database, EagerVec, ImportableVec, LazyVecFrom1, ReadableCloneableVec}; use super::Vecs; -use crate::{indexes, internal::ComputedHeightDerivedFirst}; +use crate::internal::EagerIndexes; impl Vecs { - pub(crate) fn forced_import( - db: &Database, - version: Version, - indexer: &Indexer, - indexes: &indexes::Vecs, - ) -> Result { + pub(crate) fn forced_import(db: &Database, version: Version) -> Result { let timestamp_monotonic = EagerVec::forced_import(db, "timestamp_monotonic", version)?; @@ -24,12 +18,7 @@ impl Vecs { |_height: Height, timestamp| Date::from(timestamp), ), timestamp_monotonic, - timestamp: ComputedHeightDerivedFirst::forced_import( - "timestamp", - indexer.vecs.blocks.timestamp.read_only_boxed_clone(), - version, - indexes, - ), + timestamp: EagerIndexes::forced_import(db, "timestamp", version)?, }) } } diff --git a/crates/brk_computer/src/blocks/time/vecs.rs b/crates/brk_computer/src/blocks/time/vecs.rs index d3a3b5155..1a7a179b8 100644 --- a/crates/brk_computer/src/blocks/time/vecs.rs +++ b/crates/brk_computer/src/blocks/time/vecs.rs @@ -2,12 +2,12 @@ use brk_traversable::Traversable; use brk_types::{Date, Height, Timestamp}; use vecdb::{EagerVec, LazyVecFrom1, PcoVec, Rw, StorageMode}; -use crate::internal::ComputedHeightDerivedFirst; +use crate::internal::EagerIndexes; /// Timestamp and date metrics for blocks #[derive(Traversable)] pub struct Vecs { pub date: LazyVecFrom1, pub timestamp_monotonic: M::Stored>>, - pub timestamp: ComputedHeightDerivedFirst, + pub timestamp: EagerIndexes, } diff --git a/crates/brk_computer/src/blocks/weight/compute.rs b/crates/brk_computer/src/blocks/weight/compute.rs index b425b69ca..d184782c5 100644 --- a/crates/brk_computer/src/blocks/weight/compute.rs +++ b/crates/brk_computer/src/blocks/weight/compute.rs @@ -1,19 +1,41 @@ use brk_error::Result; use brk_indexer::Indexer; +use brk_types::StoredF32; use vecdb::Exit; use super::Vecs; -use crate::ComputeIndexes; +use crate::{blocks, ComputeIndexes}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, + count_vecs: &blocks::CountVecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.weight - .compute_cumulative(starting_indexes, &indexer.vecs.blocks.weight, exit)?; + let window_starts = count_vecs.window_starts(); + + self.weight.compute( + starting_indexes.height, + &window_starts, + &indexer.vecs.blocks.weight, + exit, + )?; + + self.fullness.height.compute_transform( + starting_indexes.height, + &indexer.vecs.blocks.weight, + |(h, weight, ..)| (h, StoredF32::from(weight.fullness())), + exit, + )?; + + self.fullness_rolling.compute_distribution( + starting_indexes.height, + &window_starts, + &self.fullness.height, + exit, + )?; Ok(()) } diff --git a/crates/brk_computer/src/blocks/weight/import.rs b/crates/brk_computer/src/blocks/weight/import.rs index 227189ca6..513282a06 100644 --- a/crates/brk_computer/src/blocks/weight/import.rs +++ b/crates/brk_computer/src/blocks/weight/import.rs @@ -6,7 +6,7 @@ use vecdb::{Database, ReadableCloneableVec}; use super::Vecs; use crate::{ indexes, - internal::{ComputedHeightDerivedFull, LazyFromHeightTransformDistribution, WeightToFullness}, + internal::{ComputedFromHeightLast, ComputedHeightDerivedCumFull, RollingDistribution}, }; impl Vecs { @@ -16,7 +16,7 @@ impl Vecs { indexer: &Indexer, indexes: &indexes::Vecs, ) -> Result { - let weight = ComputedHeightDerivedFull::forced_import( + let weight = ComputedHeightDerivedCumFull::forced_import( db, "block_weight", indexer.vecs.blocks.weight.read_only_boxed_clone(), @@ -24,13 +24,16 @@ impl Vecs { indexes, )?; - let fullness = LazyFromHeightTransformDistribution::from_derived::( - "block_fullness", - version, - indexer.vecs.blocks.weight.read_only_boxed_clone(), - &weight, - ); + let fullness = + ComputedFromHeightLast::forced_import(db, "block_fullness", version, indexes)?; - Ok(Self { weight, fullness }) + let fullness_rolling = + RollingDistribution::forced_import(db, "block_fullness", version, indexes)?; + + Ok(Self { + weight, + fullness, + fullness_rolling, + }) } } diff --git a/crates/brk_computer/src/blocks/weight/vecs.rs b/crates/brk_computer/src/blocks/weight/vecs.rs index e5600004a..e07b91c7a 100644 --- a/crates/brk_computer/src/blocks/weight/vecs.rs +++ b/crates/brk_computer/src/blocks/weight/vecs.rs @@ -2,10 +2,13 @@ use brk_traversable::Traversable; use brk_types::{StoredF32, Weight}; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedHeightDerivedFull, LazyFromHeightTransformDistribution}; +use crate::internal::{ + ComputedFromHeightLast, ComputedHeightDerivedCumFull, RollingDistribution, +}; #[derive(Traversable)] pub struct Vecs { - pub weight: ComputedHeightDerivedFull, - pub fullness: LazyFromHeightTransformDistribution, + pub weight: ComputedHeightDerivedCumFull, + pub fullness: ComputedFromHeightLast, + pub fullness_rolling: RollingDistribution, } diff --git a/crates/brk_computer/src/cointime/activity/compute.rs b/crates/brk_computer/src/cointime/activity/compute.rs index 74ebd9ead..c3aa5d16d 100644 --- a/crates/brk_computer/src/cointime/activity/compute.rs +++ b/crates/brk_computer/src/cointime/activity/compute.rs @@ -3,15 +3,18 @@ use brk_types::{Bitcoin, CheckedSub, StoredF64}; use vecdb::Exit; use super::Vecs; -use crate::{ComputeIndexes, distribution}; +use crate::{ComputeIndexes, blocks, distribution}; impl Vecs { pub(crate) fn compute( &mut self, starting_indexes: &ComputeIndexes, + blocks: &blocks::Vecs, distribution: &distribution::Vecs, exit: &Exit, ) -> Result<()> { + let window_starts = blocks.count.window_starts(); + let circulating_supply = &distribution .utxo_cohorts .all @@ -22,7 +25,7 @@ impl Vecs { .height; self.coinblocks_created - .compute(starting_indexes, exit, |vec| { + .compute(starting_indexes.height, &window_starts, exit, |vec| { vec.compute_transform( starting_indexes.height, circulating_supply, @@ -40,7 +43,7 @@ impl Vecs { .coinblocks_destroyed; self.coinblocks_stored - .compute(starting_indexes, exit, |vec| { + .compute(starting_indexes.height, &window_starts, exit, |vec| { vec.compute_transform2( starting_indexes.height, &self.coinblocks_created.height, @@ -53,8 +56,8 @@ impl Vecs { self.liveliness.height.compute_divide( starting_indexes.height, - &*coinblocks_destroyed.height_cumulative, - &*self.coinblocks_created.height_cumulative, + &coinblocks_destroyed.cumulative.height, + &self.coinblocks_created.cumulative.height, exit, )?; diff --git a/crates/brk_computer/src/cointime/activity/import.rs b/crates/brk_computer/src/cointime/activity/import.rs index c4f42f865..d549f6539 100644 --- a/crates/brk_computer/src/cointime/activity/import.rs +++ b/crates/brk_computer/src/cointime/activity/import.rs @@ -5,19 +5,19 @@ use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightLast, ComputedFromHeightSumCum}, + internal::{ComputedFromHeightCumSum, ComputedFromHeightLast}, }; impl Vecs { pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result { Ok(Self { - coinblocks_created: ComputedFromHeightSumCum::forced_import( + coinblocks_created: ComputedFromHeightCumSum::forced_import( db, "coinblocks_created", version, indexes, )?, - coinblocks_stored: ComputedFromHeightSumCum::forced_import( + coinblocks_stored: ComputedFromHeightCumSum::forced_import( db, "coinblocks_stored", version, diff --git a/crates/brk_computer/src/cointime/activity/vecs.rs b/crates/brk_computer/src/cointime/activity/vecs.rs index 9f9d480f1..71a98691b 100644 --- a/crates/brk_computer/src/cointime/activity/vecs.rs +++ b/crates/brk_computer/src/cointime/activity/vecs.rs @@ -2,12 +2,12 @@ use brk_traversable::Traversable; use brk_types::StoredF64; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightLast, ComputedFromHeightSumCum}; +use crate::internal::{ComputedFromHeightCumSum, ComputedFromHeightLast}; #[derive(Traversable)] pub struct Vecs { - pub coinblocks_created: ComputedFromHeightSumCum, - pub coinblocks_stored: ComputedFromHeightSumCum, + pub coinblocks_created: ComputedFromHeightCumSum, + pub coinblocks_stored: ComputedFromHeightCumSum, pub liveliness: ComputedFromHeightLast, pub vaultedness: ComputedFromHeightLast, pub activity_to_vaultedness_ratio: ComputedFromHeightLast, diff --git a/crates/brk_computer/src/cointime/cap/compute.rs b/crates/brk_computer/src/cointime/cap/compute.rs index 47892850d..8e6f71425 100644 --- a/crates/brk_computer/src/cointime/cap/compute.rs +++ b/crates/brk_computer/src/cointime/cap/compute.rs @@ -65,9 +65,9 @@ impl Vecs { // cointime_cap = (cointime_value_destroyed_cumulative * circulating_supply) / coinblocks_stored_cumulative self.cointime_cap.height.compute_transform3( starting_indexes.height, - &value.cointime_value_destroyed.height_cumulative.0, + &value.cointime_value_destroyed.cumulative.height, circulating_supply, - &activity.coinblocks_stored.height_cumulative.0, + &activity.coinblocks_stored.cumulative.height, |(i, destroyed, supply, stored, ..)| { let destroyed: f64 = *destroyed; let supply: f64 = supply.into(); diff --git a/crates/brk_computer/src/cointime/compute.rs b/crates/brk_computer/src/cointime/compute.rs index b11b57ade..248b5b012 100644 --- a/crates/brk_computer/src/cointime/compute.rs +++ b/crates/brk_computer/src/cointime/compute.rs @@ -18,7 +18,7 @@ impl Vecs { ) -> Result<()> { // Activity computes first (liveliness, vaultedness, etc.) self.activity - .compute(starting_indexes, distribution, exit)?; + .compute(starting_indexes, blocks, distribution, exit)?; // Supply computes next (depends on activity) self.supply.compute( @@ -36,6 +36,7 @@ impl Vecs { self.value.compute( starting_indexes, prices, + blocks, distribution, &self.activity, exit, diff --git a/crates/brk_computer/src/cointime/value/compute.rs b/crates/brk_computer/src/cointime/value/compute.rs index f1c28f1ba..896c7fd38 100644 --- a/crates/brk_computer/src/cointime/value/compute.rs +++ b/crates/brk_computer/src/cointime/value/compute.rs @@ -4,17 +4,20 @@ use vecdb::Exit; use super::super::activity; use super::Vecs; -use crate::{ComputeIndexes, distribution, prices}; +use crate::{ComputeIndexes, blocks, distribution, prices}; impl Vecs { pub(crate) fn compute( &mut self, starting_indexes: &ComputeIndexes, prices: &prices::Vecs, + blocks: &blocks::Vecs, distribution: &distribution::Vecs, activity: &activity::Vecs, exit: &Exit, ) -> Result<()> { + let window_starts = blocks.count.window_starts(); + let coinblocks_destroyed = &distribution .utxo_cohorts .all @@ -39,7 +42,7 @@ impl Vecs { .height; self.cointime_value_destroyed - .compute(starting_indexes, exit,|vec| { + .compute(starting_indexes.height, &window_starts, exit, |vec| { vec.compute_multiply( starting_indexes.height, &prices.usd.price, @@ -50,7 +53,7 @@ impl Vecs { })?; self.cointime_value_created - .compute(starting_indexes, exit,|vec| { + .compute(starting_indexes.height, &window_starts, exit, |vec| { vec.compute_multiply( starting_indexes.height, &prices.usd.price, @@ -61,7 +64,7 @@ impl Vecs { })?; self.cointime_value_stored - .compute(starting_indexes, exit,|vec| { + .compute(starting_indexes.height, &window_starts, exit, |vec| { vec.compute_multiply( starting_indexes.height, &prices.usd.price, @@ -75,7 +78,7 @@ impl Vecs { // Supply-adjusted to account for growing supply over time // This is a key input for Reserve Risk / HODL Bank calculation self.vocdd - .compute(starting_indexes, exit,|vec| { + .compute(starting_indexes.height, &window_starts, exit, |vec| { vec.compute_transform3( starting_indexes.height, &prices.usd.price, diff --git a/crates/brk_computer/src/cointime/value/import.rs b/crates/brk_computer/src/cointime/value/import.rs index e89f6a681..6a863399f 100644 --- a/crates/brk_computer/src/cointime/value/import.rs +++ b/crates/brk_computer/src/cointime/value/import.rs @@ -3,30 +3,30 @@ use brk_types::Version; use vecdb::Database; use super::Vecs; -use crate::{indexes, internal::ComputedFromHeightSumCum}; +use crate::{indexes, internal::ComputedFromHeightCumSum}; impl Vecs { pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result { Ok(Self { - cointime_value_destroyed: ComputedFromHeightSumCum::forced_import( + cointime_value_destroyed: ComputedFromHeightCumSum::forced_import( db, "cointime_value_destroyed", version, indexes, )?, - cointime_value_created: ComputedFromHeightSumCum::forced_import( + cointime_value_created: ComputedFromHeightCumSum::forced_import( db, "cointime_value_created", version, indexes, )?, - cointime_value_stored: ComputedFromHeightSumCum::forced_import( + cointime_value_stored: ComputedFromHeightCumSum::forced_import( db, "cointime_value_stored", version, indexes, )?, - vocdd: ComputedFromHeightSumCum::forced_import( + vocdd: ComputedFromHeightCumSum::forced_import( db, "vocdd", version + Version::ONE, diff --git a/crates/brk_computer/src/cointime/value/vecs.rs b/crates/brk_computer/src/cointime/value/vecs.rs index 511fa38b4..e1b4c5ab8 100644 --- a/crates/brk_computer/src/cointime/value/vecs.rs +++ b/crates/brk_computer/src/cointime/value/vecs.rs @@ -2,12 +2,12 @@ use brk_traversable::Traversable; use brk_types::StoredF64; use vecdb::{Rw, StorageMode}; -use crate::internal::ComputedFromHeightSumCum; +use crate::internal::ComputedFromHeightCumSum; #[derive(Traversable)] pub struct Vecs { - pub cointime_value_destroyed: ComputedFromHeightSumCum, - pub cointime_value_created: ComputedFromHeightSumCum, - pub cointime_value_stored: ComputedFromHeightSumCum, - pub vocdd: ComputedFromHeightSumCum, + pub cointime_value_destroyed: ComputedFromHeightCumSum, + pub cointime_value_created: ComputedFromHeightCumSum, + pub cointime_value_stored: ComputedFromHeightCumSum, + pub vocdd: ComputedFromHeightCumSum, } diff --git a/crates/brk_computer/src/distribution/compute/block_loop.rs b/crates/brk_computer/src/distribution/compute/block_loop.rs index 6bc08c7bd..da8354787 100644 --- a/crates/brk_computer/src/distribution/compute/block_loop.rs +++ b/crates/brk_computer/src/distribution/compute/block_loop.rs @@ -67,7 +67,7 @@ pub(crate) fn process_blocks( // From transactions and inputs/outputs (via .height or .height.sum_cum.sum patterns): let height_to_tx_count = &transactions.count.tx_count.height; - let height_to_output_count = &outputs.count.total_count.height.sum_cum.sum.0; + let height_to_output_count = &outputs.count.total_count.sum_cum.sum.0; let height_to_input_count = &inputs.count.height.sum_cum.sum.0; // From blocks: let height_to_timestamp = &blocks.time.timestamp_monotonic; @@ -114,8 +114,11 @@ pub(crate) fn process_blocks( debug!("building txindex_to_height RangeMap"); let mut txindex_to_height: RangeMap = { let first_txindex_len = indexer.vecs.transactions.first_txindex.len(); - let all_first_txindexes: Vec = - indexer.vecs.transactions.first_txindex.collect_range_at(0, first_txindex_len); + let all_first_txindexes: Vec = indexer + .vecs + .transactions + .first_txindex + .collect_range_at(0, first_txindex_len); let mut map = RangeMap::with_capacity(first_txindex_len); for first_txindex in all_first_txindexes { map.push(first_txindex); @@ -129,14 +132,46 @@ pub(crate) fn process_blocks( let mut txin_iters = TxInReaders::new(indexer, inputs, &mut txindex_to_height); // Pre-collect first address indexes per type for the block range - let first_p2a_vec = indexer.vecs.addresses.first_p2aaddressindex.collect_range_at(start_usize, end_usize); - let first_p2pk33_vec = indexer.vecs.addresses.first_p2pk33addressindex.collect_range_at(start_usize, end_usize); - let first_p2pk65_vec = indexer.vecs.addresses.first_p2pk65addressindex.collect_range_at(start_usize, end_usize); - let first_p2pkh_vec = indexer.vecs.addresses.first_p2pkhaddressindex.collect_range_at(start_usize, end_usize); - let first_p2sh_vec = indexer.vecs.addresses.first_p2shaddressindex.collect_range_at(start_usize, end_usize); - let first_p2tr_vec = indexer.vecs.addresses.first_p2traddressindex.collect_range_at(start_usize, end_usize); - let first_p2wpkh_vec = indexer.vecs.addresses.first_p2wpkhaddressindex.collect_range_at(start_usize, end_usize); - let first_p2wsh_vec = indexer.vecs.addresses.first_p2wshaddressindex.collect_range_at(start_usize, end_usize); + let first_p2a_vec = indexer + .vecs + .addresses + .first_p2aaddressindex + .collect_range_at(start_usize, end_usize); + let first_p2pk33_vec = indexer + .vecs + .addresses + .first_p2pk33addressindex + .collect_range_at(start_usize, end_usize); + let first_p2pk65_vec = indexer + .vecs + .addresses + .first_p2pk65addressindex + .collect_range_at(start_usize, end_usize); + let first_p2pkh_vec = indexer + .vecs + .addresses + .first_p2pkhaddressindex + .collect_range_at(start_usize, end_usize); + let first_p2sh_vec = indexer + .vecs + .addresses + .first_p2shaddressindex + .collect_range_at(start_usize, end_usize); + let first_p2tr_vec = indexer + .vecs + .addresses + .first_p2traddressindex + .collect_range_at(start_usize, end_usize); + let first_p2wpkh_vec = indexer + .vecs + .addresses + .first_p2wpkhaddressindex + .collect_range_at(start_usize, end_usize); + let first_p2wsh_vec = indexer + .vecs + .addresses + .first_p2wshaddressindex + .collect_range_at(start_usize, end_usize); // Track running totals - recover from previous height if resuming debug!("recovering addr_counts from height {}", starting_height); @@ -423,7 +458,7 @@ pub(crate) fn process_blocks( )?; // Compute unrealized peak regret by age range (once per day) - if let Some(day1) = day1_opt { + if day1_opt.is_some() { vecs.utxo_cohorts.compute_and_push_peak_regret( chain_state, height, diff --git a/crates/brk_computer/src/distribution/metrics/activity.rs b/crates/brk_computer/src/distribution/metrics/activity.rs index d48bfa794..1e489007d 100644 --- a/crates/brk_computer/src/distribution/metrics/activity.rs +++ b/crates/brk_computer/src/distribution/metrics/activity.rs @@ -6,7 +6,7 @@ use vecdb::{AnyStoredVec, AnyVec, EagerVec, Exit, ImportableVec, PcoVec, Rw, Sto use crate::{ ComputeIndexes, blocks, - internal::{ComputedFromHeightSumCum, LazyComputedValueFromHeightSumCum, ValueEmaFromHeight}, + internal::{ComputedFromHeightCumSum, LazyComputedValueFromHeightCum, ValueEmaFromHeight}, }; use super::ImportConfig; @@ -15,7 +15,7 @@ use super::ImportConfig; #[derive(Traversable)] pub struct ActivityMetrics { /// Total satoshis sent at each height + derived indexes - pub sent: LazyComputedValueFromHeightSumCum, + pub sent: LazyComputedValueFromHeightCum, /// 14-day EMA of sent supply (sats, btc, usd) pub sent_14d_ema: ValueEmaFromHeight, @@ -27,17 +27,17 @@ pub struct ActivityMetrics { pub satdays_destroyed: M::Stored>>, /// Coin-blocks destroyed (in BTC rather than sats) - pub coinblocks_destroyed: ComputedFromHeightSumCum, + pub coinblocks_destroyed: ComputedFromHeightCumSum, /// Coin-days destroyed (in BTC rather than sats) - pub coindays_destroyed: ComputedFromHeightSumCum, + pub coindays_destroyed: ComputedFromHeightCumSum, } impl ActivityMetrics { /// Import activity metrics from database. pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { Ok(Self { - sent: LazyComputedValueFromHeightSumCum::forced_import( + sent: LazyComputedValueFromHeightCum::forced_import( cfg.db, &cfg.name("sent"), cfg.version, @@ -64,14 +64,14 @@ impl ActivityMetrics { cfg.version, )?, - coinblocks_destroyed: ComputedFromHeightSumCum::forced_import( + coinblocks_destroyed: ComputedFromHeightCumSum::forced_import( cfg.db, &cfg.name("coinblocks_destroyed"), cfg.version, cfg.indexes, )?, - coindays_destroyed: ComputedFromHeightSumCum::forced_import( + coindays_destroyed: ComputedFromHeightCumSum::forced_import( cfg.db, &cfg.name("coindays_destroyed"), cfg.version, @@ -163,7 +163,9 @@ impl ActivityMetrics { starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.sent.compute_cumulative(starting_indexes, exit)?; + let window_starts = blocks.count.window_starts(); + + self.sent.compute_cumulative(starting_indexes.height, exit)?; // 14-day rolling average of sent (sats and dollars) self.sent_14d_ema.compute_rolling_average( @@ -174,7 +176,7 @@ impl ActivityMetrics { exit, )?; - self.coinblocks_destroyed.compute(starting_indexes, exit, |v| { + self.coinblocks_destroyed.compute(starting_indexes.height, &window_starts, exit, |v| { v.compute_transform( starting_indexes.height, &self.satblocks_destroyed, @@ -184,7 +186,7 @@ impl ActivityMetrics { Ok(()) })?; - self.coindays_destroyed.compute(starting_indexes, exit, |v| { + self.coindays_destroyed.compute(starting_indexes.height, &window_starts, exit, |v| { v.compute_transform( starting_indexes.height, &self.satdays_destroyed, diff --git a/crates/brk_computer/src/distribution/metrics/realized.rs b/crates/brk_computer/src/distribution/metrics/realized.rs index bae7bc0b6..45fe2dc02 100644 --- a/crates/brk_computer/src/distribution/metrics/realized.rs +++ b/crates/brk_computer/src/distribution/metrics/realized.rs @@ -14,12 +14,11 @@ use crate::{ ComputeIndexes, blocks, distribution::state::RealizedState, internal::{ - CentsUnsignedToDollars, ComputedFromHeightLast, ComputedFromHeightRatio, - ComputedFromHeightSum, ComputedFromHeightSumCum, DollarsMinus, DollarsPlus, - DollarsSquaredDivide, LazyBinaryFromHeightLast, LazyBinaryFromHeightSum, - LazyBinaryFromHeightSumCum, LazyBinaryPriceFromHeight, - LazyComputedValueFromHeightSumCum, LazyFromHeightLast, LazyFromHeightSum, - LazyFromHeightSumCum, LazyPriceFromCents, PercentageDollarsF32, Price, PriceFromHeight, + CentsUnsignedToDollars, ComputedFromHeightCum, ComputedFromHeightLast, + ComputedFromHeightRatio, DollarsMinus, DollarsPlus, + DollarsSquaredDivide, LazyBinaryFromHeightLast, + LazyBinaryPriceFromHeight, LazyComputedValueFromHeightCum, LazyFromHeightLast, + LazyPriceFromCents, PercentageDollarsF32, Price, PriceFromHeight, Ratio64, StoredF32Identity, ValueEmaFromHeight, }, prices, @@ -58,24 +57,24 @@ pub struct RealizedMetrics { pub mvrv: LazyFromHeightLast, // === Realized Profit/Loss === - pub realized_profit: ComputedFromHeightSumCum, + pub realized_profit: ComputedFromHeightCum, pub realized_profit_7d_ema: ComputedFromHeightLast, - pub realized_loss: ComputedFromHeightSumCum, + pub realized_loss: ComputedFromHeightCum, pub realized_loss_7d_ema: ComputedFromHeightLast, - pub neg_realized_loss: LazyFromHeightSumCum, - pub net_realized_pnl: ComputedFromHeightSumCum, + pub neg_realized_loss: LazyFromHeightLast, + pub net_realized_pnl: ComputedFromHeightCum, pub net_realized_pnl_7d_ema: ComputedFromHeightLast, - pub realized_value: ComputedFromHeightSum, + pub realized_value: ComputedFromHeightLast, // === Realized vs Realized Cap Ratios (lazy) === pub realized_profit_rel_to_realized_cap: - LazyBinaryFromHeightSumCum, - pub realized_loss_rel_to_realized_cap: LazyBinaryFromHeightSumCum, + LazyBinaryFromHeightLast, + pub realized_loss_rel_to_realized_cap: LazyBinaryFromHeightLast, pub net_realized_pnl_rel_to_realized_cap: - LazyBinaryFromHeightSumCum, + LazyBinaryFromHeightLast, // === Total Realized PnL === - pub total_realized_pnl: LazyFromHeightSum, + pub total_realized_pnl: LazyFromHeightLast, // === Realized Profit/Loss Rolling Sums === pub realized_profit_24h: Option>, @@ -94,22 +93,22 @@ pub struct RealizedMetrics { pub realized_profit_to_loss_ratio_1y: Option>, // === Value Created/Destroyed Splits (stored) === - pub profit_value_created: ComputedFromHeightSum, - pub profit_value_destroyed: ComputedFromHeightSum, - pub loss_value_created: ComputedFromHeightSum, - pub loss_value_destroyed: ComputedFromHeightSum, + pub profit_value_created: ComputedFromHeightLast, + pub profit_value_destroyed: ComputedFromHeightLast, + pub loss_value_created: ComputedFromHeightLast, + pub loss_value_destroyed: ComputedFromHeightLast, // === Value Created/Destroyed Totals (lazy: profit + loss) === - pub value_created: LazyBinaryFromHeightSum, - pub value_destroyed: LazyBinaryFromHeightSum, + pub value_created: LazyBinaryFromHeightLast, + pub value_destroyed: LazyBinaryFromHeightLast, // === Capitulation/Profit Flow (lazy aliases) === - pub capitulation_flow: LazyFromHeightSum, - pub profit_flow: LazyFromHeightSum, + pub capitulation_flow: LazyFromHeightLast, + pub profit_flow: LazyFromHeightLast, // === Adjusted Value (lazy: cohort - up_to_1h) === - pub adjusted_value_created: Option>, - pub adjusted_value_destroyed: Option>, + pub adjusted_value_created: Option>, + pub adjusted_value_destroyed: Option>, // === Value Created/Destroyed Rolling Sums === pub value_created_24h: ComputedFromHeightLast, @@ -179,17 +178,17 @@ pub struct RealizedMetrics { /// Realized peak regret: Σ((peak - sell_price) × sats) /// where peak = max price during holding period. /// "How much more could have been made by selling at peak instead" - pub peak_regret: ComputedFromHeightSumCum, + pub peak_regret: ComputedFromHeightCum, /// Peak regret as % of realized cap - pub peak_regret_rel_to_realized_cap: LazyBinaryFromHeightSum, + pub peak_regret_rel_to_realized_cap: LazyBinaryFromHeightLast, // === Sent in Profit/Loss === /// Sats sent in profit (sats/btc/usd) - pub sent_in_profit: LazyComputedValueFromHeightSumCum, + pub sent_in_profit: LazyComputedValueFromHeightCum, /// 14-day EMA of sent in profit (sats, btc, usd) pub sent_in_profit_14d_ema: ValueEmaFromHeight, /// Sats sent in loss (sats/btc/usd) - pub sent_in_loss: LazyComputedValueFromHeightSumCum, + pub sent_in_loss: LazyComputedValueFromHeightCum, /// 14-day EMA of sent in loss (sats, btc, usd) pub sent_in_loss_14d_ema: ValueEmaFromHeight, } @@ -218,7 +217,7 @@ impl RealizedMetrics { &realized_cap_cents, ); - let realized_profit = ComputedFromHeightSumCum::forced_import( + let realized_profit = ComputedFromHeightCum::forced_import( cfg.db, &cfg.name("realized_profit"), cfg.version, @@ -232,7 +231,7 @@ impl RealizedMetrics { cfg.indexes, )?; - let realized_loss = ComputedFromHeightSumCum::forced_import( + let realized_loss = ComputedFromHeightCum::forced_import( cfg.db, &cfg.name("realized_loss"), cfg.version, @@ -246,14 +245,14 @@ impl RealizedMetrics { cfg.indexes, )?; - let neg_realized_loss = LazyFromHeightSumCum::from_computed::( + let neg_realized_loss = LazyFromHeightLast::from_computed::( &cfg.name("neg_realized_loss"), cfg.version + v1, realized_loss.height.read_only_boxed_clone(), &realized_loss, ); - let net_realized_pnl = ComputedFromHeightSumCum::forced_import( + let net_realized_pnl = ComputedFromHeightCum::forced_import( cfg.db, &cfg.name("net_realized_pnl"), cfg.version, @@ -267,7 +266,7 @@ impl RealizedMetrics { cfg.indexes, )?; - let peak_regret = ComputedFromHeightSumCum::forced_import( + let peak_regret = ComputedFromHeightCum::forced_import( cfg.db, &cfg.name("realized_peak_regret"), cfg.version + v2, @@ -275,7 +274,7 @@ impl RealizedMetrics { )?; // realized_value is the source for total_realized_pnl (they're identical) - let realized_value = ComputedFromHeightSum::forced_import( + let realized_value = ComputedFromHeightLast::forced_import( cfg.db, &cfg.name("realized_value"), cfg.version, @@ -283,7 +282,7 @@ impl RealizedMetrics { )?; // total_realized_pnl is a lazy alias to realized_value - let total_realized_pnl = LazyFromHeightSum::from_computed::( + let total_realized_pnl = LazyFromHeightLast::from_computed::( &cfg.name("total_realized_pnl"), cfg.version + v1, realized_value.height.read_only_boxed_clone(), @@ -292,31 +291,25 @@ impl RealizedMetrics { // Construct lazy ratio vecs let realized_profit_rel_to_realized_cap = - LazyBinaryFromHeightSumCum::from_computed_lazy_last::( + LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( &cfg.name("realized_profit_rel_to_realized_cap"), cfg.version + v1, - realized_profit.height.read_only_boxed_clone(), - realized_cap.height.read_only_boxed_clone(), &realized_profit, &realized_cap, ); let realized_loss_rel_to_realized_cap = - LazyBinaryFromHeightSumCum::from_computed_lazy_last::( + LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( &cfg.name("realized_loss_rel_to_realized_cap"), cfg.version + v1, - realized_loss.height.read_only_boxed_clone(), - realized_cap.height.read_only_boxed_clone(), &realized_loss, &realized_cap, ); let net_realized_pnl_rel_to_realized_cap = - LazyBinaryFromHeightSumCum::from_computed_lazy_last::( + LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( &cfg.name("net_realized_pnl_rel_to_realized_cap"), cfg.version + v1, - net_realized_pnl.height.read_only_boxed_clone(), - realized_cap.height.read_only_boxed_clone(), &net_realized_pnl, &realized_cap, ); @@ -375,28 +368,28 @@ impl RealizedMetrics { BytesVec::forced_import(cfg.db, &cfg.name("investor_cap_raw"), cfg.version)?; // Import the 4 splits (stored) - let profit_value_created = ComputedFromHeightSum::forced_import( + let profit_value_created = ComputedFromHeightLast::forced_import( cfg.db, &cfg.name("profit_value_created"), cfg.version, cfg.indexes, )?; - let profit_value_destroyed = ComputedFromHeightSum::forced_import( + let profit_value_destroyed = ComputedFromHeightLast::forced_import( cfg.db, &cfg.name("profit_value_destroyed"), cfg.version, cfg.indexes, )?; - let loss_value_created = ComputedFromHeightSum::forced_import( + let loss_value_created = ComputedFromHeightLast::forced_import( cfg.db, &cfg.name("loss_value_created"), cfg.version, cfg.indexes, )?; - let loss_value_destroyed = ComputedFromHeightSum::forced_import( + let loss_value_destroyed = ComputedFromHeightLast::forced_import( cfg.db, &cfg.name("loss_value_destroyed"), cfg.version, @@ -404,14 +397,14 @@ impl RealizedMetrics { )?; // Create lazy totals (profit + loss) - let value_created = LazyBinaryFromHeightSum::from_computed::( + let value_created = LazyBinaryFromHeightLast::from_computed_last::( &cfg.name("value_created"), cfg.version, &profit_value_created, &loss_value_created, ); - let value_destroyed = LazyBinaryFromHeightSum::from_computed::( + let value_destroyed = LazyBinaryFromHeightLast::from_computed_last::( &cfg.name("value_destroyed"), cfg.version, &profit_value_destroyed, @@ -419,14 +412,14 @@ impl RealizedMetrics { ); // Create lazy aliases - let capitulation_flow = LazyFromHeightSum::from_computed::( + let capitulation_flow = LazyFromHeightLast::from_computed::( &cfg.name("capitulation_flow"), cfg.version, loss_value_destroyed.height.read_only_boxed_clone(), &loss_value_destroyed, ); - let profit_flow = LazyFromHeightSum::from_computed::( + let profit_flow = LazyFromHeightLast::from_computed::( &cfg.name("profit_flow"), cfg.version, profit_value_destroyed.height.read_only_boxed_clone(), @@ -437,7 +430,7 @@ impl RealizedMetrics { let adjusted_value_created = (compute_adjusted && cfg.up_to_1h_realized.is_some()).then(|| { let up_to_1h = cfg.up_to_1h_realized.unwrap(); - LazyBinaryFromHeightSum::from_binary::< + LazyBinaryFromHeightLast::from_both_binary_block::< DollarsMinus, Dollars, Dollars, @@ -453,7 +446,7 @@ impl RealizedMetrics { let adjusted_value_destroyed = (compute_adjusted && cfg.up_to_1h_realized.is_some()).then(|| { let up_to_1h = cfg.up_to_1h_realized.unwrap(); - LazyBinaryFromHeightSum::from_binary::< + LazyBinaryFromHeightLast::from_both_binary_block::< DollarsMinus, Dollars, Dollars, @@ -626,17 +619,13 @@ impl RealizedMetrics { sell_side_risk_ratio_24h_30d_ema.height.read_only_boxed_clone(), &sell_side_risk_ratio_24h_30d_ema, ); - let peak_regret_rel_to_realized_cap = LazyBinaryFromHeightSum::from_sumcum_lazy_last::< - PercentageDollarsF32, - _, - >( - &cfg.name("peak_regret_rel_to_realized_cap"), - cfg.version + v1, - peak_regret.height.read_only_boxed_clone(), - realized_cap.height.read_only_boxed_clone(), - &peak_regret, - &realized_cap, - ); + let peak_regret_rel_to_realized_cap = + LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( + &cfg.name("peak_regret_rel_to_realized_cap"), + cfg.version + v1, + &peak_regret, + &realized_cap, + ); Ok(Self { // === Realized Cap === @@ -814,7 +803,7 @@ impl RealizedMetrics { peak_regret_rel_to_realized_cap, // === Sent in Profit/Loss === - sent_in_profit: LazyComputedValueFromHeightSumCum::forced_import( + sent_in_profit: LazyComputedValueFromHeightCum::forced_import( cfg.db, &cfg.name("sent_in_profit"), cfg.version, @@ -827,7 +816,7 @@ impl RealizedMetrics { cfg.version, cfg.indexes, )?, - sent_in_loss: LazyComputedValueFromHeightSumCum::forced_import( + sent_in_loss: LazyComputedValueFromHeightCum::forced_import( cfg.db, &cfg.name("sent_in_loss"), cfg.version, @@ -1099,15 +1088,15 @@ impl RealizedMetrics { // realized_cap_cents: ComputedFromHeightLast - day1 is lazy, nothing to compute // investor_price_cents: ComputedFromHeightLast - day1 is lazy, nothing to compute - // realized_profit/loss: ComputedFromHeightSumCum - compute cumulative from height + // realized_profit/loss: ComputedFromHeightCum - compute cumulative from height self.realized_profit - .compute_cumulative(starting_indexes, exit)?; + .compute_cumulative(starting_indexes.height, exit)?; self.realized_loss - .compute_cumulative(starting_indexes, exit)?; + .compute_cumulative(starting_indexes.height, exit)?; // net_realized_pnl = profit - loss self.net_realized_pnl - .compute(starting_indexes, exit, |vec| { + .compute(starting_indexes.height, exit, |vec| { vec.compute_subtract( starting_indexes.height, &self.realized_profit.height, @@ -1120,7 +1109,7 @@ impl RealizedMetrics { // realized_value = profit + loss // Note: total_realized_pnl is a lazy alias to realized_value since both // compute profit + loss with sum aggregation, making them identical. - // ComputedFromHeightSum: day1 is lazy, just compute the height vec directly + // ComputedFromHeightLast: day1 is lazy, just compute the height vec directly self.realized_value.height.compute_add( starting_indexes.height, &self.realized_profit.height, @@ -1130,17 +1119,17 @@ impl RealizedMetrics { // Compute derived aggregations for the 4 splits // (value_created, value_destroyed, capitulation_flow, profit_flow are derived lazily) - // ComputedFromHeightSum: day1 is lazy, nothing to compute + // ComputedFromHeightLast: day1 is lazy, nothing to compute - // ATH regret: ComputedFromHeightSumCum - compute cumulative from height + // ATH regret: ComputedFromHeightCum - compute cumulative from height self.peak_regret - .compute_cumulative(starting_indexes, exit)?; + .compute_cumulative(starting_indexes.height, exit)?; - // Volume at profit/loss: LazyComputedValueFromHeightSumCum - compute cumulative + // Volume at profit/loss: LazyComputedValueFromHeightCum - compute cumulative self.sent_in_profit - .compute_cumulative(starting_indexes, exit)?; + .compute_cumulative(starting_indexes.height, exit)?; self.sent_in_loss - .compute_cumulative(starting_indexes, exit)?; + .compute_cumulative(starting_indexes.height, exit)?; Ok(()) } @@ -1378,7 +1367,7 @@ impl RealizedMetrics { .compute_rolling_change( starting_indexes.height, &blocks.count.height_1m_ago, - &*self.net_realized_pnl.rest.height_cumulative, + &self.net_realized_pnl.cumulative.height, exit, )?; diff --git a/crates/brk_computer/src/inputs/compute.rs b/crates/brk_computer/src/inputs/compute.rs index f546d3ba4..d596cc7c1 100644 --- a/crates/brk_computer/src/inputs/compute.rs +++ b/crates/brk_computer/src/inputs/compute.rs @@ -3,20 +3,21 @@ use brk_indexer::Indexer; use vecdb::Exit; use super::Vecs; -use crate::{indexes, ComputeIndexes}; +use crate::{blocks, indexes, ComputeIndexes}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, + blocks: &blocks::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { self.spent .compute(&self.db, indexer, starting_indexes, exit)?; self.count - .compute(indexer, indexes, starting_indexes, exit)?; + .compute(indexer, indexes, blocks, starting_indexes, exit)?; let _lock = exit.lock(); self.db.compact()?; diff --git a/crates/brk_computer/src/inputs/count/compute.rs b/crates/brk_computer/src/inputs/count/compute.rs index 31492a8e3..263eab2a3 100644 --- a/crates/brk_computer/src/inputs/count/compute.rs +++ b/crates/brk_computer/src/inputs/count/compute.rs @@ -3,23 +3,34 @@ use brk_indexer::Indexer; use vecdb::Exit; use super::Vecs; -use crate::{ComputeIndexes, indexes}; +use crate::{blocks, indexes, ComputeIndexes}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, + blocks: &blocks::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.derive_from( - indexer, - indexes, - starting_indexes, + self.height.compute_with_skip( + starting_indexes.height, &indexes.txindex.input_count, + &indexer.vecs.transactions.first_txindex, + &indexes.height.txindex_count, + exit, + 0, + )?; + + let window_starts = blocks.count.window_starts(); + self.rolling.compute( + starting_indexes.height, + &window_starts, + self.height.sum_cum.sum.inner(), exit, )?; + Ok(()) } } diff --git a/crates/brk_computer/src/inputs/count/import.rs b/crates/brk_computer/src/inputs/count/import.rs index 68eb17f5e..071ebede8 100644 --- a/crates/brk_computer/src/inputs/count/import.rs +++ b/crates/brk_computer/src/inputs/count/import.rs @@ -3,15 +3,16 @@ use brk_types::Version; use vecdb::Database; use super::Vecs; -use crate::{indexes, internal::TxDerivedFull}; +use crate::{ + indexes, + internal::{Full, RollingFull}, +}; impl Vecs { pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result { - Ok(Self(TxDerivedFull::forced_import( - db, - "input_count", - version, - indexes, - )?)) + Ok(Self { + height: Full::forced_import(db, "input_count", version)?, + rolling: RollingFull::forced_import(db, "input_count", version, indexes)?, + }) } } diff --git a/crates/brk_computer/src/inputs/count/vecs.rs b/crates/brk_computer/src/inputs/count/vecs.rs index 2c0838dbc..8921c7569 100644 --- a/crates/brk_computer/src/inputs/count/vecs.rs +++ b/crates/brk_computer/src/inputs/count/vecs.rs @@ -1,9 +1,11 @@ use brk_traversable::Traversable; -use brk_types::StoredU64; -use derive_more::{Deref, DerefMut}; +use brk_types::{Height, StoredU64}; use vecdb::{Rw, StorageMode}; -use crate::internal::TxDerivedFull; +use crate::internal::{Full, RollingFull}; -#[derive(Deref, DerefMut, Traversable)] -pub struct Vecs(pub TxDerivedFull); +#[derive(Traversable)] +pub struct Vecs { + pub height: Full, + pub rolling: RollingFull, +} diff --git a/crates/brk_computer/src/internal/distribution_stats.rs b/crates/brk_computer/src/internal/distribution_stats.rs new file mode 100644 index 000000000..841a30419 --- /dev/null +++ b/crates/brk_computer/src/internal/distribution_stats.rs @@ -0,0 +1,18 @@ +//! Base generic struct with 8 type parameters — one per distribution statistic. +//! +//! Foundation for all distribution-style types (average, min, max, percentiles). + +use brk_traversable::Traversable; + +#[derive(Clone, Traversable)] +#[traversable(merge)] +pub struct DistributionStats { + pub average: A, + pub min: B, + pub max: C, + pub p10: D, + pub p25: E, + pub median: F, + pub p75: G, + pub p90: H, +} diff --git a/crates/brk_computer/src/internal/eager_indexes.rs b/crates/brk_computer/src/internal/eager_indexes.rs new file mode 100644 index 000000000..022e05cc1 --- /dev/null +++ b/crates/brk_computer/src/internal/eager_indexes.rs @@ -0,0 +1,218 @@ +//! EagerIndexes - newtype on Indexes with EagerVec> per field. +//! +//! Used for data eagerly computed and stored per period during indexing, +//! such as timestamp (first value per period) and OHLC (first/min/max per period). + +use brk_error::Result; + +use brk_traversable::Traversable; +use brk_types::{ + Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, + Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, +}; +use derive_more::{Deref, DerefMut}; +use schemars::JsonSchema; +use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw, StorageMode, VecIndex}; + +use crate::{ + indexes, + indexes_from, + internal::{ComputedVecValue, Indexes, NumericValue}, + ComputeIndexes, +}; + +pub type EagerIndexesInner = Indexes< + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, +>; + +#[derive(Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct EagerIndexes(pub EagerIndexesInner) +where + T: ComputedVecValue + PartialOrd + JsonSchema; + +const VERSION: Version = Version::ZERO; + +impl EagerIndexes +where + T: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result { + let v = version + VERSION; + + macro_rules! period { + ($idx:ident) => { + ImportableVec::forced_import(db, &format!("{name}_{}", stringify!($idx)), v)? + }; + } + + Ok(Self(indexes_from!(period))) + } + + /// Compute "first value per period" — for each period, looks up `source[first_height[period]]`. + pub(crate) fn compute_first( + &mut self, + starting_indexes: &ComputeIndexes, + height_source: &impl ReadableVec, + indexes: &indexes::Vecs, + exit: &Exit, + ) -> Result<()> { + macro_rules! period { + ($field:ident) => { + self.0.$field.compute_transform( + starting_indexes.$field, + &indexes.$field.first_height, + |(idx, first_h, _)| { + let v = height_source + .collect_one(first_h) + .unwrap_or_else(|| T::from(0_usize)); + (idx, v) + }, + exit, + )?; + }; + } + + period!(minute1); + period!(minute5); + period!(minute10); + period!(minute30); + period!(hour1); + period!(hour4); + period!(hour12); + period!(day1); + period!(day3); + period!(week1); + period!(month1); + period!(month3); + period!(month6); + period!(year1); + period!(year10); + period!(halvingepoch); + period!(difficultyepoch); + + Ok(()) + } + + /// Compute "max value per period" — for each period, finds `max(source[first_height[period]..first_height[period+1]])`. + pub(crate) fn compute_max( + &mut self, + starting_indexes: &ComputeIndexes, + height_source: &impl ReadableVec, + indexes: &indexes::Vecs, + exit: &Exit, + ) -> Result<()> { + let src_len = height_source.len(); + + macro_rules! period { + ($field:ident) => {{ + let fh = &indexes.$field.first_height; + self.0.$field.compute_transform( + starting_indexes.$field, + fh, + |(idx, first_h, _)| { + let end_h = Height::from( + fh.collect_one_at(idx.to_usize() + 1) + .map(|h: Height| h.to_usize()) + .unwrap_or(src_len), + ); + let v = height_source + .max(first_h, end_h) + .unwrap_or_else(|| T::from(0_usize)); + (idx, v) + }, + exit, + )?; + }}; + } + + period!(minute1); + period!(minute5); + period!(minute10); + period!(minute30); + period!(hour1); + period!(hour4); + period!(hour12); + period!(day1); + period!(day3); + period!(week1); + period!(month1); + period!(month3); + period!(month6); + period!(year1); + period!(year10); + period!(halvingepoch); + period!(difficultyepoch); + + Ok(()) + } + + /// Compute "min value per period" — for each period, finds `min(source[first_height[period]..first_height[period+1]])`. + pub(crate) fn compute_min( + &mut self, + starting_indexes: &ComputeIndexes, + height_source: &impl ReadableVec, + indexes: &indexes::Vecs, + exit: &Exit, + ) -> Result<()> { + let src_len = height_source.len(); + + macro_rules! period { + ($field:ident) => {{ + let fh = &indexes.$field.first_height; + self.0.$field.compute_transform( + starting_indexes.$field, + fh, + |(idx, first_h, _)| { + let end_h = Height::from( + fh.collect_one_at(idx.to_usize() + 1) + .map(|h: Height| h.to_usize()) + .unwrap_or(src_len), + ); + let v = height_source + .min(first_h, end_h) + .unwrap_or_else(|| T::from(0_usize)); + (idx, v) + }, + exit, + )?; + }}; + } + + period!(minute1); + period!(minute5); + period!(minute10); + period!(minute30); + period!(hour1); + period!(hour4); + period!(hour12); + period!(day1); + period!(day3); + period!(week1); + period!(month1); + period!(month3); + period!(month6); + period!(year1); + period!(year10); + period!(halvingepoch); + period!(difficultyepoch); + + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/indexes.rs b/crates/brk_computer/src/internal/indexes.rs new file mode 100644 index 000000000..083f734d0 --- /dev/null +++ b/crates/brk_computer/src/internal/indexes.rs @@ -0,0 +1,91 @@ +//! Base generic struct with 17 type parameters — one per time period/epoch index. +//! +//! Foundation for all per-index types. Replaces the repetitive 17-field pattern +//! found throughout height_derived types. + +use brk_traversable::Traversable; + +#[derive(Clone, Traversable)] +#[traversable(merge)] +pub struct Indexes { + pub minute1: M1, + pub minute5: M5, + pub minute10: M10, + pub minute30: M30, + pub hour1: H1, + pub hour4: H4, + pub hour12: H12, + pub day1: D1, + pub day3: D3, + pub week1: W1, + pub month1: Mo1, + pub month3: Mo3, + pub month6: Mo6, + pub year1: Y1, + pub year10: Y10, + pub halvingepoch: HE, + pub difficultyepoch: DE, +} + +/// Helper macro to construct an `Indexes` by applying a macro to each field. +/// +/// Usage: +/// ```ignore +/// indexes_from!(period, epoch) +/// ``` +/// where `period!($field)` and `epoch!($field)` are locally-defined macros. +#[macro_export] +macro_rules! indexes_from { + ($period:ident, $epoch:ident) => { + $crate::internal::Indexes { + minute1: $period!(minute1), + minute5: $period!(minute5), + minute10: $period!(minute10), + minute30: $period!(minute30), + hour1: $period!(hour1), + hour4: $period!(hour4), + hour12: $period!(hour12), + day1: $period!(day1), + day3: $period!(day3), + week1: $period!(week1), + month1: $period!(month1), + month3: $period!(month3), + month6: $period!(month6), + year1: $period!(year1), + year10: $period!(year10), + halvingepoch: $epoch!(halvingepoch), + difficultyepoch: $epoch!(difficultyepoch), + } + }; + // Variant where period and epoch use the same macro + ($m:ident) => { + $crate::indexes_from!($m, $m) + }; +} + +/// Helper macro to apply a function/macro to each field of an `Indexes` value. +#[macro_export] +macro_rules! indexes_map { + ($indexes:expr, |$field:ident| $body:expr) => {{ + let src = $indexes; + $crate::internal::Indexes { + minute1: { let $field = src.minute1; $body }, + minute5: { let $field = src.minute5; $body }, + minute10: { let $field = src.minute10; $body }, + minute30: { let $field = src.minute30; $body }, + hour1: { let $field = src.hour1; $body }, + hour4: { let $field = src.hour4; $body }, + hour12: { let $field = src.hour12; $body }, + day1: { let $field = src.day1; $body }, + day3: { let $field = src.day3; $body }, + week1: { let $field = src.week1; $body }, + month1: { let $field = src.month1; $body }, + month3: { let $field = src.month3; $body }, + month6: { let $field = src.month6; $body }, + year1: { let $field = src.year1; $body }, + year10: { let $field = src.year10; $body }, + halvingepoch: { let $field = src.halvingepoch; $body }, + difficultyepoch: { let $field = src.difficultyepoch; $body }, + } + }}; +} diff --git a/crates/brk_computer/src/internal/lazy_eager_indexes.rs b/crates/brk_computer/src/internal/lazy_eager_indexes.rs new file mode 100644 index 000000000..8f3475dc2 --- /dev/null +++ b/crates/brk_computer/src/internal/lazy_eager_indexes.rs @@ -0,0 +1,70 @@ +//! LazyEagerIndexes - lazy per-period transform of EagerIndexes. +//! +//! Used for lazy currency transforms (e.g., cents→dollars, cents→sats) +//! of eagerly computed per-period data like OHLC. + +use brk_traversable::Traversable; +use brk_types::{ + Day1, Day3, DifficultyEpoch, HalvingEpoch, Minute1, Minute10, Minute30, Minute5, Month1, + Month3, Month6, Version, Week1, Year1, Year10, Hour1, Hour4, Hour12, +}; +use derive_more::{Deref, DerefMut}; +use schemars::JsonSchema; +use vecdb::{LazyVecFrom1, ReadableCloneableVec, UnaryTransform}; + +use crate::{ + indexes_from, + internal::{ComputedVecValue, EagerIndexes, Indexes}, +}; + +pub type LazyEagerIndexesInner = Indexes< + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, +>; + +#[derive(Clone, Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct LazyEagerIndexes(pub LazyEagerIndexesInner) +where + T: ComputedVecValue + PartialOrd + JsonSchema, + S: ComputedVecValue; + +impl LazyEagerIndexes +where + T: ComputedVecValue + PartialOrd + JsonSchema, + S: ComputedVecValue + PartialOrd + JsonSchema, +{ + /// Create lazy per-period transforms from an EagerIndexes source. + pub(crate) fn from_eager_indexes>( + name: &str, + version: Version, + source: &EagerIndexes, + ) -> Self { + macro_rules! period { + ($idx:ident) => { + LazyVecFrom1::transformed::( + &format!("{name}_{}", stringify!($idx)), + version, + source.$idx.read_only_boxed_clone(), + ) + }; + } + + Self(indexes_from!(period)) + } +} diff --git a/crates/brk_computer/src/internal/mod.rs b/crates/brk_computer/src/internal/mod.rs index 38d76b1ee..5092153b7 100644 --- a/crates/brk_computer/src/internal/mod.rs +++ b/crates/brk_computer/src/internal/mod.rs @@ -1,9 +1,19 @@ mod compute; +mod distribution_stats; +mod eager_indexes; +mod indexes; +mod lazy_eager_indexes; mod multi; mod single; mod traits; +mod windows; pub(crate) use compute::*; +pub(crate) use distribution_stats::*; +pub(crate) use eager_indexes::*; +pub(crate) use indexes::*; +pub(crate) use lazy_eager_indexes::*; pub(crate) use multi::*; pub(crate) use single::*; pub(crate) use traits::*; +pub(crate) use windows::*; diff --git a/crates/brk_computer/src/internal/multi/from_height/binary_full.rs b/crates/brk_computer/src/internal/multi/from_height/binary_full.rs deleted file mode 100644 index 820db075a..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/binary_full.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! Lazy binary transform from Full sources. - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableBoxedVec, LazyVecFrom2}; - -use crate::internal::{ - ComputedFromHeightFull, ComputedVecValue, TxDerivedFull, LazyBinaryHeightDerivedSumCum, - NumericValue, -}; - -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryFromHeightFull -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - #[traversable(rename = "base")] - pub height: LazyVecFrom2, - #[deref] - #[deref_mut] - pub rest: Box>, -} - -const VERSION: Version = Version::ZERO; - -impl LazyBinaryFromHeightFull -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: NumericValue + JsonSchema, - S2T: NumericValue + JsonSchema, -{ - pub(crate) fn from_height_and_txindex>( - name: &str, - version: Version, - height_source1: ReadableBoxedVec, - height_source2: ReadableBoxedVec, - source1: &ComputedFromHeightFull, - source2: &TxDerivedFull, - ) -> Self { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::(name, v, height_source1, height_source2), - rest: Box::new(LazyBinaryHeightDerivedSumCum::from_full_sources::( - name, v, &source1.rest, source2, - )), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/binary_last.rs b/crates/brk_computer/src/internal/multi/from_height/binary_last.rs index bb4aed44d..4d17863e6 100644 --- a/crates/brk_computer/src/internal/multi/from_height/binary_last.rs +++ b/crates/brk_computer/src/internal/multi/from_height/binary_last.rs @@ -6,11 +6,13 @@ use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2}; -use crate::internal::{ - ComputedFromHeightLast, ComputedFromHeightSumCum, ComputedHeightDerivedLast, ComputedVecValue, - LazyBinaryComputedFromHeightLast, LazyBinaryComputedFromHeightSum, - LazyBinaryHeightDerivedLast, LazyBinaryTransformLast, - LazyFromHeightLast, NumericValue, +use crate::{ + indexes_from, + internal::{ + ComputedFromHeightLast, ComputedFromHeightSumCum, ComputedHeightDerivedLast, + ComputedVecValue, LazyBinaryComputedFromHeightLast, LazyBinaryHeightDerivedLast, + LazyBinaryTransformLast, LazyFromHeightLast, NumericValue, + }, }; #[derive(Clone, Deref, DerefMut, Traversable)] @@ -43,25 +45,7 @@ macro_rules! build_rest { ) }; } - Box::new(LazyBinaryHeightDerivedLast { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - }) + Box::new(LazyBinaryHeightDerivedLast(indexes_from!(period))) }}; } @@ -269,12 +253,12 @@ where } } - /// Create from a LazyBinaryComputedFromHeightLast and a LazyBinaryComputedFromHeightSum. - pub(crate) fn from_lazy_binary_block_last_and_lazy_binary_sum( + /// Create from two LazyBinaryComputedFromHeightLast sources. + pub(crate) fn from_both_lazy_binary_computed_block_last( name: &str, version: Version, source1: &LazyBinaryComputedFromHeightLast, - source2: &LazyBinaryComputedFromHeightSum, + source2: &LazyBinaryComputedFromHeightLast, ) -> Self where F: BinaryTransform, diff --git a/crates/brk_computer/src/internal/multi/from_height/binary_sum.rs b/crates/brk_computer/src/internal/multi/from_height/binary_sum.rs deleted file mode 100644 index d746c0043..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/binary_sum.rs +++ /dev/null @@ -1,114 +0,0 @@ -//! Lazy binary transform from two Sum-only sources with height level. - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2}; - -use crate::internal::{ - ComputedFromHeightSum, ComputedFromHeightSumCum, ComputedVecValue, - LazyBinaryHeightDerivedSum, LazyFromHeightLast, NumericValue, -}; - -const VERSION: Version = Version::ZERO; - -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryFromHeightSum -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - #[traversable(rename = "sum")] - pub height: LazyVecFrom2, - #[deref] - #[deref_mut] - pub rest: Box>, -} - -impl LazyBinaryFromHeightSum -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: NumericValue + JsonSchema, - S2T: NumericValue + JsonSchema, -{ - pub(crate) fn from_computed>( - name: &str, - version: Version, - source1: &ComputedFromHeightSum, - source2: &ComputedFromHeightSum, - ) -> Self { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::( - name, - v, - source1.height.read_only_boxed_clone(), - source2.height.read_only_boxed_clone(), - ), - rest: Box::new(LazyBinaryHeightDerivedSum::from_derived::(name, v, &source1.rest, &source2.rest)), - } - } - - /// Create from two LazyBinaryFromHeightSum sources. - pub(crate) fn from_binary( - name: &str, - version: Version, - source1: &LazyBinaryFromHeightSum, - source2: &LazyBinaryFromHeightSum, - ) -> Self - where - F: BinaryTransform, - S1aT: ComputedVecValue + JsonSchema, - S1bT: ComputedVecValue + JsonSchema, - S2aT: ComputedVecValue + JsonSchema, - S2bT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::( - name, - v, - source1.height.read_only_boxed_clone(), - source2.height.read_only_boxed_clone(), - ), - rest: Box::new(LazyBinaryHeightDerivedSum::from_binary::( - name, - v, - &source1.rest, - &source2.rest, - )), - } - } - - /// Create from a SumCum source (using only sum) and a LazyLast source. - /// Produces sum-only output (no cumulative). - pub(crate) fn from_sumcum_lazy_last( - name: &str, - version: Version, - height_source1: ReadableBoxedVec, - height_source2: ReadableBoxedVec, - source1: &ComputedFromHeightSumCum, - source2: &LazyFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S2ST: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::(name, v, height_source1, height_source2), - rest: Box::new(LazyBinaryHeightDerivedSum::from_sumcum_lazy_last::( - name, - v, - source1, - source2, - )), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/binary_sum_cum.rs b/crates/brk_computer/src/internal/multi/from_height/binary_sum_cum.rs deleted file mode 100644 index dd949f478..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/binary_sum_cum.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! Lazy binary transform from two SumCum sources. - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2}; - -use crate::internal::{ - ComputedFromHeightSumCum, ComputedHeightDerivedSumCum, - ComputedVecValue, LazyBinaryHeightDerivedSumCum, LazyFromHeightLast, NumericValue, -}; - -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryFromHeightSumCum -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - #[traversable(rename = "sum")] - pub height: LazyVecFrom2, - #[traversable(rename = "cumulative")] - pub height_cumulative: LazyVecFrom2, - #[deref] - #[deref_mut] - pub rest: Box>, -} - -const VERSION: Version = Version::ZERO; - -impl LazyBinaryFromHeightSumCum -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_derived>( - name: &str, - version: Version, - height_source1: ReadableBoxedVec, - height_source2: ReadableBoxedVec, - source1: &ComputedHeightDerivedSumCum, - source2: &ComputedHeightDerivedSumCum, - ) -> Self - where - S1T: PartialOrd, - S2T: PartialOrd, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::(name, v, height_source1, height_source2), - height_cumulative: LazyVecFrom2::transformed::( - &format!("{name}_cumulative"), - v, - source1.height_cumulative.read_only_boxed_clone(), - source2.height_cumulative.read_only_boxed_clone(), - ), - rest: Box::new(LazyBinaryHeightDerivedSumCum::from_computed_sum_raw::( - name, - v, - source1, - source2, - )), - } - } - - // --- Methods accepting SumCum + LazyLast sources --- - - pub(crate) fn from_computed_lazy_last( - name: &str, - version: Version, - height_source1: ReadableBoxedVec, - height_source2: ReadableBoxedVec, - source1: &ComputedFromHeightSumCum, - source2: &LazyFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S1T: PartialOrd, - S2T: NumericValue, - S2ST: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - Self { - height: LazyVecFrom2::transformed::(name, v, height_source1, height_source2), - height_cumulative: LazyVecFrom2::transformed::( - &format!("{name}_cumulative"), - v, - source1.height_cumulative.read_only_boxed_clone(), - source2.height.read_only_boxed_clone(), - ), - rest: Box::new(LazyBinaryHeightDerivedSumCum::from_computed_lazy_last::(name, v, source1, source2)), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/cum.rs b/crates/brk_computer/src/internal/multi/from_height/cum.rs new file mode 100644 index 000000000..c12e88469 --- /dev/null +++ b/crates/brk_computer/src/internal/multi/from_height/cum.rs @@ -0,0 +1,89 @@ +//! ComputedFromHeightCum - stored height + LazyLast + cumulative (from height). +//! +//! Like ComputedFromHeightCumSum but without RollingWindows. +//! Used for distribution metrics where rolling is optional per cohort. +//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views. + +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{Height, Version}; +use derive_more::{Deref, DerefMut}; +use schemars::JsonSchema; +use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode}; + +use crate::{ + indexes, + internal::{ComputedFromHeightLast, NumericValue}, +}; + +#[derive(Deref, DerefMut, Traversable)] +#[traversable(merge)] +pub struct ComputedFromHeightCum +where + T: NumericValue + JsonSchema, +{ + #[deref] + #[deref_mut] + #[traversable(flatten)] + pub last: ComputedFromHeightLast, + #[traversable(flatten)] + pub cumulative: ComputedFromHeightLast, +} + +const VERSION: Version = Version::ZERO; + +impl ComputedFromHeightCum +where + T: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let v = version + VERSION; + + let last = ComputedFromHeightLast::forced_import(db, name, v, indexes)?; + let cumulative = ComputedFromHeightLast::forced_import( + db, + &format!("{name}_cumulative"), + v, + indexes, + )?; + + Ok(Self { last, cumulative }) + } + + /// Compute height data via closure, then cumulative only (no rolling). + pub(crate) fn compute( + &mut self, + max_from: Height, + exit: &Exit, + compute_height: impl FnOnce(&mut EagerVec>) -> Result<()>, + ) -> Result<()> + where + T: Default, + { + compute_height(&mut self.last.height)?; + self.cumulative + .height + .compute_cumulative(max_from, &self.last.height, exit)?; + Ok(()) + } + + /// Compute cumulative from already-filled height vec. + pub(crate) fn compute_cumulative( + &mut self, + max_from: Height, + exit: &Exit, + ) -> Result<()> + where + T: Default, + { + self.cumulative + .height + .compute_cumulative(max_from, &self.last.height, exit)?; + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/multi/from_height/cum_rolling_full.rs b/crates/brk_computer/src/internal/multi/from_height/cum_rolling_full.rs new file mode 100644 index 000000000..712513e52 --- /dev/null +++ b/crates/brk_computer/src/internal/multi/from_height/cum_rolling_full.rs @@ -0,0 +1,86 @@ +//! ComputedFromHeightCumFull - stored height + LazyLast + cumulative (from height) + RollingFull. +//! +//! For metrics with stored per-block data, cumulative sums, and rolling windows. +//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views too. + +use std::ops::SubAssign; + +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{Height, Version}; +use derive_more::{Deref, DerefMut}; +use schemars::JsonSchema; +use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode}; + +use crate::{ + indexes, + internal::{ComputedFromHeightLast, NumericValue, RollingFull, WindowStarts}, +}; + +#[derive(Deref, DerefMut, Traversable)] +#[traversable(merge)] +pub struct ComputedFromHeightCumFull +where + T: NumericValue + JsonSchema, +{ + #[deref] + #[deref_mut] + #[traversable(flatten)] + pub last: ComputedFromHeightLast, + #[traversable(flatten)] + pub cumulative: ComputedFromHeightLast, + #[traversable(flatten)] + pub rolling: RollingFull, +} + +const VERSION: Version = Version::ZERO; + +impl ComputedFromHeightCumFull +where + T: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let v = version + VERSION; + + let last = ComputedFromHeightLast::forced_import(db, name, v, indexes)?; + let cumulative = ComputedFromHeightLast::forced_import( + db, + &format!("{name}_cumulative"), + v, + indexes, + )?; + let rolling = RollingFull::forced_import(db, name, v, indexes)?; + + Ok(Self { + last, + cumulative, + rolling, + }) + } + + /// Compute height data via closure, then cumulative + rolling. + pub(crate) fn compute( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + exit: &Exit, + compute_height: impl FnOnce(&mut EagerVec>) -> Result<()>, + ) -> Result<()> + where + T: From + Default + SubAssign + Copy + Ord, + f64: From, + { + compute_height(&mut self.last.height)?; + self.cumulative + .height + .compute_cumulative(max_from, &self.last.height, exit)?; + self.rolling + .compute(max_from, windows, &self.last.height, exit)?; + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/multi/from_height/cum_rolling_sum.rs b/crates/brk_computer/src/internal/multi/from_height/cum_rolling_sum.rs new file mode 100644 index 000000000..b62b00ecb --- /dev/null +++ b/crates/brk_computer/src/internal/multi/from_height/cum_rolling_sum.rs @@ -0,0 +1,86 @@ +//! ComputedFromHeightCumSum - stored height + LazyLast + cumulative (from height) + RollingWindows (sum). +//! +//! Like ComputedFromHeightCumFull but with rolling sum only (no distribution). +//! Used for count metrics where distribution stats aren't meaningful. +//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views too. + +use std::ops::SubAssign; + +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{Height, Version}; +use derive_more::{Deref, DerefMut}; +use schemars::JsonSchema; +use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode}; + +use crate::{ + indexes, + internal::{ComputedFromHeightLast, NumericValue, RollingWindows, WindowStarts}, +}; + +#[derive(Deref, DerefMut, Traversable)] +#[traversable(merge)] +pub struct ComputedFromHeightCumSum +where + T: NumericValue + JsonSchema, +{ + #[deref] + #[deref_mut] + #[traversable(flatten)] + pub last: ComputedFromHeightLast, + #[traversable(flatten)] + pub cumulative: ComputedFromHeightLast, + #[traversable(flatten)] + pub rolling: RollingWindows, +} + +const VERSION: Version = Version::ZERO; + +impl ComputedFromHeightCumSum +where + T: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let v = version + VERSION; + + let last = ComputedFromHeightLast::forced_import(db, name, v, indexes)?; + let cumulative = ComputedFromHeightLast::forced_import( + db, + &format!("{name}_cumulative"), + v, + indexes, + )?; + let rolling = RollingWindows::forced_import(db, name, v, indexes)?; + + Ok(Self { + last, + cumulative, + rolling, + }) + } + + /// Compute height data via closure, then cumulative + rolling sum. + pub(crate) fn compute( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + exit: &Exit, + compute_height: impl FnOnce(&mut EagerVec>) -> Result<()>, + ) -> Result<()> + where + T: Default + SubAssign, + { + compute_height(&mut self.last.height)?; + self.cumulative + .height + .compute_cumulative(max_from, &self.last.height, exit)?; + self.rolling + .compute_rolling_sum(max_from, windows, &self.last.height, exit)?; + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/multi/from_height/full.rs b/crates/brk_computer/src/internal/multi/from_height/full.rs index 478c13caf..efd95342e 100644 --- a/crates/brk_computer/src/internal/multi/from_height/full.rs +++ b/crates/brk_computer/src/internal/multi/from_height/full.rs @@ -6,11 +6,9 @@ use brk_traversable::Traversable; use brk_types::{Height, Version}; use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{ - Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableCloneableVec, Rw, StorageMode, -}; +use vecdb::{Database, EagerVec, ImportableVec, PcoVec, ReadableCloneableVec, Rw, StorageMode}; -use crate::{ComputeIndexes, indexes}; +use crate::indexes; use crate::internal::{ComputedHeightDerivedFull, ComputedVecValue, NumericValue}; @@ -51,17 +49,9 @@ where indexes, )?; - Ok(Self { height, rest: Box::new(rest) }) - } - - pub(crate) fn compute( - &mut self, - starting_indexes: &ComputeIndexes, - exit: &Exit, - mut compute: impl FnMut(&mut EagerVec>) -> Result<()>, - ) -> Result<()> { - compute(&mut self.height)?; - self.rest.compute_cumulative(starting_indexes, &self.height, exit)?; - Ok(()) + Ok(Self { + height, + rest: Box::new(rest), + }) } } diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_sum.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_sum.rs deleted file mode 100644 index 9dae82554..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_sum.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! LazyBinaryComputedFromHeightSum - block sum with lazy binary transform at height level. -//! -//! Height-level sum is lazy: `transform(source1[h], source2[h])`. -//! Day1 stats are stored since they require aggregation across heights. - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2}; - -use crate::{ - indexes, - internal::{ComputedHeightDerivedSum, ComputedVecValue, NumericValue}, -}; - -const VERSION: Version = Version::ZERO; - -/// Block sum aggregation with lazy binary transform at height + computed derived indexes. -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryComputedFromHeightSum -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - #[traversable(rename = "sum")] - pub height: LazyVecFrom2, - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub rest: Box>, -} - -impl LazyBinaryComputedFromHeightSum -where - T: NumericValue + JsonSchema, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn forced_import>( - name: &str, - version: Version, - source1: ReadableBoxedVec, - source2: ReadableBoxedVec, - indexes: &indexes::Vecs, - ) -> Self { - let v = version + VERSION; - - let height = LazyVecFrom2::transformed::(name, v, source1, source2); - - let rest = - ComputedHeightDerivedSum::forced_import(name, height.read_only_boxed_clone(), v, indexes); - - Self { height, rest: Box::new(rest) } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_sum.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_sum.rs deleted file mode 100644 index 5b98c6760..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_sum.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! Lazy unary transform from height with Sum aggregation. - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform}; - -use crate::internal::{ - ComputedFromHeightSum, ComputedVecValue, LazyHeightDerivedSum, NumericValue, -}; -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyFromHeightSum -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - pub height: LazyVecFrom1, - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub rest: Box>, -} - -const VERSION: Version = Version::ZERO; - -impl LazyFromHeightSum -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_computed>( - name: &str, - version: Version, - height_source: ReadableBoxedVec, - source: &ComputedFromHeightSum, - ) -> Self - where - S1T: NumericValue, - { - let v = version + VERSION; - Self { - height: LazyVecFrom1::transformed::(name, v, height_source), - rest: Box::new(LazyHeightDerivedSum::from_derived_computed::(name, v, &source.rest)), - } - } - -} diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_transform_distribution.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_transform_distribution.rs deleted file mode 100644 index 1f19f9446..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_transform_distribution.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! Lazy unary transform from height with Distribution aggregation. -//! Like LazyFromHeightFull but without sum/cumulative (for ratio/percentage metrics). - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform}; - -use crate::internal::{ - ComputedHeightDerivedFull, ComputedVecValue, LazyHeightDerivedDistribution, NumericValue, -}; - -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyFromHeightTransformDistribution -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - #[traversable(rename = "base")] - pub height: LazyVecFrom1, - #[deref] - #[deref_mut] - pub rest: Box>, -} - -const VERSION: Version = Version::ZERO; - -impl LazyFromHeightTransformDistribution -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_derived>( - name: &str, - version: Version, - height_source: ReadableBoxedVec, - source: &ComputedHeightDerivedFull, - ) -> Self - where - S1T: NumericValue, - { - let v = version + VERSION; - Self { - height: LazyVecFrom1::transformed::(name, v, height_source), - rest: Box::new(LazyHeightDerivedDistribution::from_derived_computed::(name, v, source)), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/mod.rs b/crates/brk_computer/src/internal/multi/from_height/mod.rs index b2f558bfa..a579f8d3c 100644 --- a/crates/brk_computer/src/internal/multi/from_height/mod.rs +++ b/crates/brk_computer/src/internal/multi/from_height/mod.rs @@ -1,79 +1,67 @@ -mod binary_full; mod binary_last; -mod binary_sum; -mod binary_sum_cum; mod constant; +mod cum; +mod cum_rolling_full; +mod cum_rolling_sum; mod distribution; mod full; mod last; mod lazy_binary_computed_distribution; mod lazy_binary_computed_full; mod lazy_binary_computed_last; -mod lazy_binary_computed_sum; mod lazy_binary_computed_sum_cum; mod lazy_computed_full; mod lazy_computed_sum_cum; mod lazy_full; mod lazy_last; -mod lazy_sum; mod lazy_sum_cum; -mod lazy_transform_distribution; mod lazy_value; mod percentiles; mod price; mod ratio; mod stddev; mod stored_value_last; -mod sum; mod sum_cum; -mod value_binary; mod value_change; mod value_ema; mod value_full; mod value_last; mod value_lazy_binary_last; -mod value_lazy_computed_sum_cum; +mod value_lazy_computed_cum; mod value_lazy_last; mod value_lazy_sum_cum; -mod value_sum; mod value_sum_cum; -pub use binary_full::*; pub use binary_last::*; -pub use binary_sum::*; -pub use binary_sum_cum::*; pub use constant::*; +pub use cum::*; +pub use cum_rolling_full::*; +pub use cum_rolling_sum::*; pub use distribution::*; pub use full::*; pub use last::*; pub use lazy_binary_computed_distribution::*; pub use lazy_binary_computed_full::*; pub use lazy_binary_computed_last::*; -pub use lazy_binary_computed_sum::*; pub use lazy_binary_computed_sum_cum::*; pub use lazy_computed_full::*; pub use lazy_computed_sum_cum::*; pub use lazy_full::*; pub use lazy_last::*; -pub use lazy_sum::*; pub use lazy_sum_cum::*; -pub use lazy_transform_distribution::*; pub use lazy_value::*; pub use percentiles::*; pub use price::*; pub use ratio::*; pub use stddev::*; pub use stored_value_last::*; -pub use sum::*; pub use sum_cum::*; -pub use value_binary::*; pub use value_change::*; pub use value_ema::*; pub use value_full::*; pub use value_last::*; pub use value_lazy_binary_last::*; -pub use value_lazy_computed_sum_cum::*; +pub use value_lazy_computed_cum::*; pub use value_lazy_last::*; pub use value_lazy_sum_cum::*; -pub use value_sum::*; pub use value_sum_cum::*; diff --git a/crates/brk_computer/src/internal/multi/from_height/sum.rs b/crates/brk_computer/src/internal/multi/from_height/sum.rs deleted file mode 100644 index ca9eaa59f..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/sum.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! ComputedFromHeight using Sum-only aggregation. - -use brk_error::Result; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{Database, EagerVec, ImportableVec, PcoVec, ReadableCloneableVec, Rw, StorageMode}; - -use crate::indexes; - -use crate::internal::{ComputedHeightDerivedSum, ComputedVecValue, NumericValue}; - -#[derive(Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct ComputedFromHeightSum -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - pub height: M::Stored>>, - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub rest: Box>, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedFromHeightSum -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let v = version + VERSION; - - let height: EagerVec> = EagerVec::forced_import(db, name, v)?; - - let rest = - ComputedHeightDerivedSum::forced_import(name, height.read_only_boxed_clone(), v, indexes); - - Ok(Self { height, rest: Box::new(rest) }) - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/value_binary.rs b/crates/brk_computer/src/internal/multi/from_height/value_binary.rs deleted file mode 100644 index f55ad98c5..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/value_binary.rs +++ /dev/null @@ -1,64 +0,0 @@ -use brk_traversable::Traversable; -use brk_types::{Bitcoin, Dollars, Sats, Version}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableCloneableVec}; - -use crate::internal::{ComputedVecValue, LazyBinaryFromHeightSumCum, LazyValueFromHeightSumCum}; - -/// Lazy value vecs computed from two ValueFromHeightSumCum sources via binary transforms. -/// Used for computing coinbase = subsidy + fee. -#[derive(Clone, Traversable)] -pub struct ValueBinaryFromHeight { - pub sats: LazyBinaryFromHeightSumCum, - pub btc: LazyBinaryFromHeightSumCum, - pub usd: LazyBinaryFromHeightSumCum, -} - -impl ValueBinaryFromHeight { - pub(crate) fn from_lazy( - name: &str, - version: Version, - source1: &LazyValueFromHeightSumCum, - source2: &LazyValueFromHeightSumCum, - ) -> Self - where - SatsF: BinaryTransform, - BitcoinF: BinaryTransform, - DollarsF: BinaryTransform, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, - { - let sats = LazyBinaryFromHeightSumCum::from_derived::( - name, - version, - source1.sats.height.read_only_boxed_clone(), - source2.sats.height.read_only_boxed_clone(), - &source1.sats.rest, - &source2.sats.rest, - ); - - let btc = LazyBinaryFromHeightSumCum::from_derived::( - &format!("{name}_btc"), - version, - source1.sats.height.read_only_boxed_clone(), - source2.sats.height.read_only_boxed_clone(), - &source1.sats.rest, - &source2.sats.rest, - ); - - let usd = LazyBinaryFromHeightSumCum::from_derived::( - &format!("{name}_usd"), - version, - source1.usd.height.read_only_boxed_clone(), - source2.usd.height.read_only_boxed_clone(), - &source1.usd.rest, - &source2.usd.rest, - ); - - Self { - sats, - btc, - usd, - } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_cum.rs b/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_cum.rs new file mode 100644 index 000000000..f20daec75 --- /dev/null +++ b/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_cum.rs @@ -0,0 +1,71 @@ +//! Value type with stored sats height + cumulative, lazy btc + lazy dollars. +//! +//! Like LazyComputedValueFromHeightSumCum but with Cum (no old period aggregations). +//! - Sats: stored height + cumulative (ComputedFromHeightCum) +//! - BTC: lazy transform from sats (LazyFromHeightLast) +//! - USD: lazy binary (price × sats), LazyLast per index (no stored cumulative) + +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{Bitcoin, Dollars, Height, Sats, Version}; +use vecdb::{Database, Exit, ReadableCloneableVec, Rw, StorageMode}; + +use crate::{ + indexes, + internal::{ + ComputedFromHeightCum, LazyBinaryComputedFromHeightLast, LazyFromHeightLast, + PriceTimesSats, SatsToBitcoin, + }, + prices, +}; + +/// Value wrapper with stored sats height + cumulative, lazy btc + lazy usd. +#[derive(Traversable)] +pub struct LazyComputedValueFromHeightCum { + pub sats: ComputedFromHeightCum, + pub btc: LazyFromHeightLast, + pub usd: LazyBinaryComputedFromHeightLast, +} + +const VERSION: Version = Version::ZERO; + +impl LazyComputedValueFromHeightCum { + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + prices: &prices::Vecs, + ) -> Result { + let v = version + VERSION; + + let sats = ComputedFromHeightCum::forced_import(db, name, v, indexes)?; + + let btc = LazyFromHeightLast::from_computed::( + &format!("{name}_btc"), + v, + sats.height.read_only_boxed_clone(), + &sats, + ); + + let usd = LazyBinaryComputedFromHeightLast::forced_import::( + &format!("{name}_usd"), + v, + prices.usd.price.read_only_boxed_clone(), + sats.height.read_only_boxed_clone(), + indexes, + ); + + Ok(Self { sats, btc, usd }) + } + + /// Compute cumulative from already-filled sats height vec. + pub(crate) fn compute_cumulative( + &mut self, + max_from: Height, + exit: &Exit, + ) -> Result<()> { + self.sats.compute_cumulative(max_from, exit)?; + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_sum_cum.rs b/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_sum_cum.rs deleted file mode 100644 index e7dffc632..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_sum_cum.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Value type with stored height + lazy dollars for SumCum pattern. -//! -//! Use this when: -//! - Sats height is stored (primary source of truth) -//! - Sats indexes are derived from height -//! - Bitcoin is lazy (transform from sats) -//! - Dollars height is lazy (price × sats), with stored indexes - -use brk_error::Result; -use brk_traversable::Traversable; -use brk_types::{Bitcoin, Dollars, Sats, Version}; -use vecdb::{Database, Exit, ReadableCloneableVec, LazyVecFrom2, Rw, StorageMode}; - -use crate::{ - ComputeIndexes, indexes, - internal::{ - ComputedFromHeightSumCum, LazyComputedFromHeightSumCum, LazyFromHeightSumCum, - PriceTimesSats, SatsToBitcoin, - }, - prices, -}; - -/// Value wrapper with stored sats height + lazy dollars. -/// -/// Sats height is stored (computed directly or from stateful loop). -/// Dollars height is lazy (price × sats). -/// Cumulative and day1 aggregates are stored for both. -#[derive(Traversable)] -pub struct LazyComputedValueFromHeightSumCum { - pub sats: ComputedFromHeightSumCum, - pub btc: LazyFromHeightSumCum, - pub usd: LazyComputedFromHeightSumCum, -} - -const VERSION: Version = Version::ZERO; - -impl LazyComputedValueFromHeightSumCum { - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - prices: &prices::Vecs, - ) -> Result { - let v = version + VERSION; - - let sats = ComputedFromHeightSumCum::forced_import(db, name, v, indexes)?; - - let btc = LazyFromHeightSumCum::from_computed::( - &format!("{name}_btc"), - v, - sats.height.read_only_boxed_clone(), - &sats, - ); - - let usd_height = LazyVecFrom2::transformed::( - &format!("{name}_usd"), - v, - prices.usd.price.read_only_boxed_clone(), - sats.height.read_only_boxed_clone(), - ); - - let usd = LazyComputedFromHeightSumCum::forced_import( - db, - &format!("{name}_usd"), - v, - indexes, - usd_height, - )?; - - Ok(Self { - sats, - btc, - usd, - }) - } - - /// Compute cumulative from already-computed height. - pub(crate) fn compute_cumulative( - &mut self, - starting_indexes: &ComputeIndexes, - exit: &Exit, - ) -> Result<()> { - self.sats.compute_cumulative(starting_indexes, exit)?; - self.usd.compute_cumulative(starting_indexes, exit)?; - Ok(()) - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/value_sum.rs b/crates/brk_computer/src/internal/multi/from_height/value_sum.rs deleted file mode 100644 index 376b32a4b..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/value_sum.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! Value type for Sum pattern from Height. -//! -//! Height-level USD value is lazy: `sats * price`. -//! Day1 sum is stored since it requires aggregation across heights with varying prices. - -use brk_error::Result; -use brk_traversable::Traversable; -use brk_types::{Bitcoin, Dollars, Sats, Version}; -use vecdb::{Database, ReadableCloneableVec, Rw, StorageMode}; - -use crate::{ - indexes, - internal::{ - ComputedFromHeightSum, LazyBinaryComputedFromHeightSum, LazyFromHeightSum, SatsTimesPrice, - SatsToBitcoin, - }, - prices, -}; - -#[derive(Traversable)] -pub struct ValueFromHeightSum { - pub sats: ComputedFromHeightSum, - pub btc: LazyFromHeightSum, - pub usd: LazyBinaryComputedFromHeightSum, -} - -const VERSION: Version = Version::ONE; // Bumped for lazy height dollars - -impl ValueFromHeightSum { - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - prices: &prices::Vecs, - ) -> Result { - let v = version + VERSION; - - let sats = ComputedFromHeightSum::forced_import(db, name, v, indexes)?; - - let btc = LazyFromHeightSum::from_computed::( - &format!("{name}_btc"), - v, - sats.height.read_only_boxed_clone(), - &sats, - ); - - let usd = LazyBinaryComputedFromHeightSum::forced_import::( - &format!("{name}_usd"), - v, - sats.height.read_only_boxed_clone(), - prices.usd.price.read_only_boxed_clone(), - indexes, - ); - - Ok(Self { - sats, - btc, - usd, - }) - } -} diff --git a/crates/brk_computer/src/internal/multi/from_tx/mod.rs b/crates/brk_computer/src/internal/multi/from_tx/mod.rs index c8faef681..2fe8bcd49 100644 --- a/crates/brk_computer/src/internal/multi/from_tx/mod.rs +++ b/crates/brk_computer/src/internal/multi/from_tx/mod.rs @@ -1,7 +1,3 @@ mod lazy_distribution; -mod value_dollars; -mod value_full; pub use lazy_distribution::*; -pub use value_dollars::*; -pub use value_full::*; diff --git a/crates/brk_computer/src/internal/multi/from_tx/value_dollars.rs b/crates/brk_computer/src/internal/multi/from_tx/value_dollars.rs deleted file mode 100644 index 39e601588..000000000 --- a/crates/brk_computer/src/internal/multi/from_tx/value_dollars.rs +++ /dev/null @@ -1,204 +0,0 @@ -//! Dollars from TxIndex with lazy height stats and stored day1. -//! -//! Height-level USD stats (min/max/avg/sum/percentiles) are lazy: `sats_stat * price`. -//! Height cumulative and day1 stats are stored since they require aggregation -//! across heights with varying prices. - -use brk_error::Result; -use brk_traversable::Traversable; -use brk_types::{ - Bitcoin, Day1, Day3, DifficultyEpoch, Dollars, HalvingEpoch, Height, Hour1, Hour4, Hour12, - Minute1, Minute5, Minute10, Minute30, Month1, Month3, Month6, Sats, TxIndex, Version, Week1, - Year1, Year10, -}; -use vecdb::{ - Database, Exit, LazyVecFrom3, ReadableBoxedVec, ReadableCloneableVec, Rw, StorageMode, -}; - -use crate::{ - ComputeIndexes, indexes, - internal::{CumulativeVec, Full, LazyBinaryTransformFull, LazyFull, SatsTimesPrice}, -}; - -/// Lazy dollars at TxIndex: `sats * price[height]` -pub type LazyDollarsTxIndex = - LazyVecFrom3; - -/// Lazy dollars height stats: `sats_height_stat * price` -pub type LazyDollarsHeightFull = LazyBinaryTransformFull; - -/// Dollars with lazy txindex and height fields, stored day1. -/// -/// Height-level stats (except cumulative) are lazy: `sats * price[height]`. -/// Cumulative at height level is stored since it requires summing historical values. -/// Day1 stats are stored since they aggregate across heights with varying prices. -#[derive(Traversable)] -#[traversable(merge)] -pub struct ValueDollarsFromTxFull { - #[traversable(skip)] - pub txindex: LazyDollarsTxIndex, - #[traversable(flatten)] - pub height: LazyDollarsHeightFull, - #[traversable(rename = "cumulative")] - pub height_cumulative: CumulativeVec, - pub minute1: LazyFull, - pub minute5: LazyFull, - pub minute10: LazyFull, - pub minute30: LazyFull, - pub hour1: LazyFull, - pub hour4: LazyFull, - pub hour12: LazyFull, - pub day1: LazyFull, - pub day3: LazyFull, - pub week1: LazyFull, - pub month1: LazyFull, - pub month3: LazyFull, - pub month6: LazyFull, - pub year1: LazyFull, - pub year10: LazyFull, - pub halvingepoch: LazyFull, - pub difficultyepoch: LazyFull, -} - -const VERSION: Version = Version::ONE; // Bumped for lazy height change - -impl ValueDollarsFromTxFull { - #[allow(clippy::too_many_arguments)] - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - sats_height: &Full, - height_to_price: ReadableBoxedVec, - sats_txindex: ReadableBoxedVec, - txindex_to_height: ReadableBoxedVec, - ) -> Result { - let v = version + VERSION; - - let txindex = create_lazy_txindex( - name, - v, - sats_txindex, - txindex_to_height, - height_to_price.clone(), - ); - - // Lazy height stats: sats_stat * price - let height = LazyBinaryTransformFull::from_full_and_source::( - name, - v, - sats_height, - height_to_price.clone(), - ); - - // Stored cumulative - must be computed by summing historical sum*price - let height_cumulative = CumulativeVec::forced_import(db, name, v)?; - - macro_rules! period { - ($idx:ident) => { - LazyFull::from_height_source( - name, - v, - height.boxed_sum(), - height_cumulative.read_only_boxed_clone(), - indexes.$idx.first_height.read_only_boxed_clone(), - ) - }; - } - - macro_rules! epoch { - ($idx:ident) => { - LazyFull::from_stats_aggregate( - name, - v, - height.boxed_average(), - height.boxed_min(), - height.boxed_max(), - height.boxed_sum(), - height_cumulative.read_only_boxed_clone(), - height.boxed_average(), - indexes.$idx.identity.read_only_boxed_clone(), - ) - }; - } - - let minute1 = period!(minute1); - let minute5 = period!(minute5); - let minute10 = period!(minute10); - let minute30 = period!(minute30); - let hour1 = period!(hour1); - let hour4 = period!(hour4); - let hour12 = period!(hour12); - let day1 = period!(day1); - let day3 = period!(day3); - let week1 = period!(week1); - let month1 = period!(month1); - let month3 = period!(month3); - let month6 = period!(month6); - let year1 = period!(year1); - let year10 = period!(year10); - let halvingepoch = epoch!(halvingepoch); - let difficultyepoch = epoch!(difficultyepoch); - - Ok(Self { - txindex, - height, - height_cumulative, - minute1, - minute5, - minute10, - minute30, - hour1, - hour4, - hour12, - day1, - day3, - week1, - month1, - month3, - month6, - year1, - year10, - halvingepoch, - difficultyepoch, - }) - } - - /// Compute stored fields (cumulative and day1) from lazy height stats. - /// - /// This is MUCH faster than the old approach since it only iterates heights, - /// not all transactions per block. - pub(crate) fn derive_from( - &mut self, - _indexes: &indexes::Vecs, - starting_indexes: &ComputeIndexes, - exit: &Exit, - ) -> Result<()> { - // Compute height cumulative by summing lazy height.sum values - self.height_cumulative.0.compute_cumulative( - starting_indexes.height, - &self.height.sum, - exit, - )?; - - Ok(()) - } -} - -fn create_lazy_txindex( - name: &str, - version: Version, - sats_txindex: ReadableBoxedVec, - txindex_to_height: ReadableBoxedVec, - height_to_price: ReadableBoxedVec, -) -> LazyDollarsTxIndex { - LazyVecFrom3::init( - &format!("{name}_txindex"), - version, - sats_txindex, - txindex_to_height, - height_to_price, - |_index, sats, _height, close| close * Bitcoin::from(sats), - ) -} diff --git a/crates/brk_computer/src/internal/multi/from_tx/value_full.rs b/crates/brk_computer/src/internal/multi/from_tx/value_full.rs deleted file mode 100644 index aae3ef62b..000000000 --- a/crates/brk_computer/src/internal/multi/from_tx/value_full.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! ValueFromTxFull - eager txindex Sats source + ValueTxDerivedFull (sats/bitcoin/dollars). - -use brk_error::Result; -use brk_indexer::Indexer; -use brk_traversable::Traversable; -use brk_types::{Sats, TxIndex, Version}; -use derive_more::{Deref, DerefMut}; -use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode}; - -use crate::{ComputeIndexes, indexes, internal::ValueTxDerivedFull, prices}; - -const VERSION: Version = Version::ZERO; - -#[derive(Deref, DerefMut, Traversable)] -pub struct ValueFromTxFull { - #[traversable(rename = "txindex")] - pub base: M::Stored>>, - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub indexes: ValueTxDerivedFull, -} - -impl ValueFromTxFull { - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - indexer: &Indexer, - prices: &prices::Vecs, - ) -> Result { - let v = version + VERSION; - let txindex = EagerVec::forced_import(db, name, v)?; - let derived = - ValueTxDerivedFull::forced_import(db, name, v, indexes, indexer, prices, &txindex)?; - Ok(Self { - base: txindex, - indexes: derived, - }) - } - - /// Derive from source, skipping first N transactions per block from all calculations. - /// - /// Use `skip_count: 1` to exclude coinbase transactions from fee/feerate stats. - pub(crate) fn derive_from_with_skip( - &mut self, - indexer: &Indexer, - indexes: &indexes::Vecs, - starting_indexes: &ComputeIndexes, - exit: &Exit, - skip_count: usize, - ) -> Result<()> { - self.indexes.derive_from_with_skip( - indexer, - indexes, - starting_indexes, - &self.base, - exit, - skip_count, - ) - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/binary_last.rs b/crates/brk_computer/src/internal/multi/height_derived/binary_last.rs index 0903878b6..0ebb4089b 100644 --- a/crates/brk_computer/src/internal/multi/height_derived/binary_last.rs +++ b/crates/brk_computer/src/internal/multi/height_derived/binary_last.rs @@ -1,44 +1,53 @@ //! Lazy binary transform for derived block with Last aggregation only. +//! +//! Newtype on `Indexes` with `LazyBinaryTransformLast` per field. use brk_traversable::Traversable; use brk_types::{ Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, }; +use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; use vecdb::{BinaryTransform, ReadableCloneableVec}; -use crate::internal::{ - ComputedFromHeightLast, ComputedFromHeightSumCum, ComputedVecValue, - LazyBinaryTransformLast, LazyFromHeightLast, NumericValue, +use crate::{ + indexes_from, + internal::{ + ComputedFromHeightLast, ComputedFromHeightSumCum, ComputedVecValue, Indexes, + LazyBinaryTransformLast, LazyFromHeightLast, NumericValue, + }, }; -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryHeightDerivedLast +pub type LazyBinaryHeightDerivedLastInner = Indexes< + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, + LazyBinaryTransformLast, +>; + +#[derive(Clone, Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct LazyBinaryHeightDerivedLast( + pub LazyBinaryHeightDerivedLastInner, +) where T: ComputedVecValue + PartialOrd + JsonSchema, S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - pub minute1: LazyBinaryTransformLast, - pub minute5: LazyBinaryTransformLast, - pub minute10: LazyBinaryTransformLast, - pub minute30: LazyBinaryTransformLast, - pub hour1: LazyBinaryTransformLast, - pub hour4: LazyBinaryTransformLast, - pub hour12: LazyBinaryTransformLast, - pub day1: LazyBinaryTransformLast, - pub day3: LazyBinaryTransformLast, - pub week1: LazyBinaryTransformLast, - pub month1: LazyBinaryTransformLast, - pub month3: LazyBinaryTransformLast, - pub month6: LazyBinaryTransformLast, - pub year1: LazyBinaryTransformLast, - pub year10: LazyBinaryTransformLast, - pub halvingepoch: LazyBinaryTransformLast, - pub difficultyepoch: LazyBinaryTransformLast, -} + S2T: ComputedVecValue; const VERSION: Version = Version::ZERO; @@ -71,25 +80,7 @@ where }; } - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } + Self(indexes_from!(period)) } pub(crate) fn from_computed_last>( @@ -115,25 +106,7 @@ where }; } - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } + Self(indexes_from!(period)) } pub(crate) fn from_lazy_block_last_and_block_last( @@ -160,25 +133,7 @@ where }; } - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } + Self(indexes_from!(period)) } pub(crate) fn from_block_last_and_lazy_block_last( @@ -205,24 +160,6 @@ where }; } - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } + Self(indexes_from!(period)) } } diff --git a/crates/brk_computer/src/internal/multi/height_derived/binary_sum.rs b/crates/brk_computer/src/internal/multi/height_derived/binary_sum.rs deleted file mode 100644 index cfbb13eb5..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/binary_sum.rs +++ /dev/null @@ -1,183 +0,0 @@ -//! Lazy aggregated binary transform for Sum-only pattern across all time periods. - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30, - Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableCloneableVec}; - -use crate::internal::{ - ComputedFromHeightSumCum, ComputedHeightDerivedSum, ComputedVecValue, LazyBinaryTransformSum, - LazyFromHeightLast, NumericValue, -}; - -const VERSION: Version = Version::ZERO; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryHeightDerivedSum -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - pub minute1: LazyBinaryTransformSum, - pub minute5: LazyBinaryTransformSum, - pub minute10: LazyBinaryTransformSum, - pub minute30: LazyBinaryTransformSum, - pub hour1: LazyBinaryTransformSum, - pub hour4: LazyBinaryTransformSum, - pub hour12: LazyBinaryTransformSum, - pub day1: LazyBinaryTransformSum, - pub day3: LazyBinaryTransformSum, - pub week1: LazyBinaryTransformSum, - pub month1: LazyBinaryTransformSum, - pub month3: LazyBinaryTransformSum, - pub month6: LazyBinaryTransformSum, - pub year1: LazyBinaryTransformSum, - pub year10: LazyBinaryTransformSum, - pub halvingepoch: LazyBinaryTransformSum, - pub difficultyepoch: LazyBinaryTransformSum, -} - -impl LazyBinaryHeightDerivedSum -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: NumericValue + JsonSchema, - S2T: NumericValue + JsonSchema, -{ - pub(crate) fn from_derived>( - name: &str, - version: Version, - source1: &ComputedHeightDerivedSum, - source2: &ComputedHeightDerivedSum, - ) -> Self { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyBinaryTransformSum::from_boxed::( - name, - v, - source1.$p.read_only_boxed_clone(), - source2.$p.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } - - /// Create from two LazyBinaryHeightDerivedSum sources. - pub(crate) fn from_binary( - name: &str, - version: Version, - source1: &LazyBinaryHeightDerivedSum, - source2: &LazyBinaryHeightDerivedSum, - ) -> Self - where - F: BinaryTransform, - S1aT: ComputedVecValue + JsonSchema, - S1bT: ComputedVecValue + JsonSchema, - S2aT: ComputedVecValue + JsonSchema, - S2bT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyBinaryTransformSum::from_boxed::( - name, - v, - source1.$p.read_only_boxed_clone(), - source2.$p.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } - - /// Create from a SumCum source (using only sum) and a LazyLast source. - pub(crate) fn from_sumcum_lazy_last( - name: &str, - version: Version, - source1: &ComputedFromHeightSumCum, - source2: &LazyFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S2ST: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyBinaryTransformSum::from_boxed::( - name, - v, - source1.$p.sum.read_only_boxed_clone(), - source2.$p.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/binary_sum_cum.rs b/crates/brk_computer/src/internal/multi/height_derived/binary_sum_cum.rs deleted file mode 100644 index 8732e42e8..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/binary_sum_cum.rs +++ /dev/null @@ -1,184 +0,0 @@ -//! Lazy aggregated SumCum - binary transform version. - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour4, Hour12, Minute1, Minute5, Minute10, - Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableCloneableVec}; - -use crate::internal::{ - ComputedFromHeightSumCum, ComputedHeightDerivedFull, ComputedHeightDerivedSumCum, - ComputedVecValue, LazyBinaryTransformSumCum, LazyFromHeightLast, NumericValue, TxDerivedFull, -}; - -const VERSION: Version = Version::ZERO; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryHeightDerivedSumCum -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - pub minute1: LazyBinaryTransformSumCum, - pub minute5: LazyBinaryTransformSumCum, - pub minute10: LazyBinaryTransformSumCum, - pub minute30: LazyBinaryTransformSumCum, - pub hour1: LazyBinaryTransformSumCum, - pub hour4: LazyBinaryTransformSumCum, - pub hour12: LazyBinaryTransformSumCum, - pub day1: LazyBinaryTransformSumCum, - pub day3: LazyBinaryTransformSumCum, - pub week1: LazyBinaryTransformSumCum, - pub month1: LazyBinaryTransformSumCum, - pub month3: LazyBinaryTransformSumCum, - pub month6: LazyBinaryTransformSumCum, - pub year1: LazyBinaryTransformSumCum, - pub year10: LazyBinaryTransformSumCum, - pub halvingepoch: LazyBinaryTransformSumCum, - pub difficultyepoch: LazyBinaryTransformSumCum, -} - -impl LazyBinaryHeightDerivedSumCum -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - /// Create from two ComputedHeightDerivedSumCum sources. - pub(crate) fn from_computed_sum_raw>( - name: &str, - version: Version, - source1: &ComputedHeightDerivedSumCum, - source2: &ComputedHeightDerivedSumCum, - ) -> Self { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyBinaryTransformSumCum::from_sources_sum_raw::( - name, - v, - source1.$p.sum.read_only_boxed_clone(), - source2.$p.sum.read_only_boxed_clone(), - source1.$p.cumulative.read_only_boxed_clone(), - source2.$p.cumulative.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } - - /// Create from ComputedHeightDerivedFull + TxDerivedFull sources. - pub(crate) fn from_full_sources>( - name: &str, - version: Version, - source1: &ComputedHeightDerivedFull, - source2: &TxDerivedFull, - ) -> Self - where - S1T: PartialOrd, - S2T: PartialOrd, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyBinaryTransformSumCum::from_lazy_stats_aggregate::( - name, v, &source1.$p, &source2.$p, - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } - - // --- Methods accepting SumCum + LazyLast sources --- - - pub(crate) fn from_computed_lazy_last( - name: &str, - version: Version, - source1: &ComputedFromHeightSumCum, - source2: &LazyFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S1T: PartialOrd, - S2T: NumericValue, - S2ST: ComputedVecValue + schemars::JsonSchema, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyBinaryTransformSumCum::from_sources_last_sum_raw::( - name, - v, - source1.$p.sum.read_only_boxed_clone(), - source1.$p.cumulative.read_only_boxed_clone(), - source2.$p.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/cum_full.rs b/crates/brk_computer/src/internal/multi/height_derived/cum_full.rs new file mode 100644 index 000000000..e4cad197f --- /dev/null +++ b/crates/brk_computer/src/internal/multi/height_derived/cum_full.rs @@ -0,0 +1,83 @@ +//! ComputedHeightDerivedCumFull - LazyLast index views + cumulative (from height) + RollingFull. +//! +//! For metrics derived from indexer sources (no stored height vec). +//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views too. + +use std::ops::SubAssign; + +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{Height, Version}; +use schemars::JsonSchema; +use vecdb::{Database, Exit, ReadableBoxedVec, ReadableVec, Rw, StorageMode}; + +use crate::{ + indexes, + internal::{ + ComputedFromHeightLast, ComputedHeightDerivedLast, NumericValue, RollingFull, WindowStarts, + }, +}; + +#[derive(Traversable)] +#[traversable(merge)] +pub struct ComputedHeightDerivedCumFull +where + T: NumericValue + JsonSchema, +{ + #[traversable(flatten)] + pub last: ComputedHeightDerivedLast, + #[traversable(flatten)] + pub cumulative: ComputedFromHeightLast, + #[traversable(flatten)] + pub rolling: RollingFull, +} + +const VERSION: Version = Version::ZERO; + +impl ComputedHeightDerivedCumFull +where + T: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + height_source: ReadableBoxedVec, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let v = version + VERSION; + + let last = ComputedHeightDerivedLast::forced_import(name, height_source, v, indexes); + let cumulative = ComputedFromHeightLast::forced_import( + db, + &format!("{name}_cumulative"), + v, + indexes, + )?; + let rolling = RollingFull::forced_import(db, name, v, indexes)?; + + Ok(Self { + last, + cumulative, + rolling, + }) + } + + pub(crate) fn compute( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + height_source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + T: From + Default + SubAssign + Copy + Ord, + f64: From, + { + self.cumulative + .height + .compute_cumulative(max_from, height_source, exit)?; + self.rolling.compute(max_from, windows, height_source, exit)?; + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/multi/height_derived/first.rs b/crates/brk_computer/src/internal/multi/height_derived/first.rs deleted file mode 100644 index 7625b49d6..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/first.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! ComputedHeightDerivedFirst - lazy time periods + epochs (first value). - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, ReadableCloneableVec}; - -use crate::{ - indexes, - internal::{ComputedVecValue, LazyFirst, NumericValue}, -}; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct ComputedHeightDerivedFirst -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - pub minute1: LazyFirst, - pub minute5: LazyFirst, - pub minute10: LazyFirst, - pub minute30: LazyFirst, - pub hour1: LazyFirst, - pub hour4: LazyFirst, - pub hour12: LazyFirst, - pub day1: LazyFirst, - pub day3: LazyFirst, - pub week1: LazyFirst, - pub month1: LazyFirst, - pub month3: LazyFirst, - pub month6: LazyFirst, - pub year1: LazyFirst, - pub year10: LazyFirst, - pub halvingepoch: LazyFirst, - pub difficultyepoch: LazyFirst, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedHeightDerivedFirst -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - name: &str, - height_source: ReadableBoxedVec, - version: Version, - indexes: &indexes::Vecs, - ) -> Self { - let v = version + VERSION; - - macro_rules! period { - ($idx:ident) => { - LazyFirst::from_height_source( - name, - v, - height_source.clone(), - indexes.$idx.first_height.read_only_boxed_clone(), - ) - }; - } - - macro_rules! epoch { - ($idx:ident) => { - LazyFirst::from_source( - name, - v, - height_source.clone(), - indexes.$idx.identity.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: epoch!(halvingepoch), - difficultyepoch: epoch!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/last.rs b/crates/brk_computer/src/internal/multi/height_derived/last.rs index 73e5422c9..7791cb69c 100644 --- a/crates/brk_computer/src/internal/multi/height_derived/last.rs +++ b/crates/brk_computer/src/internal/multi/height_derived/last.rs @@ -1,42 +1,48 @@ //! ComputedHeightDerivedLast - lazy time periods + epochs (last value). +//! +//! Newtype on `Indexes` with `LazyLast` per field. use brk_traversable::Traversable; use brk_types::{ Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, }; +use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; use vecdb::{ReadableBoxedVec, ReadableCloneableVec}; use crate::{ indexes, - internal::{ComputedVecValue, LazyLast, NumericValue, SparseLast}, + indexes_from, + internal::{ComputedVecValue, Indexes, LazyLast, NumericValue}, }; -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct ComputedHeightDerivedLast +/// All 17 time-period/epoch `LazyLast` vecs, packed as a newtype on `Indexes`. +pub type ComputedHeightDerivedLastInner = Indexes< + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, + LazyLast, +>; + +#[derive(Clone, Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct ComputedHeightDerivedLast(pub ComputedHeightDerivedLastInner) where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - pub minute1: LazyLast, - pub minute5: LazyLast, - pub minute10: LazyLast, - pub minute30: LazyLast, - pub hour1: LazyLast, - pub hour4: LazyLast, - pub hour12: LazyLast, - pub day1: LazyLast, - pub day3: LazyLast, - pub week1: LazyLast, - pub month1: LazyLast, - pub month3: LazyLast, - pub month6: LazyLast, - pub year1: LazyLast, - pub year10: LazyLast, - pub halvingepoch: LazyLast, - pub difficultyepoch: LazyLast, -} + T: ComputedVecValue + PartialOrd + JsonSchema; const VERSION: Version = Version::ZERO; @@ -74,24 +80,6 @@ where }; } - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: epoch!(halvingepoch), - difficultyepoch: epoch!(difficultyepoch), - } + Self(indexes_from!(period, epoch)) } } diff --git a/crates/brk_computer/src/internal/multi/height_derived/lazy_distribution.rs b/crates/brk_computer/src/internal/multi/height_derived/lazy_distribution.rs deleted file mode 100644 index 19a300501..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/lazy_distribution.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! Lazy aggregated Distribution for block-level sources. -//! Like LazyHeightDerivedFull but without sum/cumulative (for ratio/percentage metrics). - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30, - Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{ReadableCloneableVec, UnaryTransform}; - -use crate::internal::{ - ComputedHeightDerivedFull, ComputedVecValue, LazyTransformDistribution, NumericValue, -}; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct LazyHeightDerivedDistribution -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - pub minute1: LazyTransformDistribution, - pub minute5: LazyTransformDistribution, - pub minute10: LazyTransformDistribution, - pub minute30: LazyTransformDistribution, - pub hour1: LazyTransformDistribution, - pub hour4: LazyTransformDistribution, - pub hour12: LazyTransformDistribution, - pub day1: LazyTransformDistribution, - pub day3: LazyTransformDistribution, - pub week1: LazyTransformDistribution, - pub month1: LazyTransformDistribution, - pub month3: LazyTransformDistribution, - pub month6: LazyTransformDistribution, - pub year1: LazyTransformDistribution, - pub year10: LazyTransformDistribution, - pub halvingepoch: LazyTransformDistribution, - pub difficultyepoch: LazyTransformDistribution, -} - -const VERSION: Version = Version::ZERO; - -impl LazyHeightDerivedDistribution -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_derived_computed>( - name: &str, - version: Version, - source: &ComputedHeightDerivedFull, - ) -> Self - where - S1T: NumericValue, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyTransformDistribution::from_boxed::( - name, - v, - source.$p.average.read_only_boxed_clone(), - source.$p.min.read_only_boxed_clone(), - source.$p.max.read_only_boxed_clone(), - source.$p.percentiles.pct10.read_only_boxed_clone(), - source.$p.percentiles.pct25.read_only_boxed_clone(), - source.$p.percentiles.median.read_only_boxed_clone(), - source.$p.percentiles.pct75.read_only_boxed_clone(), - source.$p.percentiles.pct90.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/lazy_last.rs b/crates/brk_computer/src/internal/multi/height_derived/lazy_last.rs index e0e8946cf..aed940493 100644 --- a/crates/brk_computer/src/internal/multi/height_derived/lazy_last.rs +++ b/crates/brk_computer/src/internal/multi/height_derived/lazy_last.rs @@ -1,43 +1,50 @@ //! Lazy aggregated Last for block-level sources. +//! +//! Newtype on `Indexes` with `LazyTransformLast` per field. use brk_traversable::Traversable; use brk_types::{ Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, }; +use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; use vecdb::{ReadableCloneableVec, UnaryTransform}; -use crate::internal::{ - ComputedFromHeightLast, ComputedHeightDerivedLast, ComputedVecValue, - LazyBinaryHeightDerivedLast, LazyTransformLast, NumericValue, +use crate::{ + indexes_from, + internal::{ + ComputedFromHeightLast, ComputedHeightDerivedLast, ComputedVecValue, Indexes, + LazyBinaryHeightDerivedLast, LazyTransformLast, NumericValue, + }, }; -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct LazyHeightDerivedLast +pub type LazyHeightDerivedLastInner = Indexes< + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, + LazyTransformLast, +>; + +#[derive(Clone, Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct LazyHeightDerivedLast(pub LazyHeightDerivedLastInner) where T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - pub minute1: LazyTransformLast, - pub minute5: LazyTransformLast, - pub minute10: LazyTransformLast, - pub minute30: LazyTransformLast, - pub hour1: LazyTransformLast, - pub hour4: LazyTransformLast, - pub hour12: LazyTransformLast, - pub day1: LazyTransformLast, - pub day3: LazyTransformLast, - pub week1: LazyTransformLast, - pub month1: LazyTransformLast, - pub month3: LazyTransformLast, - pub month6: LazyTransformLast, - pub year1: LazyTransformLast, - pub year10: LazyTransformLast, - pub halvingepoch: LazyTransformLast, - pub difficultyepoch: LazyTransformLast, -} + S1T: ComputedVecValue; const VERSION: Version = Version::ZERO; @@ -62,25 +69,7 @@ where }; } - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } + Self(indexes_from!(period)) } pub(crate) fn from_derived_computed>( @@ -99,25 +88,7 @@ where }; } - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } + Self(indexes_from!(period)) } /// Create by unary-transforming a LazyHeightDerivedLast source. @@ -138,25 +109,7 @@ where }; } - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } + Self(indexes_from!(period)) } /// Create by unary-transforming a LazyBinaryHeightDerivedLast source. @@ -178,25 +131,6 @@ where }; } - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } + Self(indexes_from!(period)) } - } diff --git a/crates/brk_computer/src/internal/multi/height_derived/lazy_sum.rs b/crates/brk_computer/src/internal/multi/height_derived/lazy_sum.rs deleted file mode 100644 index ece9da82a..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/lazy_sum.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! Lazy aggregated Sum for block-level sources. - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30, - Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{ReadableCloneableVec, UnaryTransform}; - -use crate::internal::{ - ComputedHeightDerivedSum, ComputedVecValue, LazyTransformSum, NumericValue, -}; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct LazyHeightDerivedSum -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - pub minute1: LazyTransformSum, - pub minute5: LazyTransformSum, - pub minute10: LazyTransformSum, - pub minute30: LazyTransformSum, - pub hour1: LazyTransformSum, - pub hour4: LazyTransformSum, - pub hour12: LazyTransformSum, - pub day1: LazyTransformSum, - pub day3: LazyTransformSum, - pub week1: LazyTransformSum, - pub month1: LazyTransformSum, - pub month3: LazyTransformSum, - pub month6: LazyTransformSum, - pub year1: LazyTransformSum, - pub year10: LazyTransformSum, - pub halvingepoch: LazyTransformSum, - pub difficultyepoch: LazyTransformSum, -} - -const VERSION: Version = Version::ZERO; - -impl LazyHeightDerivedSum -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_derived_computed>( - name: &str, - version: Version, - source: &ComputedHeightDerivedSum, - ) -> Self - where - S1T: NumericValue, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyTransformSum::from_boxed::(name, v, source.$p.read_only_boxed_clone()) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/max.rs b/crates/brk_computer/src/internal/multi/height_derived/max.rs deleted file mode 100644 index d98b927c3..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/max.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! ComputedHeightDerivedMax - lazy time periods + epochs (max value). - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, ReadableCloneableVec}; - -use crate::{ - indexes, - internal::{ComputedVecValue, LazyMax, NumericValue}, -}; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct ComputedHeightDerivedMax -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - pub minute1: LazyMax, - pub minute5: LazyMax, - pub minute10: LazyMax, - pub minute30: LazyMax, - pub hour1: LazyMax, - pub hour4: LazyMax, - pub hour12: LazyMax, - pub day1: LazyMax, - pub day3: LazyMax, - pub week1: LazyMax, - pub month1: LazyMax, - pub month3: LazyMax, - pub month6: LazyMax, - pub year1: LazyMax, - pub year10: LazyMax, - pub halvingepoch: LazyMax, - pub difficultyepoch: LazyMax, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedHeightDerivedMax -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - name: &str, - height_source: ReadableBoxedVec, - version: Version, - indexes: &indexes::Vecs, - ) -> Self { - let v = version + VERSION; - - macro_rules! period { - ($idx:ident) => { - LazyMax::from_height_source_raw( - name, - v, - height_source.clone(), - indexes.$idx.first_height.read_only_boxed_clone(), - ) - }; - } - - macro_rules! epoch { - ($idx:ident) => { - LazyMax::from_source_raw( - name, - v, - height_source.clone(), - indexes.$idx.identity.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: epoch!(halvingepoch), - difficultyepoch: epoch!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/min.rs b/crates/brk_computer/src/internal/multi/height_derived/min.rs deleted file mode 100644 index 5a50f365a..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/min.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! ComputedHeightDerivedMin - lazy time periods + epochs (min value). - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, ReadableCloneableVec}; - -use crate::{ - indexes, - internal::{ComputedVecValue, LazyMin, NumericValue}, -}; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct ComputedHeightDerivedMin -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - pub minute1: LazyMin, - pub minute5: LazyMin, - pub minute10: LazyMin, - pub minute30: LazyMin, - pub hour1: LazyMin, - pub hour4: LazyMin, - pub hour12: LazyMin, - pub day1: LazyMin, - pub day3: LazyMin, - pub week1: LazyMin, - pub month1: LazyMin, - pub month3: LazyMin, - pub month6: LazyMin, - pub year1: LazyMin, - pub year10: LazyMin, - pub halvingepoch: LazyMin, - pub difficultyepoch: LazyMin, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedHeightDerivedMin -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - name: &str, - height_source: ReadableBoxedVec, - version: Version, - indexes: &indexes::Vecs, - ) -> Self { - let v = version + VERSION; - - macro_rules! period { - ($idx:ident) => { - LazyMin::from_height_source_raw( - name, - v, - height_source.clone(), - indexes.$idx.first_height.read_only_boxed_clone(), - ) - }; - } - - macro_rules! epoch { - ($idx:ident) => { - LazyMin::from_source_raw( - name, - v, - height_source.clone(), - indexes.$idx.identity.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: epoch!(halvingepoch), - difficultyepoch: epoch!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/mod.rs b/crates/brk_computer/src/internal/multi/height_derived/mod.rs index 61ec320fc..883b12b41 100644 --- a/crates/brk_computer/src/internal/multi/height_derived/mod.rs +++ b/crates/brk_computer/src/internal/multi/height_derived/mod.rs @@ -1,39 +1,21 @@ mod binary_last; -mod binary_sum; -mod binary_sum_cum; +mod cum_full; mod distribution; -mod first; mod full; mod last; -mod lazy_distribution; mod lazy_full; mod lazy_last; -mod lazy_sum; mod lazy_sum_cum; -mod max; -mod min; -mod ohlc; -mod split_ohlc; -mod sum; mod sum_cum; mod value_lazy_last; pub use binary_last::*; -pub use binary_sum::*; -pub use binary_sum_cum::*; +pub use cum_full::*; pub use distribution::*; -pub use first::*; pub use full::*; pub use last::*; -pub use lazy_distribution::*; pub use lazy_full::*; pub use lazy_last::*; -pub use lazy_sum::*; pub use lazy_sum_cum::*; -pub use max::*; -pub use min::*; -pub use ohlc::*; -pub use split_ohlc::*; -pub use sum::*; pub use sum_cum::*; pub use value_lazy_last::*; diff --git a/crates/brk_computer/src/internal/multi/height_derived/ohlc.rs b/crates/brk_computer/src/internal/multi/height_derived/ohlc.rs deleted file mode 100644 index a79c834b9..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/ohlc.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! Lazy OHLC period groupings derived from height-level data. -//! -//! Each period's OHLC is computed lazily in a single pass over the source range: -//! open = first, high = max, low = min, close = last. - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, ReadableCloneableVec}; - -use crate::{ - indexes, - internal::{ComputedVecValue, LazyOHLC, OHLCRecord}, -}; - -/// Lazy bundled OHLC vecs for all periods, derived from height-level data. -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct ComputedHeightDerivedOHLC -where - OHLC: OHLCRecord + 'static, -{ - pub minute1: LazyOHLC, - pub minute5: LazyOHLC, - pub minute10: LazyOHLC, - pub minute30: LazyOHLC, - pub hour1: LazyOHLC, - pub hour4: LazyOHLC, - pub hour12: LazyOHLC, - pub day1: LazyOHLC, - pub day3: LazyOHLC, - pub week1: LazyOHLC, - pub month1: LazyOHLC, - pub month3: LazyOHLC, - pub month6: LazyOHLC, - pub year1: LazyOHLC, - pub year10: LazyOHLC, - pub halvingepoch: LazyOHLC, - pub difficultyepoch: LazyOHLC, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedHeightDerivedOHLC -where - OHLC: OHLCRecord + 'static, - OHLC::Inner: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn forced_import( - name: &str, - version: Version, - indexes: &indexes::Vecs, - height_source: ReadableBoxedVec, - ) -> Self { - let v = version + VERSION; - - macro_rules! period { - ($idx:ident) => { - LazyOHLC::from_height_source( - name, - v, - height_source.clone(), - indexes.$idx.first_height.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/split_ohlc.rs b/crates/brk_computer/src/internal/multi/height_derived/split_ohlc.rs deleted file mode 100644 index f0f752650..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/split_ohlc.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! OHLC split into separate First/Last/Max/Min period groupings derived from height-level data. - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::ReadableBoxedVec; - -use crate::{ - indexes, - internal::{ - ComputedHeightDerivedFirst, ComputedHeightDerivedLast, ComputedHeightDerivedMax, - ComputedHeightDerivedMin, ComputedVecValue, NumericValue, - }, -}; - -/// Split OHLC vecs for all periods, derived from height data. -#[derive(Clone, Traversable)] -pub struct ComputedHeightDerivedSplitOHLC -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - pub open: ComputedHeightDerivedFirst, - pub high: ComputedHeightDerivedMax, - pub low: ComputedHeightDerivedMin, - pub close: ComputedHeightDerivedLast, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedHeightDerivedSplitOHLC -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - name: &str, - version: Version, - indexes: &indexes::Vecs, - height_source: ReadableBoxedVec, - ) -> Self { - let v = version + VERSION; - - Self { - open: ComputedHeightDerivedFirst::forced_import(&format!("{name}_open"), height_source.clone(), v, indexes), - high: ComputedHeightDerivedMax::forced_import(&format!("{name}_high"), height_source.clone(), v, indexes), - low: ComputedHeightDerivedMin::forced_import(&format!("{name}_low"), height_source.clone(), v, indexes), - close: ComputedHeightDerivedLast::forced_import(&format!("{name}_close"), height_source, v, indexes), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/sum.rs b/crates/brk_computer/src/internal/multi/height_derived/sum.rs deleted file mode 100644 index aa5528d2e..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/sum.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! ComputedHeightDerivedSum - lazy time periods + epochs. - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, ReadableCloneableVec}; - -use crate::{ - indexes, - internal::{ComputedVecValue, LazySum, NumericValue}, -}; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct ComputedHeightDerivedSum -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - pub minute1: LazySum, - pub minute5: LazySum, - pub minute10: LazySum, - pub minute30: LazySum, - pub hour1: LazySum, - pub hour4: LazySum, - pub hour12: LazySum, - pub day1: LazySum, - pub day3: LazySum, - pub week1: LazySum, - pub month1: LazySum, - pub month3: LazySum, - pub month6: LazySum, - pub year1: LazySum, - pub year10: LazySum, - pub halvingepoch: LazySum, - pub difficultyepoch: LazySum, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedHeightDerivedSum -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - name: &str, - height_source: ReadableBoxedVec, - version: Version, - indexes: &indexes::Vecs, - ) -> Self { - let v = version + VERSION; - - macro_rules! period { - ($idx:ident) => { - LazySum::from_height_source_raw( - name, - v, - height_source.clone(), - indexes.$idx.first_height.read_only_boxed_clone(), - ) - }; - } - - macro_rules! epoch { - ($idx:ident) => { - LazySum::from_source_raw( - name, - v, - height_source.clone(), - indexes.$idx.identity.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: epoch!(halvingepoch), - difficultyepoch: epoch!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/tx_derived/full.rs b/crates/brk_computer/src/internal/multi/tx_derived/full.rs deleted file mode 100644 index 07bc81ec8..000000000 --- a/crates/brk_computer/src/internal/multi/tx_derived/full.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! TxDerivedFull - aggregates from TxIndex to height Full + lazy time periods + epochs. - -use brk_error::Result; -use brk_indexer::Indexer; - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, TxIndex, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode}; - -use crate::{ - indexes, ComputeIndexes, - internal::{ComputedVecValue, Full, LazyFull, NumericValue}, -}; - -/// Aggregates from TxIndex to height/time periods with full stats. -#[derive(Traversable)] -#[traversable(merge)] -pub struct TxDerivedFull -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - pub height: Full, - pub minute1: LazyFull, - pub minute5: LazyFull, - pub minute10: LazyFull, - pub minute30: LazyFull, - pub hour1: LazyFull, - pub hour4: LazyFull, - pub hour12: LazyFull, - pub day1: LazyFull, - pub day3: LazyFull, - pub week1: LazyFull, - pub month1: LazyFull, - pub month3: LazyFull, - pub month6: LazyFull, - pub year1: LazyFull, - pub year10: LazyFull, - pub halvingepoch: LazyFull, - pub difficultyepoch: LazyFull, -} - -const VERSION: Version = Version::ONE; - -impl TxDerivedFull -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let height = Full::forced_import(db, name, version + VERSION)?; - let v = version + VERSION; - - macro_rules! period { - ($idx:ident) => { - LazyFull::from_height_source( - name, - v, - height.boxed_sum(), - height.boxed_cumulative(), - indexes.$idx.first_height.read_only_boxed_clone(), - ) - }; - } - - macro_rules! epoch { - ($idx:ident) => { - LazyFull::from_stats_aggregate( - name, - v, - height.boxed_average(), - height.boxed_min(), - height.boxed_max(), - height.boxed_sum(), - height.boxed_cumulative(), - height.boxed_average(), - indexes.$idx.identity.read_only_boxed_clone(), - ) - }; - } - - let minute1 = period!(minute1); - let minute5 = period!(minute5); - let minute10 = period!(minute10); - let minute30 = period!(minute30); - let hour1 = period!(hour1); - let hour4 = period!(hour4); - let hour12 = period!(hour12); - let day1 = period!(day1); - let day3 = period!(day3); - let week1 = period!(week1); - let month1 = period!(month1); - let month3 = period!(month3); - let month6 = period!(month6); - let year1 = period!(year1); - let year10 = period!(year10); - let halvingepoch = epoch!(halvingepoch); - let difficultyepoch = epoch!(difficultyepoch); - - Ok(Self { - height, - minute1, - minute5, - minute10, - minute30, - hour1, - hour4, - hour12, - day1, - day3, - week1, - month1, - month3, - month6, - year1, - year10, - halvingepoch, - difficultyepoch, - }) - } - - pub(crate) fn derive_from( - &mut self, - indexer: &Indexer, - indexes: &indexes::Vecs, - starting_indexes: &ComputeIndexes, - txindex_source: &impl ReadableVec, - exit: &Exit, - ) -> Result<()> { - self.derive_from_with_skip(indexer, indexes, starting_indexes, txindex_source, exit, 0) - } - - /// Derive from source, skipping first N transactions per block from all calculations. - /// - /// Use `skip_count: 1` to exclude coinbase transactions from fee/feerate stats. - pub(crate) fn derive_from_with_skip( - &mut self, - indexer: &Indexer, - indexes: &indexes::Vecs, - starting_indexes: &ComputeIndexes, - txindex_source: &impl ReadableVec, - exit: &Exit, - skip_count: usize, - ) -> Result<()> { - self.height.compute_with_skip( - starting_indexes.height, - txindex_source, - &indexer.vecs.transactions.first_txindex, - &indexes.height.txindex_count, - exit, - skip_count, - )?; - - Ok(()) - } -} diff --git a/crates/brk_computer/src/internal/multi/tx_derived/lazy_full.rs b/crates/brk_computer/src/internal/multi/tx_derived/lazy_full.rs deleted file mode 100644 index f84595e99..000000000 --- a/crates/brk_computer/src/internal/multi/tx_derived/lazy_full.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Lazy transform of TxDerivedFull. - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{ReadableCloneableVec, UnaryTransform}; - -use crate::internal::{ComputedVecValue, TxDerivedFull, LazyTransformFull}; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct LazyTxDerivedFull -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - pub height: LazyTransformFull, - pub minute1: LazyTransformFull, - pub minute5: LazyTransformFull, - pub minute10: LazyTransformFull, - pub minute30: LazyTransformFull, - pub hour1: LazyTransformFull, - pub hour4: LazyTransformFull, - pub hour12: LazyTransformFull, - pub day1: LazyTransformFull, - pub day3: LazyTransformFull, - pub week1: LazyTransformFull, - pub month1: LazyTransformFull, - pub month3: LazyTransformFull, - pub month6: LazyTransformFull, - pub year1: LazyTransformFull, - pub year10: LazyTransformFull, - pub halvingepoch: LazyTransformFull, - pub difficultyepoch: LazyTransformFull, -} - -const VERSION: Version = Version::ZERO; - -impl LazyTxDerivedFull -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_computed>( - name: &str, - version: Version, - source: &TxDerivedFull, - ) -> Self { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyTransformFull::from_boxed::( - name, - v, - source.$p.average.read_only_boxed_clone(), - source.$p.min.read_only_boxed_clone(), - source.$p.max.read_only_boxed_clone(), - source.$p.percentiles.pct10.read_only_boxed_clone(), - source.$p.percentiles.pct25.read_only_boxed_clone(), - source.$p.percentiles.median.read_only_boxed_clone(), - source.$p.percentiles.pct75.read_only_boxed_clone(), - source.$p.percentiles.pct90.read_only_boxed_clone(), - source.$p.sum.read_only_boxed_clone(), - source.$p.cumulative.read_only_boxed_clone(), - ) - }; - } - - Self { - height: LazyTransformFull::from_stats_aggregate::(name, v, &source.height), - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/tx_derived/mod.rs b/crates/brk_computer/src/internal/multi/tx_derived/mod.rs index e5df1d57c..79fe1f2d5 100644 --- a/crates/brk_computer/src/internal/multi/tx_derived/mod.rs +++ b/crates/brk_computer/src/internal/multi/tx_derived/mod.rs @@ -1,9 +1,3 @@ mod distribution; -mod full; -mod lazy_full; -mod value_full; pub use distribution::*; -pub use full::*; -pub use lazy_full::*; -pub use value_full::*; diff --git a/crates/brk_computer/src/internal/multi/tx_derived/value_full.rs b/crates/brk_computer/src/internal/multi/tx_derived/value_full.rs deleted file mode 100644 index 5202f5c50..000000000 --- a/crates/brk_computer/src/internal/multi/tx_derived/value_full.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! Value type for Full pattern from TxIndex. - -use brk_error::Result; -use brk_indexer::Indexer; -use brk_traversable::Traversable; -use brk_types::{Bitcoin, Sats, TxIndex, Version}; -use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode}; - -use crate::{ - ComputeIndexes, indexes, - internal::{LazyTxDerivedFull, SatsToBitcoin, TxDerivedFull, ValueDollarsFromTxFull}, - prices, -}; - -#[derive(Traversable)] -pub struct ValueTxDerivedFull { - pub sats: TxDerivedFull, - pub btc: LazyTxDerivedFull, - pub usd: ValueDollarsFromTxFull, -} - -const VERSION: Version = Version::ZERO; - -impl ValueTxDerivedFull { - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - indexer: &Indexer, - prices: &prices::Vecs, - sats_txindex: &impl ReadableCloneableVec, - ) -> Result { - let v = version + VERSION; - - let sats = TxDerivedFull::forced_import(db, name, v, indexes)?; - - let btc = - LazyTxDerivedFull::from_computed::(&format!("{name}_btc"), v, &sats); - - let usd = ValueDollarsFromTxFull::forced_import( - db, - &format!("{name}_usd"), - v, - indexes, - &sats.height, - prices.usd.price.read_only_boxed_clone(), - sats_txindex.read_only_boxed_clone(), - indexer.vecs.transactions.height.read_only_boxed_clone(), - )?; - - Ok(Self { - sats, - btc, - usd, - }) - } - - /// Derive from source, skipping first N transactions per block from all calculations. - /// - /// Use `skip_count: 1` to exclude coinbase transactions from fee/feerate stats. - pub(crate) fn derive_from_with_skip( - &mut self, - indexer: &Indexer, - indexes: &indexes::Vecs, - starting_indexes: &ComputeIndexes, - txindex_source: &impl ReadableVec, - exit: &Exit, - skip_count: usize, - ) -> Result<()> { - self.sats.derive_from_with_skip( - indexer, - indexes, - starting_indexes, - txindex_source, - exit, - skip_count, - )?; - - self.usd.derive_from(indexes, starting_indexes, exit)?; - - Ok(()) - } -} diff --git a/crates/brk_computer/src/internal/single/group/distribution.rs b/crates/brk_computer/src/internal/single/group/distribution.rs index c4dc8f575..4016e503f 100644 --- a/crates/brk_computer/src/internal/single/group/distribution.rs +++ b/crates/brk_computer/src/internal/single/group/distribution.rs @@ -68,14 +68,6 @@ impl Distribution { self.min_max_average.boxed_average() } - pub(crate) fn boxed_min(&self) -> ReadableBoxedVec { - self.min_max_average.boxed_min() - } - - pub(crate) fn boxed_max(&self) -> ReadableBoxedVec { - self.min_max_average.boxed_max() - } - pub fn read_only_clone(&self) -> Distribution { Distribution { min_max_average: self.min_max_average.read_only_clone(), diff --git a/crates/brk_computer/src/internal/single/group/full.rs b/crates/brk_computer/src/internal/single/group/full.rs index 0d6aea6fc..2c4b8e108 100644 --- a/crates/brk_computer/src/internal/single/group/full.rs +++ b/crates/brk_computer/src/internal/single/group/full.rs @@ -65,27 +65,10 @@ impl Full { ) } - // Boxed accessors - pub(crate) fn boxed_average(&self) -> ReadableBoxedVec { - self.distribution.boxed_average() - } - - pub(crate) fn boxed_min(&self) -> ReadableBoxedVec { - self.distribution.boxed_min() - } - - pub(crate) fn boxed_max(&self) -> ReadableBoxedVec { - self.distribution.boxed_max() - } - pub(crate) fn boxed_sum(&self) -> ReadableBoxedVec { self.sum_cum.sum.0.read_only_boxed_clone() } - pub(crate) fn boxed_cumulative(&self) -> ReadableBoxedVec { - self.sum_cum.cumulative.0.read_only_boxed_clone() - } - pub fn read_only_clone(&self) -> Full { Full { distribution: self.distribution.read_only_clone(), diff --git a/crates/brk_computer/src/internal/single/group/min_max_average.rs b/crates/brk_computer/src/internal/single/group/min_max_average.rs index 9a8079b52..9ae668dcc 100644 --- a/crates/brk_computer/src/internal/single/group/min_max_average.rs +++ b/crates/brk_computer/src/internal/single/group/min_max_average.rs @@ -1,7 +1,9 @@ use brk_error::Result; use brk_traversable::Traversable; use schemars::JsonSchema; -use vecdb::{Database, ReadableBoxedVec, ReadableCloneableVec, Ro, Rw, StorageMode, VecIndex, Version}; +use vecdb::{ + Database, ReadableBoxedVec, ReadableCloneableVec, Ro, Rw, StorageMode, VecIndex, Version, +}; use crate::internal::{AverageVec, ComputedVecValue}; @@ -28,14 +30,6 @@ impl MinMaxAverage { self.average.0.read_only_boxed_clone() } - pub(crate) fn boxed_min(&self) -> ReadableBoxedVec { - self.minmax.min.0.read_only_boxed_clone() - } - - pub(crate) fn boxed_max(&self) -> ReadableBoxedVec { - self.minmax.max.0.read_only_boxed_clone() - } - pub fn read_only_clone(&self) -> MinMaxAverage { MinMaxAverage { average: self.average.read_only_clone(), diff --git a/crates/brk_computer/src/internal/single/group/percentiles.rs b/crates/brk_computer/src/internal/single/group/percentiles.rs index 62bd85993..bbb06ed1d 100644 --- a/crates/brk_computer/src/internal/single/group/percentiles.rs +++ b/crates/brk_computer/src/internal/single/group/percentiles.rs @@ -1,9 +1,7 @@ use brk_error::Result; use brk_traversable::Traversable; use schemars::JsonSchema; -use vecdb::{ - Database, ReadableBoxedVec, ReadableCloneableVec, Ro, Rw, StorageMode, VecIndex, Version, -}; +use vecdb::{Database, Ro, Rw, StorageMode, VecIndex, Version}; use crate::internal::{ComputedVecValue, MedianVec, Pct10Vec, Pct25Vec, Pct75Vec, Pct90Vec}; @@ -28,27 +26,6 @@ impl Percentiles { }) } - // Boxed accessors - pub(crate) fn boxed_pct10(&self) -> ReadableBoxedVec { - self.pct10.0.read_only_boxed_clone() - } - - pub(crate) fn boxed_pct25(&self) -> ReadableBoxedVec { - self.pct25.0.read_only_boxed_clone() - } - - pub(crate) fn boxed_median(&self) -> ReadableBoxedVec { - self.median.0.read_only_boxed_clone() - } - - pub(crate) fn boxed_pct75(&self) -> ReadableBoxedVec { - self.pct75.0.read_only_boxed_clone() - } - - pub(crate) fn boxed_pct90(&self) -> ReadableBoxedVec { - self.pct90.0.read_only_boxed_clone() - } - pub fn read_only_clone(&self) -> Percentiles { Percentiles { pct10: self.pct10.read_only_clone(), diff --git a/crates/brk_computer/src/internal/single/lazy/first.rs b/crates/brk_computer/src/internal/single/lazy/first.rs deleted file mode 100644 index 1ec3c7fe4..000000000 --- a/crates/brk_computer/src/internal/single/lazy/first.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! Lazy first-value aggregation. - -use std::sync::Arc; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -type ForEachRangeFn = - fn(usize, usize, &ReadableBoxedVec, &ReadableBoxedVec, &mut dyn FnMut(T)); - -pub struct LazyFirst -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - name: Arc, - version: Version, - source: ReadableBoxedVec, - mapping: ReadableBoxedVec, - for_each_range: ForEachRangeFn, -} - -impl_lazy_agg!(LazyFirst); - -impl LazyFirst -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - pub(crate) fn from_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - fn for_each_range< - I: VecIndex, - T: VecValue, - S1I: VecIndex + FromCoarserIndex, - S2T: VecValue, - >( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let mapping_len = mapping.len(); - let mut cursor = Cursor::from_dyn(&**source); - for i in from..to { - if i >= mapping_len { - break; - } - let target = S1I::min_from(I::from(i)); - if cursor.position() <= target { - cursor.advance(target - cursor.position()); - if let Some(v) = cursor.next() { - f(v); - } - } else if let Some(v) = source.collect_one_at(target) { - f(v); - } - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: len_source, - for_each_range: for_each_range::, - } - } -} - -impl LazyFirst -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn from_height_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - fn for_each_range( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let heights = mapping.collect_range_dyn(from, to.min(mapping.len())); - let mut cursor = Cursor::from_dyn(&**source); - for idx in 0..(to - from) { - let Some(&first_h) = heights.get(idx) else { - continue; - }; - let target = first_h.to_usize(); - if cursor.position() <= target { - cursor.advance(target - cursor.position()); - if let Some(v) = cursor.next() { - f(v); - } - } else if let Some(v) = source.collect_one_at(target) { - f(v); - } - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: first_height, - for_each_range: for_each_range::, - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/max.rs b/crates/brk_computer/src/internal/single/lazy/max.rs index 4cf8e7e32..2764614b5 100644 --- a/crates/brk_computer/src/internal/single/lazy/max.rs +++ b/crates/brk_computer/src/internal/single/lazy/max.rs @@ -46,15 +46,6 @@ where Self::from_source_inner(&format!("{name}_max"), version, source, len_source) } - pub(crate) fn from_source_raw( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - Self::from_source_inner(name, version, source, len_source) - } - fn from_source_inner( name: &str, version: Version, @@ -117,15 +108,6 @@ where Self::from_height_source_inner(&format!("{name}_max"), version, source, first_height) } - pub(crate) fn from_height_source_raw( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - Self::from_height_source_inner(name, version, source, first_height) - } - fn from_height_source_inner( name: &str, version: Version, diff --git a/crates/brk_computer/src/internal/single/lazy/min.rs b/crates/brk_computer/src/internal/single/lazy/min.rs index dc52bf5b1..37055ebab 100644 --- a/crates/brk_computer/src/internal/single/lazy/min.rs +++ b/crates/brk_computer/src/internal/single/lazy/min.rs @@ -46,15 +46,6 @@ where Self::from_source_inner(&format!("{name}_min"), version, source, len_source) } - pub(crate) fn from_source_raw( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - Self::from_source_inner(name, version, source, len_source) - } - fn from_source_inner( name: &str, version: Version, @@ -117,15 +108,6 @@ where Self::from_height_source_inner(&format!("{name}_min"), version, source, first_height) } - pub(crate) fn from_height_source_raw( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - Self::from_height_source_inner(name, version, source, first_height) - } - fn from_height_source_inner( name: &str, version: Version, diff --git a/crates/brk_computer/src/internal/single/lazy/mod.rs b/crates/brk_computer/src/internal/single/lazy/mod.rs index 243bdbf0f..75aa2cb6f 100644 --- a/crates/brk_computer/src/internal/single/lazy/mod.rs +++ b/crates/brk_computer/src/internal/single/lazy/mod.rs @@ -181,12 +181,10 @@ macro_rules! impl_lazy_agg { mod average; mod cumulative; mod distribution; -mod first; mod full; mod last; mod max; mod min; -mod ohlc; mod percentile; mod percentiles; mod sparse_last; @@ -196,12 +194,10 @@ mod sum_cum; pub use average::*; pub use cumulative::*; pub use distribution::*; -pub use first::*; pub use full::*; pub use last::*; pub use max::*; pub use min::*; -pub use ohlc::*; pub use percentile::*; pub use percentiles::*; pub use sparse_last::*; diff --git a/crates/brk_computer/src/internal/single/lazy/ohlc.rs b/crates/brk_computer/src/internal/single/lazy/ohlc.rs deleted file mode 100644 index a4f563d24..000000000 --- a/crates/brk_computer/src/internal/single/lazy/ohlc.rs +++ /dev/null @@ -1,370 +0,0 @@ -//! Lazy OHLC aggregation — single-pass first/max/min/last from height-level data. - -use std::sync::Arc; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use serde::Serialize; -use vecdb::{Cursor, Formattable, ReadableBoxedVec, VecIndex, VecValue}; - -use brk_types::{Cents, Close, Dollars, High, Low, OHLCCents, OHLCDollars, OHLCSats, Open, Sats}; - -use crate::internal::ComputedVecValue; - -/// Trait for OHLC bundle types that can be constructed from / decomposed into -/// their open/high/low/close components. -pub trait OHLCRecord: VecValue + Formattable + Serialize + JsonSchema { - type Inner: ComputedVecValue + JsonSchema + Copy; - fn ohlc_open(&self) -> Self::Inner; - fn ohlc_high(&self) -> Self::Inner; - fn ohlc_low(&self) -> Self::Inner; - fn ohlc_close(&self) -> Self::Inner; - fn from_parts( - open: Self::Inner, - high: Self::Inner, - low: Self::Inner, - close: Self::Inner, - ) -> Self; -} - -impl OHLCRecord for OHLCCents { - type Inner = Cents; - fn ohlc_open(&self) -> Cents { - *self.open - } - fn ohlc_high(&self) -> Cents { - *self.high - } - fn ohlc_low(&self) -> Cents { - *self.low - } - fn ohlc_close(&self) -> Cents { - *self.close - } - fn from_parts(open: Cents, high: Cents, low: Cents, close: Cents) -> Self { - Self { - open: Open::new(open), - high: High::new(high), - low: Low::new(low), - close: Close::new(close), - } - } -} - -impl OHLCRecord for OHLCDollars { - type Inner = Dollars; - fn ohlc_open(&self) -> Dollars { - *self.open - } - fn ohlc_high(&self) -> Dollars { - *self.high - } - fn ohlc_low(&self) -> Dollars { - *self.low - } - fn ohlc_close(&self) -> Dollars { - *self.close - } - fn from_parts(open: Dollars, high: Dollars, low: Dollars, close: Dollars) -> Self { - Self { - open: Open::new(open), - high: High::new(high), - low: Low::new(low), - close: Close::new(close), - } - } -} - -impl OHLCRecord for OHLCSats { - type Inner = Sats; - fn ohlc_open(&self) -> Sats { - *self.open - } - fn ohlc_high(&self) -> Sats { - *self.high - } - fn ohlc_low(&self) -> Sats { - *self.low - } - fn ohlc_close(&self) -> Sats { - *self.close - } - fn from_parts(open: Sats, high: Sats, low: Sats, close: Sats) -> Self { - Self { - open: Open::new(open), - high: High::new(high), - low: Low::new(low), - close: Close::new(close), - } - } -} - -const VERSION: Version = Version::ZERO; - -type ForEachRangeFn = - fn(usize, usize, &ReadableBoxedVec, &ReadableBoxedVec, &mut dyn FnMut(OHLC)); - -/// Lazy OHLC aggregation vec. For each coarser period, computes open (first), -/// high (max), low (min), close (last) in a single pass over the source range. -pub struct LazyOHLC -where - I: VecIndex, - OHLC: OHLCRecord, - S1I: VecIndex, - ST: VecValue, - S2T: VecValue, -{ - name: Arc, - version: Version, - source: ReadableBoxedVec, - mapping: ReadableBoxedVec, - for_each_range: ForEachRangeFn, -} - -// --- From height source (Day1, DifficultyEpoch) --- - -impl LazyOHLC -where - I: VecIndex, - OHLC: OHLCRecord + 'static, - T: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn from_height_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - fn for_each_range< - I: VecIndex, - OHLC: OHLCRecord, - T: ComputedVecValue + JsonSchema, - >( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(OHLC), - ) { - let map_end = (to + 1).min(mapping.len()); - let heights = mapping.collect_range_dyn(from, map_end); - let source_len = source.len(); - let Some(&first_h) = heights.first() else { - return; - }; - let mut cursor = Cursor::from_dyn(&**source); - cursor.advance(first_h.to_usize()); - for idx in 0..(to - from) { - let Some(&cur_h) = heights.get(idx) else { - continue; - }; - let first = cur_h.to_usize(); - let next_first = heights - .get(idx + 1) - .map(|h| h.to_usize()) - .unwrap_or(source_len); - let count = next_first.saturating_sub(first); - if count == 0 { - continue; - } - if let Some(first_val) = cursor.next() { - let (high, low, close) = cursor.fold( - count - 1, - (first_val, first_val, first_val), - |(hi, lo, _), v| { - (if v > hi { v } else { hi }, if v < lo { v } else { lo }, v) - }, - ); - f(OHLC::from_parts(first_val, high, low, close)); - } - } - } - Self { - name: Arc::from(format!("{name}_ohlc")), - version: version + VERSION, - source, - mapping: first_height, - for_each_range: for_each_range::, - } - } -} - -// --- Trait implementations --- - -impl Clone for LazyOHLC -where - I: VecIndex, - OHLC: OHLCRecord, - S1I: VecIndex, - ST: VecValue, - S2T: VecValue, -{ - fn clone(&self) -> Self { - Self { - name: self.name.clone(), - version: self.version, - source: self.source.clone(), - mapping: self.mapping.clone(), - for_each_range: self.for_each_range, - } - } -} - -impl vecdb::AnyVec for LazyOHLC -where - I: VecIndex, - OHLC: OHLCRecord, - S1I: VecIndex, - ST: VecValue, - S2T: VecValue, -{ - fn version(&self) -> Version { - self.version + self.source.version() + self.mapping.version() - } - fn name(&self) -> &str { - &self.name - } - fn index_type_to_string(&self) -> &'static str { - I::to_string() - } - fn len(&self) -> usize { - self.mapping.len() - } - #[inline] - fn value_type_to_size_of(&self) -> usize { - size_of::() - } - #[inline] - fn value_type_to_string(&self) -> &'static str { - vecdb::short_type_name::() - } - #[inline] - fn region_names(&self) -> Vec { - vec![] - } -} - -impl vecdb::TypedVec for LazyOHLC -where - I: VecIndex, - OHLC: OHLCRecord, - S1I: VecIndex, - ST: VecValue, - S2T: VecValue, -{ - type I = I; - type T = OHLC; -} - -impl vecdb::ReadableVec for LazyOHLC -where - I: VecIndex, - OHLC: OHLCRecord, - S1I: VecIndex, - ST: VecValue, - S2T: VecValue, -{ - fn read_into_at(&self, from: usize, to: usize, buf: &mut Vec) { - let to = to.min(self.mapping.len()); - if from >= to { - return; - } - buf.reserve(to - from); - (self.for_each_range)(from, to, &self.source, &self.mapping, &mut |v| buf.push(v)); - } - - fn for_each_range_dyn_at(&self, from: usize, to: usize, f: &mut dyn FnMut(OHLC)) { - let to = to.min(self.mapping.len()); - if from >= to { - return; - } - (self.for_each_range)(from, to, &self.source, &self.mapping, f); - } - - #[inline] - fn fold_range_at B>( - &self, - from: usize, - to: usize, - init: B, - mut f: F, - ) -> B - where - Self: Sized, - { - let to = to.min(self.mapping.len()); - if from >= to { - return init; - } - let mut acc = Some(init); - (self.for_each_range)(from, to, &self.source, &self.mapping, &mut |v| { - acc = Some(f(acc.take().unwrap(), v)); - }); - acc.unwrap() - } - - #[inline] - fn try_fold_range_at std::result::Result>( - &self, - from: usize, - to: usize, - init: B, - mut f: F, - ) -> std::result::Result - where - Self: Sized, - { - let to = to.min(self.mapping.len()); - if from >= to { - return Ok(init); - } - let mut acc: Option> = Some(Ok(init)); - (self.for_each_range)(from, to, &self.source, &self.mapping, &mut |v| { - if let Some(Ok(a)) = acc.take() { - acc = Some(f(a, v)); - } - }); - acc.unwrap() - } - - #[inline] - fn collect_one_at(&self, index: usize) -> Option { - if index >= self.mapping.len() { - return None; - } - let mut result = None; - (self.for_each_range)(index, index + 1, &self.source, &self.mapping, &mut |v| { - result = Some(v) - }); - result - } -} - -impl Traversable for LazyOHLC -where - I: VecIndex + 'static, - OHLC: OHLCRecord + 'static, - S1I: VecIndex + 'static, - ST: VecValue, - S2T: VecValue, -{ - fn iter_any_exportable(&self) -> impl Iterator { - std::iter::once(self as &dyn vecdb::AnyExportableVec) - } - - fn to_tree_node(&self) -> brk_types::TreeNode { - use vecdb::AnyVec; - let index_str = I::to_string(); - let index = brk_types::Index::try_from(index_str).ok(); - let indexes = index.into_iter().collect(); - let leaf = brk_types::MetricLeaf::new( - self.name().to_string(), - self.value_type_to_string().to_string(), - indexes, - ); - let schema = schemars::SchemaGenerator::default().into_root_schema_for::(); - let schema_json = serde_json::to_value(schema).unwrap_or_default(); - brk_types::TreeNode::Leaf(brk_types::MetricLeafWithSchema::new(leaf, schema_json)) - } -} diff --git a/crates/brk_computer/src/internal/single/lazy_transform/binary_full.rs b/crates/brk_computer/src/internal/single/lazy_transform/binary_full.rs deleted file mode 100644 index ca80b71e8..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/binary_full.rs +++ /dev/null @@ -1,101 +0,0 @@ -//! Lazy binary transform for Full (without cumulative). -//! -//! Used for USD conversion where `usd = sats * price[height]`. -//! Cumulative cannot be lazy because `cum_usd ≠ cum_sats * price` - -//! it must be computed by summing historical `sum * price` values. - -use brk_traversable::Traversable; -use brk_types::Version; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2, VecIndex}; - -use crate::internal::{ComputedVecValue, Full}; - -use super::LazyBinaryPercentiles; - -/// Lazy binary transform for Full stats (excluding cumulative). -/// -/// For USD conversion: each stat is computed as `sats_stat * price`. -/// Cumulative is excluded because it requires summing historical values. -#[derive(Clone, Traversable)] -pub struct LazyBinaryTransformFull -where - I: VecIndex, - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - pub average: LazyVecFrom2, - pub min: LazyVecFrom2, - pub max: LazyVecFrom2, - #[traversable(flatten)] - pub percentiles: LazyBinaryPercentiles, - pub sum: LazyVecFrom2, -} - -impl LazyBinaryTransformFull -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - /// Create from Full source and a second source (e.g., price). - /// - /// The transform F is applied as `F(source1_stat, source2)` for each stat. - pub(crate) fn from_full_and_source>( - name: &str, - version: Version, - source1: &Full, - source2: ReadableBoxedVec, - ) -> Self { - Self { - average: LazyVecFrom2::transformed::( - &format!("{name}_average"), - version, - source1.boxed_average(), - source2.clone(), - ), - min: LazyVecFrom2::transformed::( - &format!("{name}_min"), - version, - source1.boxed_min(), - source2.clone(), - ), - max: LazyVecFrom2::transformed::( - &format!("{name}_max"), - version, - source1.boxed_max(), - source2.clone(), - ), - percentiles: LazyBinaryPercentiles::from_percentiles::( - name, - version, - &source1.distribution.percentiles, - source2.clone(), - ), - sum: LazyVecFrom2::transformed::( - &format!("{name}_sum"), - version, - source1.boxed_sum(), - source2, - ), - } - } - - pub(crate) fn boxed_average(&self) -> ReadableBoxedVec { - self.average.read_only_boxed_clone() - } - - pub(crate) fn boxed_min(&self) -> ReadableBoxedVec { - self.min.read_only_boxed_clone() - } - - pub(crate) fn boxed_max(&self) -> ReadableBoxedVec { - self.max.read_only_boxed_clone() - } - - pub(crate) fn boxed_sum(&self) -> ReadableBoxedVec { - self.sum.read_only_boxed_clone() - } -} diff --git a/crates/brk_computer/src/internal/single/lazy_transform/binary_percentiles.rs b/crates/brk_computer/src/internal/single/lazy_transform/binary_percentiles.rs deleted file mode 100644 index 29a419f09..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/binary_percentiles.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Lazy binary transform for Percentiles. - -use brk_traversable::Traversable; -use brk_types::Version; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableBoxedVec, LazyVecFrom2, VecIndex}; - -use crate::internal::{ComputedVecValue, Percentiles}; - -#[derive(Clone, Traversable)] -pub struct LazyBinaryPercentiles -where - I: VecIndex, - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - pub pct10: LazyVecFrom2, - pub pct25: LazyVecFrom2, - pub median: LazyVecFrom2, - pub pct75: LazyVecFrom2, - pub pct90: LazyVecFrom2, -} - -impl LazyBinaryPercentiles -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_percentiles>( - name: &str, - version: Version, - source: &Percentiles, - source2: ReadableBoxedVec, - ) -> Self { - Self { - pct10: LazyVecFrom2::transformed::( - &format!("{name}_pct10"), - version, - source.boxed_pct10(), - source2.clone(), - ), - pct25: LazyVecFrom2::transformed::( - &format!("{name}_pct25"), - version, - source.boxed_pct25(), - source2.clone(), - ), - median: LazyVecFrom2::transformed::( - &format!("{name}_median"), - version, - source.boxed_median(), - source2.clone(), - ), - pct75: LazyVecFrom2::transformed::( - &format!("{name}_pct75"), - version, - source.boxed_pct75(), - source2.clone(), - ), - pct90: LazyVecFrom2::transformed::( - &format!("{name}_pct90"), - version, - source.boxed_pct90(), - source2, - ), - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy_transform/binary_sum.rs b/crates/brk_computer/src/internal/single/lazy_transform/binary_sum.rs deleted file mode 100644 index ed7c402e1..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/binary_sum.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Lazy binary transform for Sum-only aggregation at a single index level. - -use brk_traversable::Traversable; -use brk_types::Version; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableBoxedVec, LazyVecFrom2, VecIndex}; - -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(wrap = "sum")] -pub struct LazyBinaryTransformSum(pub LazyVecFrom2) -where - I: VecIndex, - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue; - -impl LazyBinaryTransformSum -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_boxed>( - name: &str, - version: Version, - source1: ReadableBoxedVec, - source2: ReadableBoxedVec, - ) -> Self { - let v = version + VERSION; - - Self(LazyVecFrom2::transformed::(name, v, source1, source2)) - } -} - diff --git a/crates/brk_computer/src/internal/single/lazy_transform/binary_sum_cum.rs b/crates/brk_computer/src/internal/single/lazy_transform/binary_sum_cum.rs deleted file mode 100644 index 816b0f3c7..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/binary_sum_cum.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! Lazy binary transform for SumCum. - -use brk_traversable::Traversable; -use brk_types::Version; -use schemars::JsonSchema; -use vecdb::{ - BinaryTransform, LazyVecFrom2, ReadableBoxedVec, ReadableCloneableVec, VecIndex, VecValue, -}; - -use crate::internal::{ComputedVecValue, LazyFull}; - -#[derive(Clone, Traversable)] -pub struct LazyBinaryTransformSumCum -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - pub sum: LazyVecFrom2, - pub cumulative: LazyVecFrom2, -} - -impl LazyBinaryTransformSumCum -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - /// Create from sources without adding _sum suffix. - pub(crate) fn from_sources_sum_raw>( - name: &str, - version: Version, - sum_source1: ReadableBoxedVec, - sum_source2: ReadableBoxedVec, - cum_source1: ReadableBoxedVec, - cum_source2: ReadableBoxedVec, - ) -> Self { - Self { - sum: LazyVecFrom2::transformed::(name, version, sum_source1, sum_source2), - cumulative: LazyVecFrom2::transformed::( - &format!("{name}_cumulative"), - version, - cum_source1, - cum_source2, - ), - } - } - - pub(crate) fn from_lazy_stats_aggregate( - name: &str, - version: Version, - source1: &LazyFull, - source2: &LazyFull, - ) -> Self - where - F: BinaryTransform, - S1I: VecIndex + 'static, - S1L: VecValue, - S2I: VecIndex + 'static, - S2L: VecValue, - { - Self { - sum: LazyVecFrom2::transformed::( - &format!("{name}_sum"), - version, - source1.sum.read_only_boxed_clone(), - source2.sum.read_only_boxed_clone(), - ), - cumulative: LazyVecFrom2::transformed::( - &format!("{name}_cumulative"), - version, - source1.cumulative.read_only_boxed_clone(), - source2.cumulative.read_only_boxed_clone(), - ), - } - } - - /// Create from boxed SumCum + Last sources without adding _sum suffix. - pub(crate) fn from_sources_last_sum_raw>( - name: &str, - version: Version, - sum_source1: ReadableBoxedVec, - cum_source1: ReadableBoxedVec, - last_source: ReadableBoxedVec, - ) -> Self { - Self { - sum: LazyVecFrom2::transformed::(name, version, sum_source1, last_source.clone()), - cumulative: LazyVecFrom2::transformed::( - &format!("{name}_cumulative"), - version, - cum_source1, - last_source, - ), - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy_transform/distribution.rs b/crates/brk_computer/src/internal/single/lazy_transform/distribution.rs deleted file mode 100644 index 7435a1827..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/distribution.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Lazy unary transform for Distribution metrics. -//! Has average, min, max, and percentiles - but no sum/cumulative. -//! Use for ratio/percentage metrics where aggregation doesn't make sense. - -use brk_traversable::Traversable; -use brk_types::Version; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform, VecIndex}; - -use crate::internal::ComputedVecValue; - -use super::LazyPercentiles; - -/// Distribution stats: average, min, max, percentiles. -/// Excludes sum and cumulative (meaningless for ratios/percentages). -#[derive(Clone, Traversable)] -pub struct LazyTransformDistribution -where - I: VecIndex, - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - pub average: LazyVecFrom1, - pub min: LazyVecFrom1, - pub max: LazyVecFrom1, - #[traversable(flatten)] - pub percentiles: LazyPercentiles, -} - -impl LazyTransformDistribution -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - #[allow(clippy::too_many_arguments)] - pub(crate) fn from_boxed>( - name: &str, - version: Version, - average: ReadableBoxedVec, - min: ReadableBoxedVec, - max: ReadableBoxedVec, - pct10: ReadableBoxedVec, - pct25: ReadableBoxedVec, - median: ReadableBoxedVec, - pct75: ReadableBoxedVec, - pct90: ReadableBoxedVec, - ) -> Self { - Self { - average: LazyVecFrom1::transformed::(&format!("{name}_average"), version, average), - min: LazyVecFrom1::transformed::(&format!("{name}_min"), version, min), - max: LazyVecFrom1::transformed::(&format!("{name}_max"), version, max), - percentiles: LazyPercentiles::from_boxed::(name, version, pct10, pct25, median, pct75, pct90), - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy_transform/full.rs b/crates/brk_computer/src/internal/single/lazy_transform/full.rs index e67229587..6df1e653f 100644 --- a/crates/brk_computer/src/internal/single/lazy_transform/full.rs +++ b/crates/brk_computer/src/internal/single/lazy_transform/full.rs @@ -3,9 +3,9 @@ use brk_traversable::Traversable; use brk_types::Version; use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform, VecIndex}; +use vecdb::{LazyVecFrom1, ReadableBoxedVec, UnaryTransform, VecIndex}; -use crate::internal::{ComputedVecValue, Full}; +use crate::internal::ComputedVecValue; use super::LazyPercentiles; @@ -31,27 +31,6 @@ where T: ComputedVecValue + JsonSchema + 'static, S1T: ComputedVecValue + JsonSchema, { - pub(crate) fn from_stats_aggregate>( - name: &str, - version: Version, - source: &Full, - ) -> Self { - Self::from_boxed::( - name, - version, - source.boxed_average(), - source.boxed_min(), - source.boxed_max(), - source.distribution.percentiles.boxed_pct10(), - source.distribution.percentiles.boxed_pct25(), - source.distribution.percentiles.boxed_median(), - source.distribution.percentiles.boxed_pct75(), - source.distribution.percentiles.boxed_pct90(), - source.boxed_sum(), - source.boxed_cumulative(), - ) - } - #[allow(clippy::too_many_arguments)] pub(crate) fn from_boxed>( name: &str, @@ -71,9 +50,15 @@ where average: LazyVecFrom1::transformed::(&format!("{name}_average"), version, average), min: LazyVecFrom1::transformed::(&format!("{name}_min"), version, min), max: LazyVecFrom1::transformed::(&format!("{name}_max"), version, max), - percentiles: LazyPercentiles::from_boxed::(name, version, pct10, pct25, median, pct75, pct90), + percentiles: LazyPercentiles::from_boxed::( + name, version, pct10, pct25, median, pct75, pct90, + ), sum: LazyVecFrom1::transformed::(&format!("{name}_sum"), version, sum), - cumulative: LazyVecFrom1::transformed::(&format!("{name}_cumulative"), version, cumulative), + cumulative: LazyVecFrom1::transformed::( + &format!("{name}_cumulative"), + version, + cumulative, + ), } } } diff --git a/crates/brk_computer/src/internal/single/lazy_transform/mod.rs b/crates/brk_computer/src/internal/single/lazy_transform/mod.rs index ba02a31ac..539d9771f 100644 --- a/crates/brk_computer/src/internal/single/lazy_transform/mod.rs +++ b/crates/brk_computer/src/internal/single/lazy_transform/mod.rs @@ -1,23 +1,11 @@ -mod binary_full; mod binary_last; -mod binary_percentiles; -mod binary_sum; -mod binary_sum_cum; -mod distribution; mod full; mod last; mod percentiles; -mod sum; mod sum_cum; -pub use binary_full::*; pub use binary_last::*; -pub use binary_percentiles::*; -pub use binary_sum::*; -pub use binary_sum_cum::*; -pub use distribution::*; pub use full::*; pub use last::*; pub use percentiles::*; -pub use sum::*; pub use sum_cum::*; diff --git a/crates/brk_computer/src/internal/single/lazy_transform/sum.rs b/crates/brk_computer/src/internal/single/lazy_transform/sum.rs deleted file mode 100644 index ebbdce24f..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/sum.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Lazy unary transform for Sum-only. - -use brk_traversable::Traversable; -use brk_types::Version; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform, VecIndex}; - -use crate::internal::ComputedVecValue; - -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(wrap = "sum")] -pub struct LazyTransformSum(pub LazyVecFrom1) -where - I: VecIndex, - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue; - -impl LazyTransformSum -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_boxed>( - name: &str, - version: Version, - sum_source: ReadableBoxedVec, - ) -> Self { - Self(LazyVecFrom1::transformed::(name, version, sum_source)) - } -} diff --git a/crates/brk_computer/src/internal/single/mod.rs b/crates/brk_computer/src/internal/single/mod.rs index 2b14269a9..7f10eb152 100644 --- a/crates/brk_computer/src/internal/single/mod.rs +++ b/crates/brk_computer/src/internal/single/mod.rs @@ -4,14 +4,14 @@ mod group; mod height; mod lazy; mod lazy_transform; +mod rolling; mod transform; -mod tx; mod vec; pub use group::*; pub use height::*; pub use lazy::*; pub use lazy_transform::*; +pub use rolling::*; pub use transform::*; -pub use tx::*; pub use vec::*; diff --git a/crates/brk_computer/src/internal/single/rolling/distribution.rs b/crates/brk_computer/src/internal/single/rolling/distribution.rs new file mode 100644 index 000000000..11e20308e --- /dev/null +++ b/crates/brk_computer/src/internal/single/rolling/distribution.rs @@ -0,0 +1,131 @@ +//! RollingDistribution - 8 distribution stats, each a RollingWindows. +//! +//! Computes average, min, max, p10, p25, median, p75, p90 rolling windows +//! from a single source vec. + +use brk_error::Result; + +use brk_traversable::Traversable; +use brk_types::{Height, Version}; +use derive_more::{Deref, DerefMut}; +use schemars::JsonSchema; +use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode}; + +use crate::{ + indexes, + internal::{ComputedVecValue, DistributionStats, NumericValue, RollingWindows, WindowStarts}, + traits::compute_rolling_percentiles_from_starts, +}; + +/// 8 distribution stats × 4 windows = 32 stored height vecs, each with 17 index views. +#[derive(Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct RollingDistribution( + pub DistributionStats>, +) +where + T: ComputedVecValue + PartialOrd + JsonSchema; + +const VERSION: Version = Version::ZERO; + +impl RollingDistribution +where + T: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let v = version + VERSION; + Ok(Self(DistributionStats { + average: RollingWindows::forced_import(db, &format!("{name}_average"), v, indexes)?, + min: RollingWindows::forced_import(db, &format!("{name}_min"), v, indexes)?, + max: RollingWindows::forced_import(db, &format!("{name}_max"), v, indexes)?, + p10: RollingWindows::forced_import(db, &format!("{name}_p10"), v, indexes)?, + p25: RollingWindows::forced_import(db, &format!("{name}_p25"), v, indexes)?, + median: RollingWindows::forced_import(db, &format!("{name}_median"), v, indexes)?, + p75: RollingWindows::forced_import(db, &format!("{name}_p75"), v, indexes)?, + p90: RollingWindows::forced_import(db, &format!("{name}_p90"), v, indexes)?, + })) + } + + /// Compute all 8 distribution stats across all 4 windows from a single source. + /// + /// - average: running sum / count (O(n) per window) + /// - min/max: deque-based (O(n) amortized per window) + /// - p10/p25/median/p75/p90: single-pass sorted vec per window + pub(crate) fn compute_distribution( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + T: Copy + Ord + From + Default, + f64: From, + { + // Average: O(n) per window using running sum + self.0 + .average + .compute_rolling_average(max_from, windows, source, exit)?; + + // Min/Max: O(n) amortized per window using deques + self.0 + .min + .compute_rolling_min(max_from, windows, source, exit)?; + self.0 + .max + .compute_rolling_max(max_from, windows, source, exit)?; + + // Percentiles + median: single-pass per window using sorted vec + compute_rolling_percentiles_from_starts( + max_from, + windows._24h, + source, + &mut self.0.p10._24h.height, + &mut self.0.p25._24h.height, + &mut self.0.median._24h.height, + &mut self.0.p75._24h.height, + &mut self.0.p90._24h.height, + exit, + )?; + compute_rolling_percentiles_from_starts( + max_from, + windows._7d, + source, + &mut self.0.p10._7d.height, + &mut self.0.p25._7d.height, + &mut self.0.median._7d.height, + &mut self.0.p75._7d.height, + &mut self.0.p90._7d.height, + exit, + )?; + compute_rolling_percentiles_from_starts( + max_from, + windows._30d, + source, + &mut self.0.p10._30d.height, + &mut self.0.p25._30d.height, + &mut self.0.median._30d.height, + &mut self.0.p75._30d.height, + &mut self.0.p90._30d.height, + exit, + )?; + compute_rolling_percentiles_from_starts( + max_from, + windows._1y, + source, + &mut self.0.p10._1y.height, + &mut self.0.p25._1y.height, + &mut self.0.median._1y.height, + &mut self.0.p75._1y.height, + &mut self.0.p90._1y.height, + exit, + )?; + + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/single/rolling/full.rs b/crates/brk_computer/src/internal/single/rolling/full.rs new file mode 100644 index 000000000..69e827b16 --- /dev/null +++ b/crates/brk_computer/src/internal/single/rolling/full.rs @@ -0,0 +1,69 @@ +//! RollingFull - Sum + Distribution per rolling window. +//! +//! 36 stored height vecs per metric (4 sum + 32 distribution), each with 17 index views. + +use std::ops::SubAssign; + +use brk_error::Result; + +use brk_traversable::Traversable; +use brk_types::{Height, Version}; +use schemars::JsonSchema; +use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode}; + +use crate::{ + indexes, + internal::{ComputedVecValue, NumericValue, RollingDistribution, RollingWindows, WindowStarts}, +}; + +/// Sum (4 windows) + Distribution (8 stats × 4 windows) = 36 stored height vecs. +#[derive(Traversable)] +#[traversable(merge)] +pub struct RollingFull +where + T: ComputedVecValue + PartialOrd + JsonSchema, +{ + #[traversable(flatten)] + pub sum: RollingWindows, + #[traversable(flatten)] + pub distribution: RollingDistribution, +} + +const VERSION: Version = Version::ZERO; + +impl RollingFull +where + T: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let v = version + VERSION; + Ok(Self { + sum: RollingWindows::forced_import(db, &format!("{name}_sum"), v, indexes)?, + distribution: RollingDistribution::forced_import(db, name, v, indexes)?, + }) + } + + /// Compute rolling sum + all 8 distribution stats across all 4 windows. + pub(crate) fn compute( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + T: From + Default + SubAssign + Copy + Ord, + f64: From, + { + self.sum + .compute_rolling_sum(max_from, windows, source, exit)?; + self.distribution + .compute_distribution(max_from, windows, source, exit)?; + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/single/rolling/mod.rs b/crates/brk_computer/src/internal/single/rolling/mod.rs new file mode 100644 index 000000000..8e62a242c --- /dev/null +++ b/crates/brk_computer/src/internal/single/rolling/mod.rs @@ -0,0 +1,9 @@ +mod distribution; +mod full; +mod value_windows; +mod windows; + +pub use distribution::*; +pub use full::*; +pub use value_windows::*; +pub use windows::*; diff --git a/crates/brk_computer/src/internal/single/rolling/value_windows.rs b/crates/brk_computer/src/internal/single/rolling/value_windows.rs new file mode 100644 index 000000000..f5025ffd3 --- /dev/null +++ b/crates/brk_computer/src/internal/single/rolling/value_windows.rs @@ -0,0 +1,89 @@ +//! StoredValueRollingWindows - window-first ordering. +//! +//! Access pattern: `coinbase_sum._24h.sats.height` +//! Each window (24h, 7d, 30d, 1y) contains sats (stored) + btc (lazy) + usd (stored). + +use brk_error::Result; + +use brk_traversable::Traversable; +use brk_types::{Height, Version}; +use derive_more::{Deref, DerefMut}; +use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode}; + +use brk_types::{Dollars, Sats}; + +use crate::{ + indexes, + internal::{StoredValueFromHeightLast, WindowStarts, Windows}, +}; + +const VERSION: Version = Version::ZERO; + +/// Stored value rolling windows — window-first, currency-last. +/// +/// Each window contains `StoredValueFromHeightLast` (sats + btc lazy + usd). +#[derive(Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct StoredValueRollingWindows( + pub Windows>, +); + +impl StoredValueRollingWindows { + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let v = version + VERSION; + Ok(Self(Windows { + _24h: StoredValueFromHeightLast::forced_import( + db, + &format!("{name}_24h"), + v, + indexes, + )?, + _7d: StoredValueFromHeightLast::forced_import( + db, + &format!("{name}_7d"), + v, + indexes, + )?, + _30d: StoredValueFromHeightLast::forced_import( + db, + &format!("{name}_30d"), + v, + indexes, + )?, + _1y: StoredValueFromHeightLast::forced_import( + db, + &format!("{name}_1y"), + v, + indexes, + )?, + })) + } + + pub(crate) fn compute_rolling_sum( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + sats_source: &impl ReadableVec, + usd_source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + self.0 + ._24h + .compute_rolling_sum(max_from, windows._24h, sats_source, usd_source, exit)?; + self.0 + ._7d + .compute_rolling_sum(max_from, windows._7d, sats_source, usd_source, exit)?; + self.0 + ._30d + .compute_rolling_sum(max_from, windows._30d, sats_source, usd_source, exit)?; + self.0 + ._1y + .compute_rolling_sum(max_from, windows._1y, sats_source, usd_source, exit)?; + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/single/rolling/windows.rs b/crates/brk_computer/src/internal/single/rolling/windows.rs new file mode 100644 index 000000000..6540f5b04 --- /dev/null +++ b/crates/brk_computer/src/internal/single/rolling/windows.rs @@ -0,0 +1,178 @@ +//! RollingWindows - newtype on Windows with ComputedFromHeightLast per window duration. +//! +//! Each of the 4 windows (24h, 7d, 30d, 1y) contains a height-level stored vec +//! plus all 17 LazyLast index views. + +use std::ops::SubAssign; + +use brk_error::Result; + +use brk_traversable::Traversable; +use brk_types::{Height, Version}; +use derive_more::{Deref, DerefMut}; +use schemars::JsonSchema; +use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode}; + +use crate::{ + indexes, + internal::{ComputedFromHeightLast, ComputedVecValue, NumericValue, Windows}, +}; + +/// Rolling window start heights — references to the 4 height-ago vecs. +pub struct WindowStarts<'a> { + pub _24h: &'a EagerVec>, + pub _7d: &'a EagerVec>, + pub _30d: &'a EagerVec>, + pub _1y: &'a EagerVec>, +} + +/// 4 rolling window vecs (24h, 7d, 30d, 1y), each with height data + all 17 index views. +#[derive(Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct RollingWindows(pub Windows>) +where + T: ComputedVecValue + PartialOrd + JsonSchema; + +const VERSION: Version = Version::ZERO; + +impl RollingWindows +where + T: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let v = version + VERSION; + Ok(Self(Windows { + _24h: ComputedFromHeightLast::forced_import(db, &format!("{name}_24h"), v, indexes)?, + _7d: ComputedFromHeightLast::forced_import(db, &format!("{name}_7d"), v, indexes)?, + _30d: ComputedFromHeightLast::forced_import(db, &format!("{name}_30d"), v, indexes)?, + _1y: ComputedFromHeightLast::forced_import(db, &format!("{name}_1y"), v, indexes)?, + })) + } + + pub(crate) fn compute_rolling_sum( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + T: Default + SubAssign, + { + self.0 + ._24h + .height + .compute_rolling_sum(max_from, windows._24h, source, exit)?; + self.0 + ._7d + .height + .compute_rolling_sum(max_from, windows._7d, source, exit)?; + self.0 + ._30d + .height + .compute_rolling_sum(max_from, windows._30d, source, exit)?; + self.0 + ._1y + .height + .compute_rolling_sum(max_from, windows._1y, source, exit)?; + Ok(()) + } + + pub(crate) fn compute_rolling_min( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + A: vecdb::VecValue + Ord, + T: From, + { + use crate::traits::ComputeRollingMinFromStarts; + self.0 + ._24h + .height + .compute_rolling_min_from_starts(max_from, windows._24h, source, exit)?; + self.0 + ._7d + .height + .compute_rolling_min_from_starts(max_from, windows._7d, source, exit)?; + self.0 + ._30d + .height + .compute_rolling_min_from_starts(max_from, windows._30d, source, exit)?; + self.0 + ._1y + .height + .compute_rolling_min_from_starts(max_from, windows._1y, source, exit)?; + Ok(()) + } + + pub(crate) fn compute_rolling_max( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + A: vecdb::VecValue + Ord, + T: From, + { + use crate::traits::ComputeRollingMaxFromStarts; + self.0 + ._24h + .height + .compute_rolling_max_from_starts(max_from, windows._24h, source, exit)?; + self.0 + ._7d + .height + .compute_rolling_max_from_starts(max_from, windows._7d, source, exit)?; + self.0 + ._30d + .height + .compute_rolling_max_from_starts(max_from, windows._30d, source, exit)?; + self.0 + ._1y + .height + .compute_rolling_max_from_starts(max_from, windows._1y, source, exit)?; + Ok(()) + } + + pub(crate) fn compute_rolling_average( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + A: vecdb::VecValue, + f64: From + From, + T: From + Default, + { + self.0 + ._24h + .height + .compute_rolling_average(max_from, windows._24h, source, exit)?; + self.0 + ._7d + .height + .compute_rolling_average(max_from, windows._7d, source, exit)?; + self.0 + ._30d + .height + .compute_rolling_average(max_from, windows._30d, source, exit)?; + self.0 + ._1y + .height + .compute_rolling_average(max_from, windows._1y, source, exit)?; + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/single/transform/mod.rs b/crates/brk_computer/src/internal/single/transform/mod.rs index 06e10ac56..41eb275b0 100644 --- a/crates/brk_computer/src/internal/single/transform/mod.rs +++ b/crates/brk_computer/src/internal/single/transform/mod.rs @@ -17,7 +17,6 @@ mod percentage_dollars_f32; mod percentage_dollars_f32_neg; mod percentage_sats_f64; mod percentage_u32_f32; -mod percentage_u64_f32; mod price_times_ratio; mod ratio32; mod ratio64; @@ -31,8 +30,6 @@ mod sat_halve; mod sat_halve_to_bitcoin; mod sat_identity; mod sat_mask; -mod sat_plus; -mod sat_plus_to_bitcoin; mod sat_to_bitcoin; mod sats_times_close_price; mod u16_to_years; @@ -40,8 +37,6 @@ mod u64_plus; mod volatility_sqrt30; mod volatility_sqrt365; mod volatility_sqrt7; -mod weight_to_fullness; -mod weight_to_vbytes; pub use block_count_target::*; pub use cents_to_dollars::*; @@ -62,7 +57,6 @@ pub use percentage_dollars_f32::*; pub use percentage_dollars_f32_neg::*; pub use percentage_sats_f64::*; pub use percentage_u32_f32::*; -pub use percentage_u64_f32::*; pub use price_times_ratio::*; pub use ratio_f32::*; pub use ratio_u64_f32::*; @@ -76,8 +70,6 @@ pub use sat_halve::*; pub use sat_halve_to_bitcoin::*; pub use sat_identity::*; pub use sat_mask::*; -pub use sat_plus::*; -pub use sat_plus_to_bitcoin::*; pub use sat_to_bitcoin::*; pub use sats_times_close_price::*; pub use u16_to_years::*; @@ -85,5 +77,3 @@ pub use u64_plus::*; pub use volatility_sqrt7::*; pub use volatility_sqrt30::*; pub use volatility_sqrt365::*; -pub use weight_to_fullness::*; -pub use weight_to_vbytes::*; diff --git a/crates/brk_computer/src/internal/single/transform/percentage_u64_f32.rs b/crates/brk_computer/src/internal/single/transform/percentage_u64_f32.rs deleted file mode 100644 index cb72df424..000000000 --- a/crates/brk_computer/src/internal/single/transform/percentage_u64_f32.rs +++ /dev/null @@ -1,17 +0,0 @@ -use brk_types::{StoredF32, StoredU64}; -use vecdb::BinaryTransform; - -/// (StoredU64, StoredU64) -> StoredF32 percentage (a/b × 100) -/// Used for adoption ratio calculations (type_count / total_count × 100) -pub struct PercentageU64F32; - -impl BinaryTransform for PercentageU64F32 { - #[inline(always)] - fn apply(numerator: StoredU64, denominator: StoredU64) -> StoredF32 { - if *denominator == 0 { - StoredF32::default() - } else { - StoredF32::from((*numerator as f64 / *denominator as f64) * 100.0) - } - } -} diff --git a/crates/brk_computer/src/internal/single/transform/sat_plus.rs b/crates/brk_computer/src/internal/single/transform/sat_plus.rs deleted file mode 100644 index 11d41a5af..000000000 --- a/crates/brk_computer/src/internal/single/transform/sat_plus.rs +++ /dev/null @@ -1,13 +0,0 @@ -use brk_types::Sats; -use vecdb::BinaryTransform; - -/// (Sats, Sats) -> Sats addition -/// Used for computing coinbase = subsidy + fee -pub struct SatsPlus; - -impl BinaryTransform for SatsPlus { - #[inline(always)] - fn apply(lhs: Sats, rhs: Sats) -> Sats { - lhs + rhs - } -} diff --git a/crates/brk_computer/src/internal/single/transform/sat_plus_to_bitcoin.rs b/crates/brk_computer/src/internal/single/transform/sat_plus_to_bitcoin.rs deleted file mode 100644 index 7ea0f43ed..000000000 --- a/crates/brk_computer/src/internal/single/transform/sat_plus_to_bitcoin.rs +++ /dev/null @@ -1,13 +0,0 @@ -use brk_types::{Bitcoin, Sats}; -use vecdb::BinaryTransform; - -/// (Sats, Sats) -> Bitcoin addition with conversion -/// Used for computing coinbase_btc = (subsidy + fee) / 1e8 -pub struct SatsPlusToBitcoin; - -impl BinaryTransform for SatsPlusToBitcoin { - #[inline(always)] - fn apply(lhs: Sats, rhs: Sats) -> Bitcoin { - Bitcoin::from(lhs + rhs) - } -} diff --git a/crates/brk_computer/src/internal/single/transform/weight_to_fullness.rs b/crates/brk_computer/src/internal/single/transform/weight_to_fullness.rs deleted file mode 100644 index 6812bc253..000000000 --- a/crates/brk_computer/src/internal/single/transform/weight_to_fullness.rs +++ /dev/null @@ -1,13 +0,0 @@ -use brk_types::{StoredF32, Weight}; -use vecdb::UnaryTransform; - -/// Weight -> StoredF32 percentage (weight / MAX_BLOCK × 100) -/// Used for computing block fullness as a percentage of max capacity -pub struct WeightToFullness; - -impl UnaryTransform for WeightToFullness { - #[inline(always)] - fn apply(weight: Weight) -> StoredF32 { - StoredF32::from(weight.fullness()) - } -} diff --git a/crates/brk_computer/src/internal/single/transform/weight_to_vbytes.rs b/crates/brk_computer/src/internal/single/transform/weight_to_vbytes.rs deleted file mode 100644 index b08a4d6c0..000000000 --- a/crates/brk_computer/src/internal/single/transform/weight_to_vbytes.rs +++ /dev/null @@ -1,12 +0,0 @@ -use brk_types::{StoredU64, Weight}; -use vecdb::UnaryTransform; - -/// Weight -> StoredU64 virtual bytes (vbytes = ceil(weight/4)) -pub struct WeightToVbytes; - -impl UnaryTransform for WeightToVbytes { - #[inline(always)] - fn apply(weight: Weight) -> StoredU64 { - StoredU64::from(weight.to_vbytes_floor()) - } -} diff --git a/crates/brk_computer/src/internal/single/tx/distribution.rs b/crates/brk_computer/src/internal/single/tx/distribution.rs deleted file mode 100644 index 316652485..000000000 --- a/crates/brk_computer/src/internal/single/tx/distribution.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! ComputedFromTxDistribution - eager txindex source + derived distribution. - -use brk_error::Result; -use brk_indexer::Indexer; -use brk_traversable::Traversable; -use brk_types::{TxIndex, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode}; - -use crate::{ - ComputeIndexes, indexes, - internal::{ComputedVecValue, TxDerivedDistribution, NumericValue}, -}; - -const VERSION: Version = Version::ZERO; - -#[derive(Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct ComputedFromTxDistribution -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - pub txindex: M::Stored>>, - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub distribution: TxDerivedDistribution, -} - -impl ComputedFromTxDistribution -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let v = version + VERSION; - let txindex = EagerVec::forced_import(db, name, v)?; - let distribution = TxDerivedDistribution::forced_import(db, name, v, indexes)?; - Ok(Self { txindex, distribution }) - } - - /// Derive from source, skipping first N transactions per block from all calculations. - /// - /// Use `skip_count: 1` to exclude coinbase transactions from fee/feerate stats. - pub(crate) fn derive_from_with_skip( - &mut self, - indexer: &Indexer, - indexes: &indexes::Vecs, - starting_indexes: &ComputeIndexes, - exit: &Exit, - skip_count: usize, - ) -> Result<()> { - self.distribution.derive_from_with_skip( - indexer, - indexes, - starting_indexes, - &self.txindex, - exit, - skip_count, - ) - } -} diff --git a/crates/brk_computer/src/internal/single/tx/mod.rs b/crates/brk_computer/src/internal/single/tx/mod.rs deleted file mode 100644 index 79fe1f2d5..000000000 --- a/crates/brk_computer/src/internal/single/tx/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod distribution; - -pub use distribution::*; diff --git a/crates/brk_computer/src/internal/windows.rs b/crates/brk_computer/src/internal/windows.rs new file mode 100644 index 000000000..01cde46c9 --- /dev/null +++ b/crates/brk_computer/src/internal/windows.rs @@ -0,0 +1,18 @@ +//! Base generic struct with 4 type parameters — one per rolling window duration. +//! +//! Foundation for all rolling window types (24h, 7d, 30d, 1y). + +use brk_traversable::Traversable; + +#[derive(Clone, Traversable)] +#[traversable(merge)] +pub struct Windows { + #[traversable(rename = "24h")] + pub _24h: A, + #[traversable(rename = "7d")] + pub _7d: B, + #[traversable(rename = "30d")] + pub _30d: C, + #[traversable(rename = "1y")] + pub _1y: D, +} diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index 413139e03..a9e90a2a8 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -124,14 +124,12 @@ impl Computer { )?)) })?; - // Import scripts module (depends on outputs for adoption ratio denominators) let scripts_handle = big_thread().spawn_scoped(s, || -> Result<_> { Ok(Box::new(scripts::Vecs::forced_import( &computed_path, VERSION, &indexes, &prices, - &outputs, )?)) })?; @@ -275,7 +273,8 @@ impl Computer { // 2. Prices info!("Computing prices..."); let i = Instant::now(); - self.prices.compute(indexer, &starting_indexes, exit)?; + self.prices + .compute(indexer, &self.indexes, &starting_indexes, exit)?; info!("Computed prices in {:?}", i.elapsed()); // 3. Main scope @@ -289,47 +288,40 @@ impl Computer { Ok(()) }); - // Nested scope: blocks (mut) runs in parallel with inputs chain - // The nested scope ensures blocks' mutable borrow ends before transactions - thread::scope(|inner| -> Result<()> { - let blocks = inner.spawn(|| -> Result<()> { - info!("Computing blocks..."); - let i = Instant::now(); - self.blocks - .compute(indexer, &self.indexes, &starting_indexes, exit)?; - info!("Computed blocks in {:?}", i.elapsed()); - Ok(()) - }); + // Blocks first (needed for window starts used by scripts, transactions, etc.) + info!("Computing blocks..."); + let i = Instant::now(); + self.blocks + .compute(indexer, &self.indexes, &starting_indexes, exit)?; + info!("Computed blocks in {:?}", i.elapsed()); - // Inputs → scripts → outputs (sequential) - info!("Computing inputs..."); - let i = Instant::now(); - self.inputs - .compute(indexer, &self.indexes, &starting_indexes, exit)?; - info!("Computed inputs in {:?}", i.elapsed()); + // Inputs → scripts → outputs (sequential) + info!("Computing inputs..."); + let i = Instant::now(); + self.inputs + .compute(indexer, &self.indexes, &self.blocks, &starting_indexes, exit)?; + info!("Computed inputs in {:?}", i.elapsed()); - info!("Computing scripts..."); - let i = Instant::now(); - self.scripts.compute(indexer, &starting_indexes, exit)?; - info!("Computed scripts in {:?}", i.elapsed()); + info!("Computing scripts..."); + let i = Instant::now(); + self.scripts + .compute(indexer, &self.blocks, &self.outputs, &starting_indexes, exit)?; + info!("Computed scripts in {:?}", i.elapsed()); - info!("Computing outputs..."); - let i = Instant::now(); - self.outputs.compute( - indexer, - &self.indexes, - &self.inputs, - &self.scripts, - &starting_indexes, - exit, - )?; - info!("Computed outputs in {:?}", i.elapsed()); + info!("Computing outputs..."); + let i = Instant::now(); + self.outputs.compute( + indexer, + &self.indexes, + &self.inputs, + &self.scripts, + &self.blocks, + &starting_indexes, + exit, + )?; + info!("Computed outputs in {:?}", i.elapsed()); - blocks.join().unwrap()?; - Ok(()) - })?; - - // Transactions (needs blocks for count/interval) + // Transactions (needs blocks for count/interval, prices for USD conversion) info!("Computing transactions..."); let i = Instant::now(); self.transactions.compute( @@ -338,6 +330,7 @@ impl Computer { &self.blocks, &self.inputs, &self.outputs, + &self.prices, &starting_indexes, exit, )?; diff --git a/crates/brk_computer/src/market/ath/import.rs b/crates/brk_computer/src/market/ath/import.rs index eeaf912d7..ae6ba1d2f 100644 --- a/crates/brk_computer/src/market/ath/import.rs +++ b/crates/brk_computer/src/market/ath/import.rs @@ -50,7 +50,7 @@ impl Vecs { version, prices.usd.price.read_only_boxed_clone(), price_ath.height.read_only_boxed_clone(), - &prices.usd.split.close, + &prices.usd.close, &price_ath.rest, ); diff --git a/crates/brk_computer/src/market/dca/compute.rs b/crates/brk_computer/src/market/dca/compute.rs index 3fff02b99..0a86defd9 100644 --- a/crates/brk_computer/src/market/dca/compute.rs +++ b/crates/brk_computer/src/market/dca/compute.rs @@ -23,7 +23,7 @@ impl Vecs { exit: &Exit, ) -> Result<()> { let h2d = &indexes.height.day1; - let close = &prices.usd.split.close.day1; + let close = &prices.usd.close.day1; let first_price_di = Day1::try_from(Date::new(2010, 7, 12)) .unwrap() diff --git a/crates/brk_computer/src/market/dca/import.rs b/crates/brk_computer/src/market/dca/import.rs index 4f7745274..3f5cab14d 100644 --- a/crates/brk_computer/src/market/dca/import.rs +++ b/crates/brk_computer/src/market/dca/import.rs @@ -53,7 +53,7 @@ impl Vecs { version, prices.usd.price.read_only_boxed_clone(), average_price.height.read_only_boxed_clone(), - &prices.usd.split.close, + &prices.usd.close, &average_price.rest, ) }); @@ -124,7 +124,7 @@ impl Vecs { version, prices.usd.price.read_only_boxed_clone(), lookback_price.height.read_only_boxed_clone(), - &prices.usd.split.close, + &prices.usd.close, &lookback_price.rest, ) }); @@ -193,7 +193,7 @@ impl Vecs { version, prices.usd.price.read_only_boxed_clone(), average_price.height.read_only_boxed_clone(), - &prices.usd.split.close, + &prices.usd.close, &average_price.rest, ) }); diff --git a/crates/brk_computer/src/market/indicators/import.rs b/crates/brk_computer/src/market/indicators/import.rs index ceeb92bfe..9da7ce156 100644 --- a/crates/brk_computer/src/market/indicators/import.rs +++ b/crates/brk_computer/src/market/indicators/import.rs @@ -100,7 +100,7 @@ impl Vecs { ) -> Result { let v = version + VERSION; - let nvt = LazyBinaryFromHeightLast::from_lazy_binary_block_last_and_lazy_binary_sum::< + let nvt = LazyBinaryFromHeightLast::from_both_lazy_binary_computed_block_last::< Ratio32, _, _, diff --git a/crates/brk_computer/src/market/indicators/timeframe.rs b/crates/brk_computer/src/market/indicators/timeframe.rs index b2f64c316..ccd2c7a77 100644 --- a/crates/brk_computer/src/market/indicators/timeframe.rs +++ b/crates/brk_computer/src/market/indicators/timeframe.rs @@ -29,10 +29,10 @@ pub(super) fn collect_returns(tf: &str, returns: &ReturnsVecs) -> Vec { /// Returns period-level close prices pub(super) fn collect_closes(tf: &str, prices: &prices::Vecs) -> Vec { match tf { - "1d" => prices.usd.split.close.day1.collect(), - "1w" => prices.usd.split.close.week1.collect(), - "1m" => prices.usd.split.close.month1.collect(), - "1y" => prices.usd.split.close.year1.collect(), + "1d" => prices.usd.close.day1.collect(), + "1w" => prices.usd.close.week1.collect(), + "1m" => prices.usd.close.month1.collect(), + "1y" => prices.usd.close.year1.collect(), _ => unreachable!(), } } diff --git a/crates/brk_computer/src/market/moving_average/compute.rs b/crates/brk_computer/src/market/moving_average/compute.rs index 2750f7a49..b972f4fa6 100644 --- a/crates/brk_computer/src/market/moving_average/compute.rs +++ b/crates/brk_computer/src/market/moving_average/compute.rs @@ -42,7 +42,7 @@ impl Vecs { } let h2d = &indexes.height.day1; - let closes: Vec = prices.usd.split.close.day1.collect(); + let closes: Vec = prices.usd.close.day1.collect(); for (ema, period) in [ (&mut self.price_1w_ema, 7), diff --git a/crates/brk_computer/src/market/returns/import.rs b/crates/brk_computer/src/market/returns/import.rs index b81413fcd..f59cab8aa 100644 --- a/crates/brk_computer/src/market/returns/import.rs +++ b/crates/brk_computer/src/market/returns/import.rs @@ -35,7 +35,7 @@ impl Vecs { version, prices.usd.price.read_only_boxed_clone(), price_ago.height.read_only_boxed_clone(), - &prices.usd.split.close, + &prices.usd.close, &price_ago.rest, ) }); diff --git a/crates/brk_computer/src/mining/compute.rs b/crates/brk_computer/src/mining/compute.rs index 6f1b2e993..5f9394f4d 100644 --- a/crates/brk_computer/src/mining/compute.rs +++ b/crates/brk_computer/src/mining/compute.rs @@ -25,11 +25,12 @@ impl Vecs { exit, )?; - // Hashrate metrics (uses rewards.coinbase_24h_sum — disjoint field borrow) + // Hashrate metrics (disjoint field borrow via coinbase_sum) self.hashrate.compute( &blocks.count, &blocks.difficulty, - &self.rewards.coinbase_24h_sum, + &self.rewards.coinbase_sum._24h.sats.height, + &self.rewards.coinbase_sum._24h.usd.height, starting_indexes, exit, )?; diff --git a/crates/brk_computer/src/mining/hashrate/compute.rs b/crates/brk_computer/src/mining/hashrate/compute.rs index dd1bb7922..8851f5a81 100644 --- a/crates/brk_computer/src/mining/hashrate/compute.rs +++ b/crates/brk_computer/src/mining/hashrate/compute.rs @@ -1,11 +1,10 @@ use brk_error::Result; -use brk_types::{StoredF32, StoredF64}; -use vecdb::Exit; +use brk_types::{Dollars, Height, Sats, StoredF32, StoredF64}; +use vecdb::{Exit, ReadableVec}; use super::Vecs; use crate::{ blocks::{self, ONE_TERA_HASH, TARGET_BLOCKS_PER_DAY_F64}, - internal::StoredValueFromHeightLast, ComputeIndexes, traits::ComputeDrawdown, }; @@ -15,13 +14,14 @@ impl Vecs { &mut self, count_vecs: &blocks::CountVecs, difficulty_vecs: &blocks::DifficultyVecs, - coinbase_sum_24h: &StoredValueFromHeightLast, + coinbase_sats_24h_sum: &impl ReadableVec, + coinbase_usd_24h_sum: &impl ReadableVec, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { self.hash_rate.height.compute_transform2( starting_indexes.height, - &count_vecs.block_count_24h_sum.height, + &count_vecs.block_count_sum._24h.height, &difficulty_vecs.as_hash.height, |(i, block_count_sum, difficulty_as_hash, ..)| { ( @@ -78,7 +78,7 @@ impl Vecs { self.hash_price_ths.height.compute_transform2( starting_indexes.height, - &coinbase_sum_24h.usd.height, + coinbase_usd_24h_sum, &self.hash_rate.height, |(i, coinbase_sum, hashrate, ..)| { let hashrate_ths = *hashrate / ONE_TERA_HASH; @@ -101,7 +101,7 @@ impl Vecs { self.hash_value_ths.height.compute_transform2( starting_indexes.height, - &coinbase_sum_24h.sats.height, + coinbase_sats_24h_sum, &self.hash_rate.height, |(i, coinbase_sum, hashrate, ..)| { let hashrate_ths = *hashrate / ONE_TERA_HASH; diff --git a/crates/brk_computer/src/mining/rewards/compute.rs b/crates/brk_computer/src/mining/rewards/compute.rs index d83ab6137..13712b61e 100644 --- a/crates/brk_computer/src/mining/rewards/compute.rs +++ b/crates/brk_computer/src/mining/rewards/compute.rs @@ -48,61 +48,20 @@ impl Vecs { Ok(()) })?; - self.coinbase_24h_sum.compute_rolling_sum( + let window_starts = count_vecs.window_starts(); + self.coinbase_sum.compute_rolling_sum( starting_indexes.height, - &count_vecs.height_24h_ago, - &self.coinbase.sats.height, - &self.coinbase.usd.height, - exit, - )?; - self.coinbase_7d_sum.compute_rolling_sum( - starting_indexes.height, - &count_vecs.height_1w_ago, - &self.coinbase.sats.height, - &self.coinbase.usd.height, - exit, - )?; - self.coinbase_30d_sum.compute_rolling_sum( - starting_indexes.height, - &count_vecs.height_1m_ago, - &self.coinbase.sats.height, - &self.coinbase.usd.height, - exit, - )?; - self.coinbase_1y_sum.compute_rolling_sum( - starting_indexes.height, - &count_vecs.height_1y_ago, + &window_starts, &self.coinbase.sats.height, &self.coinbase.usd.height, exit, )?; - let fee_sats_source = transactions_fees.fee.sats.height.sum_cum.sum.inner(); - let fee_usd_source = &transactions_fees.fee.usd.height.sum; - self.fee_24h_sum.compute_rolling_sum( + let fee_sats_source = transactions_fees.fee.sum_cum.sum.inner(); + let fee_usd_source = &transactions_fees.fee_usd_sum; + self.fee_sum.compute_rolling_sum( starting_indexes.height, - &count_vecs.height_24h_ago, - fee_sats_source, - fee_usd_source, - exit, - )?; - self.fee_7d_sum.compute_rolling_sum( - starting_indexes.height, - &count_vecs.height_1w_ago, - fee_sats_source, - fee_usd_source, - exit, - )?; - self.fee_30d_sum.compute_rolling_sum( - starting_indexes.height, - &count_vecs.height_1m_ago, - fee_sats_source, - fee_usd_source, - exit, - )?; - self.fee_1y_sum.compute_rolling_sum( - starting_indexes.height, - &count_vecs.height_1y_ago, + &window_starts, fee_sats_source, fee_usd_source, exit, @@ -112,7 +71,7 @@ impl Vecs { vec.compute_transform2( starting_indexes.height, &self.coinbase.sats.height, - transactions_fees.fee.sats.height.sum_cum.sum.inner(), + transactions_fees.fee.sum_cum.sum.inner(), |(height, coinbase, fees, ..)| { ( height, @@ -145,7 +104,7 @@ impl Vecs { // All-time cumulative fee dominance self.fee_dominance.height.compute_percentage( starting_indexes.height, - transactions_fees.fee.sats.height.sum_cum.cumulative.inner(), + transactions_fees.fee.sum_cum.cumulative.inner(), self.coinbase.sats.rest.height_cumulative.inner(), exit, )?; @@ -153,26 +112,26 @@ impl Vecs { // Rolling fee dominance = sum(fees) / sum(coinbase) * 100 self.fee_dominance_24h.height.compute_percentage( starting_indexes.height, - &self.fee_24h_sum.sats.height, - &self.coinbase_24h_sum.sats.height, + &self.fee_sum._24h.sats.height, + &self.coinbase_sum._24h.sats.height, exit, )?; self.fee_dominance_7d.height.compute_percentage( starting_indexes.height, - &self.fee_7d_sum.sats.height, - &self.coinbase_7d_sum.sats.height, + &self.fee_sum._7d.sats.height, + &self.coinbase_sum._7d.sats.height, exit, )?; self.fee_dominance_30d.height.compute_percentage( starting_indexes.height, - &self.fee_30d_sum.sats.height, - &self.coinbase_30d_sum.sats.height, + &self.fee_sum._30d.sats.height, + &self.coinbase_sum._30d.sats.height, exit, )?; self.fee_dominance_1y.height.compute_percentage( starting_indexes.height, - &self.fee_1y_sum.sats.height, - &self.coinbase_1y_sum.sats.height, + &self.fee_sum._1y.sats.height, + &self.coinbase_sum._1y.sats.height, exit, )?; diff --git a/crates/brk_computer/src/mining/rewards/import.rs b/crates/brk_computer/src/mining/rewards/import.rs index bd7643925..56b78b81a 100644 --- a/crates/brk_computer/src/mining/rewards/import.rs +++ b/crates/brk_computer/src/mining/rewards/import.rs @@ -5,7 +5,7 @@ use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightLast, StoredValueFromHeightLast, ValueFromHeightFull, ValueFromHeightSumCum}, + internal::{ComputedFromHeightLast, StoredValueRollingWindows, ValueFromHeightFull, ValueFromHeightSumCum}, prices, }; @@ -17,14 +17,8 @@ impl Vecs { prices: &prices::Vecs, ) -> Result { Ok(Self { - coinbase_24h_sum: StoredValueFromHeightLast::forced_import(db, "coinbase_24h_sum", version, indexes)?, - coinbase_7d_sum: StoredValueFromHeightLast::forced_import(db, "coinbase_7d_sum", version, indexes)?, - coinbase_30d_sum: StoredValueFromHeightLast::forced_import(db, "coinbase_30d_sum", version, indexes)?, - coinbase_1y_sum: StoredValueFromHeightLast::forced_import(db, "coinbase_1y_sum", version, indexes)?, - fee_24h_sum: StoredValueFromHeightLast::forced_import(db, "fee_24h_sum", version, indexes)?, - fee_7d_sum: StoredValueFromHeightLast::forced_import(db, "fee_7d_sum", version, indexes)?, - fee_30d_sum: StoredValueFromHeightLast::forced_import(db, "fee_30d_sum", version, indexes)?, - fee_1y_sum: StoredValueFromHeightLast::forced_import(db, "fee_1y_sum", version, indexes)?, + coinbase_sum: StoredValueRollingWindows::forced_import(db, "coinbase_sum", version, indexes)?, + fee_sum: StoredValueRollingWindows::forced_import(db, "fee_sum", version, indexes)?, coinbase: ValueFromHeightFull::forced_import(db, "coinbase", version, indexes, prices)?, subsidy: ValueFromHeightFull::forced_import(db, "subsidy", version, indexes, prices)?, unclaimed_rewards: ValueFromHeightSumCum::forced_import( diff --git a/crates/brk_computer/src/mining/rewards/vecs.rs b/crates/brk_computer/src/mining/rewards/vecs.rs index 32cd24453..1119b2aa9 100644 --- a/crates/brk_computer/src/mining/rewards/vecs.rs +++ b/crates/brk_computer/src/mining/rewards/vecs.rs @@ -2,19 +2,13 @@ use brk_traversable::Traversable; use brk_types::{Dollars, StoredF32}; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightLast, StoredValueFromHeightLast, ValueFromHeightFull, ValueFromHeightSumCum}; +use crate::internal::{ComputedFromHeightLast, StoredValueRollingWindows, ValueFromHeightFull, ValueFromHeightSumCum}; /// Coinbase/subsidy/rewards metrics #[derive(Traversable)] pub struct Vecs { - pub coinbase_24h_sum: StoredValueFromHeightLast, - pub coinbase_7d_sum: StoredValueFromHeightLast, - pub coinbase_30d_sum: StoredValueFromHeightLast, - pub coinbase_1y_sum: StoredValueFromHeightLast, - pub fee_24h_sum: StoredValueFromHeightLast, - pub fee_7d_sum: StoredValueFromHeightLast, - pub fee_30d_sum: StoredValueFromHeightLast, - pub fee_1y_sum: StoredValueFromHeightLast, + pub coinbase_sum: StoredValueRollingWindows, + pub fee_sum: StoredValueRollingWindows, pub coinbase: ValueFromHeightFull, pub subsidy: ValueFromHeightFull, pub unclaimed_rewards: ValueFromHeightSumCum, diff --git a/crates/brk_computer/src/outputs/compute.rs b/crates/brk_computer/src/outputs/compute.rs index 10d248e7e..89bfa8f6f 100644 --- a/crates/brk_computer/src/outputs/compute.rs +++ b/crates/brk_computer/src/outputs/compute.rs @@ -3,15 +3,17 @@ use brk_indexer::Indexer; use vecdb::Exit; use super::Vecs; -use crate::{indexes, inputs, scripts, ComputeIndexes}; +use crate::{ComputeIndexes, blocks, indexes, inputs, scripts}; impl Vecs { + #[allow(clippy::too_many_arguments)] pub(crate) fn compute( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, inputs: &inputs::Vecs, scripts: &scripts::Vecs, + blocks: &blocks::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { @@ -22,6 +24,7 @@ impl Vecs { indexes, &inputs.count, &scripts.count, + blocks, starting_indexes, exit, )?; diff --git a/crates/brk_computer/src/outputs/count/compute.rs b/crates/brk_computer/src/outputs/count/compute.rs index ac0e1a12f..90eaca03e 100644 --- a/crates/brk_computer/src/outputs/count/compute.rs +++ b/crates/brk_computer/src/outputs/count/compute.rs @@ -4,31 +4,42 @@ use brk_types::{Height, StoredU64}; use vecdb::Exit; use super::Vecs; -use crate::{ComputeIndexes, indexes, inputs, scripts}; +use crate::{ComputeIndexes, blocks, indexes, inputs, scripts}; impl Vecs { + #[allow(clippy::too_many_arguments)] pub(crate) fn compute( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, inputs_count: &inputs::CountVecs, scripts_count: &scripts::CountVecs, + blocks: &blocks::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.total_count.derive_from( - indexer, - indexes, - starting_indexes, + self.total_count.compute_with_skip( + starting_indexes.height, &indexes.txindex.output_count, + &indexer.vecs.transactions.first_txindex, + &indexes.height.txindex_count, + exit, + 0, + )?; + + let window_starts = blocks.count.window_starts(); + self.total_count_rolling.compute( + starting_indexes.height, + &window_starts, + self.total_count.sum_cum.sum.inner(), exit, )?; self.utxo_count.height.compute_transform3( starting_indexes.height, - &*self.total_count.height.sum_cum.cumulative, + &*self.total_count.sum_cum.cumulative, &*inputs_count.height.sum_cum.cumulative, - &*scripts_count.opreturn.height_cumulative, + &scripts_count.opreturn.cumulative.height, |(h, output_count, input_count, opreturn_count, ..)| { let block_count = u64::from(h + 1_usize); // -1 > genesis output is unspendable diff --git a/crates/brk_computer/src/outputs/count/import.rs b/crates/brk_computer/src/outputs/count/import.rs index e7ec5ca83..f58ab0cef 100644 --- a/crates/brk_computer/src/outputs/count/import.rs +++ b/crates/brk_computer/src/outputs/count/import.rs @@ -5,13 +5,14 @@ use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightLast, TxDerivedFull}, + internal::{ComputedFromHeightLast, Full, RollingFull}, }; impl Vecs { pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result { Ok(Self { - total_count: TxDerivedFull::forced_import(db, "output_count", version, indexes)?, + total_count: Full::forced_import(db, "output_count", version)?, + total_count_rolling: RollingFull::forced_import(db, "output_count", version, indexes)?, utxo_count: ComputedFromHeightLast::forced_import(db, "exact_utxo_count", version, indexes)?, }) } diff --git a/crates/brk_computer/src/outputs/count/vecs.rs b/crates/brk_computer/src/outputs/count/vecs.rs index 044dcf1cc..a5edf71a0 100644 --- a/crates/brk_computer/src/outputs/count/vecs.rs +++ b/crates/brk_computer/src/outputs/count/vecs.rs @@ -1,11 +1,12 @@ use brk_traversable::Traversable; -use brk_types::StoredU64; +use brk_types::{Height, StoredU64}; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightLast, TxDerivedFull}; +use crate::internal::{ComputedFromHeightLast, Full, RollingFull}; #[derive(Traversable)] pub struct Vecs { - pub total_count: TxDerivedFull, + pub total_count: Full, + pub total_count_rolling: RollingFull, pub utxo_count: ComputedFromHeightLast, } diff --git a/crates/brk_computer/src/pools/vecs.rs b/crates/brk_computer/src/pools/vecs.rs index e7fb7cb13..2c1ac824b 100644 --- a/crates/brk_computer/src/pools/vecs.rs +++ b/crates/brk_computer/src/pools/vecs.rs @@ -1,17 +1,14 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, PoolSlug, Sats, StoredF32, StoredU16, StoredU32}; -use vecdb::{ - Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, Version, -}; +use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, Version}; use crate::{ blocks, indexes::{self, ComputeIndexes}, internal::{ - ComputedFromHeightLast, ComputedFromHeightSumCum, DollarsPlus, - LazyBinaryFromHeightLast, LazyValueFromHeightSumCum, MaskSats, PercentageU32F32, SatsPlus, - SatsPlusToBitcoin, ValueBinaryFromHeight, + ComputedFromHeightLast, ComputedFromHeightSumCum, LazyBinaryFromHeightLast, + LazyValueFromHeightSumCum, MaskSats, PercentageU32F32, }, mining, prices, transactions, }; @@ -27,7 +24,7 @@ pub struct Vecs { pub blocks_mined_1y_sum: ComputedFromHeightLast, pub subsidy: LazyValueFromHeightSumCum, pub fee: LazyValueFromHeightSumCum, - pub coinbase: ValueBinaryFromHeight, + pub coinbase: LazyValueFromHeightSumCum, pub dominance: LazyBinaryFromHeightLast, pub dominance_24h: LazyBinaryFromHeightLast, @@ -97,7 +94,17 @@ impl Vecs { version, indexes, blocks_mined.height.read_only_boxed_clone(), - transactions.fees.fee.sats.height.boxed_sum(), + transactions.fees.fee.boxed_sum(), + prices, + )?; + + let coinbase = LazyValueFromHeightSumCum::forced_import::( + db, + &suffix("coinbase"), + version, + indexes, + blocks_mined.height.read_only_boxed_clone(), + mining.rewards.coinbase.sats.height.read_only_boxed_clone(), prices, )?; @@ -112,25 +119,25 @@ impl Vecs { &suffix("dominance_24h"), version, &blocks_mined_24h_sum, - &blocks.count.block_count_24h_sum, + &blocks.count.block_count_sum._24h, ), dominance_1w: LazyBinaryFromHeightLast::from_computed_last::( &suffix("dominance_1w"), version, &blocks_mined_1w_sum, - &blocks.count.block_count_1w_sum, + &blocks.count.block_count_sum._7d, ), dominance_1m: LazyBinaryFromHeightLast::from_computed_last::( &suffix("dominance_1m"), version, &blocks_mined_1m_sum, - &blocks.count.block_count_1m_sum, + &blocks.count.block_count_sum._30d, ), dominance_1y: LazyBinaryFromHeightLast::from_computed_last::( &suffix("dominance_1y"), version, &blocks_mined_1y_sum, - &blocks.count.block_count_1y_sum, + &blocks.count.block_count_sum._1y, ), slug, blocks_mined, @@ -138,13 +145,7 @@ impl Vecs { blocks_mined_1w_sum, blocks_mined_1m_sum, blocks_mined_1y_sum, - coinbase: ValueBinaryFromHeight::from_lazy::< - SatsPlus, - SatsPlusToBitcoin, - DollarsPlus, - StoredU32, - Sats, - >(&suffix("coinbase"), version, &subsidy, &fee), + coinbase, subsidy, fee, blocks_since_block: ComputedFromHeightLast::forced_import( @@ -169,25 +170,24 @@ impl Vecs { blocks: &blocks::Vecs, exit: &Exit, ) -> Result<()> { - self.blocks_mined - .compute(starting_indexes, exit, |vec| { - vec.compute_transform( - starting_indexes.height, - height_to_pool, - |(h, id, ..)| { - ( - h, - if id == self.slug { - StoredU32::ONE - } else { - StoredU32::ZERO - }, - ) - }, - exit, - )?; - Ok(()) - })?; + self.blocks_mined.compute(starting_indexes, exit, |vec| { + vec.compute_transform( + starting_indexes.height, + height_to_pool, + |(h, id, ..)| { + ( + h, + if id == self.slug { + StoredU32::ONE + } else { + StoredU32::ZERO + }, + ) + }, + exit, + )?; + Ok(()) + })?; // Compute rolling window blocks mined using the start heights from blocks.count self.blocks_mined_24h_sum.height.compute_rolling_sum( @@ -222,6 +222,8 @@ impl Vecs { self.fee.compute_cumulative(starting_indexes, exit)?; + self.coinbase.compute_cumulative(starting_indexes, exit)?; + { let mut prev = StoredU32::ZERO; self.blocks_since_block.height.compute_transform( @@ -243,7 +245,12 @@ impl Vecs { self.days_since_block.height.compute_transform( starting_indexes.height, &self.blocks_since_block.height, - |(h, blocks, ..)| (h, StoredU16::from(u16::try_from(*blocks).unwrap_or(u16::MAX))), + |(h, blocks, ..)| { + ( + h, + StoredU16::from(u16::try_from(*blocks).unwrap_or(u16::MAX)), + ) + }, exit, )?; diff --git a/crates/brk_computer/src/prices/cents/compute.rs b/crates/brk_computer/src/prices/cents/compute.rs index 861d0ae44..04e435964 100644 --- a/crates/brk_computer/src/prices/cents/compute.rs +++ b/crates/brk_computer/src/prices/cents/compute.rs @@ -8,16 +8,23 @@ use tracing::info; use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, StorageMode, WritableVec, VecIndex}; use super::Vecs; -use crate::ComputeIndexes; +use crate::{ComputeIndexes, indexes}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, + indexes: &indexes::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { self.compute_prices(indexer, starting_indexes, exit)?; + self.open + .compute_first(starting_indexes, &self.price, indexes, exit)?; + self.high + .compute_max(starting_indexes, &self.price, indexes, exit)?; + self.low + .compute_min(starting_indexes, &self.price, indexes, exit)?; Ok(()) } diff --git a/crates/brk_computer/src/prices/cents/import.rs b/crates/brk_computer/src/prices/cents/import.rs index bcae71cc9..589ca3934 100644 --- a/crates/brk_computer/src/prices/cents/import.rs +++ b/crates/brk_computer/src/prices/cents/import.rs @@ -1,10 +1,10 @@ use brk_error::Result; use brk_types::Version; -use vecdb::{Database, ImportableVec, ReadableCloneableVec, PcoVec}; +use vecdb::{Database, ImportableVec, PcoVec, ReadableCloneableVec}; use super::Vecs; use crate::indexes; -use crate::internal::{ComputedHeightDerivedOHLC, ComputedHeightDerivedSplitOHLC}; +use crate::internal::{ComputedHeightDerivedLast, EagerIndexes}; impl Vecs { pub(crate) fn forced_import( @@ -16,20 +16,23 @@ impl Vecs { let price = PcoVec::forced_import(db, "price_cents", version)?; - let split = ComputedHeightDerivedSplitOHLC::forced_import( - "price_cents", + let open = EagerIndexes::forced_import(db, "price_cents_open", version)?; + let high = EagerIndexes::forced_import(db, "price_cents_high", version)?; + let low = EagerIndexes::forced_import(db, "price_cents_low", version)?; + + let close = ComputedHeightDerivedLast::forced_import( + "price_cents_close", + price.read_only_boxed_clone(), version, indexes, - price.read_only_boxed_clone(), ); - let ohlc = ComputedHeightDerivedOHLC::forced_import( - "price_cents", - version, - indexes, - price.read_only_boxed_clone(), - ); - - Ok(Self { price, split, ohlc }) + Ok(Self { + price, + open, + high, + low, + close, + }) } } diff --git a/crates/brk_computer/src/prices/cents/vecs.rs b/crates/brk_computer/src/prices/cents/vecs.rs index 87dc24117..694b0dd83 100644 --- a/crates/brk_computer/src/prices/cents/vecs.rs +++ b/crates/brk_computer/src/prices/cents/vecs.rs @@ -1,12 +1,14 @@ use brk_traversable::Traversable; -use brk_types::{Cents, Height, OHLCCents}; +use brk_types::{Cents, Height}; use vecdb::{PcoVec, Rw, StorageMode}; -use crate::internal::{ComputedHeightDerivedOHLC, ComputedHeightDerivedSplitOHLC}; +use crate::internal::{ComputedHeightDerivedLast, EagerIndexes}; #[derive(Traversable)] pub struct Vecs { pub price: M::Stored>, - pub split: ComputedHeightDerivedSplitOHLC, - pub ohlc: ComputedHeightDerivedOHLC, + pub open: EagerIndexes, + pub high: EagerIndexes, + pub low: EagerIndexes, + pub close: ComputedHeightDerivedLast, } diff --git a/crates/brk_computer/src/prices/compute.rs b/crates/brk_computer/src/prices/compute.rs index 27b322841..746ec0385 100644 --- a/crates/brk_computer/src/prices/compute.rs +++ b/crates/brk_computer/src/prices/compute.rs @@ -3,17 +3,18 @@ use brk_indexer::Indexer; use vecdb::Exit; use super::Vecs; -use crate::ComputeIndexes; +use crate::{ComputeIndexes, indexes}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, + indexes: &indexes::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { self.cents - .compute(indexer, starting_indexes, exit)?; + .compute(indexer, indexes, starting_indexes, exit)?; let _lock = exit.lock(); self.db().compact()?; diff --git a/crates/brk_computer/src/prices/sats/import.rs b/crates/brk_computer/src/prices/sats/import.rs index 9d101c3a2..f8ee770cb 100644 --- a/crates/brk_computer/src/prices/sats/import.rs +++ b/crates/brk_computer/src/prices/sats/import.rs @@ -1,11 +1,11 @@ use brk_types::Version; -use vecdb::{ReadableCloneableVec, LazyVecFrom1}; +use vecdb::{LazyVecFrom1, ReadableCloneableVec}; use super::super::cents; use super::Vecs; use crate::{ indexes, - internal::{CentsUnsignedToSats, ComputedHeightDerivedOHLC, ComputedHeightDerivedSplitOHLC}, + internal::{CentsUnsignedToSats, ComputedHeightDerivedLast, LazyEagerIndexes}, }; impl Vecs { @@ -20,20 +20,27 @@ impl Vecs { cents.price.read_only_boxed_clone(), ); - let split = ComputedHeightDerivedSplitOHLC::forced_import( - "price_sats", + // Sats are inversely related to cents (sats = 10B/cents), so high↔low are swapped + let open = + LazyEagerIndexes::from_eager_indexes::("price_sats_open", version, ¢s.open); + let high = + LazyEagerIndexes::from_eager_indexes::("price_sats_high", version, ¢s.low); + let low = + LazyEagerIndexes::from_eager_indexes::("price_sats_low", version, ¢s.high); + + let close = ComputedHeightDerivedLast::forced_import( + "price_sats_close", + price.read_only_boxed_clone(), version, indexes, - price.read_only_boxed_clone(), ); - let ohlc = ComputedHeightDerivedOHLC::forced_import( - "price_sats", - version, - indexes, - price.read_only_boxed_clone(), - ); - - Self { price, split, ohlc } + Self { + price, + open, + high, + low, + close, + } } } diff --git a/crates/brk_computer/src/prices/sats/vecs.rs b/crates/brk_computer/src/prices/sats/vecs.rs index ee2184460..3b066ffcd 100644 --- a/crates/brk_computer/src/prices/sats/vecs.rs +++ b/crates/brk_computer/src/prices/sats/vecs.rs @@ -1,12 +1,14 @@ use brk_traversable::Traversable; -use brk_types::{Cents, Height, OHLCSats, Sats}; +use brk_types::{Cents, Height, Sats}; use vecdb::LazyVecFrom1; -use crate::internal::{ComputedHeightDerivedOHLC, ComputedHeightDerivedSplitOHLC}; +use crate::internal::{ComputedHeightDerivedLast, LazyEagerIndexes}; #[derive(Clone, Traversable)] pub struct Vecs { pub price: LazyVecFrom1, - pub split: ComputedHeightDerivedSplitOHLC, - pub ohlc: ComputedHeightDerivedOHLC, + pub open: LazyEagerIndexes, + pub high: LazyEagerIndexes, + pub low: LazyEagerIndexes, + pub close: ComputedHeightDerivedLast, } diff --git a/crates/brk_computer/src/prices/usd/import.rs b/crates/brk_computer/src/prices/usd/import.rs index 661acd242..de7510fee 100644 --- a/crates/brk_computer/src/prices/usd/import.rs +++ b/crates/brk_computer/src/prices/usd/import.rs @@ -1,11 +1,11 @@ use brk_types::Version; -use vecdb::{ReadableCloneableVec, LazyVecFrom1}; +use vecdb::{LazyVecFrom1, ReadableCloneableVec}; use super::super::cents; use super::Vecs; use crate::{ indexes, - internal::{CentsUnsignedToDollars, ComputedHeightDerivedOHLC, ComputedHeightDerivedSplitOHLC}, + internal::{CentsUnsignedToDollars, ComputedHeightDerivedLast, LazyEagerIndexes}, }; impl Vecs { @@ -20,20 +20,27 @@ impl Vecs { cents.price.read_only_boxed_clone(), ); - let split = ComputedHeightDerivedSplitOHLC::forced_import( - "price", + // Dollars are monotonically increasing from cents, so open→open, high→high, low→low + let open = + LazyEagerIndexes::from_eager_indexes::("price_usd_open", version, ¢s.open); + let high = + LazyEagerIndexes::from_eager_indexes::("price_usd_high", version, ¢s.high); + let low = + LazyEagerIndexes::from_eager_indexes::("price_usd_low", version, ¢s.low); + + let close = ComputedHeightDerivedLast::forced_import( + "price_usd_close", + price.read_only_boxed_clone(), version, indexes, - price.read_only_boxed_clone(), ); - let ohlc = ComputedHeightDerivedOHLC::forced_import( - "price_usd", - version, - indexes, - price.read_only_boxed_clone(), - ); - - Self { price, split, ohlc } + Self { + price, + open, + high, + low, + close, + } } } diff --git a/crates/brk_computer/src/prices/usd/vecs.rs b/crates/brk_computer/src/prices/usd/vecs.rs index 629f21378..9bc2d8c54 100644 --- a/crates/brk_computer/src/prices/usd/vecs.rs +++ b/crates/brk_computer/src/prices/usd/vecs.rs @@ -1,12 +1,14 @@ use brk_traversable::Traversable; -use brk_types::{Cents, Dollars, Height, OHLCDollars}; +use brk_types::{Cents, Dollars, Height}; use vecdb::LazyVecFrom1; -use crate::internal::{ComputedHeightDerivedOHLC, ComputedHeightDerivedSplitOHLC}; +use crate::internal::{ComputedHeightDerivedLast, LazyEagerIndexes}; #[derive(Clone, Traversable)] pub struct Vecs { pub price: LazyVecFrom1, - pub split: ComputedHeightDerivedSplitOHLC, - pub ohlc: ComputedHeightDerivedOHLC, + pub open: LazyEagerIndexes, + pub high: LazyEagerIndexes, + pub low: LazyEagerIndexes, + pub close: ComputedHeightDerivedLast, } diff --git a/crates/brk_computer/src/scripts/compute.rs b/crates/brk_computer/src/scripts/compute.rs index b5f558825..a1a9a598a 100644 --- a/crates/brk_computer/src/scripts/compute.rs +++ b/crates/brk_computer/src/scripts/compute.rs @@ -2,7 +2,7 @@ use brk_error::Result; use brk_indexer::Indexer; use vecdb::Exit; -use crate::ComputeIndexes; +use crate::{blocks, outputs, ComputeIndexes}; use super::Vecs; @@ -10,11 +10,13 @@ impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, + blocks: &blocks::Vecs, + outputs: &outputs::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { self.count - .compute(indexer, starting_indexes, exit)?; + .compute(indexer, &blocks.count, &outputs.count, starting_indexes, exit)?; self.value .compute(indexer, starting_indexes, exit)?; diff --git a/crates/brk_computer/src/scripts/count/compute.rs b/crates/brk_computer/src/scripts/count/compute.rs index 2d069457d..71270d090 100644 --- a/crates/brk_computer/src/scripts/count/compute.rs +++ b/crates/brk_computer/src/scripts/count/compute.rs @@ -1,164 +1,175 @@ use brk_error::Result; use brk_indexer::Indexer; +use brk_types::StoredF32; use brk_types::StoredU64; use vecdb::Exit; use super::Vecs; -use crate::ComputeIndexes; +use crate::{blocks, outputs, ComputeIndexes}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, + count_vecs: &blocks::CountVecs, + outputs_count: &outputs::CountVecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.p2a.compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( + let window_starts = count_vecs.window_starts(); + + self.p2a.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( starting_indexes.height, &indexer.vecs.addresses.first_p2aaddressindex, &indexer.vecs.addresses.p2abytes, exit, - )?; - Ok(()) + )?) })?; - self.p2ms - .compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( - starting_indexes.height, - &indexer.vecs.scripts.first_p2msoutputindex, - &indexer.vecs.scripts.p2ms_to_txindex, - exit, - )?; - Ok(()) - })?; + self.p2ms.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( + starting_indexes.height, + &indexer.vecs.scripts.first_p2msoutputindex, + &indexer.vecs.scripts.p2ms_to_txindex, + exit, + )?) + })?; - self.p2pk33 - .compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( - starting_indexes.height, - &indexer.vecs.addresses.first_p2pk33addressindex, - &indexer.vecs.addresses.p2pk33bytes, - exit, - )?; - Ok(()) - })?; + self.p2pk33.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( + starting_indexes.height, + &indexer.vecs.addresses.first_p2pk33addressindex, + &indexer.vecs.addresses.p2pk33bytes, + exit, + )?) + })?; - self.p2pk65 - .compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( - starting_indexes.height, - &indexer.vecs.addresses.first_p2pk65addressindex, - &indexer.vecs.addresses.p2pk65bytes, - exit, - )?; - Ok(()) - })?; + self.p2pk65.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( + starting_indexes.height, + &indexer.vecs.addresses.first_p2pk65addressindex, + &indexer.vecs.addresses.p2pk65bytes, + exit, + )?) + })?; - self.p2pkh - .compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( - starting_indexes.height, - &indexer.vecs.addresses.first_p2pkhaddressindex, - &indexer.vecs.addresses.p2pkhbytes, - exit, - )?; - Ok(()) - })?; + self.p2pkh.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( + starting_indexes.height, + &indexer.vecs.addresses.first_p2pkhaddressindex, + &indexer.vecs.addresses.p2pkhbytes, + exit, + )?) + })?; - self.p2sh - .compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( - starting_indexes.height, - &indexer.vecs.addresses.first_p2shaddressindex, - &indexer.vecs.addresses.p2shbytes, - exit, - )?; - Ok(()) - })?; + self.p2sh.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( + starting_indexes.height, + &indexer.vecs.addresses.first_p2shaddressindex, + &indexer.vecs.addresses.p2shbytes, + exit, + )?) + })?; - self.p2tr - .compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( - starting_indexes.height, - &indexer.vecs.addresses.first_p2traddressindex, - &indexer.vecs.addresses.p2trbytes, - exit, - )?; - Ok(()) - })?; + self.p2tr.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( + starting_indexes.height, + &indexer.vecs.addresses.first_p2traddressindex, + &indexer.vecs.addresses.p2trbytes, + exit, + )?) + })?; - self.p2wpkh - .compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( - starting_indexes.height, - &indexer.vecs.addresses.first_p2wpkhaddressindex, - &indexer.vecs.addresses.p2wpkhbytes, - exit, - )?; - Ok(()) - })?; + self.p2wpkh.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( + starting_indexes.height, + &indexer.vecs.addresses.first_p2wpkhaddressindex, + &indexer.vecs.addresses.p2wpkhbytes, + exit, + )?) + })?; - self.p2wsh - .compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( - starting_indexes.height, - &indexer.vecs.addresses.first_p2wshaddressindex, - &indexer.vecs.addresses.p2wshbytes, - exit, - )?; - Ok(()) - })?; + self.p2wsh.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( + starting_indexes.height, + &indexer.vecs.addresses.first_p2wshaddressindex, + &indexer.vecs.addresses.p2wshbytes, + exit, + )?) + })?; - self.opreturn - .compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( - starting_indexes.height, - &indexer.vecs.scripts.first_opreturnindex, - &indexer.vecs.scripts.opreturn_to_txindex, - exit, - )?; - Ok(()) - })?; + self.opreturn.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( + starting_indexes.height, + &indexer.vecs.scripts.first_opreturnindex, + &indexer.vecs.scripts.opreturn_to_txindex, + exit, + )?) + })?; - self.unknownoutput - .compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( - starting_indexes.height, - &indexer.vecs.scripts.first_unknownoutputindex, - &indexer.vecs.scripts.unknown_to_txindex, - exit, - )?; - Ok(()) - })?; + self.unknownoutput.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( + starting_indexes.height, + &indexer.vecs.scripts.first_unknownoutputindex, + &indexer.vecs.scripts.unknown_to_txindex, + exit, + )?) + })?; - self.emptyoutput - .compute(starting_indexes, exit,|v| { - v.compute_count_from_indexes( - starting_indexes.height, - &indexer.vecs.scripts.first_emptyoutputindex, - &indexer.vecs.scripts.empty_to_txindex, - exit, - )?; - Ok(()) - })?; + self.emptyoutput.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_count_from_indexes( + starting_indexes.height, + &indexer.vecs.scripts.first_emptyoutputindex, + &indexer.vecs.scripts.empty_to_txindex, + exit, + )?) + })?; // Compute segwit = p2wpkh + p2wsh + p2tr - self.segwit - .compute(starting_indexes, exit,|v| { - v.compute_transform3( - starting_indexes.height, - &self.p2wpkh.height, - &self.p2wsh.height, - &self.p2tr.height, - |(h, p2wpkh, p2wsh, p2tr, ..)| { - (h, StoredU64::from(*p2wpkh + *p2wsh + *p2tr)) - }, - exit, - )?; - Ok(()) - })?; + self.segwit.compute(starting_indexes.height, &window_starts, exit, |v| { + Ok(v.compute_transform3( + starting_indexes.height, + &self.p2wpkh.last.height, + &self.p2wsh.last.height, + &self.p2tr.last.height, + |(h, p2wpkh, p2wsh, p2tr, ..)| { + (h, StoredU64::from(*p2wpkh + *p2wsh + *p2tr)) + }, + exit, + )?) + })?; + + // Adoption ratios: per-block ratio of script type / total outputs + self.taproot_adoption.height.compute_transform2( + starting_indexes.height, + &self.p2tr.last.height, + &outputs_count.total_count.sum_cum.sum.0, + |(h, p2tr, total, ..)| { + let ratio = if *total > 0 { + StoredF32::from(*p2tr as f64 / *total as f64) + } else { + StoredF32::from(0.0) + }; + (h, ratio) + }, + exit, + )?; + + self.segwit_adoption.height.compute_transform2( + starting_indexes.height, + &self.segwit.last.height, + &outputs_count.total_count.sum_cum.sum.0, + |(h, segwit, total, ..)| { + let ratio = if *total > 0 { + StoredF32::from(*segwit as f64 / *total as f64) + } else { + StoredF32::from(0.0) + }; + (h, ratio) + }, + exit, + )?; Ok(()) } diff --git a/crates/brk_computer/src/scripts/count/import.rs b/crates/brk_computer/src/scripts/count/import.rs index 5e34e1376..7656f3cfb 100644 --- a/crates/brk_computer/src/scripts/count/import.rs +++ b/crates/brk_computer/src/scripts/count/import.rs @@ -1,12 +1,11 @@ use brk_error::Result; use brk_types::Version; -use vecdb::{Database, ReadableCloneableVec}; +use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightFull, LazyBinaryFromHeightFull, PercentageU64F32}, - outputs, + internal::{ComputedFromHeightCumSum, ComputedFromHeightLast}, }; impl Vecs { @@ -14,40 +13,21 @@ impl Vecs { db: &Database, version: Version, indexes: &indexes::Vecs, - outputs: &outputs::Vecs, ) -> Result { - let p2a = ComputedFromHeightFull::forced_import(db, "p2a_count", version, indexes)?; - let p2ms = ComputedFromHeightFull::forced_import(db, "p2ms_count", version, indexes)?; - let p2pk33 = ComputedFromHeightFull::forced_import(db, "p2pk33_count", version, indexes)?; - let p2pk65 = ComputedFromHeightFull::forced_import(db, "p2pk65_count", version, indexes)?; - let p2pkh = ComputedFromHeightFull::forced_import(db, "p2pkh_count", version, indexes)?; - let p2sh = ComputedFromHeightFull::forced_import(db, "p2sh_count", version, indexes)?; - let p2tr = ComputedFromHeightFull::forced_import(db, "p2tr_count", version, indexes)?; - let p2wpkh = ComputedFromHeightFull::forced_import(db, "p2wpkh_count", version, indexes)?; - let p2wsh = ComputedFromHeightFull::forced_import(db, "p2wsh_count", version, indexes)?; - - // Aggregate counts (computed from per-type counts) - let segwit = ComputedFromHeightFull::forced_import(db, "segwit_count", version, indexes)?; - - // Adoption ratios (lazy) - // Uses outputs.count.count as denominator (total output count) - // At height level: per-block ratio; at day1 level: sum-based ratio (% of new outputs) - let taproot_adoption = LazyBinaryFromHeightFull::from_height_and_txindex::( - "taproot_adoption", - version, - p2tr.height.read_only_boxed_clone(), - outputs.count.total_count.height.sum_cum.sum.0.read_only_boxed_clone(), - &p2tr, - &outputs.count.total_count, - ); - let segwit_adoption = LazyBinaryFromHeightFull::from_height_and_txindex::( - "segwit_adoption", - version, - segwit.height.read_only_boxed_clone(), - outputs.count.total_count.height.sum_cum.sum.0.read_only_boxed_clone(), - &segwit, - &outputs.count.total_count, - ); + let p2a = ComputedFromHeightCumSum::forced_import(db, "p2a_count", version, indexes)?; + let p2ms = ComputedFromHeightCumSum::forced_import(db, "p2ms_count", version, indexes)?; + let p2pk33 = + ComputedFromHeightCumSum::forced_import(db, "p2pk33_count", version, indexes)?; + let p2pk65 = + ComputedFromHeightCumSum::forced_import(db, "p2pk65_count", version, indexes)?; + let p2pkh = ComputedFromHeightCumSum::forced_import(db, "p2pkh_count", version, indexes)?; + let p2sh = ComputedFromHeightCumSum::forced_import(db, "p2sh_count", version, indexes)?; + let p2tr = ComputedFromHeightCumSum::forced_import(db, "p2tr_count", version, indexes)?; + let p2wpkh = + ComputedFromHeightCumSum::forced_import(db, "p2wpkh_count", version, indexes)?; + let p2wsh = ComputedFromHeightCumSum::forced_import(db, "p2wsh_count", version, indexes)?; + let segwit = + ComputedFromHeightCumSum::forced_import(db, "segwit_count", version, indexes)?; Ok(Self { p2a, @@ -59,22 +39,37 @@ impl Vecs { p2tr, p2wpkh, p2wsh, - opreturn: ComputedFromHeightFull::forced_import(db, "opreturn_count", version, indexes)?, - emptyoutput: ComputedFromHeightFull::forced_import( + opreturn: ComputedFromHeightCumSum::forced_import( + db, + "opreturn_count", + version, + indexes, + )?, + emptyoutput: ComputedFromHeightCumSum::forced_import( db, "emptyoutput_count", version, indexes, )?, - unknownoutput: ComputedFromHeightFull::forced_import( + unknownoutput: ComputedFromHeightCumSum::forced_import( db, "unknownoutput_count", version, indexes, )?, segwit, - taproot_adoption, - segwit_adoption, + taproot_adoption: ComputedFromHeightLast::forced_import( + db, + "taproot_adoption", + version, + indexes, + )?, + segwit_adoption: ComputedFromHeightLast::forced_import( + db, + "segwit_adoption", + version, + indexes, + )?, }) } } diff --git a/crates/brk_computer/src/scripts/count/vecs.rs b/crates/brk_computer/src/scripts/count/vecs.rs index 173f2f309..88a6a14e9 100644 --- a/crates/brk_computer/src/scripts/count/vecs.rs +++ b/crates/brk_computer/src/scripts/count/vecs.rs @@ -2,29 +2,29 @@ use brk_traversable::Traversable; use brk_types::{StoredF32, StoredU64}; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightFull, LazyBinaryFromHeightFull}; +use crate::internal::{ComputedFromHeightCumSum, ComputedFromHeightLast}; #[derive(Traversable)] pub struct Vecs { // Per-type output counts - pub p2a: ComputedFromHeightFull, - pub p2ms: ComputedFromHeightFull, - pub p2pk33: ComputedFromHeightFull, - pub p2pk65: ComputedFromHeightFull, - pub p2pkh: ComputedFromHeightFull, - pub p2sh: ComputedFromHeightFull, - pub p2tr: ComputedFromHeightFull, - pub p2wpkh: ComputedFromHeightFull, - pub p2wsh: ComputedFromHeightFull, - pub opreturn: ComputedFromHeightFull, - pub emptyoutput: ComputedFromHeightFull, - pub unknownoutput: ComputedFromHeightFull, + pub p2a: ComputedFromHeightCumSum, + pub p2ms: ComputedFromHeightCumSum, + pub p2pk33: ComputedFromHeightCumSum, + pub p2pk65: ComputedFromHeightCumSum, + pub p2pkh: ComputedFromHeightCumSum, + pub p2sh: ComputedFromHeightCumSum, + pub p2tr: ComputedFromHeightCumSum, + pub p2wpkh: ComputedFromHeightCumSum, + pub p2wsh: ComputedFromHeightCumSum, + pub opreturn: ComputedFromHeightCumSum, + pub emptyoutput: ComputedFromHeightCumSum, + pub unknownoutput: ComputedFromHeightCumSum, // Aggregate counts /// SegWit output count (p2wpkh + p2wsh + p2tr) - pub segwit: ComputedFromHeightFull, + pub segwit: ComputedFromHeightCumSum, - // Adoption ratios - pub taproot_adoption: LazyBinaryFromHeightFull, - pub segwit_adoption: LazyBinaryFromHeightFull, + // Adoption ratios (stored per-block, lazy period views) + pub taproot_adoption: ComputedFromHeightLast, + pub segwit_adoption: ComputedFromHeightLast, } diff --git a/crates/brk_computer/src/scripts/import.rs b/crates/brk_computer/src/scripts/import.rs index 31f391ffd..4e4196ab9 100644 --- a/crates/brk_computer/src/scripts/import.rs +++ b/crates/brk_computer/src/scripts/import.rs @@ -5,7 +5,7 @@ use brk_traversable::Traversable; use brk_types::Version; use vecdb::{Database, PAGE_SIZE}; -use crate::{indexes, outputs, prices}; +use crate::{indexes, prices}; use super::{CountVecs, ValueVecs, Vecs}; @@ -15,14 +15,13 @@ impl Vecs { parent_version: Version, indexes: &indexes::Vecs, prices: &prices::Vecs, - outputs: &outputs::Vecs, ) -> Result { let db = Database::open(&parent_path.join(super::DB_NAME))?; db.set_min_len(PAGE_SIZE * 50_000_000)?; let version = parent_version; - let count = CountVecs::forced_import(&db, version, indexes, outputs)?; + let count = CountVecs::forced_import(&db, version, indexes)?; let value = ValueVecs::forced_import(&db, version, indexes, prices)?; let this = Self { db, count, value }; diff --git a/crates/brk_computer/src/traits/mod.rs b/crates/brk_computer/src/traits/mod.rs index 20e67865e..c38d23cf0 100644 --- a/crates/brk_computer/src/traits/mod.rs +++ b/crates/brk_computer/src/traits/mod.rs @@ -280,6 +280,133 @@ where } } +/// Compute rolling percentiles (p10, p25, median, p75, p90) in a single pass. +/// +/// Maintains one sorted vec per window, extracting all 5 percentiles at each position. +/// Much faster than 5 separate passes since sorted vec maintenance is the bottleneck. +#[allow(clippy::too_many_arguments)] +pub fn compute_rolling_percentiles_from_starts( + max_from: I, + window_starts: &impl ReadableVec, + values: &impl ReadableVec, + p10_out: &mut EagerVec>, + p25_out: &mut EagerVec>, + median_out: &mut EagerVec>, + p75_out: &mut EagerVec>, + p90_out: &mut EagerVec>, + _exit: &Exit, +) -> Result<()> +where + I: VecIndex, + T: PcoVecValue + From, + A: VecValue + Copy, + f64: From, +{ + let version = window_starts.version() + values.version(); + p10_out.validate_computed_version_or_reset(version)?; + p25_out.validate_computed_version_or_reset(version)?; + median_out.validate_computed_version_or_reset(version)?; + p75_out.validate_computed_version_or_reset(version)?; + p90_out.validate_computed_version_or_reset(version)?; + + p10_out.truncate_if_needed(max_from)?; + p25_out.truncate_if_needed(max_from)?; + median_out.truncate_if_needed(max_from)?; + p75_out.truncate_if_needed(max_from)?; + p90_out.truncate_if_needed(max_from)?; + + // All 5 vecs should be at the same length; use min to be safe + let skip = p10_out + .len() + .min(p25_out.len()) + .min(median_out.len()) + .min(p75_out.len()) + .min(p90_out.len()); + + let end = window_starts.len().min(values.len()); + if skip >= end { + return Ok(()); + } + + let range_start = if skip > 0 { + window_starts.collect_one_at(skip - 1).unwrap().to_usize() + } else { + 0 + }; + let partial_values: Vec = values.collect_range_at(range_start, end); + + let mut sorted: Vec = Vec::new(); + let mut prev_start_usize: usize = range_start; + + // Reconstruct sorted state from historical data + if skip > 0 { + (range_start..skip).for_each(|idx| { + let v = f64::from(partial_values[idx - range_start]); + let pos = sorted + .binary_search_by(|a| a.partial_cmp(&v).unwrap_or(std::cmp::Ordering::Equal)) + .unwrap_or_else(|x| x); + sorted.insert(pos, v); + }); + } + + let starts_batch = window_starts.collect_range_at(skip, end); + + for (j, start) in starts_batch.into_iter().enumerate() { + let i = skip + j; + let v = f64::from(partial_values[i - range_start]); + let pos = sorted + .binary_search_by(|a| a.partial_cmp(&v).unwrap_or(std::cmp::Ordering::Equal)) + .unwrap_or_else(|x| x); + sorted.insert(pos, v); + + let start_usize = start.to_usize(); + while prev_start_usize < start_usize { + let old = f64::from(partial_values[prev_start_usize - range_start]); + if let Ok(pos) = sorted + .binary_search_by(|a| a.partial_cmp(&old).unwrap_or(std::cmp::Ordering::Equal)) + { + sorted.remove(pos); + } + prev_start_usize += 1; + } + + let len = sorted.len(); + if len == 0 { + let zero = T::from(0.0); + p10_out.checked_push_at(i, zero)?; + p25_out.checked_push_at(i, zero)?; + median_out.checked_push_at(i, zero)?; + p75_out.checked_push_at(i, zero)?; + p90_out.checked_push_at(i, zero)?; + } else { + p10_out.checked_push_at(i, T::from(percentile_of_sorted(&sorted, 0.10)))?; + p25_out.checked_push_at(i, T::from(percentile_of_sorted(&sorted, 0.25)))?; + median_out.checked_push_at(i, T::from(percentile_of_sorted(&sorted, 0.50)))?; + p75_out.checked_push_at(i, T::from(percentile_of_sorted(&sorted, 0.75)))?; + p90_out.checked_push_at(i, T::from(percentile_of_sorted(&sorted, 0.90)))?; + } + } + + Ok(()) +} + +/// Extract a percentile (0.0-1.0) from a sorted slice using linear interpolation. +fn percentile_of_sorted(sorted: &[f64], p: f64) -> f64 { + let len = sorted.len(); + if len == 1 { + return sorted[0]; + } + let rank = p * (len - 1) as f64; + let lo = rank.floor() as usize; + let hi = rank.ceil() as usize; + if lo == hi { + sorted[lo] + } else { + let frac = rank - lo as f64; + sorted[lo] * (1.0 - frac) + sorted[hi] * frac + } +} + pub trait ComputeDrawdown { fn compute_drawdown( &mut self, diff --git a/crates/brk_computer/src/transactions/compute.rs b/crates/brk_computer/src/transactions/compute.rs index 8eafec644..25743bc98 100644 --- a/crates/brk_computer/src/transactions/compute.rs +++ b/crates/brk_computer/src/transactions/compute.rs @@ -2,7 +2,7 @@ use brk_error::Result; use brk_indexer::Indexer; use vecdb::Exit; -use crate::{blocks, indexes, inputs, outputs, ComputeIndexes}; +use crate::{blocks, indexes, inputs, outputs, prices, ComputeIndexes}; use super::Vecs; @@ -15,12 +15,13 @@ impl Vecs { blocks: &blocks::Vecs, inputs: &inputs::Vecs, outputs: &outputs::Vecs, + prices: &prices::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { // Count computes first self.count - .compute(indexer, starting_indexes, exit)?; + .compute(indexer, &blocks.count, starting_indexes, exit)?; // Versions depends on count self.versions @@ -30,12 +31,14 @@ impl Vecs { self.size .compute(indexer, indexes, starting_indexes, exit)?; - // Fees depends on size + // Fees depends on size, blocks (window starts), prices (USD conversion) self.fees.compute( indexer, indexes, inputs, &self.size, + blocks, + prices, starting_indexes, exit, )?; diff --git a/crates/brk_computer/src/transactions/count/compute.rs b/crates/brk_computer/src/transactions/count/compute.rs index 6190a689c..8443e36d0 100644 --- a/crates/brk_computer/src/transactions/count/compute.rs +++ b/crates/brk_computer/src/transactions/count/compute.rs @@ -3,25 +3,30 @@ use brk_indexer::Indexer; use vecdb::Exit; use super::Vecs; -use crate::ComputeIndexes; +use crate::{blocks, ComputeIndexes}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, + count_vecs: &blocks::CountVecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.tx_count - .compute(starting_indexes, exit, |v| { - v.compute_count_from_indexes( + let window_starts = count_vecs.window_starts(); + self.tx_count.compute( + starting_indexes.height, + &window_starts, + exit, + |height| { + Ok(height.compute_count_from_indexes( starting_indexes.height, &indexer.vecs.transactions.first_txindex, &indexer.vecs.transactions.txid, exit, - )?; - Ok(()) - })?; + )?) + }, + )?; Ok(()) } diff --git a/crates/brk_computer/src/transactions/count/import.rs b/crates/brk_computer/src/transactions/count/import.rs index 3261aa30f..c9c1aa3c1 100644 --- a/crates/brk_computer/src/transactions/count/import.rs +++ b/crates/brk_computer/src/transactions/count/import.rs @@ -1,10 +1,10 @@ use brk_error::Result; use brk_indexer::Indexer; use brk_types::{StoredBool, TxIndex, Version}; -use vecdb::{Database, ReadableCloneableVec, LazyVecFrom2}; +use vecdb::{Database, LazyVecFrom2, ReadableCloneableVec}; use super::Vecs; -use crate::{indexes, internal::ComputedFromHeightFull}; +use crate::{indexes, internal::ComputedFromHeightCumFull}; impl Vecs { pub(crate) fn forced_import( @@ -24,7 +24,9 @@ impl Vecs { ); Ok(Self { - tx_count: ComputedFromHeightFull::forced_import(db, "tx_count", version, indexes)?, + tx_count: ComputedFromHeightCumFull::forced_import( + db, "tx_count", version, indexes, + )?, is_coinbase: txindex_to_is_coinbase, }) } diff --git a/crates/brk_computer/src/transactions/count/vecs.rs b/crates/brk_computer/src/transactions/count/vecs.rs index d1291f7cc..185734e95 100644 --- a/crates/brk_computer/src/transactions/count/vecs.rs +++ b/crates/brk_computer/src/transactions/count/vecs.rs @@ -2,10 +2,10 @@ use brk_traversable::Traversable; use brk_types::{Height, StoredBool, StoredU64, TxIndex}; use vecdb::{LazyVecFrom2, Rw, StorageMode}; -use crate::internal::ComputedFromHeightFull; +use crate::internal::ComputedFromHeightCumFull; #[derive(Traversable)] pub struct Vecs { - pub tx_count: ComputedFromHeightFull, + pub tx_count: ComputedFromHeightCumFull, pub is_coinbase: LazyVecFrom2, } diff --git a/crates/brk_computer/src/transactions/fees/compute.rs b/crates/brk_computer/src/transactions/fees/compute.rs index 7998ae3d7..ea5411c0f 100644 --- a/crates/brk_computer/src/transactions/fees/compute.rs +++ b/crates/brk_computer/src/transactions/fees/compute.rs @@ -1,11 +1,11 @@ use brk_error::Result; use brk_indexer::Indexer; -use brk_types::{FeeRate, Sats}; +use brk_types::{Bitcoin, FeeRate, Sats}; use vecdb::{Exit, unlikely}; use super::super::size; use super::Vecs; -use crate::{ComputeIndexes, indexes, inputs}; +use crate::{blocks, indexes, inputs, prices, ComputeIndexes}; impl Vecs { #[allow(clippy::too_many_arguments)] @@ -15,6 +15,8 @@ impl Vecs { indexes: &indexes::Vecs, txins: &inputs::Vecs, size_vecs: &size::Vecs, + blocks: &blocks::Vecs, + prices: &prices::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { @@ -34,7 +36,7 @@ impl Vecs { exit, )?; - self.fee.base.compute_transform2( + self.fee_txindex.compute_transform2( starting_indexes.txindex, &self.input_value, &self.output_value, @@ -49,21 +51,59 @@ impl Vecs { exit, )?; - self.fee_rate.txindex.compute_transform2( + self.fee_rate_txindex.compute_transform2( starting_indexes.txindex, - &self.fee.base, + &self.fee_txindex, &size_vecs.vsize.txindex, |(txindex, fee, vsize, ..)| (txindex, FeeRate::from((fee, vsize))), exit, )?; // Skip coinbase (first tx per block) since it has no fee - self.fee - .derive_from_with_skip(indexer, indexes, starting_indexes, exit, 1)?; + self.fee.compute_with_skip( + starting_indexes.height, + &self.fee_txindex, + &indexer.vecs.transactions.first_txindex, + &indexes.height.txindex_count, + exit, + 1, + )?; // Skip coinbase (first tx per block) since it has no feerate - self.fee_rate - .derive_from_with_skip(indexer, indexes, starting_indexes, exit, 1)?; + self.fee_rate.compute_with_skip( + starting_indexes.height, + &self.fee_rate_txindex, + &indexer.vecs.transactions.first_txindex, + &indexes.height.txindex_count, + exit, + 1, + )?; + + // Compute fee USD sum per block: price * Bitcoin::from(sats) + self.fee_usd_sum.compute_transform2( + starting_indexes.height, + self.fee.sum_cum.sum.inner(), + &prices.usd.price, + |(h, sats, price, ..)| (h, price * Bitcoin::from(sats)), + exit, + )?; + + // Rolling fee stats (from per-block sum) + let window_starts = blocks.count.window_starts(); + self.fee_rolling.compute( + starting_indexes.height, + &window_starts, + self.fee.sum_cum.sum.inner(), + exit, + )?; + + // Rolling fee rate distribution (from per-block average) + self.fee_rate_rolling.compute_distribution( + starting_indexes.height, + &window_starts, + &self.fee_rate.min_max_average.average.0, + exit, + )?; Ok(()) } diff --git a/crates/brk_computer/src/transactions/fees/import.rs b/crates/brk_computer/src/transactions/fees/import.rs index 75aebddbe..c9e93b35c 100644 --- a/crates/brk_computer/src/transactions/fees/import.rs +++ b/crates/brk_computer/src/transactions/fees/import.rs @@ -1,32 +1,33 @@ use brk_error::Result; -use brk_indexer::Indexer; use brk_types::Version; use vecdb::{Database, EagerVec, ImportableVec}; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromTxDistribution, ValueFromTxFull}, - prices, + internal::{Distribution, Full, RollingDistribution, RollingFull}, }; /// Bump this when fee/feerate aggregation logic changes (e.g., skip coinbase). -const VERSION: Version = Version::ONE; +const VERSION: Version = Version::new(2); impl Vecs { pub(crate) fn forced_import( db: &Database, version: Version, - indexer: &Indexer, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { let v = version + VERSION; Ok(Self { input_value: EagerVec::forced_import(db, "input_value", version)?, output_value: EagerVec::forced_import(db, "output_value", version)?, - fee: ValueFromTxFull::forced_import(db, "fee", v, indexes, indexer, prices)?, - fee_rate: ComputedFromTxDistribution::forced_import(db, "fee_rate", v, indexes)?, + fee_txindex: EagerVec::forced_import(db, "fee", v)?, + fee: Full::forced_import(db, "fee", v)?, + fee_usd_sum: EagerVec::forced_import(db, "fee_usd_sum", v)?, + fee_rolling: RollingFull::forced_import(db, "fee", v, indexes)?, + fee_rate_txindex: EagerVec::forced_import(db, "fee_rate", v)?, + fee_rate: Distribution::forced_import(db, "fee_rate", v)?, + fee_rate_rolling: RollingDistribution::forced_import(db, "fee_rate", v, indexes)?, }) } } diff --git a/crates/brk_computer/src/transactions/fees/vecs.rs b/crates/brk_computer/src/transactions/fees/vecs.rs index a4a22315c..687c166a5 100644 --- a/crates/brk_computer/src/transactions/fees/vecs.rs +++ b/crates/brk_computer/src/transactions/fees/vecs.rs @@ -1,13 +1,18 @@ use brk_traversable::Traversable; -use brk_types::{FeeRate, Sats, TxIndex}; +use brk_types::{Dollars, FeeRate, Height, Sats, TxIndex}; use vecdb::{EagerVec, PcoVec, Rw, StorageMode}; -use crate::internal::{ComputedFromTxDistribution, ValueFromTxFull}; +use crate::internal::{Distribution, Full, RollingDistribution, RollingFull}; #[derive(Traversable)] pub struct Vecs { pub input_value: M::Stored>>, pub output_value: M::Stored>>, - pub fee: ValueFromTxFull, - pub fee_rate: ComputedFromTxDistribution, + pub fee_txindex: M::Stored>>, + pub fee: Full, + pub fee_usd_sum: M::Stored>>, + pub fee_rolling: RollingFull, + pub fee_rate_txindex: M::Stored>>, + pub fee_rate: Distribution, + pub fee_rate_rolling: RollingDistribution, } diff --git a/crates/brk_computer/src/transactions/import.rs b/crates/brk_computer/src/transactions/import.rs index 5e7478000..511fcaa38 100644 --- a/crates/brk_computer/src/transactions/import.rs +++ b/crates/brk_computer/src/transactions/import.rs @@ -25,7 +25,7 @@ impl Vecs { let count = CountVecs::forced_import(&db, version, indexer, indexes)?; let size = SizeVecs::forced_import(&db, version, indexer, indexes)?; - let fees = FeesVecs::forced_import(&db, version, indexer, indexes, prices)?; + let fees = FeesVecs::forced_import(&db, version, indexes)?; let versions = VersionsVecs::forced_import(&db, version, indexes)?; let volume = VolumeVecs::forced_import(&db, version, indexes, prices)?; diff --git a/crates/brk_computer/src/transactions/volume/compute.rs b/crates/brk_computer/src/transactions/volume/compute.rs index c0ac6552d..ee86419c2 100644 --- a/crates/brk_computer/src/transactions/volume/compute.rs +++ b/crates/brk_computer/src/transactions/volume/compute.rs @@ -46,6 +46,23 @@ impl Vecs { exit, )?; + // Rolling sums for sent and received + let window_starts = blocks.count.window_starts(); + self.sent_sum_rolling.compute_rolling_sum( + starting_indexes.height, + &window_starts, + &self.sent_sum.sats.height, + &self.sent_sum.usd.height, + exit, + )?; + self.received_sum_rolling.compute_rolling_sum( + starting_indexes.height, + &window_starts, + &self.received_sum.sats.height, + &self.received_sum.usd.height, + exit, + )?; + // tx_per_sec: per-block tx count / block interval self.tx_per_sec.height.compute_transform2( starting_indexes.height, @@ -83,7 +100,7 @@ impl Vecs { // outputs_per_sec: per-block output count / block interval self.outputs_per_sec.height.compute_transform2( starting_indexes.height, - &outputs_count.total_count.height.sum_cum.sum.0, + &outputs_count.total_count.sum_cum.sum.0, &blocks.interval.interval.height, |(h, output_count, interval, ..)| { let interval_f64 = f64::from(*interval); diff --git a/crates/brk_computer/src/transactions/volume/import.rs b/crates/brk_computer/src/transactions/volume/import.rs index c2d93ab99..91bb05f8b 100644 --- a/crates/brk_computer/src/transactions/volume/import.rs +++ b/crates/brk_computer/src/transactions/volume/import.rs @@ -5,7 +5,7 @@ use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightLast, ValueFromHeightLast, ValueFromHeightSum}, + internal::{ComputedFromHeightLast, StoredValueRollingWindows, ValueFromHeightLast}, prices, }; @@ -18,13 +18,17 @@ impl Vecs { ) -> Result { let v2 = Version::TWO; Ok(Self { - sent_sum: ValueFromHeightSum::forced_import(db, "sent_sum", version, indexes, prices)?, - received_sum: ValueFromHeightSum::forced_import( - db, - "received_sum", - version, - indexes, - prices, + sent_sum: ValueFromHeightLast::forced_import( + db, "sent_sum", version, indexes, prices, + )?, + sent_sum_rolling: StoredValueRollingWindows::forced_import( + db, "sent_sum", version, indexes, + )?, + received_sum: ValueFromHeightLast::forced_import( + db, "received_sum", version, indexes, prices, + )?, + received_sum_rolling: StoredValueRollingWindows::forced_import( + db, "received_sum", version, indexes, )?, annualized_volume: ValueFromHeightLast::forced_import( db, diff --git a/crates/brk_computer/src/transactions/volume/vecs.rs b/crates/brk_computer/src/transactions/volume/vecs.rs index 54a8240e2..37e1c5e45 100644 --- a/crates/brk_computer/src/transactions/volume/vecs.rs +++ b/crates/brk_computer/src/transactions/volume/vecs.rs @@ -2,13 +2,20 @@ use brk_traversable::Traversable; use brk_types::StoredF32; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightLast, ValueFromHeightLast, ValueFromHeightSum}; +use crate::internal::{ + ComputedFromHeightLast, StoredValueRollingWindows, ValueFromHeightLast, +}; /// Volume metrics #[derive(Traversable)] pub struct Vecs { - pub sent_sum: ValueFromHeightSum, - pub received_sum: ValueFromHeightSum, + #[traversable(flatten)] + pub sent_sum: ValueFromHeightLast, + pub sent_sum_rolling: StoredValueRollingWindows, + #[traversable(flatten)] + pub received_sum: ValueFromHeightLast, + pub received_sum_rolling: StoredValueRollingWindows, + #[traversable(flatten)] pub annualized_volume: ValueFromHeightLast, pub tx_per_sec: ComputedFromHeightLast, pub outputs_per_sec: ComputedFromHeightLast, diff --git a/crates/brk_query/src/impl/cost_basis.rs b/crates/brk_query/src/impl/cost_basis.rs index e833759d6..058b71f7f 100644 --- a/crates/brk_query/src/impl/cost_basis.rs +++ b/crates/brk_query/src/impl/cost_basis.rs @@ -85,7 +85,6 @@ impl Query { let price = &self.computer().prices; let spot = price .cents - .split .close .day1 .collect_one(day1) diff --git a/crates/brk_query/src/impl/mining/block_fees.rs b/crates/brk_query/src/impl/mining/block_fees.rs index 7f1621f6e..09ad2de2c 100644 --- a/crates/brk_query/src/impl/mining/block_fees.rs +++ b/crates/brk_query/src/impl/mining/block_fees.rs @@ -1,5 +1,5 @@ use brk_error::Result; -use brk_types::{BlockFeesEntry, TimePeriod}; +use brk_types::{BlockFeesEntry, Height, Sats, TimePeriod}; use vecdb::{ReadableVec, VecIndex}; use super::day1_iter::Day1Iter; @@ -15,19 +15,32 @@ impl Query { let iter = Day1Iter::new(computer, start, current_height.to_usize()); - let fees_vec = &computer - .transactions - .fees - .fee - .sats - .day1 - .average; + let cumulative = &computer.transactions.fees.fee.sum_cum.cumulative; + let first_height = &computer.indexes.day1.first_height; Ok(iter.collect(|di, ts, h| { - fees_vec.collect_one(di).map(|fee| BlockFeesEntry { + let h_start = first_height.collect_one(di)?; + let h_end = first_height + .collect_one(di + 1_usize) + .unwrap_or(Height::from(current_height.to_usize() + 1)); + let block_count = h_end.to_usize() - h_start.to_usize(); + if block_count == 0 { + return None; + } + + let cum_end = cumulative.collect_one_at(h_end.to_usize() - 1)?; + let cum_start = if h_start.to_usize() > 0 { + cumulative.collect_one_at(h_start.to_usize() - 1).unwrap_or(Sats::ZERO) + } else { + Sats::ZERO + }; + let daily_sum = cum_end - cum_start; + let avg_fees = Sats::from(*daily_sum / block_count as u64); + + Some(BlockFeesEntry { avg_height: h, timestamp: ts, - avg_fees: fee, + avg_fees, }) })) } diff --git a/crates/brk_query/src/impl/mining/block_sizes.rs b/crates/brk_query/src/impl/mining/block_sizes.rs index 501d6e668..8ad1eb1be 100644 --- a/crates/brk_query/src/impl/mining/block_sizes.rs +++ b/crates/brk_query/src/impl/mining/block_sizes.rs @@ -15,28 +15,35 @@ impl Query { let iter = Day1Iter::new(computer, start, current_height.to_usize()); + // Rolling 24h average, sampled at day1 boundaries let sizes_vec = &computer .blocks .size .size - .day1 - .average; + .rolling + .distribution + .average + ._24h + .day1; let weights_vec = &computer .blocks .weight .weight - .day1 - .average; + .rolling + .distribution + .average + ._24h + .day1; let entries: Vec<_> = iter.collect(|di, ts, h| { - let size = sizes_vec.collect_one(di).map(|s| *s); - let weight = weights_vec.collect_one(di).map(|w| *w); - Some((h.into(), (*ts), size, weight)) + let size: Option = sizes_vec.collect_one(di).map(|s| *s); + let weight: Option = weights_vec.collect_one(di).map(|w| *w); + Some((u32::from(h), (*ts), size, weight)) }); let sizes = entries .iter() - .filter_map(|(h, ts, size, _): &(u32, _, _, _)| { + .filter_map(|(h, ts, size, _)| { size.map(|s| BlockSizeEntry { avg_height: *h, timestamp: *ts, @@ -47,7 +54,7 @@ impl Query { let weights = entries .iter() - .filter_map(|(h, ts, _, weight): &(u32, _, _, _)| { + .filter_map(|(h, ts, _, weight)| { weight.map(|w| BlockWeightEntry { avg_height: *h, timestamp: *ts, diff --git a/crates/brk_query/src/impl/mining/reward_stats.rs b/crates/brk_query/src/impl/mining/reward_stats.rs index 967bde264..2f139a8af 100644 --- a/crates/brk_query/src/impl/mining/reward_stats.rs +++ b/crates/brk_query/src/impl/mining/reward_stats.rs @@ -13,7 +13,7 @@ impl Query { let start_block = Height::from(current_height.to_usize().saturating_sub(block_count - 1)); let coinbase_vec = &computer.mining.rewards.coinbase.sats.height; - let fee_vec = &computer.transactions.fees.fee.sats.height.sum_cum.sum.0; + let fee_vec = &computer.transactions.fees.fee.sum_cum.sum.0; let tx_count_vec = &computer.transactions.count.tx_count.height; let start = start_block.to_usize(); diff --git a/crates/brk_types/src/stored_f64.rs b/crates/brk_types/src/stored_f64.rs index dda9104be..7c5d86cef 100644 --- a/crates/brk_types/src/stored_f64.rs +++ b/crates/brk_types/src/stored_f64.rs @@ -2,7 +2,7 @@ use std::{ cmp::Ordering, f64, iter::Sum, - ops::{Add, AddAssign, Div, Mul, Sub}, + ops::{Add, AddAssign, Div, Mul, Sub, SubAssign}, }; use derive_more::Deref; @@ -136,6 +136,12 @@ impl AddAssign for StoredF64 { } } +impl SubAssign for StoredF64 { + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs + } +} + impl From for f64 { #[inline] fn from(value: StoredF64) -> Self { diff --git a/crates/brk_types/src/stored_u64.rs b/crates/brk_types/src/stored_u64.rs index c94cdbd94..7523d4420 100644 --- a/crates/brk_types/src/stored_u64.rs +++ b/crates/brk_types/src/stored_u64.rs @@ -1,4 +1,4 @@ -use std::ops::{Add, AddAssign, Div}; +use std::ops::{Add, AddAssign, Div, Sub, SubAssign}; use derive_more::Deref; use schemars::JsonSchema; @@ -92,6 +92,19 @@ impl AddAssign for StoredU64 { } } +impl Sub for StoredU64 { + type Output = Self; + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } +} + +impl SubAssign for StoredU64 { + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs + } +} + impl From for StoredU64 { #[inline] fn from(value: f64) -> Self { diff --git a/crates/brk_types/src/weight.rs b/crates/brk_types/src/weight.rs index f13c99666..53b6cb5cf 100644 --- a/crates/brk_types/src/weight.rs +++ b/crates/brk_types/src/weight.rs @@ -1,4 +1,4 @@ -use std::ops::{Add, AddAssign, Div}; +use std::ops::{Add, AddAssign, Div, Sub, SubAssign}; use derive_more::Deref; use schemars::JsonSchema; @@ -8,6 +8,7 @@ use vecdb::{Formattable, Pco}; /// Transaction or block weight in weight units (WU) #[derive( Debug, + Default, Deref, Clone, Copy, @@ -99,6 +100,19 @@ impl AddAssign for Weight { } } +impl Sub for Weight { + type Output = Self; + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } +} + +impl SubAssign for Weight { + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs + } +} + impl Div for Weight { type Output = Self; fn div(self, rhs: usize) -> Self::Output {