diff --git a/crates/brk_client/examples/basic.rs b/crates/brk_client/examples/basic.rs index 3cd1c6383..25441f997 100644 --- a/crates/brk_client/examples/basic.rs +++ b/crates/brk_client/examples/basic.rs @@ -42,6 +42,7 @@ fn main() -> brk_client::Result<()> { .count .block_count .sum + ._24h .by .day1() .last(3) diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index c872c2efd..d0f7a7d96 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -2193,7 +2193,7 @@ impl GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern { /// Pattern struct for repeated tree structure. pub struct BlocksCoinbaseDaysDominanceFeeSubsidyPattern { - pub blocks_mined: CumulativeHeightRollingPattern, + pub blocks_mined: CumulativeHeightSumPattern, pub blocks_mined_1m_sum: MetricPattern1, pub blocks_mined_1w_sum: MetricPattern1, pub blocks_mined_1y_sum: MetricPattern1, @@ -2214,7 +2214,7 @@ impl BlocksCoinbaseDaysDominanceFeeSubsidyPattern { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { - blocks_mined: CumulativeHeightRollingPattern::new(client.clone(), _m(&acc, "blocks_mined")), + blocks_mined: CumulativeHeightSumPattern::new(client.clone(), _m(&acc, "blocks_mined")), blocks_mined_1m_sum: MetricPattern1::new(client.clone(), _m(&acc, "blocks_mined_1m_sum")), blocks_mined_1w_sum: MetricPattern1::new(client.clone(), _m(&acc, "blocks_mined_1w_sum")), blocks_mined_1y_sum: MetricPattern1::new(client.clone(), _m(&acc, "blocks_mined_1y_sum")), @@ -2817,8 +2817,8 @@ impl BalanceBothReactivatedReceivingSendingPattern { /// Pattern struct for repeated tree structure. pub struct CoinblocksCoindaysSatblocksSatdaysSentPattern { - pub coinblocks_destroyed: CumulativeHeightRollingPattern, - pub coindays_destroyed: CumulativeHeightRollingPattern, + pub coinblocks_destroyed: CumulativeHeightSumPattern, + pub coindays_destroyed: CumulativeHeightSumPattern, pub satblocks_destroyed: MetricPattern20, pub satdays_destroyed: MetricPattern20, pub sent: BtcSatsUsdPattern2, @@ -2829,8 +2829,8 @@ impl CoinblocksCoindaysSatblocksSatdaysSentPattern { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { - coinblocks_destroyed: CumulativeHeightRollingPattern::new(client.clone(), _m(&acc, "coinblocks_destroyed")), - coindays_destroyed: CumulativeHeightRollingPattern::new(client.clone(), _m(&acc, "coindays_destroyed")), + coinblocks_destroyed: CumulativeHeightSumPattern::new(client.clone(), _m(&acc, "coinblocks_destroyed")), + coindays_destroyed: CumulativeHeightSumPattern::new(client.clone(), _m(&acc, "coindays_destroyed")), satblocks_destroyed: MetricPattern20::new(client.clone(), _m(&acc, "satblocks_destroyed")), satdays_destroyed: MetricPattern20::new(client.clone(), _m(&acc, "satdays_destroyed")), sent: BtcSatsUsdPattern2::new(client.clone(), _m(&acc, "sent")), @@ -3002,13 +3002,13 @@ impl BtcSatsUsdPattern2 { } /// Pattern struct for repeated tree structure. -pub struct BtcSatsUsdPattern4 { +pub struct BtcSatsUsdPattern3 { pub btc: MetricPattern1, pub sats: CumulativeHeightRollingPattern, pub usd: CumulativeHeightRollingPattern, } -impl BtcSatsUsdPattern4 { +impl BtcSatsUsdPattern3 { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { @@ -3020,19 +3020,19 @@ impl BtcSatsUsdPattern4 { } /// Pattern struct for repeated tree structure. -pub struct BtcSatsUsdPattern3 { +pub struct BtcSatsUsdPattern4 { pub btc: MetricPattern1, - pub sats: CumulativeHeightRollingPattern2, - pub usd: CumulativeHeightRollingPattern2, + pub sats: CumulativeHeightSumPattern, + pub usd: CumulativeHeightSumPattern, } -impl BtcSatsUsdPattern3 { +impl BtcSatsUsdPattern4 { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { btc: MetricPattern1::new(client.clone(), _m(&acc, "btc")), - sats: CumulativeHeightRollingPattern2::new(client.clone(), acc.clone()), - usd: CumulativeHeightRollingPattern2::new(client.clone(), _m(&acc, "usd")), + sats: CumulativeHeightSumPattern::new(client.clone(), acc.clone()), + usd: CumulativeHeightSumPattern::new(client.clone(), _m(&acc, "usd")), } } } @@ -3074,13 +3074,13 @@ impl HistogramLineSignalPattern { } /// Pattern struct for repeated tree structure. -pub struct CumulativeHeightRollingPattern2 { +pub struct CumulativeHeightRollingPattern { pub cumulative: MetricPattern1, pub height: MetricPattern20, pub rolling: AverageMaxMedianMinP10P25P75P90SumPattern, } -impl CumulativeHeightRollingPattern2 { +impl CumulativeHeightRollingPattern { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { @@ -3092,19 +3092,19 @@ impl CumulativeHeightRollingPattern2 { } /// Pattern struct for repeated tree structure. -pub struct CumulativeHeightRollingPattern { +pub struct CumulativeHeightSumPattern { pub cumulative: MetricPattern1, pub height: MetricPattern20, - pub rolling: _1y24h30d7dPattern, + pub sum: _1y24h30d7dPattern, } -impl CumulativeHeightRollingPattern { +impl CumulativeHeightSumPattern { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { cumulative: MetricPattern1::new(client.clone(), _m(&acc, "cumulative")), height: MetricPattern20::new(client.clone(), acc.clone()), - rolling: _1y24h30d7dPattern::new(client.clone(), acc.clone()), + sum: _1y24h30d7dPattern::new(client.clone(), acc.clone()), } } } @@ -3290,7 +3290,7 @@ pub struct MetricsTree_Blocks { pub count: MetricsTree_Blocks_Count, pub interval: AverageHeightMaxMedianMinP10P25P75P90Pattern, pub halving: MetricsTree_Blocks_Halving, - pub vbytes: CumulativeHeightRollingPattern2, + pub vbytes: CumulativeHeightRollingPattern, pub size: AverageCumulativeMaxMedianMinP10P25P75P90SumPattern, pub fullness: AverageHeightMaxMedianMinP10P25P75P90Pattern, } @@ -3306,7 +3306,7 @@ impl MetricsTree_Blocks { count: MetricsTree_Blocks_Count::new(client.clone(), format!("{base_path}_count")), interval: AverageHeightMaxMedianMinP10P25P75P90Pattern::new(client.clone(), "block_interval".to_string()), halving: MetricsTree_Blocks_Halving::new(client.clone(), format!("{base_path}_halving")), - vbytes: CumulativeHeightRollingPattern2::new(client.clone(), "block_vbytes".to_string()), + vbytes: CumulativeHeightRollingPattern::new(client.clone(), "block_vbytes".to_string()), size: AverageCumulativeMaxMedianMinP10P25P75P90SumPattern::new(client.clone(), "block_size".to_string()), fullness: AverageHeightMaxMedianMinP10P25P75P90Pattern::new(client.clone(), "block_fullness".to_string()), } @@ -3436,7 +3436,7 @@ impl MetricsTree_Blocks_Weight { /// Metrics tree node. pub struct MetricsTree_Blocks_Count { pub block_count_target: MetricPattern1, - pub block_count: CumulativeHeightRollingPattern, + pub block_count: CumulativeHeightSumPattern, pub block_count_sum: _1y24h30d7dPattern, pub height_1h_ago: MetricPattern20, pub height_24h_ago: MetricPattern20, @@ -3475,7 +3475,7 @@ impl MetricsTree_Blocks_Count { pub fn new(client: Arc, base_path: String) -> Self { Self { block_count_target: MetricPattern1::new(client.clone(), "block_count_target".to_string()), - block_count: CumulativeHeightRollingPattern::new(client.clone(), "block_count".to_string()), + block_count: CumulativeHeightSumPattern::new(client.clone(), "block_count".to_string()), block_count_sum: _1y24h30d7dPattern::new(client.clone(), "block_count_sum".to_string()), height_1h_ago: MetricPattern20::new(client.clone(), "height_1h_ago".to_string()), height_24h_ago: MetricPattern20::new(client.clone(), "height_24h_ago".to_string()), @@ -3572,14 +3572,14 @@ impl MetricsTree_Transactions { /// Metrics tree node. pub struct MetricsTree_Transactions_Count { - pub tx_count: CumulativeHeightRollingPattern2, + pub tx_count: CumulativeHeightRollingPattern, pub is_coinbase: MetricPattern21, } impl MetricsTree_Transactions_Count { pub fn new(client: Arc, base_path: String) -> Self { Self { - tx_count: CumulativeHeightRollingPattern2::new(client.clone(), "tx_count".to_string()), + tx_count: CumulativeHeightRollingPattern::new(client.clone(), "tx_count".to_string()), is_coinbase: MetricPattern21::new(client.clone(), "is_coinbase".to_string()), } } @@ -3621,17 +3621,17 @@ impl MetricsTree_Transactions_Fees { /// Metrics tree node. pub struct MetricsTree_Transactions_Versions { - pub v1: CumulativeHeightRollingPattern, - pub v2: CumulativeHeightRollingPattern, - pub v3: CumulativeHeightRollingPattern, + pub v1: CumulativeHeightSumPattern, + pub v2: CumulativeHeightSumPattern, + pub v3: CumulativeHeightSumPattern, } impl MetricsTree_Transactions_Versions { pub fn new(client: Arc, base_path: String) -> Self { Self { - v1: CumulativeHeightRollingPattern::new(client.clone(), "tx_v1".to_string()), - v2: CumulativeHeightRollingPattern::new(client.clone(), "tx_v2".to_string()), - v3: CumulativeHeightRollingPattern::new(client.clone(), "tx_v3".to_string()), + v1: CumulativeHeightSumPattern::new(client.clone(), "tx_v1".to_string()), + v2: CumulativeHeightSumPattern::new(client.clone(), "tx_v2".to_string()), + v3: CumulativeHeightSumPattern::new(client.clone(), "tx_v3".to_string()), } } } @@ -3828,19 +3828,19 @@ impl MetricsTree_Scripts { /// Metrics tree node. pub struct MetricsTree_Scripts_Count { - pub p2a: CumulativeHeightRollingPattern, - pub p2ms: CumulativeHeightRollingPattern, - pub p2pk33: CumulativeHeightRollingPattern, - pub p2pk65: CumulativeHeightRollingPattern, - pub p2pkh: CumulativeHeightRollingPattern, - pub p2sh: CumulativeHeightRollingPattern, - pub p2tr: CumulativeHeightRollingPattern, - pub p2wpkh: CumulativeHeightRollingPattern, - pub p2wsh: CumulativeHeightRollingPattern, - pub opreturn: CumulativeHeightRollingPattern, - pub emptyoutput: CumulativeHeightRollingPattern, - pub unknownoutput: CumulativeHeightRollingPattern, - pub segwit: CumulativeHeightRollingPattern, + pub p2a: CumulativeHeightSumPattern, + pub p2ms: CumulativeHeightSumPattern, + pub p2pk33: CumulativeHeightSumPattern, + pub p2pk65: CumulativeHeightSumPattern, + pub p2pkh: CumulativeHeightSumPattern, + pub p2sh: CumulativeHeightSumPattern, + pub p2tr: CumulativeHeightSumPattern, + pub p2wpkh: CumulativeHeightSumPattern, + pub p2wsh: CumulativeHeightSumPattern, + pub opreturn: CumulativeHeightSumPattern, + pub emptyoutput: CumulativeHeightSumPattern, + pub unknownoutput: CumulativeHeightSumPattern, + pub segwit: CumulativeHeightSumPattern, pub taproot_adoption: MetricPattern1, pub segwit_adoption: MetricPattern1, } @@ -3848,19 +3848,19 @@ pub struct MetricsTree_Scripts_Count { impl MetricsTree_Scripts_Count { pub fn new(client: Arc, base_path: String) -> Self { Self { - p2a: CumulativeHeightRollingPattern::new(client.clone(), "p2a_count".to_string()), - p2ms: CumulativeHeightRollingPattern::new(client.clone(), "p2ms_count".to_string()), - p2pk33: CumulativeHeightRollingPattern::new(client.clone(), "p2pk33_count".to_string()), - p2pk65: CumulativeHeightRollingPattern::new(client.clone(), "p2pk65_count".to_string()), - p2pkh: CumulativeHeightRollingPattern::new(client.clone(), "p2pkh_count".to_string()), - p2sh: CumulativeHeightRollingPattern::new(client.clone(), "p2sh_count".to_string()), - p2tr: CumulativeHeightRollingPattern::new(client.clone(), "p2tr_count".to_string()), - p2wpkh: CumulativeHeightRollingPattern::new(client.clone(), "p2wpkh_count".to_string()), - p2wsh: CumulativeHeightRollingPattern::new(client.clone(), "p2wsh_count".to_string()), - opreturn: CumulativeHeightRollingPattern::new(client.clone(), "opreturn_count".to_string()), - emptyoutput: CumulativeHeightRollingPattern::new(client.clone(), "emptyoutput_count".to_string()), - unknownoutput: CumulativeHeightRollingPattern::new(client.clone(), "unknownoutput_count".to_string()), - segwit: CumulativeHeightRollingPattern::new(client.clone(), "segwit_count".to_string()), + p2a: CumulativeHeightSumPattern::new(client.clone(), "p2a_count".to_string()), + p2ms: CumulativeHeightSumPattern::new(client.clone(), "p2ms_count".to_string()), + p2pk33: CumulativeHeightSumPattern::new(client.clone(), "p2pk33_count".to_string()), + p2pk65: CumulativeHeightSumPattern::new(client.clone(), "p2pk65_count".to_string()), + p2pkh: CumulativeHeightSumPattern::new(client.clone(), "p2pkh_count".to_string()), + p2sh: CumulativeHeightSumPattern::new(client.clone(), "p2sh_count".to_string()), + p2tr: CumulativeHeightSumPattern::new(client.clone(), "p2tr_count".to_string()), + p2wpkh: CumulativeHeightSumPattern::new(client.clone(), "p2wpkh_count".to_string()), + p2wsh: CumulativeHeightSumPattern::new(client.clone(), "p2wsh_count".to_string()), + opreturn: CumulativeHeightSumPattern::new(client.clone(), "opreturn_count".to_string()), + emptyoutput: CumulativeHeightSumPattern::new(client.clone(), "emptyoutput_count".to_string()), + unknownoutput: CumulativeHeightSumPattern::new(client.clone(), "unknownoutput_count".to_string()), + segwit: CumulativeHeightSumPattern::new(client.clone(), "segwit_count".to_string()), taproot_adoption: MetricPattern1::new(client.clone(), "taproot_adoption".to_string()), segwit_adoption: MetricPattern1::new(client.clone(), "segwit_adoption".to_string()), } @@ -4023,8 +4023,8 @@ impl MetricsTree_Cointime { /// Metrics tree node. pub struct MetricsTree_Cointime_Activity { - pub coinblocks_created: CumulativeHeightRollingPattern, - pub coinblocks_stored: CumulativeHeightRollingPattern, + pub coinblocks_created: CumulativeHeightSumPattern, + pub coinblocks_stored: CumulativeHeightSumPattern, pub liveliness: MetricPattern1, pub vaultedness: MetricPattern1, pub activity_to_vaultedness_ratio: MetricPattern1, @@ -4033,8 +4033,8 @@ pub struct MetricsTree_Cointime_Activity { impl MetricsTree_Cointime_Activity { pub fn new(client: Arc, base_path: String) -> Self { Self { - coinblocks_created: CumulativeHeightRollingPattern::new(client.clone(), "coinblocks_created".to_string()), - coinblocks_stored: CumulativeHeightRollingPattern::new(client.clone(), "coinblocks_stored".to_string()), + coinblocks_created: CumulativeHeightSumPattern::new(client.clone(), "coinblocks_created".to_string()), + coinblocks_stored: CumulativeHeightSumPattern::new(client.clone(), "coinblocks_stored".to_string()), liveliness: MetricPattern1::new(client.clone(), "liveliness".to_string()), vaultedness: MetricPattern1::new(client.clone(), "vaultedness".to_string()), activity_to_vaultedness_ratio: MetricPattern1::new(client.clone(), "activity_to_vaultedness_ratio".to_string()), @@ -4059,19 +4059,19 @@ impl MetricsTree_Cointime_Supply { /// Metrics tree node. pub struct MetricsTree_Cointime_Value { - pub cointime_value_destroyed: CumulativeHeightRollingPattern, - pub cointime_value_created: CumulativeHeightRollingPattern, - pub cointime_value_stored: CumulativeHeightRollingPattern, - pub vocdd: CumulativeHeightRollingPattern, + pub cointime_value_destroyed: CumulativeHeightSumPattern, + pub cointime_value_created: CumulativeHeightSumPattern, + pub cointime_value_stored: CumulativeHeightSumPattern, + pub vocdd: CumulativeHeightSumPattern, } impl MetricsTree_Cointime_Value { pub fn new(client: Arc, base_path: String) -> Self { Self { - cointime_value_destroyed: CumulativeHeightRollingPattern::new(client.clone(), "cointime_value_destroyed".to_string()), - cointime_value_created: CumulativeHeightRollingPattern::new(client.clone(), "cointime_value_created".to_string()), - cointime_value_stored: CumulativeHeightRollingPattern::new(client.clone(), "cointime_value_stored".to_string()), - vocdd: CumulativeHeightRollingPattern::new(client.clone(), "vocdd".to_string()), + cointime_value_destroyed: CumulativeHeightSumPattern::new(client.clone(), "cointime_value_destroyed".to_string()), + cointime_value_created: CumulativeHeightSumPattern::new(client.clone(), "cointime_value_created".to_string()), + cointime_value_stored: CumulativeHeightSumPattern::new(client.clone(), "cointime_value_stored".to_string()), + vocdd: CumulativeHeightSumPattern::new(client.clone(), "vocdd".to_string()), } } } diff --git a/crates/brk_computer/src/blocks/count/compute.rs b/crates/brk_computer/src/blocks/count/compute.rs index 191e9ad31..0f7f6dafc 100644 --- a/crates/brk_computer/src/blocks/count/compute.rs +++ b/crates/brk_computer/src/blocks/count/compute.rs @@ -171,7 +171,7 @@ impl Vecs { _30d: &self.height_1m_ago, _1y: &self.height_1y_ago, }; - self.block_count.rolling.compute_rolling_sum( + self.block_count.sum.compute_rolling_sum( starting_indexes.height, &ws, &self.block_count.height, diff --git a/crates/brk_computer/src/distribution/compute/block_loop.rs b/crates/brk_computer/src/distribution/compute/block_loop.rs index b1aaa3534..3c23f9e06 100644 --- a/crates/brk_computer/src/distribution/compute/block_loop.rs +++ b/crates/brk_computer/src/distribution/compute/block_loop.rs @@ -128,7 +128,7 @@ pub(crate) fn process_blocks( debug!("txindex_to_height RangeMap built"); // Create reusable iterators for sequential txout/txin reads (16KB buffered) - let txout_iters = TxOutReaders::new(indexer); + let mut txout_iters = TxOutReaders::new(indexer); let mut txin_iters = TxInReaders::new(indexer, inputs, &mut txindex_to_height); // Pre-collect first address indexes per type for the block range diff --git a/crates/brk_computer/src/distribution/compute/readers.rs b/crates/brk_computer/src/distribution/compute/readers.rs index b3b1cf5ed..84659e8df 100644 --- a/crates/brk_computer/src/distribution/compute/readers.rs +++ b/crates/brk_computer/src/distribution/compute/readers.rs @@ -21,32 +21,40 @@ pub struct TxOutData { pub typeindex: TypeIndex, } -/// Readers for txout vectors. Uses collect_range for bulk reads. +/// Readers for txout vectors. Reuses internal buffers across blocks. pub struct TxOutReaders<'a> { indexer: &'a Indexer, + values_buf: Vec, + outputtypes_buf: Vec, + typeindexes_buf: Vec, } impl<'a> TxOutReaders<'a> { pub(crate) fn new(indexer: &'a Indexer) -> Self { - Self { indexer } + Self { + indexer, + values_buf: Vec::new(), + outputtypes_buf: Vec::new(), + typeindexes_buf: Vec::new(), + } } - /// Collect output data for a block range using bulk reads. + /// Collect output data for a block range using bulk reads with buffer reuse. pub(crate) fn collect_block_outputs( - &self, + &mut self, first_txoutindex: usize, output_count: usize, ) -> Vec { let end = first_txoutindex + output_count; - let values: Vec = self.indexer.vecs.outputs.value.collect_range_at(first_txoutindex, end); - let outputtypes: Vec = self.indexer.vecs.outputs.outputtype.collect_range_at(first_txoutindex, end); - let typeindexes: Vec = self.indexer.vecs.outputs.typeindex.collect_range_at(first_txoutindex, end); + self.indexer.vecs.outputs.value.collect_range_into_at(first_txoutindex, end, &mut self.values_buf); + self.indexer.vecs.outputs.outputtype.collect_range_into_at(first_txoutindex, end, &mut self.outputtypes_buf); + self.indexer.vecs.outputs.typeindex.collect_range_into_at(first_txoutindex, end, &mut self.typeindexes_buf); - values - .into_iter() - .zip(outputtypes) - .zip(typeindexes) - .map(|((value, outputtype), typeindex)| TxOutData { + self.values_buf + .iter() + .zip(&self.outputtypes_buf) + .zip(&self.typeindexes_buf) + .map(|((&value, &outputtype), &typeindex)| TxOutData { value, outputtype, typeindex, @@ -55,11 +63,12 @@ impl<'a> TxOutReaders<'a> { } } -/// Readers for txin vectors. Uses collect_range for bulk reads. +/// Readers for txin vectors. Reuses outpoint buffer across blocks. pub struct TxInReaders<'a> { indexer: &'a Indexer, txins: &'a inputs::Vecs, txindex_to_height: &'a mut RangeMap, + outpoints_buf: Vec, } impl<'a> TxInReaders<'a> { @@ -72,11 +81,12 @@ impl<'a> TxInReaders<'a> { indexer, txins, txindex_to_height, + outpoints_buf: Vec::new(), } } /// Collect input data for a block range using bulk reads. - /// Computes prev_height on-the-fly from outpoint using RangeMap lookup. + /// Outpoint buffer is reused across blocks; returned vecs are fresh (caller-owned). pub(crate) fn collect_block_inputs( &mut self, first_txinindex: usize, @@ -85,11 +95,11 @@ impl<'a> TxInReaders<'a> { ) -> (Vec, Vec, Vec, Vec) { let end = first_txinindex + input_count; let values: Vec = self.txins.spent.value.collect_range_at(first_txinindex, end); - let outpoints: Vec = self.indexer.vecs.inputs.outpoint.collect_range_at(first_txinindex, end); + self.indexer.vecs.inputs.outpoint.collect_range_into_at(first_txinindex, end, &mut self.outpoints_buf); let outputtypes: Vec = self.indexer.vecs.inputs.outputtype.collect_range_at(first_txinindex, end); let typeindexes: Vec = self.indexer.vecs.inputs.typeindex.collect_range_at(first_txinindex, end); - let prev_heights: Vec = outpoints + let prev_heights: Vec = self.outpoints_buf .iter() .map(|outpoint| { if outpoint.is_coinbase() { diff --git a/crates/brk_computer/src/distribution/state/cost_basis/data.rs b/crates/brk_computer/src/distribution/state/cost_basis/data.rs index b5c3fbf5f..091858e70 100644 --- a/crates/brk_computer/src/distribution/state/cost_basis/data.rs +++ b/crates/brk_computer/src/distribution/state/cost_basis/data.rs @@ -11,8 +11,6 @@ use brk_types::{ use rustc_hash::FxHashMap; use vecdb::Bytes; -use crate::utils::OptionExt; - use super::{CachedUnrealizedState, Percentiles, UnrealizedState}; /// Type alias for the price-to-sats map used in cost basis data. @@ -97,17 +95,17 @@ impl CostBasisData { pub(crate) fn iter(&self) -> impl Iterator { self.assert_pending_empty(); - self.state.u().base.map.iter().map(|(&k, v)| (k, v)) + self.state.as_ref().unwrap().base.map.iter().map(|(&k, v)| (k, v)) } pub(crate) fn is_empty(&self) -> bool { - self.pending.is_empty() && self.state.u().base.map.is_empty() + self.pending.is_empty() && self.state.as_ref().unwrap().base.map.is_empty() } pub(crate) fn first_key_value(&self) -> Option<(CentsCompact, &Sats)> { self.assert_pending_empty(); self.state - .u() + .as_ref().unwrap() .base .map .first_key_value() @@ -117,7 +115,7 @@ impl CostBasisData { pub(crate) fn last_key_value(&self) -> Option<(CentsCompact, &Sats)> { self.assert_pending_empty(); self.state - .u() + .as_ref().unwrap() .base .map .last_key_value() @@ -127,13 +125,13 @@ impl CostBasisData { /// Get the exact cap_raw value (not recomputed from map). pub(crate) fn cap_raw(&self) -> CentsSats { self.assert_pending_empty(); - self.state.u().cap_raw + self.state.as_ref().unwrap().cap_raw } /// Get the exact investor_cap_raw value (not recomputed from map). pub(crate) fn investor_cap_raw(&self) -> CentsSquaredSats { self.assert_pending_empty(); - self.state.u().investor_cap_raw + self.state.as_ref().unwrap().investor_cap_raw } /// Increment with pre-computed typed values. @@ -181,7 +179,7 @@ impl CostBasisData { self.percentiles_dirty = true; } for (cents, (inc, dec)) in self.pending.drain() { - let entry = self.state.um().base.map.entry(cents).or_default(); + let entry = self.state.as_mut().unwrap().base.map.entry(cents).or_default(); *entry += inc; if *entry < dec { panic!( @@ -198,12 +196,12 @@ impl CostBasisData { } *entry -= dec; if *entry == Sats::ZERO { - self.state.um().base.map.remove(¢s); + self.state.as_mut().unwrap().base.map.remove(¢s); } } // Apply raw values - let state = self.state.um(); + let state = self.state.as_mut().unwrap(); state.cap_raw += self.pending_raw.cap_inc; // Check for underflow before subtracting @@ -271,7 +269,7 @@ impl CostBasisData { ); } - let map = &self.state.u().base.map; + let map = &self.state.as_ref().unwrap().base.map; let date_state = date_price.map(|p| CachedUnrealizedState::compute_full_standalone(p.into(), map)); @@ -336,7 +334,7 @@ impl CostBasisData { } } - fs::write(self.path_state(height), self.state.u().serialize()?)?; + fs::write(self.path_state(height), self.state.as_ref().unwrap().serialize()?)?; Ok(()) } diff --git a/crates/brk_computer/src/inputs/spent/compute.rs b/crates/brk_computer/src/inputs/spent/compute.rs index 69ded0a85..6f31f9213 100644 --- a/crates/brk_computer/src/inputs/spent/compute.rs +++ b/crates/brk_computer/src/inputs/spent/compute.rs @@ -48,19 +48,18 @@ impl Vecs { while batch_start < target { let batch_end = (batch_start + BATCH_SIZE).min(target); - let outpoints = indexer.vecs.inputs.outpoint.collect_range_at(batch_start, batch_end); - entries.clear(); - for (j, outpoint) in outpoints.into_iter().enumerate() { - let txinindex = TxInIndex::from(batch_start + j); + let mut j = 0usize; + indexer.vecs.inputs.outpoint.for_each_range_at(batch_start, batch_end, |outpoint| { entries.push(Entry { - txinindex, + txinindex: TxInIndex::from(batch_start + j), txindex: outpoint.txindex(), vout: outpoint.vout(), txoutindex: TxOutIndex::COINBASE, value: Sats::MAX, }); - } + j += 1; + }); // Coinbase entries (txindex MAX) sorted to end entries.sort_unstable_by_key(|e| e.txindex); diff --git a/crates/brk_computer/src/internal/compute.rs b/crates/brk_computer/src/internal/compute.rs index 5b417bb31..5308e61a0 100644 --- a/crates/brk_computer/src/internal/compute.rs +++ b/crates/brk_computer/src/internal/compute.rs @@ -11,7 +11,7 @@ use vecdb::{ VecValue, }; -use crate::utils::get_percentile; +use brk_types::get_percentile; use super::ComputedVecValue; @@ -358,6 +358,7 @@ where let window_starts_batch: Vec = window_starts.collect_range_at(start, fi_len); let zero = T::from(0_usize); + let mut values: Vec = Vec::new(); first_indexes_batch .iter() @@ -389,8 +390,7 @@ where vec.truncate_push_at(idx, zero)?; } } else { - let mut values: Vec = - source.collect_range_at(range_start_usize, range_end_usize); + source.collect_range_into_at(range_start_usize, range_end_usize, &mut values); // Compute sum before sorting let len = values.len(); diff --git a/crates/brk_computer/src/internal/distribution_stats.rs b/crates/brk_computer/src/internal/distribution_stats.rs index 09a2b71f2..62e5e4821 100644 --- a/crates/brk_computer/src/internal/distribution_stats.rs +++ b/crates/brk_computer/src/internal/distribution_stats.rs @@ -15,3 +15,31 @@ pub struct DistributionStats pub p75: G, pub p90: H, } + +impl DistributionStats { + /// Apply a fallible operation to each of the 8 fields. + pub fn try_for_each_mut(&mut self, mut f: impl FnMut(&mut A) -> brk_error::Result<()>) -> brk_error::Result<()> { + f(&mut self.average)?; + f(&mut self.min)?; + f(&mut self.max)?; + f(&mut self.p10)?; + f(&mut self.p25)?; + f(&mut self.median)?; + f(&mut self.p75)?; + f(&mut self.p90)?; + Ok(()) + } + + /// Get minimum value by applying a function to each field. + pub fn min_by(&self, mut f: impl FnMut(&A) -> usize) -> usize { + f(&self.average) + .min(f(&self.min)) + .min(f(&self.max)) + .min(f(&self.p10)) + .min(f(&self.p25)) + .min(f(&self.median)) + .min(f(&self.p75)) + .min(f(&self.p90)) + } + +} diff --git a/crates/brk_computer/src/internal/mod.rs b/crates/brk_computer/src/internal/mod.rs index 5069e6584..0dbafa29b 100644 --- a/crates/brk_computer/src/internal/mod.rs +++ b/crates/brk_computer/src/internal/mod.rs @@ -6,6 +6,7 @@ mod indexes; mod lazy_eager_indexes; mod multi; mod single; +pub(crate) mod sliding_window; mod traits; mod windows; diff --git a/crates/brk_computer/src/internal/multi/from_height/cumulative_rolling_sum.rs b/crates/brk_computer/src/internal/multi/from_height/cumulative_rolling_sum.rs index 90eaa0a2a..748307cdf 100644 --- a/crates/brk_computer/src/internal/multi/from_height/cumulative_rolling_sum.rs +++ b/crates/brk_computer/src/internal/multi/from_height/cumulative_rolling_sum.rs @@ -24,7 +24,7 @@ where { pub height: M::Stored>>, pub cumulative: ComputedFromHeightLast, - pub rolling: RollingWindows, + pub sum: RollingWindows, } const VERSION: Version = Version::ZERO; @@ -49,7 +49,7 @@ where Ok(Self { height, cumulative, - rolling, + sum: rolling, }) } @@ -68,7 +68,7 @@ where self.cumulative .height .compute_cumulative(max_from, &self.height, exit)?; - self.rolling + self.sum .compute_rolling_sum(max_from, windows, &self.height, exit)?; Ok(()) } diff --git a/crates/brk_computer/src/internal/multi/from_height/ratio/extension.rs b/crates/brk_computer/src/internal/multi/from_height/ratio/extension.rs index 6ae2c3968..74f322db6 100644 --- a/crates/brk_computer/src/internal/multi/from_height/ratio/extension.rs +++ b/crates/brk_computer/src/internal/multi/from_height/ratio/extension.rs @@ -6,8 +6,8 @@ use vecdb::{AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, use crate::{ ComputeIndexes, blocks, indexes, internal::{ComputedFromHeightStdDevExtended, Price}, - utils::get_percentile, }; +use brk_types::get_percentile; use super::super::ComputedFromHeightLast; 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 8b68e5bff..c06839236 100644 --- a/crates/brk_computer/src/internal/multi/height_derived/last.rs +++ b/crates/brk_computer/src/internal/multi/height_derived/last.rs @@ -7,7 +7,7 @@ use brk_types::{ }; use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{LazyAggVec, ReadableBoxedVec, ReadableCloneableVec}; +use vecdb::{LazyAggVec, ReadOnlyClone, ReadableBoxedVec, ReadableCloneableVec}; use crate::{ indexes, indexes_from, @@ -41,6 +41,17 @@ pub struct ComputedHeightDerivedLast( where T: ComputedVecValue + PartialOrd + JsonSchema; +/// Already read-only (no StorageMode); cloning is sufficient. +impl ReadOnlyClone for ComputedHeightDerivedLast +where + T: ComputedVecValue + PartialOrd + JsonSchema, +{ + type ReadOnly = Self; + fn read_only_clone(&self) -> Self { + self.clone() + } +} + const VERSION: Version = Version::ZERO; impl ComputedHeightDerivedLast 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 963d39b68..64ee1c9e2 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 @@ -9,7 +9,9 @@ use brk_types::{ }; use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{LazyVecFrom1, ReadableBoxedVec, ReadableCloneableVec, UnaryTransform, VecIndex, VecValue}; +use vecdb::{ + LazyVecFrom1, ReadableBoxedVec, ReadableCloneableVec, UnaryTransform, VecIndex, VecValue, +}; use crate::{ indexes, indexes_from, @@ -108,7 +110,8 @@ where where S1T: NumericValue, { - let derived = ComputedHeightDerivedLast::forced_import(name, height_source, version, indexes); + let derived = + ComputedHeightDerivedLast::forced_import(name, height_source, version, indexes); Self::from_derived_computed::(name, version, &derived) } diff --git a/crates/brk_computer/src/internal/single/rolling/distribution.rs b/crates/brk_computer/src/internal/single/rolling/distribution.rs index 0b2c1ac24..325f9888e 100644 --- a/crates/brk_computer/src/internal/single/rolling/distribution.rs +++ b/crates/brk_computer/src/internal/single/rolling/distribution.rs @@ -66,62 +66,33 @@ where T: Copy + Ord + From + Default, f64: From, { - // Single pass per window: all 8 stats extracted from one sorted vec compute_rolling_distribution_from_starts( - max_from, - windows._24h, - source, - &mut self.0.average._24h.height, - &mut self.0.min._24h.height, - &mut self.0.max._24h.height, - &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, + max_from, windows._24h, source, + &mut self.0.average._24h.height, &mut self.0.min._24h.height, + &mut self.0.max._24h.height, &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_distribution_from_starts( - max_from, - windows._7d, - source, - &mut self.0.average._7d.height, - &mut self.0.min._7d.height, - &mut self.0.max._7d.height, - &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, + max_from, windows._7d, source, + &mut self.0.average._7d.height, &mut self.0.min._7d.height, + &mut self.0.max._7d.height, &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_distribution_from_starts( - max_from, - windows._30d, - source, - &mut self.0.average._30d.height, - &mut self.0.min._30d.height, - &mut self.0.max._30d.height, - &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, + max_from, windows._30d, source, + &mut self.0.average._30d.height, &mut self.0.min._30d.height, + &mut self.0.max._30d.height, &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_distribution_from_starts( - max_from, - windows._1y, - source, - &mut self.0.average._1y.height, - &mut self.0.min._1y.height, - &mut self.0.max._1y.height, - &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, + max_from, windows._1y, source, + &mut self.0.average._1y.height, &mut self.0.min._1y.height, + &mut self.0.max._1y.height, &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/value_windows.rs b/crates/brk_computer/src/internal/single/rolling/value_windows.rs index f5025ffd3..b1d23c85b 100644 --- a/crates/brk_computer/src/internal/single/rolling/value_windows.rs +++ b/crates/brk_computer/src/internal/single/rolling/value_windows.rs @@ -72,18 +72,9 @@ impl StoredValueRollingWindows { 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)?; + for (w, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) { + w.compute_rolling_sum(max_from, starts, 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 index 3b4249345..c502a82fe 100644 --- a/crates/brk_computer/src/internal/single/rolling/windows.rs +++ b/crates/brk_computer/src/internal/single/rolling/windows.rs @@ -26,6 +26,12 @@ pub struct WindowStarts<'a> { pub _1y: &'a EagerVec>, } +impl<'a> WindowStarts<'a> { + pub fn as_array(&self) -> [&'a EagerVec>; 4] { + [self._24h, self._7d, self._30d, self._1y] + } +} + /// 4 rolling window vecs (24h, 7d, 30d, 1y), each with height data + all 17 index views. #[derive(Deref, DerefMut, Traversable)] #[traversable(transparent)] @@ -64,22 +70,9 @@ where 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)?; + for (w, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) { + w.height.compute_rolling_sum(max_from, starts, 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 4bad8ec3a..f044efd61 100644 --- a/crates/brk_computer/src/internal/single/transform/mod.rs +++ b/crates/brk_computer/src/internal/single/transform/mod.rs @@ -1,6 +1,8 @@ mod block_count_target; mod cents_to_dollars; mod cents_to_sats; +mod ohlc_cents_to_dollars; +mod ohlc_cents_to_sats; mod dollar_halve; mod dollar_identity; @@ -35,6 +37,8 @@ mod volatility_sqrt7; pub use block_count_target::*; pub use cents_to_dollars::*; pub use cents_to_sats::*; +pub use ohlc_cents_to_dollars::*; +pub use ohlc_cents_to_sats::*; pub use dollar_halve::*; pub use dollar_identity::*; diff --git a/crates/brk_computer/src/internal/single/transform/ohlc_cents_to_dollars.rs b/crates/brk_computer/src/internal/single/transform/ohlc_cents_to_dollars.rs new file mode 100644 index 000000000..4e72e62b1 --- /dev/null +++ b/crates/brk_computer/src/internal/single/transform/ohlc_cents_to_dollars.rs @@ -0,0 +1,11 @@ +use brk_types::{OHLCCents, OHLCDollars}; +use vecdb::UnaryTransform; + +pub struct OhlcCentsToDollars; + +impl UnaryTransform for OhlcCentsToDollars { + #[inline(always)] + fn apply(cents: OHLCCents) -> OHLCDollars { + OHLCDollars::from(cents) + } +} diff --git a/crates/brk_computer/src/internal/single/transform/ohlc_cents_to_sats.rs b/crates/brk_computer/src/internal/single/transform/ohlc_cents_to_sats.rs new file mode 100644 index 000000000..4df785c6b --- /dev/null +++ b/crates/brk_computer/src/internal/single/transform/ohlc_cents_to_sats.rs @@ -0,0 +1,19 @@ +use brk_types::{Close, High, Low, OHLCCents, OHLCSats, Open}; +use vecdb::UnaryTransform; + +use super::CentsUnsignedToSats; + +/// OHLCCents -> OHLCSats with high/low swapped (inverse price relationship). +pub struct OhlcCentsToSats; + +impl UnaryTransform for OhlcCentsToSats { + #[inline(always)] + fn apply(cents: OHLCCents) -> OHLCSats { + OHLCSats { + open: Open::new(CentsUnsignedToSats::apply(*cents.open)), + high: High::new(CentsUnsignedToSats::apply(*cents.low)), + low: Low::new(CentsUnsignedToSats::apply(*cents.high)), + close: Close::new(CentsUnsignedToSats::apply(*cents.close)), + } + } +} diff --git a/crates/brk_computer/src/internal/sliding_window.rs b/crates/brk_computer/src/internal/sliding_window.rs new file mode 100644 index 000000000..e1096ddc0 --- /dev/null +++ b/crates/brk_computer/src/internal/sliding_window.rs @@ -0,0 +1,188 @@ +/// Sqrt-decomposed sorted structure for O(sqrt(n)) insert/remove/kth. +/// +/// Maintains `blocks` sorted sub-arrays where each block is sorted and +/// the blocks are ordered (max of block[i] <= min of block[i+1]). +/// Total element count is tracked via `total_len`. +struct SortedBlocks { + blocks: Vec>, + total_len: usize, + block_size: usize, +} + +impl SortedBlocks { + fn new(capacity: usize) -> Self { + let block_size = ((capacity as f64).sqrt() as usize).max(64); + Self { + blocks: Vec::new(), + total_len: 0, + block_size, + } + } + + fn len(&self) -> usize { + self.total_len + } + + fn is_empty(&self) -> bool { + self.total_len == 0 + } + + /// Insert a value in sorted order. O(sqrt(n)). + fn insert(&mut self, value: f64) { + self.total_len += 1; + + if self.blocks.is_empty() { + self.blocks.push(vec![value]); + return; + } + + // Find the block where value belongs: first block whose max >= value + let block_idx = self.blocks.iter().position(|b| { + *b.last().unwrap() >= value + }).unwrap_or(self.blocks.len() - 1); + + let block = &mut self.blocks[block_idx]; + let pos = block.partition_point(|a| *a < value); + block.insert(pos, value); + + // Split if block too large + if block.len() > 2 * self.block_size { + let mid = block.len() / 2; + let right = block[mid..].to_vec(); + block.truncate(mid); + self.blocks.insert(block_idx + 1, right); + } + } + + /// Remove one occurrence of value. O(sqrt(n)). + fn remove(&mut self, value: f64) -> bool { + for (bi, block) in self.blocks.iter_mut().enumerate() { + if block.is_empty() { + continue; + } + // If value > block max, it's not in this block + if *block.last().unwrap() < value { + continue; + } + let pos = block.partition_point(|a| *a < value); + if pos < block.len() && block[pos] == value { + block.remove(pos); + self.total_len -= 1; + if block.is_empty() { + self.blocks.remove(bi); + } + return true; + } + // Value not found (would be in this block range but isn't) + return false; + } + false + } + + /// Get the k-th smallest element (0-indexed). O(sqrt(n)). + fn kth(&self, mut k: usize) -> f64 { + for block in &self.blocks { + if k < block.len() { + return block[k]; + } + k -= block.len(); + } + unreachable!("kth out of bounds") + } + + fn first(&self) -> f64 { + self.blocks.first().unwrap().first().copied().unwrap() + } + + fn last(&self) -> f64 { + self.blocks.last().unwrap().last().copied().unwrap() + } +} + +/// Sorted sliding window for rolling distribution/median computations. +/// +/// Uses sqrt-decomposition for O(sqrt(n)) insert/remove/kth instead of +/// O(n) memmoves with a flat sorted Vec. +pub(crate) struct SlidingWindowSorted { + sorted: SortedBlocks, + running_sum: f64, + prev_start: usize, +} + +impl SlidingWindowSorted { + pub fn with_capacity(cap: usize) -> Self { + Self { + sorted: SortedBlocks::new(cap), + running_sum: 0.0, + prev_start: 0, + } + } + + /// Reconstruct state from historical data (the elements in [range_start..skip]). + pub fn reconstruct(&mut self, partial_values: &[f64], range_start: usize, skip: usize) { + self.prev_start = range_start; + for idx in range_start..skip { + let v = partial_values[idx - range_start]; + self.running_sum += v; + self.sorted.insert(v); + } + } + + /// Add a new value and remove all expired values up to `new_start`. + pub fn advance(&mut self, value: f64, new_start: usize, partial_values: &[f64], range_start: usize) { + self.running_sum += value; + self.sorted.insert(value); + + while self.prev_start < new_start { + let old = partial_values[self.prev_start - range_start]; + self.running_sum -= old; + self.sorted.remove(old); + self.prev_start += 1; + } + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.sorted.is_empty() + } + + #[inline] + pub fn average(&self) -> f64 { + if self.sorted.is_empty() { + 0.0 + } else { + self.running_sum / self.sorted.len() as f64 + } + } + + #[inline] + pub fn min(&self) -> f64 { + if self.sorted.is_empty() { 0.0 } else { self.sorted.first() } + } + + #[inline] + pub fn max(&self) -> f64 { + if self.sorted.is_empty() { 0.0 } else { self.sorted.last() } + } + + /// Extract a percentile (0.0-1.0) using linear interpolation. + #[inline] + pub fn percentile(&self, p: f64) -> f64 { + let len = self.sorted.len(); + if len == 0 { + return 0.0; + } + if len == 1 { + return self.sorted.kth(0); + } + let rank = p * (len - 1) as f64; + let lo = rank.floor() as usize; + let hi = rank.ceil() as usize; + if lo == hi { + self.sorted.kth(lo) + } else { + let frac = rank - lo as f64; + self.sorted.kth(lo) * (1.0 - frac) + self.sorted.kth(hi) * frac + } + } +} diff --git a/crates/brk_computer/src/internal/windows.rs b/crates/brk_computer/src/internal/windows.rs index 0f84f7941..3cca1d858 100644 --- a/crates/brk_computer/src/internal/windows.rs +++ b/crates/brk_computer/src/internal/windows.rs @@ -15,3 +15,9 @@ pub struct Windows { #[traversable(rename = "1y")] pub _1y: D, } + +impl Windows { + pub fn as_mut_array(&mut self) -> [&mut A; 4] { + [&mut self._24h, &mut self._7d, &mut self._30d, &mut self._1y] + } +} diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index f22f1eef8..37f638221 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -27,7 +27,6 @@ mod scripts; mod supply; mod traits; mod transactions; -mod utils; use indexes::ComputeIndexes; diff --git a/crates/brk_computer/src/market/dca/compute.rs b/crates/brk_computer/src/market/dca/compute.rs index 47c22f48a..604da7f1f 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.close.day1; + let close = &prices.usd.split.close.day1; let first_price_di = Day1::try_from(Date::new(2010, 7, 12)) .unwrap() diff --git a/crates/brk_computer/src/market/indicators/timeframe.rs b/crates/brk_computer/src/market/indicators/timeframe.rs index b234e37ac..90cbedf8d 100644 --- a/crates/brk_computer/src/market/indicators/timeframe.rs +++ b/crates/brk_computer/src/market/indicators/timeframe.rs @@ -16,10 +16,10 @@ pub(super) fn collect_returns(tf: &str, returns: &ReturnsVecs) -> Vec { pub(super) fn collect_closes(tf: &str, prices: &prices::Vecs) -> Vec { match tf { - "1d" => prices.usd.close.day1.collect_or_default(), - "1w" => prices.usd.close.week1.collect_or_default(), - "1m" => prices.usd.close.month1.collect_or_default(), - "1y" => prices.usd.close.year1.collect_or_default(), + "1d" => prices.usd.split.close.day1.collect_or_default(), + "1w" => prices.usd.split.close.week1.collect_or_default(), + "1m" => prices.usd.split.close.month1.collect_or_default(), + "1y" => prices.usd.split.close.year1.collect_or_default(), _ => unreachable!(), } } diff --git a/crates/brk_computer/src/market/moving_average/compute.rs b/crates/brk_computer/src/market/moving_average/compute.rs index 97f62133d..6f0d127cd 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.close.day1.collect_or_default(); + let closes: Vec = prices.usd.split.close.day1.collect_or_default(); for (ema, period) in [ (&mut self.price_1w_ema, 7), diff --git a/crates/brk_computer/src/outputs/spent/compute.rs b/crates/brk_computer/src/outputs/spent/compute.rs index 3d9f28292..8a8e2f83d 100644 --- a/crates/brk_computer/src/outputs/spent/compute.rs +++ b/crates/brk_computer/src/outputs/spent/compute.rs @@ -98,18 +98,15 @@ impl Vecs { first_txinindex_data[batch_end_height.to_usize() + 1 - offset].to_usize() }; - // Collect and process txins + // Stream txins directly into pairs — avoids intermediate Vec allocation pairs.clear(); - let txoutindexes: Vec = txinindex_to_txoutindex.collect_range_at(txin_start, txin_end); - for (j, txoutindex) in txoutindexes.into_iter().enumerate() { - let txinindex = TxInIndex::from(txin_start + j); - - if txoutindex.is_coinbase() { - continue; + let mut j = txin_start; + txinindex_to_txoutindex.for_each_range_at(txin_start, txin_end, |txoutindex: TxOutIndex| { + if !txoutindex.is_coinbase() { + pairs.push((txoutindex, TxInIndex::from(j))); } - - pairs.push((txoutindex, txinindex)); - } + j += 1; + }); pairs.sort_unstable_by_key(|(txoutindex, _)| *txoutindex); diff --git a/crates/brk_computer/src/prices/cents/compute.rs b/crates/brk_computer/src/prices/cents/compute.rs index bc1fbf9da..f4bb6c7a2 100644 --- a/crates/brk_computer/src/prices/cents/compute.rs +++ b/crates/brk_computer/src/prices/cents/compute.rs @@ -19,12 +19,24 @@ impl Vecs { exit: &Exit, ) -> Result<()> { self.compute_prices(indexer, starting_indexes, exit)?; - self.open + self.split + .open .compute_first(starting_indexes, &self.price, indexes, exit)?; - self.high + self.split + .high .compute_max(starting_indexes, &self.price, indexes, exit)?; - self.low + self.split + .low .compute_min(starting_indexes, &self.price, indexes, exit)?; + self.ohlc.compute_from_split( + starting_indexes, + &self.split.open, + &self.split.high, + &self.split.low, + &self.split.close, + indexes, + exit, + )?; Ok(()) } @@ -132,6 +144,10 @@ impl Vecs { // across blocks, so the cursor only advances forward. let mut txout_cursor = indexer.vecs.transactions.first_txoutindex.cursor(); + // Reusable buffers — avoid per-block allocation + let mut values: Vec = Vec::new(); + let mut output_types: Vec = Vec::new(); + for (idx, _h) in range.enumerate() { let first_txindex = first_txindexes[idx]; let next_first_txindex = first_txindexes @@ -156,16 +172,8 @@ impl Vecs { .unwrap_or(TxOutIndex::from(total_outputs)) .to_usize(); - let values: Vec = indexer - .vecs - .outputs - .value - .collect_range_at(out_start, out_end); - let output_types: Vec = indexer - .vecs - .outputs - .outputtype - .collect_range_at(out_start, out_end); + indexer.vecs.outputs.value.collect_range_into_at(out_start, out_end, &mut values); + indexer.vecs.outputs.outputtype.collect_range_into_at(out_start, out_end, &mut output_types); let mut hist = [0u32; NUM_BINS]; for i in 0..values.len() { diff --git a/crates/brk_computer/src/prices/cents/import.rs b/crates/brk_computer/src/prices/cents/import.rs index 589ca3934..466353e38 100644 --- a/crates/brk_computer/src/prices/cents/import.rs +++ b/crates/brk_computer/src/prices/cents/import.rs @@ -5,6 +5,7 @@ use vecdb::{Database, ImportableVec, PcoVec, ReadableCloneableVec}; use super::Vecs; use crate::indexes; use crate::internal::{ComputedHeightDerivedLast, EagerIndexes}; +use crate::prices::{ohlcs::OhlcVecs, split::SplitOhlc}; impl Vecs { pub(crate) fn forced_import( @@ -16,23 +17,26 @@ impl Vecs { let price = PcoVec::forced_import(db, "price_cents", version)?; - 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 open = EagerIndexes::forced_import(db, "price_open_cents", version)?; + let high = EagerIndexes::forced_import(db, "price_high_cents", version)?; + let low = EagerIndexes::forced_import(db, "price_low_cents", version)?; let close = ComputedHeightDerivedLast::forced_import( - "price_cents_close", + "price_close_cents", price.read_only_boxed_clone(), version, indexes, ); - Ok(Self { - price, + let split = SplitOhlc { open, high, low, close, - }) + }; + + let ohlc = OhlcVecs::forced_import(db, "price_ohlc_cents", version)?; + + Ok(Self { split, ohlc, price }) } } diff --git a/crates/brk_computer/src/prices/cents/vecs.rs b/crates/brk_computer/src/prices/cents/vecs.rs index 694b0dd83..cd9f02eec 100644 --- a/crates/brk_computer/src/prices/cents/vecs.rs +++ b/crates/brk_computer/src/prices/cents/vecs.rs @@ -1,14 +1,19 @@ use brk_traversable::Traversable; -use brk_types::{Cents, Height}; +use brk_types::{Cents, Height, OHLCCents}; use vecdb::{PcoVec, Rw, StorageMode}; use crate::internal::{ComputedHeightDerivedLast, EagerIndexes}; +use crate::prices::{ohlcs::OhlcVecs, split::SplitOhlc}; #[derive(Traversable)] pub struct Vecs { + #[allow(clippy::type_complexity)] + pub split: SplitOhlc< + EagerIndexes, + EagerIndexes, + EagerIndexes, + ComputedHeightDerivedLast, + >, + pub ohlc: OhlcVecs, pub price: M::Stored>, - pub open: EagerIndexes, - pub high: EagerIndexes, - pub low: EagerIndexes, - pub close: ComputedHeightDerivedLast, } diff --git a/crates/brk_computer/src/prices/mod.rs b/crates/brk_computer/src/prices/mod.rs index b8fd55667..0f364caa7 100644 --- a/crates/brk_computer/src/prices/mod.rs +++ b/crates/brk_computer/src/prices/mod.rs @@ -1,4 +1,6 @@ mod compute; +pub(crate) mod ohlcs; +pub(crate) mod split; pub mod cents; pub mod sats; diff --git a/crates/brk_computer/src/prices/ohlcs.rs b/crates/brk_computer/src/prices/ohlcs.rs new file mode 100644 index 000000000..10d92b57c --- /dev/null +++ b/crates/brk_computer/src/prices/ohlcs.rs @@ -0,0 +1,205 @@ +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{ + Cents, Close, Day1, Day3, DifficultyEpoch, HalvingEpoch, High, Hour1, Hour4, Hour12, Low, + Minute1, Minute5, Minute10, Minute30, Month1, Month3, Month6, OHLCCents, Open, Version, Week1, + Year1, Year10, +}; +use derive_more::{Deref, DerefMut}; +use schemars::JsonSchema; +use serde::Serialize; +use vecdb::{ + BytesVec, BytesVecValue, Database, EagerVec, Exit, Formattable, ImportableVec, LazyVecFrom1, + ReadableCloneableVec, ReadableVec, Rw, StorageMode, UnaryTransform, +}; + +use crate::{ + ComputeIndexes, indexes, indexes_from, + internal::{ComputedHeightDerivedLast, EagerIndexes, Indexes}, +}; + +// ── EagerOhlcIndexes ───────────────────────────────────────────────── + +#[derive(Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct OhlcVecs( + #[allow(clippy::type_complexity)] + pub Indexes< + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + ::Stored>>, + >, +) +where + T: BytesVecValue + Formattable + Serialize + JsonSchema; + +const EAGER_VERSION: Version = Version::ZERO; + +impl OhlcVecs +where + T: BytesVecValue + Formattable + Serialize + JsonSchema, +{ + pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result { + let v = version + EAGER_VERSION; + + macro_rules! period { + ($idx:ident) => { + ImportableVec::forced_import(db, &format!("{name}_{}", stringify!($idx)), v)? + }; + } + + Ok(Self(indexes_from!(period))) + } +} + +impl OhlcVecs { + #[allow(clippy::too_many_arguments)] + pub(crate) fn compute_from_split( + &mut self, + starting_indexes: &ComputeIndexes, + open: &EagerIndexes, + high: &EagerIndexes, + low: &EagerIndexes, + close: &ComputedHeightDerivedLast, + 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 o = open.$field.collect_one(idx).unwrap_or_default(); + let h = high.$field.collect_one(idx).unwrap_or_default(); + let l = low.$field.collect_one(idx).unwrap_or_default(); + let c = close.$field.collect_one(idx).flatten().unwrap_or_default(); + ( + idx, + OHLCCents { + open: Open::new(o), + high: High::new(h), + low: Low::new(l), + close: Close::new(c), + }, + ) + }, + exit, + )?; + }; + } + + macro_rules! epoch { + ($field:ident) => { + self.0.$field.compute_transform( + starting_indexes.$field, + &indexes.$field.first_height, + |(idx, _first_h, _)| { + let o = open.$field.collect_one(idx).unwrap_or_default(); + let h = high.$field.collect_one(idx).unwrap_or_default(); + let l = low.$field.collect_one(idx).unwrap_or_default(); + let c = close.$field.collect_one(idx).unwrap_or_default(); + ( + idx, + OHLCCents { + open: Open::new(o), + high: High::new(h), + low: Low::new(l), + close: Close::new(c), + }, + ) + }, + 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); + epoch!(halvingepoch); + epoch!(difficultyepoch); + + Ok(()) + } +} + +// ── LazyOhlcIndexes ────────────────────────────────────────────────── + +#[derive(Clone, Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct LazyOhlcVecs( + #[allow(clippy::type_complexity)] + pub Indexes< + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + LazyVecFrom1, + >, +) +where + T: BytesVecValue + Formattable + Serialize + JsonSchema, + S: BytesVecValue; + +impl LazyOhlcVecs +where + T: BytesVecValue + Formattable + Serialize + JsonSchema, + S: BytesVecValue + Formattable + Serialize + JsonSchema, +{ + pub(crate) fn from_eager_ohlc_indexes>( + name: &str, + version: Version, + source: &OhlcVecs, + ) -> 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/prices/sats/import.rs b/crates/brk_computer/src/prices/sats/import.rs index f8ee770cb..afe85f5bb 100644 --- a/crates/brk_computer/src/prices/sats/import.rs +++ b/crates/brk_computer/src/prices/sats/import.rs @@ -3,9 +3,10 @@ use vecdb::{LazyVecFrom1, ReadableCloneableVec}; use super::super::cents; use super::Vecs; +use crate::prices::{ohlcs::LazyOhlcVecs, split::SplitOhlc}; use crate::{ indexes, - internal::{CentsUnsignedToSats, ComputedHeightDerivedLast, LazyEagerIndexes}, + internal::{CentsUnsignedToSats, ComputedHeightDerivedLast, LazyEagerIndexes, OhlcCentsToSats}, }; impl Vecs { @@ -21,26 +22,43 @@ impl Vecs { ); // 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 open = LazyEagerIndexes::from_eager_indexes::( + "price_open_sats", + version, + ¢s.split.open, + ); + let high = LazyEagerIndexes::from_eager_indexes::( + "price_high_sats", + version, + ¢s.split.low, + ); + let low = LazyEagerIndexes::from_eager_indexes::( + "price_low_sats", + version, + ¢s.split.high, + ); let close = ComputedHeightDerivedLast::forced_import( - "price_sats_close", + "price_close_sats", price.read_only_boxed_clone(), version, indexes, ); - Self { - price, + let split = SplitOhlc { open, high, low, close, - } + }; + + // OhlcCentsToSats handles the high↔low swap internally + let ohlc = LazyOhlcVecs::from_eager_ohlc_indexes::( + "price_ohlc_sats", + version, + ¢s.ohlc, + ); + + Self { split, ohlc, price } } } diff --git a/crates/brk_computer/src/prices/sats/vecs.rs b/crates/brk_computer/src/prices/sats/vecs.rs index 3b066ffcd..64e23e78c 100644 --- a/crates/brk_computer/src/prices/sats/vecs.rs +++ b/crates/brk_computer/src/prices/sats/vecs.rs @@ -1,14 +1,19 @@ use brk_traversable::Traversable; -use brk_types::{Cents, Height, Sats}; +use brk_types::{Cents, Height, OHLCCents, OHLCSats, Sats}; use vecdb::LazyVecFrom1; use crate::internal::{ComputedHeightDerivedLast, LazyEagerIndexes}; +use crate::prices::{ohlcs::LazyOhlcVecs, split::SplitOhlc}; #[derive(Clone, Traversable)] pub struct Vecs { + #[allow(clippy::type_complexity)] + pub split: SplitOhlc< + LazyEagerIndexes, + LazyEagerIndexes, + LazyEagerIndexes, + ComputedHeightDerivedLast, + >, + pub ohlc: LazyOhlcVecs, pub price: LazyVecFrom1, - pub open: LazyEagerIndexes, - pub high: LazyEagerIndexes, - pub low: LazyEagerIndexes, - pub close: ComputedHeightDerivedLast, } diff --git a/crates/brk_computer/src/prices/split.rs b/crates/brk_computer/src/prices/split.rs new file mode 100644 index 000000000..b89632e29 --- /dev/null +++ b/crates/brk_computer/src/prices/split.rs @@ -0,0 +1,9 @@ +use brk_traversable::Traversable; + +#[derive(Clone, Traversable)] +pub struct SplitOhlc { + pub open: O, + pub high: H, + pub low: L, + pub close: C, +} diff --git a/crates/brk_computer/src/prices/usd/import.rs b/crates/brk_computer/src/prices/usd/import.rs index de7510fee..8e4e291d5 100644 --- a/crates/brk_computer/src/prices/usd/import.rs +++ b/crates/brk_computer/src/prices/usd/import.rs @@ -3,9 +3,12 @@ use vecdb::{LazyVecFrom1, ReadableCloneableVec}; use super::super::cents; use super::Vecs; +use crate::prices::{ohlcs::LazyOhlcVecs, split::SplitOhlc}; use crate::{ indexes, - internal::{CentsUnsignedToDollars, ComputedHeightDerivedLast, LazyEagerIndexes}, + internal::{ + CentsUnsignedToDollars, ComputedHeightDerivedLast, LazyEagerIndexes, OhlcCentsToDollars, + }, }; impl Vecs { @@ -15,32 +18,48 @@ impl Vecs { cents: ¢s::Vecs, ) -> Self { let price = LazyVecFrom1::transformed::( - "price_usd", + "price", version, cents.price.read_only_boxed_clone(), ); // 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 open = LazyEagerIndexes::from_eager_indexes::( + "price_open", + version, + ¢s.split.open, + ); + let high = LazyEagerIndexes::from_eager_indexes::( + "price_high", + version, + ¢s.split.high, + ); + let low = LazyEagerIndexes::from_eager_indexes::( + "price_low", + version, + ¢s.split.low, + ); let close = ComputedHeightDerivedLast::forced_import( - "price_usd_close", + "price_close", price.read_only_boxed_clone(), version, indexes, ); - Self { - price, + let split = SplitOhlc { open, high, low, close, - } + }; + + let ohlc = LazyOhlcVecs::from_eager_ohlc_indexes::( + "price_ohlc", + version, + ¢s.ohlc, + ); + + Self { split, ohlc, price } } } diff --git a/crates/brk_computer/src/prices/usd/vecs.rs b/crates/brk_computer/src/prices/usd/vecs.rs index 9bc2d8c54..f156229e5 100644 --- a/crates/brk_computer/src/prices/usd/vecs.rs +++ b/crates/brk_computer/src/prices/usd/vecs.rs @@ -1,14 +1,19 @@ use brk_traversable::Traversable; -use brk_types::{Cents, Dollars, Height}; +use brk_types::{Cents, Dollars, Height, OHLCCents, OHLCDollars}; use vecdb::LazyVecFrom1; use crate::internal::{ComputedHeightDerivedLast, LazyEagerIndexes}; +use crate::prices::{ohlcs::LazyOhlcVecs, split::SplitOhlc}; #[derive(Clone, Traversable)] pub struct Vecs { + #[allow(clippy::type_complexity)] + pub split: SplitOhlc< + LazyEagerIndexes, + LazyEagerIndexes, + LazyEagerIndexes, + ComputedHeightDerivedLast, + >, + pub ohlc: LazyOhlcVecs, pub price: LazyVecFrom1, - pub open: LazyEagerIndexes, - pub high: LazyEagerIndexes, - pub low: LazyEagerIndexes, - pub close: ComputedHeightDerivedLast, } diff --git a/crates/brk_computer/src/scripts/value/compute.rs b/crates/brk_computer/src/scripts/value/compute.rs index d07053d5b..6a92f8033 100644 --- a/crates/brk_computer/src/scripts/value/compute.rs +++ b/crates/brk_computer/src/scripts/value/compute.rs @@ -62,16 +62,17 @@ impl Vecs { let out_start = first_txoutindex.to_usize(); let out_end = next_first_txoutindex.to_usize(); - // Sum opreturn values — batch read both ranges for the block - let values = indexer.vecs.outputs.value.collect_range_at(out_start, out_end); + // Sum opreturn values — fold over both vecs without allocation let opreturn_value = indexer.vecs.outputs.outputtype.fold_range_at( out_start, out_end, - (Sats::ZERO, 0_usize), - |(mut sum, idx), ot| { - if ot == OutputType::OpReturn { - sum += values[idx]; - } - (sum, idx + 1) + (Sats::ZERO, out_start), + |(sum, vi), ot| { + let new_sum = if ot == OutputType::OpReturn { + sum + indexer.vecs.outputs.value.collect_one_at(vi).unwrap() + } else { + sum + }; + (new_sum, vi + 1) }, ).0; diff --git a/crates/brk_computer/src/traits/mod.rs b/crates/brk_computer/src/traits/mod.rs index 8ce765d50..902f31a3b 100644 --- a/crates/brk_computer/src/traits/mod.rs +++ b/crates/brk_computer/src/traits/mod.rs @@ -5,6 +5,77 @@ use vecdb::{ WritableVec, }; +use crate::internal::sliding_window::SlidingWindowSorted; + +/// Unified rolling extremum (min or max) from window starts. +/// +/// `should_replace` determines whether to evict the deque back: +/// - For min: `|back, new| *back >= *new` +/// - For max: `|back, new| *back <= *new` +pub fn compute_rolling_extremum_from_starts( + out: &mut EagerVec>, + max_from: I, + window_starts: &impl ReadableVec, + values: &impl ReadableVec, + should_replace: fn(&A, &A) -> bool, + exit: &Exit, +) -> Result<()> +where + I: VecIndex, + T: PcoVecValue + From, + A: VecValue + Ord, +{ + out.validate_and_truncate(window_starts.version() + values.version(), max_from)?; + + out.repeat_until_complete(exit, |this| { + let skip = this.len(); + let mut deque: std::collections::VecDeque<(usize, A)> = + std::collections::VecDeque::new(); + + let start_offset = if skip > 0 { + window_starts.collect_one_at(skip - 1).unwrap().to_usize() + } else { + 0 + }; + + let end = window_starts.len().min(values.len()); + let starts_batch = window_starts.collect_range_at(start_offset, end); + let values_batch = values.collect_range_at(start_offset, end); + + for (j, (start, value)) in starts_batch.into_iter().zip(values_batch).enumerate() { + let i = start_offset + j; + let start_usize = start.to_usize(); + while let Some(&(idx, _)) = deque.front() { + if idx < start_usize { + deque.pop_front(); + } else { + break; + } + } + while let Some((_, back)) = deque.back() { + if should_replace(back, &value) { + deque.pop_back(); + } else { + break; + } + } + deque.push_back((i, value)); + + if i >= skip { + let extremum = deque.front().unwrap().1.clone(); + this.checked_push_at(i, T::from(extremum))?; + if this.batch_limit_reached() { + break; + } + } + } + + Ok(()) + })?; + + Ok(()) +} + pub trait ComputeRollingMinFromStarts { fn compute_rolling_min_from_starts( &mut self, @@ -34,56 +105,14 @@ where A: VecValue + Ord, T: From, { - self.validate_computed_version_or_reset(window_starts.version() + values.version())?; - self.truncate_if_needed(max_from)?; - - self.repeat_until_complete(exit, |this| { - let skip = this.len(); - let mut deque: std::collections::VecDeque<(usize, A)> = - std::collections::VecDeque::new(); - - let start_offset = if skip > 0 { - window_starts.collect_one_at(skip - 1).unwrap().to_usize() - } else { - 0 - }; - - let end = window_starts.len().min(values.len()); - let starts_batch = window_starts.collect_range_at(start_offset, end); - let values_batch = values.collect_range_at(start_offset, end); - - for (j, (start, value)) in starts_batch.into_iter().zip(values_batch).enumerate() { - let i = start_offset + j; - let start_usize = start.to_usize(); - while let Some(&(idx, _)) = deque.front() { - if idx < start_usize { - deque.pop_front(); - } else { - break; - } - } - while let Some((_, back)) = deque.back() { - if *back >= value { - deque.pop_back(); - } else { - break; - } - } - deque.push_back((i, value)); - - if i >= skip { - let min_val = deque.front().unwrap().1.clone(); - this.checked_push_at(i, T::from(min_val))?; - if this.batch_limit_reached() { - break; - } - } - } - - Ok(()) - })?; - - Ok(()) + compute_rolling_extremum_from_starts( + self, + max_from, + window_starts, + values, + |back, new| *back >= *new, + exit, + ) } } @@ -116,56 +145,14 @@ where A: VecValue + Ord, T: From, { - self.validate_computed_version_or_reset(window_starts.version() + values.version())?; - self.truncate_if_needed(max_from)?; - - self.repeat_until_complete(exit, |this| { - let skip = this.len(); - let mut deque: std::collections::VecDeque<(usize, A)> = - std::collections::VecDeque::new(); - - let start_offset = if skip > 0 { - window_starts.collect_one_at(skip - 1).unwrap().to_usize() - } else { - 0 - }; - - let end = window_starts.len().min(values.len()); - let starts_batch = window_starts.collect_range_at(start_offset, end); - let values_batch = values.collect_range_at(start_offset, end); - - for (j, (start, value)) in starts_batch.into_iter().zip(values_batch).enumerate() { - let i = start_offset + j; - let start_usize = start.to_usize(); - while let Some(&(idx, _)) = deque.front() { - if idx < start_usize { - deque.pop_front(); - } else { - break; - } - } - while let Some((_, back)) = deque.back() { - if *back <= value { - deque.pop_back(); - } else { - break; - } - } - deque.push_back((i, value)); - - if i >= skip { - let max_val = deque.front().unwrap().1.clone(); - this.checked_push_at(i, T::from(max_val))?; - if this.batch_limit_reached() { - break; - } - } - } - - Ok(()) - })?; - - Ok(()) + compute_rolling_extremum_from_starts( + self, + max_from, + window_starts, + values, + |back, new| *back <= *new, + exit, + ) } } @@ -198,70 +185,47 @@ where A: VecValue + Copy, f64: From, { - self.validate_computed_version_or_reset(window_starts.version() + values.version())?; - - self.truncate_if_needed(max_from)?; + self.validate_and_truncate(window_starts.version() + values.version(), max_from)?; self.repeat_until_complete(exit, |this| { let skip = this.len(); let end = window_starts.len().min(values.len()); - // Only collect the range needed: from window start of previous - // element to end. For incremental (1 block) this is ~window_size - // instead of the full history. 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 partial_values: Vec = values + .collect_range_at(range_start, end) + .into_iter() + .map(|a| f64::from(a)) + .collect(); - let mut sorted: Vec = Vec::new(); - let mut prev_start_usize: usize = range_start; + let capacity = if skip > 0 && skip < end { + let first_start = window_starts.collect_one_at(skip).unwrap().to_usize(); + (skip + 1).saturating_sub(first_start) + } else if !partial_values.is_empty() { + partial_values.len().min(1024) + } else { + 0 + }; + + let mut window = SlidingWindowSorted::with_capacity(capacity); - // Reconstruct 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); - }); + window.reconstruct(&partial_values, range_start, skip); } 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 v = partial_values[i - range_start]; 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 median = if sorted.is_empty() { - 0.0 - } else if sorted.len().is_multiple_of(2) { - let mid = sorted.len() / 2; - (sorted[mid - 1] + sorted[mid]) / 2.0 - } else { - sorted[sorted.len() / 2] - }; + window.advance(v, start_usize, &partial_values, range_start); + let median = window.percentile(0.50); this.checked_push_at(i, T::from(median))?; if this.batch_limit_reached() { @@ -278,11 +242,6 @@ where /// Compute all 8 rolling distribution stats (avg, min, max, p10, p25, median, p75, p90) /// in a single sorted-vec pass per window. -/// -/// Since the percentile pass already sorts data, min = sorted[0], max = sorted[last], -/// and average = running_sum / count — all extracted at negligible extra cost. -/// This replaces 3 separate passes (avg, min, max) + 1 percentile pass = 4 passes -/// with a single unified pass. #[allow(clippy::too_many_arguments)] pub fn compute_rolling_distribution_from_starts( max_from: I, @@ -306,34 +265,12 @@ where { let version = window_starts.version() + values.version(); - average_out.validate_computed_version_or_reset(version)?; - min_out.validate_computed_version_or_reset(version)?; - max_out.validate_computed_version_or_reset(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)?; + for v in [&mut *average_out, &mut *min_out, &mut *max_out, &mut *p10_out, &mut *p25_out, &mut *median_out, &mut *p75_out, &mut *p90_out] { + v.validate_and_truncate(version, max_from)?; + } - average_out.truncate_if_needed(max_from)?; - min_out.truncate_if_needed(max_from)?; - max_out.truncate_if_needed(max_from)?; - 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 8 vecs should be at the same length; use min to be safe - let skip = average_out - .len() - .min(min_out.len()) - .min(max_out.len()) - .min(p10_out.len()) - .min(p25_out.len()) - .min(median_out.len()) - .min(p75_out.len()) - .min(p90_out.len()); + let skip = [average_out.len(), min_out.len(), max_out.len(), p10_out.len(), p25_out.len(), median_out.len(), p75_out.len(), p90_out.len()] + .into_iter().min().unwrap(); let end = window_starts.len().min(values.len()); if skip >= end { @@ -345,113 +282,68 @@ where } else { 0 }; - let partial_values: Vec = values.collect_range_at(range_start, end); + let partial_values: Vec = values + .collect_range_at(range_start, end) + .into_iter() + .map(|a| f64::from(a)) + .collect(); - let mut sorted: Vec = Vec::new(); - let mut running_sum: f64 = 0.0; - let mut prev_start_usize: usize = range_start; + let capacity = if skip > 0 && skip < end { + let first_start = window_starts.collect_one_at(skip).unwrap().to_usize(); + (skip + 1).saturating_sub(first_start) + } else if !partial_values.is_empty() { + partial_values.len().min(1024) + } else { + 0 + }; + + let mut window = SlidingWindowSorted::with_capacity(capacity); - // Reconstruct sorted state + running sum from historical data if skip > 0 { - for idx in range_start..skip { - let v = f64::from(partial_values[idx - range_start]); - running_sum += v; - 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); - } + window.reconstruct(&partial_values, range_start, skip); } 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]); - running_sum += v; - 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 v = partial_values[i - range_start]; let start_usize = start.to_usize(); - while prev_start_usize < start_usize { - let old = f64::from(partial_values[prev_start_usize - range_start]); - running_sum -= old; - 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; - } + window.advance(v, start_usize, &partial_values, range_start); - let len = sorted.len(); - if len == 0 { + if window.is_empty() { let zero = T::from(0.0); - average_out.checked_push_at(i, zero)?; - min_out.checked_push_at(i, zero)?; - max_out.checked_push_at(i, zero)?; - 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)?; + for v in [&mut *average_out, &mut *min_out, &mut *max_out, &mut *p10_out, &mut *p25_out, &mut *median_out, &mut *p75_out, &mut *p90_out] { + v.checked_push_at(i, zero)?; + } } else { - average_out.checked_push_at(i, T::from(running_sum / len as f64))?; - min_out.checked_push_at(i, T::from(sorted[0]))?; - max_out.checked_push_at(i, T::from(sorted[len - 1]))?; - 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)))?; + average_out.checked_push_at(i, T::from(window.average()))?; + min_out.checked_push_at(i, T::from(window.min()))?; + max_out.checked_push_at(i, T::from(window.max()))?; + p10_out.checked_push_at(i, T::from(window.percentile(0.10)))?; + p25_out.checked_push_at(i, T::from(window.percentile(0.25)))?; + median_out.checked_push_at(i, T::from(window.percentile(0.50)))?; + p75_out.checked_push_at(i, T::from(window.percentile(0.75)))?; + p90_out.checked_push_at(i, T::from(window.percentile(0.90)))?; } if average_out.batch_limit_reached() { let _lock = exit.lock(); - average_out.write()?; - min_out.write()?; - max_out.write()?; - p10_out.write()?; - p25_out.write()?; - median_out.write()?; - p75_out.write()?; - p90_out.write()?; + for v in [&mut *average_out, &mut *min_out, &mut *max_out, &mut *p10_out, &mut *p25_out, &mut *median_out, &mut *p75_out, &mut *p90_out] { + v.write()?; + } } } // Final flush let _lock = exit.lock(); - average_out.write()?; - min_out.write()?; - max_out.write()?; - p10_out.write()?; - p25_out.write()?; - median_out.write()?; - p75_out.write()?; - p90_out.write()?; + for v in [average_out, min_out, max_out, p10_out, p25_out, median_out, p75_out, p90_out] { + v.write()?; + } 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/utils.rs b/crates/brk_computer/src/utils.rs deleted file mode 100644 index 3890bbb8f..000000000 --- a/crates/brk_computer/src/utils.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::ops::{Add, Div}; - -/// Extension trait for Option to provide shorter unwrap methods -pub trait OptionExt { - /// Shorthand for `.as_ref().unwrap()` - fn u(&self) -> &T; - /// Shorthand for `.as_mut().unwrap()` - fn um(&mut self) -> &mut T; -} - -impl OptionExt for Option { - #[inline] - fn u(&self) -> &T { - self.as_ref().unwrap() - } - - #[inline] - fn um(&mut self) -> &mut T { - self.as_mut().unwrap() - } -} - -pub(crate) fn get_percentile(sorted: &[T], percentile: f64) -> T -where - T: Clone + Div + Add, -{ - let len = sorted.len(); - - if len == 0 { - panic!(); - } else if len == 1 { - sorted[0].clone() - } else { - let index = (len - 1) as f64 * percentile; - - let fract = index.fract(); - - if fract != 0.0 { - let left = sorted.get(index as usize).unwrap().clone(); - let right = sorted.get(index.ceil() as usize).unwrap().clone(); - left / 2 + right / 2 - } else { - // dbg!(sorted.len(), index); - sorted.get(index as usize).unwrap().clone() - } - } -} diff --git a/crates/brk_query/src/impl/cost_basis.rs b/crates/brk_query/src/impl/cost_basis.rs index f409e048d..3384995a5 100644 --- a/crates/brk_query/src/impl/cost_basis.rs +++ b/crates/brk_query/src/impl/cost_basis.rs @@ -85,6 +85,7 @@ impl Query { let price = &self.computer().prices; let spot = price .cents + .split .close .day1 .collect_one_flat(day1) diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index 44c5d1035..1580a0b34 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -2763,7 +2763,7 @@ function createGreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern(clien /** * @typedef {Object} BlocksCoinbaseDaysDominanceFeeSubsidyPattern - * @property {CumulativeHeightRollingPattern} blocksMined + * @property {CumulativeHeightSumPattern} blocksMined * @property {MetricPattern1} blocksMined1mSum * @property {MetricPattern1} blocksMined1wSum * @property {MetricPattern1} blocksMined1ySum @@ -2788,7 +2788,7 @@ function createGreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern(clien */ function createBlocksCoinbaseDaysDominanceFeeSubsidyPattern(client, acc) { return { - blocksMined: createCumulativeHeightRollingPattern(client, _m(acc, 'blocks_mined')), + blocksMined: createCumulativeHeightSumPattern(client, _m(acc, 'blocks_mined')), blocksMined1mSum: createMetricPattern1(client, _m(acc, 'blocks_mined_1m_sum')), blocksMined1wSum: createMetricPattern1(client, _m(acc, 'blocks_mined_1w_sum')), blocksMined1ySum: createMetricPattern1(client, _m(acc, 'blocks_mined_1y_sum')), @@ -3455,8 +3455,8 @@ function createBalanceBothReactivatedReceivingSendingPattern(client, acc) { /** * @typedef {Object} CoinblocksCoindaysSatblocksSatdaysSentPattern - * @property {CumulativeHeightRollingPattern} coinblocksDestroyed - * @property {CumulativeHeightRollingPattern} coindaysDestroyed + * @property {CumulativeHeightSumPattern} coinblocksDestroyed + * @property {CumulativeHeightSumPattern} coindaysDestroyed * @property {MetricPattern20} satblocksDestroyed * @property {MetricPattern20} satdaysDestroyed * @property {BtcSatsUsdPattern2} sent @@ -3471,8 +3471,8 @@ function createBalanceBothReactivatedReceivingSendingPattern(client, acc) { */ function createCoinblocksCoindaysSatblocksSatdaysSentPattern(client, acc) { return { - coinblocksDestroyed: createCumulativeHeightRollingPattern(client, _m(acc, 'coinblocks_destroyed')), - coindaysDestroyed: createCumulativeHeightRollingPattern(client, _m(acc, 'coindays_destroyed')), + coinblocksDestroyed: createCumulativeHeightSumPattern(client, _m(acc, 'coinblocks_destroyed')), + coindaysDestroyed: createCumulativeHeightSumPattern(client, _m(acc, 'coindays_destroyed')), satblocksDestroyed: createMetricPattern20(client, _m(acc, 'satblocks_destroyed')), satdaysDestroyed: createMetricPattern20(client, _m(acc, 'satdays_destroyed')), sent: createBtcSatsUsdPattern2(client, _m(acc, 'sent')), @@ -3673,33 +3673,12 @@ function createBtcSatsUsdPattern2(client, acc) { } /** - * @typedef {Object} BtcSatsUsdPattern4 + * @typedef {Object} BtcSatsUsdPattern3 * @property {MetricPattern1} btc * @property {CumulativeHeightRollingPattern} sats * @property {CumulativeHeightRollingPattern} usd */ -/** - * Create a BtcSatsUsdPattern4 pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {BtcSatsUsdPattern4} - */ -function createBtcSatsUsdPattern4(client, acc) { - return { - btc: createMetricPattern1(client, _m(acc, 'btc')), - sats: createCumulativeHeightRollingPattern(client, acc), - usd: createCumulativeHeightRollingPattern(client, _m(acc, 'usd')), - }; -} - -/** - * @typedef {Object} BtcSatsUsdPattern3 - * @property {MetricPattern1} btc - * @property {CumulativeHeightRollingPattern2} sats - * @property {CumulativeHeightRollingPattern2} usd - */ - /** * Create a BtcSatsUsdPattern3 pattern node * @param {BrkClientBase} client @@ -3709,8 +3688,29 @@ function createBtcSatsUsdPattern4(client, acc) { function createBtcSatsUsdPattern3(client, acc) { return { btc: createMetricPattern1(client, _m(acc, 'btc')), - sats: createCumulativeHeightRollingPattern2(client, acc), - usd: createCumulativeHeightRollingPattern2(client, _m(acc, 'usd')), + sats: createCumulativeHeightRollingPattern(client, acc), + usd: createCumulativeHeightRollingPattern(client, _m(acc, 'usd')), + }; +} + +/** + * @typedef {Object} BtcSatsUsdPattern4 + * @property {MetricPattern1} btc + * @property {CumulativeHeightSumPattern} sats + * @property {CumulativeHeightSumPattern} usd + */ + +/** + * Create a BtcSatsUsdPattern4 pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {BtcSatsUsdPattern4} + */ +function createBtcSatsUsdPattern4(client, acc) { + return { + btc: createMetricPattern1(client, _m(acc, 'btc')), + sats: createCumulativeHeightSumPattern(client, acc), + usd: createCumulativeHeightSumPattern(client, _m(acc, 'usd')), }; } @@ -3756,35 +3756,12 @@ function createHistogramLineSignalPattern(client, acc) { }; } -/** - * @template T - * @typedef {Object} CumulativeHeightRollingPattern2 - * @property {MetricPattern1} cumulative - * @property {MetricPattern20} height - * @property {AverageMaxMedianMinP10P25P75P90SumPattern} rolling - */ - -/** - * Create a CumulativeHeightRollingPattern2 pattern node - * @template T - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {CumulativeHeightRollingPattern2} - */ -function createCumulativeHeightRollingPattern2(client, acc) { - return { - cumulative: createMetricPattern1(client, _m(acc, 'cumulative')), - height: createMetricPattern20(client, acc), - rolling: createAverageMaxMedianMinP10P25P75P90SumPattern(client, acc), - }; -} - /** * @template T * @typedef {Object} CumulativeHeightRollingPattern * @property {MetricPattern1} cumulative * @property {MetricPattern20} height - * @property {_1y24h30d7dPattern} rolling + * @property {AverageMaxMedianMinP10P25P75P90SumPattern} rolling */ /** @@ -3798,7 +3775,30 @@ function createCumulativeHeightRollingPattern(client, acc) { return { cumulative: createMetricPattern1(client, _m(acc, 'cumulative')), height: createMetricPattern20(client, acc), - rolling: create_1y24h30d7dPattern(client, acc), + rolling: createAverageMaxMedianMinP10P25P75P90SumPattern(client, acc), + }; +} + +/** + * @template T + * @typedef {Object} CumulativeHeightSumPattern + * @property {MetricPattern1} cumulative + * @property {MetricPattern20} height + * @property {_1y24h30d7dPattern} sum + */ + +/** + * Create a CumulativeHeightSumPattern pattern node + * @template T + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {CumulativeHeightSumPattern} + */ +function createCumulativeHeightSumPattern(client, acc) { + return { + cumulative: createMetricPattern1(client, _m(acc, 'cumulative')), + height: createMetricPattern20(client, acc), + sum: create_1y24h30d7dPattern(client, acc), }; } @@ -3986,7 +3986,7 @@ function createRatioPattern2(client, acc) { * @property {MetricsTree_Blocks_Count} count * @property {AverageHeightMaxMedianMinP10P25P75P90Pattern} interval * @property {MetricsTree_Blocks_Halving} halving - * @property {CumulativeHeightRollingPattern2} vbytes + * @property {CumulativeHeightRollingPattern} vbytes * @property {AverageCumulativeMaxMedianMinP10P25P75P90SumPattern} size * @property {AverageHeightMaxMedianMinP10P25P75P90Pattern} fullness */ @@ -4048,7 +4048,7 @@ function createRatioPattern2(client, acc) { /** * @typedef {Object} MetricsTree_Blocks_Count * @property {MetricPattern1} blockCountTarget - * @property {CumulativeHeightRollingPattern} blockCount + * @property {CumulativeHeightSumPattern} blockCount * @property {_1y24h30d7dPattern} blockCountSum * @property {MetricPattern20} height1hAgo * @property {MetricPattern20} height24hAgo @@ -4111,7 +4111,7 @@ function createRatioPattern2(client, acc) { /** * @typedef {Object} MetricsTree_Transactions_Count - * @property {CumulativeHeightRollingPattern2} txCount + * @property {CumulativeHeightRollingPattern} txCount * @property {MetricPattern21} isCoinbase */ @@ -4131,9 +4131,9 @@ function createRatioPattern2(client, acc) { /** * @typedef {Object} MetricsTree_Transactions_Versions - * @property {CumulativeHeightRollingPattern} v1 - * @property {CumulativeHeightRollingPattern} v2 - * @property {CumulativeHeightRollingPattern} v3 + * @property {CumulativeHeightSumPattern} v1 + * @property {CumulativeHeightSumPattern} v2 + * @property {CumulativeHeightSumPattern} v3 */ /** @@ -4221,19 +4221,19 @@ function createRatioPattern2(client, acc) { /** * @typedef {Object} MetricsTree_Scripts_Count - * @property {CumulativeHeightRollingPattern} p2a - * @property {CumulativeHeightRollingPattern} p2ms - * @property {CumulativeHeightRollingPattern} p2pk33 - * @property {CumulativeHeightRollingPattern} p2pk65 - * @property {CumulativeHeightRollingPattern} p2pkh - * @property {CumulativeHeightRollingPattern} p2sh - * @property {CumulativeHeightRollingPattern} p2tr - * @property {CumulativeHeightRollingPattern} p2wpkh - * @property {CumulativeHeightRollingPattern} p2wsh - * @property {CumulativeHeightRollingPattern} opreturn - * @property {CumulativeHeightRollingPattern} emptyoutput - * @property {CumulativeHeightRollingPattern} unknownoutput - * @property {CumulativeHeightRollingPattern} segwit + * @property {CumulativeHeightSumPattern} p2a + * @property {CumulativeHeightSumPattern} p2ms + * @property {CumulativeHeightSumPattern} p2pk33 + * @property {CumulativeHeightSumPattern} p2pk65 + * @property {CumulativeHeightSumPattern} p2pkh + * @property {CumulativeHeightSumPattern} p2sh + * @property {CumulativeHeightSumPattern} p2tr + * @property {CumulativeHeightSumPattern} p2wpkh + * @property {CumulativeHeightSumPattern} p2wsh + * @property {CumulativeHeightSumPattern} opreturn + * @property {CumulativeHeightSumPattern} emptyoutput + * @property {CumulativeHeightSumPattern} unknownoutput + * @property {CumulativeHeightSumPattern} segwit * @property {MetricPattern1} taprootAdoption * @property {MetricPattern1} segwitAdoption */ @@ -4308,8 +4308,8 @@ function createRatioPattern2(client, acc) { /** * @typedef {Object} MetricsTree_Cointime_Activity - * @property {CumulativeHeightRollingPattern} coinblocksCreated - * @property {CumulativeHeightRollingPattern} coinblocksStored + * @property {CumulativeHeightSumPattern} coinblocksCreated + * @property {CumulativeHeightSumPattern} coinblocksStored * @property {MetricPattern1} liveliness * @property {MetricPattern1} vaultedness * @property {MetricPattern1} activityToVaultednessRatio @@ -4323,10 +4323,10 @@ function createRatioPattern2(client, acc) { /** * @typedef {Object} MetricsTree_Cointime_Value - * @property {CumulativeHeightRollingPattern} cointimeValueDestroyed - * @property {CumulativeHeightRollingPattern} cointimeValueCreated - * @property {CumulativeHeightRollingPattern} cointimeValueStored - * @property {CumulativeHeightRollingPattern} vocdd + * @property {CumulativeHeightSumPattern} cointimeValueDestroyed + * @property {CumulativeHeightSumPattern} cointimeValueCreated + * @property {CumulativeHeightSumPattern} cointimeValueStored + * @property {CumulativeHeightSumPattern} vocdd */ /** @@ -6576,7 +6576,7 @@ class BrkClient extends BrkClientBase { }, count: { blockCountTarget: createMetricPattern1(this, 'block_count_target'), - blockCount: createCumulativeHeightRollingPattern(this, 'block_count'), + blockCount: createCumulativeHeightSumPattern(this, 'block_count'), blockCountSum: create_1y24h30d7dPattern(this, 'block_count_sum'), height1hAgo: createMetricPattern20(this, 'height_1h_ago'), height24hAgo: createMetricPattern20(this, 'height_24h_ago'), @@ -6616,7 +6616,7 @@ class BrkClient extends BrkClientBase { blocksBeforeNextHalving: createMetricPattern1(this, 'blocks_before_next_halving'), daysBeforeNextHalving: createMetricPattern1(this, 'days_before_next_halving'), }, - vbytes: createCumulativeHeightRollingPattern2(this, 'block_vbytes'), + vbytes: createCumulativeHeightRollingPattern(this, 'block_vbytes'), size: createAverageCumulativeMaxMedianMinP10P25P75P90SumPattern(this, 'block_size'), fullness: createAverageHeightMaxMedianMinP10P25P75P90Pattern(this, 'block_fullness'), }, @@ -6632,7 +6632,7 @@ class BrkClient extends BrkClientBase { firstTxinindex: createMetricPattern21(this, 'first_txinindex'), firstTxoutindex: createMetricPattern21(this, 'first_txoutindex'), count: { - txCount: createCumulativeHeightRollingPattern2(this, 'tx_count'), + txCount: createCumulativeHeightRollingPattern(this, 'tx_count'), isCoinbase: createMetricPattern21(this, 'is_coinbase'), }, size: { @@ -6646,9 +6646,9 @@ class BrkClient extends BrkClientBase { feeRate: create_1h24hBlockTxindexPattern(this, 'fee_rate'), }, versions: { - v1: createCumulativeHeightRollingPattern(this, 'tx_v1'), - v2: createCumulativeHeightRollingPattern(this, 'tx_v2'), - v3: createCumulativeHeightRollingPattern(this, 'tx_v3'), + v1: createCumulativeHeightSumPattern(this, 'tx_v1'), + v2: createCumulativeHeightSumPattern(this, 'tx_v2'), + v3: createCumulativeHeightSumPattern(this, 'tx_v3'), }, volume: { sentSum: createBtcRollingSatsUsdPattern(this, 'sent_sum'), @@ -6713,19 +6713,19 @@ class BrkClient extends BrkClientBase { p2msToTxindex: createMetricPattern27(this, 'txindex'), unknownToTxindex: createMetricPattern35(this, 'txindex'), count: { - p2a: createCumulativeHeightRollingPattern(this, 'p2a_count'), - p2ms: createCumulativeHeightRollingPattern(this, 'p2ms_count'), - p2pk33: createCumulativeHeightRollingPattern(this, 'p2pk33_count'), - p2pk65: createCumulativeHeightRollingPattern(this, 'p2pk65_count'), - p2pkh: createCumulativeHeightRollingPattern(this, 'p2pkh_count'), - p2sh: createCumulativeHeightRollingPattern(this, 'p2sh_count'), - p2tr: createCumulativeHeightRollingPattern(this, 'p2tr_count'), - p2wpkh: createCumulativeHeightRollingPattern(this, 'p2wpkh_count'), - p2wsh: createCumulativeHeightRollingPattern(this, 'p2wsh_count'), - opreturn: createCumulativeHeightRollingPattern(this, 'opreturn_count'), - emptyoutput: createCumulativeHeightRollingPattern(this, 'emptyoutput_count'), - unknownoutput: createCumulativeHeightRollingPattern(this, 'unknownoutput_count'), - segwit: createCumulativeHeightRollingPattern(this, 'segwit_count'), + p2a: createCumulativeHeightSumPattern(this, 'p2a_count'), + p2ms: createCumulativeHeightSumPattern(this, 'p2ms_count'), + p2pk33: createCumulativeHeightSumPattern(this, 'p2pk33_count'), + p2pk65: createCumulativeHeightSumPattern(this, 'p2pk65_count'), + p2pkh: createCumulativeHeightSumPattern(this, 'p2pkh_count'), + p2sh: createCumulativeHeightSumPattern(this, 'p2sh_count'), + p2tr: createCumulativeHeightSumPattern(this, 'p2tr_count'), + p2wpkh: createCumulativeHeightSumPattern(this, 'p2wpkh_count'), + p2wsh: createCumulativeHeightSumPattern(this, 'p2wsh_count'), + opreturn: createCumulativeHeightSumPattern(this, 'opreturn_count'), + emptyoutput: createCumulativeHeightSumPattern(this, 'emptyoutput_count'), + unknownoutput: createCumulativeHeightSumPattern(this, 'unknownoutput_count'), + segwit: createCumulativeHeightSumPattern(this, 'segwit_count'), taprootAdoption: createMetricPattern1(this, 'taproot_adoption'), segwitAdoption: createMetricPattern1(this, 'segwit_adoption'), }, @@ -6777,8 +6777,8 @@ class BrkClient extends BrkClientBase { }, cointime: { activity: { - coinblocksCreated: createCumulativeHeightRollingPattern(this, 'coinblocks_created'), - coinblocksStored: createCumulativeHeightRollingPattern(this, 'coinblocks_stored'), + coinblocksCreated: createCumulativeHeightSumPattern(this, 'coinblocks_created'), + coinblocksStored: createCumulativeHeightSumPattern(this, 'coinblocks_stored'), liveliness: createMetricPattern1(this, 'liveliness'), vaultedness: createMetricPattern1(this, 'vaultedness'), activityToVaultednessRatio: createMetricPattern1(this, 'activity_to_vaultedness_ratio'), @@ -6788,10 +6788,10 @@ class BrkClient extends BrkClientBase { activeSupply: createBtcSatsUsdPattern(this, 'active_supply'), }, value: { - cointimeValueDestroyed: createCumulativeHeightRollingPattern(this, 'cointime_value_destroyed'), - cointimeValueCreated: createCumulativeHeightRollingPattern(this, 'cointime_value_created'), - cointimeValueStored: createCumulativeHeightRollingPattern(this, 'cointime_value_stored'), - vocdd: createCumulativeHeightRollingPattern(this, 'vocdd'), + cointimeValueDestroyed: createCumulativeHeightSumPattern(this, 'cointime_value_destroyed'), + cointimeValueCreated: createCumulativeHeightSumPattern(this, 'cointime_value_created'), + cointimeValueStored: createCumulativeHeightSumPattern(this, 'cointime_value_stored'), + vocdd: createCumulativeHeightSumPattern(this, 'vocdd'), }, cap: { thermoCap: createMetricPattern1(this, 'thermo_cap'), diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index 67dd9d717..c98a3542a 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -2660,7 +2660,7 @@ class BlocksCoinbaseDaysDominanceFeeSubsidyPattern: def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" - self.blocks_mined: CumulativeHeightRollingPattern[StoredU32] = CumulativeHeightRollingPattern(client, _m(acc, 'blocks_mined')) + self.blocks_mined: CumulativeHeightSumPattern[StoredU32] = CumulativeHeightSumPattern(client, _m(acc, 'blocks_mined')) self.blocks_mined_1m_sum: MetricPattern1[StoredU32] = MetricPattern1(client, _m(acc, 'blocks_mined_1m_sum')) self.blocks_mined_1w_sum: MetricPattern1[StoredU32] = MetricPattern1(client, _m(acc, 'blocks_mined_1w_sum')) self.blocks_mined_1y_sum: MetricPattern1[StoredU32] = MetricPattern1(client, _m(acc, 'blocks_mined_1y_sum')) @@ -2972,8 +2972,8 @@ class CoinblocksCoindaysSatblocksSatdaysSentPattern: def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" - self.coinblocks_destroyed: CumulativeHeightRollingPattern[StoredF64] = CumulativeHeightRollingPattern(client, _m(acc, 'coinblocks_destroyed')) - self.coindays_destroyed: CumulativeHeightRollingPattern[StoredF64] = CumulativeHeightRollingPattern(client, _m(acc, 'coindays_destroyed')) + self.coinblocks_destroyed: CumulativeHeightSumPattern[StoredF64] = CumulativeHeightSumPattern(client, _m(acc, 'coinblocks_destroyed')) + self.coindays_destroyed: CumulativeHeightSumPattern[StoredF64] = CumulativeHeightSumPattern(client, _m(acc, 'coindays_destroyed')) self.satblocks_destroyed: MetricPattern20[Sats] = MetricPattern20(client, _m(acc, 'satblocks_destroyed')) self.satdays_destroyed: MetricPattern20[Sats] = MetricPattern20(client, _m(acc, 'satdays_destroyed')) self.sent: BtcSatsUsdPattern2 = BtcSatsUsdPattern2(client, _m(acc, 'sent')) @@ -3060,7 +3060,7 @@ class BtcSatsUsdPattern2: self.sats: CumulativeHeightPattern[Sats] = CumulativeHeightPattern(client, acc) self.usd: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd')) -class BtcSatsUsdPattern4: +class BtcSatsUsdPattern3: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): @@ -3069,14 +3069,14 @@ class BtcSatsUsdPattern4: self.sats: CumulativeHeightRollingPattern[Sats] = CumulativeHeightRollingPattern(client, acc) self.usd: CumulativeHeightRollingPattern[Dollars] = CumulativeHeightRollingPattern(client, _m(acc, 'usd')) -class BtcSatsUsdPattern3: +class BtcSatsUsdPattern4: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" self.btc: MetricPattern1[Bitcoin] = MetricPattern1(client, _m(acc, 'btc')) - self.sats: CumulativeHeightRollingPattern2[Sats] = CumulativeHeightRollingPattern2(client, acc) - self.usd: CumulativeHeightRollingPattern2[Dollars] = CumulativeHeightRollingPattern2(client, _m(acc, 'usd')) + self.sats: CumulativeHeightSumPattern[Sats] = CumulativeHeightSumPattern(client, acc) + self.usd: CumulativeHeightSumPattern[Dollars] = CumulativeHeightSumPattern(client, _m(acc, 'usd')) class BtcSatsUsdPattern: """Pattern struct for repeated tree structure.""" @@ -3096,7 +3096,7 @@ class HistogramLineSignalPattern: self.line: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'line_1y')) self.signal: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'signal_1y')) -class CumulativeHeightRollingPattern2(Generic[T]): +class CumulativeHeightRollingPattern(Generic[T]): """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): @@ -3105,14 +3105,14 @@ class CumulativeHeightRollingPattern2(Generic[T]): self.height: MetricPattern20[T] = MetricPattern20(client, acc) self.rolling: AverageMaxMedianMinP10P25P75P90SumPattern = AverageMaxMedianMinP10P25P75P90SumPattern(client, acc) -class CumulativeHeightRollingPattern(Generic[T]): +class CumulativeHeightSumPattern(Generic[T]): """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" self.cumulative: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'cumulative')) self.height: MetricPattern20[T] = MetricPattern20(client, acc) - self.rolling: _1y24h30d7dPattern[T] = _1y24h30d7dPattern(client, acc) + self.sum: _1y24h30d7dPattern[T] = _1y24h30d7dPattern(client, acc) class _30dCountPattern: """Pattern struct for repeated tree structure.""" @@ -3242,7 +3242,7 @@ class MetricsTree_Blocks_Count: def __init__(self, client: BrkClientBase, base_path: str = ''): self.block_count_target: MetricPattern1[StoredU64] = MetricPattern1(client, 'block_count_target') - self.block_count: CumulativeHeightRollingPattern[StoredU32] = CumulativeHeightRollingPattern(client, 'block_count') + self.block_count: CumulativeHeightSumPattern[StoredU32] = CumulativeHeightSumPattern(client, 'block_count') self.block_count_sum: _1y24h30d7dPattern[StoredU32] = _1y24h30d7dPattern(client, 'block_count_sum') self.height_1h_ago: MetricPattern20[Height] = MetricPattern20(client, 'height_1h_ago') self.height_24h_ago: MetricPattern20[Height] = MetricPattern20(client, 'height_24h_ago') @@ -3296,7 +3296,7 @@ class MetricsTree_Blocks: self.count: MetricsTree_Blocks_Count = MetricsTree_Blocks_Count(client) self.interval: AverageHeightMaxMedianMinP10P25P75P90Pattern[Timestamp] = AverageHeightMaxMedianMinP10P25P75P90Pattern(client, 'block_interval') self.halving: MetricsTree_Blocks_Halving = MetricsTree_Blocks_Halving(client) - self.vbytes: CumulativeHeightRollingPattern2[StoredU64] = CumulativeHeightRollingPattern2(client, 'block_vbytes') + self.vbytes: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'block_vbytes') self.size: AverageCumulativeMaxMedianMinP10P25P75P90SumPattern = AverageCumulativeMaxMedianMinP10P25P75P90SumPattern(client, 'block_size') self.fullness: AverageHeightMaxMedianMinP10P25P75P90Pattern[StoredF32] = AverageHeightMaxMedianMinP10P25P75P90Pattern(client, 'block_fullness') @@ -3304,7 +3304,7 @@ class MetricsTree_Transactions_Count: """Metrics tree node.""" def __init__(self, client: BrkClientBase, base_path: str = ''): - self.tx_count: CumulativeHeightRollingPattern2[StoredU64] = CumulativeHeightRollingPattern2(client, 'tx_count') + self.tx_count: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'tx_count') self.is_coinbase: MetricPattern21[StoredBool] = MetricPattern21(client, 'is_coinbase') class MetricsTree_Transactions_Size: @@ -3327,9 +3327,9 @@ class MetricsTree_Transactions_Versions: """Metrics tree node.""" def __init__(self, client: BrkClientBase, base_path: str = ''): - self.v1: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'tx_v1') - self.v2: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'tx_v2') - self.v3: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'tx_v3') + self.v1: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'tx_v1') + self.v2: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'tx_v2') + self.v3: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'tx_v3') class MetricsTree_Transactions_Volume: """Metrics tree node.""" @@ -3431,19 +3431,19 @@ class MetricsTree_Scripts_Count: """Metrics tree node.""" def __init__(self, client: BrkClientBase, base_path: str = ''): - self.p2a: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'p2a_count') - self.p2ms: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'p2ms_count') - self.p2pk33: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'p2pk33_count') - self.p2pk65: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'p2pk65_count') - self.p2pkh: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'p2pkh_count') - self.p2sh: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'p2sh_count') - self.p2tr: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'p2tr_count') - self.p2wpkh: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'p2wpkh_count') - self.p2wsh: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'p2wsh_count') - self.opreturn: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'opreturn_count') - self.emptyoutput: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'emptyoutput_count') - self.unknownoutput: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'unknownoutput_count') - self.segwit: CumulativeHeightRollingPattern[StoredU64] = CumulativeHeightRollingPattern(client, 'segwit_count') + self.p2a: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'p2a_count') + self.p2ms: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'p2ms_count') + self.p2pk33: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'p2pk33_count') + self.p2pk65: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'p2pk65_count') + self.p2pkh: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'p2pkh_count') + self.p2sh: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'p2sh_count') + self.p2tr: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'p2tr_count') + self.p2wpkh: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'p2wpkh_count') + self.p2wsh: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'p2wsh_count') + self.opreturn: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'opreturn_count') + self.emptyoutput: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'emptyoutput_count') + self.unknownoutput: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'unknownoutput_count') + self.segwit: CumulativeHeightSumPattern[StoredU64] = CumulativeHeightSumPattern(client, 'segwit_count') self.taproot_adoption: MetricPattern1[StoredF32] = MetricPattern1(client, 'taproot_adoption') self.segwit_adoption: MetricPattern1[StoredF32] = MetricPattern1(client, 'segwit_adoption') @@ -3528,8 +3528,8 @@ class MetricsTree_Cointime_Activity: """Metrics tree node.""" def __init__(self, client: BrkClientBase, base_path: str = ''): - self.coinblocks_created: CumulativeHeightRollingPattern[StoredF64] = CumulativeHeightRollingPattern(client, 'coinblocks_created') - self.coinblocks_stored: CumulativeHeightRollingPattern[StoredF64] = CumulativeHeightRollingPattern(client, 'coinblocks_stored') + self.coinblocks_created: CumulativeHeightSumPattern[StoredF64] = CumulativeHeightSumPattern(client, 'coinblocks_created') + self.coinblocks_stored: CumulativeHeightSumPattern[StoredF64] = CumulativeHeightSumPattern(client, 'coinblocks_stored') self.liveliness: MetricPattern1[StoredF64] = MetricPattern1(client, 'liveliness') self.vaultedness: MetricPattern1[StoredF64] = MetricPattern1(client, 'vaultedness') self.activity_to_vaultedness_ratio: MetricPattern1[StoredF64] = MetricPattern1(client, 'activity_to_vaultedness_ratio') @@ -3545,10 +3545,10 @@ class MetricsTree_Cointime_Value: """Metrics tree node.""" def __init__(self, client: BrkClientBase, base_path: str = ''): - self.cointime_value_destroyed: CumulativeHeightRollingPattern[StoredF64] = CumulativeHeightRollingPattern(client, 'cointime_value_destroyed') - self.cointime_value_created: CumulativeHeightRollingPattern[StoredF64] = CumulativeHeightRollingPattern(client, 'cointime_value_created') - self.cointime_value_stored: CumulativeHeightRollingPattern[StoredF64] = CumulativeHeightRollingPattern(client, 'cointime_value_stored') - self.vocdd: CumulativeHeightRollingPattern[StoredF64] = CumulativeHeightRollingPattern(client, 'vocdd') + self.cointime_value_destroyed: CumulativeHeightSumPattern[StoredF64] = CumulativeHeightSumPattern(client, 'cointime_value_destroyed') + self.cointime_value_created: CumulativeHeightSumPattern[StoredF64] = CumulativeHeightSumPattern(client, 'cointime_value_created') + self.cointime_value_stored: CumulativeHeightSumPattern[StoredF64] = CumulativeHeightSumPattern(client, 'cointime_value_stored') + self.vocdd: CumulativeHeightSumPattern[StoredF64] = CumulativeHeightSumPattern(client, 'vocdd') class MetricsTree_Cointime_Cap: """Metrics tree node.""" diff --git a/website/scripts/options/distribution/data.js b/website/scripts/options/distribution/data.js index 28cc35637..9095d22a3 100644 --- a/website/scripts/options/distribution/data.js +++ b/website/scripts/options/distribution/data.js @@ -55,7 +55,7 @@ export function buildCohortData() { name: shortNames.short, title: shortNames.long, color: colors.term.short, - tree: utxoCohorts.term.short, + tree: utxoCohorts.sth, }; const longNames = TERM_NAMES.long; @@ -63,7 +63,7 @@ export function buildCohortData() { name: longNames.short, title: longNames.long, color: colors.term.long, - tree: utxoCohorts.term.long, + tree: utxoCohorts.lth, }; // Max age cohorts (up to X time)