diff --git a/crates/brk_computer/src/blocks/count/compute.rs b/crates/brk_computer/src/blocks/count/compute.rs index 3abb1aa91..191e9ad31 100644 --- a/crates/brk_computer/src/blocks/count/compute.rs +++ b/crates/brk_computer/src/blocks/count/compute.rs @@ -15,17 +15,24 @@ impl Vecs { starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { + // Block count height + cumulative first (rolling computed after window starts) self.block_count.height.compute_range( starting_indexes.height, &indexer.vecs.blocks.weight, |h| (h, StoredU32::from(1_u32)), exit, )?; - self.block_count - .compute_cumulative(starting_indexes, exit)?; + self.block_count.cumulative.height.compute_cumulative( + starting_indexes.height, + &self.block_count.height, + exit, + )?; // Compute rolling window starts (collect monotonic data once for all windows) let monotonic_data: Vec = time.timestamp_monotonic.collect(); + self.compute_rolling_start_hours(&monotonic_data, time, starting_indexes, exit, 1, |s| { + &mut s.height_1h_ago + })?; self.compute_rolling_start(&monotonic_data, time, starting_indexes, exit, 1, |s| { &mut s.height_24h_ago })?; @@ -157,13 +164,19 @@ impl Vecs { |s| &mut s.height_10y_ago, )?; - // Compute rolling window block counts + // Compute rolling window block counts (both block_count's own rolling + separate block_count_sum) let ws = crate::internal::WindowStarts { _24h: &self.height_24h_ago, _7d: &self.height_1w_ago, _30d: &self.height_1m_ago, _1y: &self.height_1y_ago, }; + self.block_count.rolling.compute_rolling_sum( + starting_indexes.height, + &ws, + &self.block_count.height, + exit, + )?; self.block_count_sum.compute_rolling_sum( starting_indexes.height, &ws, @@ -202,4 +215,33 @@ impl Vecs { exit, )?) } + + fn compute_rolling_start_hours( + &mut self, + monotonic_data: &[Timestamp], + time: &time::Vecs, + starting_indexes: &ComputeIndexes, + exit: &Exit, + hours: usize, + get_field: F, + ) -> Result<()> + where + F: FnOnce(&mut Self) -> &mut EagerVec>, + { + let mut prev = Height::ZERO; + Ok(get_field(self).compute_transform( + starting_indexes.height, + &time.timestamp_monotonic, + |(h, t, ..)| { + while t.difference_in_hours_between(monotonic_data[prev.to_usize()]) >= hours { + prev.increment(); + if prev > h { + unreachable!() + } + } + (h, prev) + }, + exit, + )?) + } } diff --git a/crates/brk_computer/src/blocks/count/import.rs b/crates/brk_computer/src/blocks/count/import.rs index ac36c0ec9..fdac4dbdc 100644 --- a/crates/brk_computer/src/blocks/count/import.rs +++ b/crates/brk_computer/src/blocks/count/import.rs @@ -5,7 +5,7 @@ use vecdb::{Database, ImportableVec}; use super::Vecs; use crate::{ indexes, - internal::{BlockCountTarget, ComputedFromHeightSumCum, ConstantVecs, RollingWindows}, + internal::{BlockCountTarget, ComputedFromHeightCumulativeSum, ConstantVecs, RollingWindows}, }; impl Vecs { @@ -16,12 +16,13 @@ impl Vecs { version, indexes, ), - block_count: ComputedFromHeightSumCum::forced_import( + block_count: ComputedFromHeightCumulativeSum::forced_import( db, "block_count", version, indexes, )?, + height_1h_ago: ImportableVec::forced_import(db, "height_1h_ago", version)?, height_24h_ago: ImportableVec::forced_import(db, "height_24h_ago", version)?, height_3d_ago: ImportableVec::forced_import(db, "height_3d_ago", version)?, height_1w_ago: ImportableVec::forced_import(db, "height_1w_ago", version)?, diff --git a/crates/brk_computer/src/blocks/count/vecs.rs b/crates/brk_computer/src/blocks/count/vecs.rs index 87bdb0bfc..8e09c830a 100644 --- a/crates/brk_computer/src/blocks/count/vecs.rs +++ b/crates/brk_computer/src/blocks/count/vecs.rs @@ -2,14 +2,17 @@ use brk_traversable::Traversable; use brk_types::{Height, StoredU32, StoredU64}; use vecdb::{EagerVec, PcoVec, Rw, StorageMode}; -use crate::internal::{ComputedFromHeightSumCum, ConstantVecs, RollingWindows, WindowStarts}; +use crate::internal::{ + BlockWindowStarts, ComputedFromHeightCumulativeSum, ConstantVecs, RollingWindows, WindowStarts, +}; #[derive(Traversable)] pub struct Vecs { pub block_count_target: ConstantVecs, - pub block_count: ComputedFromHeightSumCum, + pub block_count: ComputedFromHeightCumulativeSum, pub block_count_sum: RollingWindows, + pub height_1h_ago: M::Stored>>, pub height_24h_ago: M::Stored>>, pub height_3d_ago: M::Stored>>, pub height_1w_ago: M::Stored>>, @@ -53,6 +56,14 @@ impl Vecs { } } + /// Get the 2 block-count rolling window start heights (1h, 24h) for tx-derived metrics. + pub fn block_window_starts(&self) -> BlockWindowStarts<'_> { + BlockWindowStarts { + _1h: &self.height_1h_ago, + _24h: &self.height_24h_ago, + } + } + pub fn start_vec(&self, days: usize) -> &EagerVec> { match days { 1 => &self.height_24h_ago, diff --git a/crates/brk_computer/src/blocks/import.rs b/crates/brk_computer/src/blocks/import.rs index b7ba09a51..25c29c3a0 100644 --- a/crates/brk_computer/src/blocks/import.rs +++ b/crates/brk_computer/src/blocks/import.rs @@ -27,8 +27,8 @@ impl Vecs { let count = CountVecs::forced_import(&db, version, indexes)?; let interval = IntervalVecs::forced_import(&db, version, indexes)?; - let size = SizeVecs::forced_import(&db, version, indexer, indexes)?; - let weight = WeightVecs::forced_import(&db, version, indexer, indexes)?; + let size = SizeVecs::forced_import(&db, version, indexes)?; + let weight = WeightVecs::forced_import(&db, version, indexes)?; let time = TimeVecs::forced_import(&db, version)?; let difficulty = DifficultyVecs::forced_import(&db, version, indexer, indexes)?; let halving = HalvingVecs::forced_import(&db, version, indexes)?; diff --git a/crates/brk_computer/src/blocks/size/import.rs b/crates/brk_computer/src/blocks/size/import.rs index 656d1eb9f..86f79b145 100644 --- a/crates/brk_computer/src/blocks/size/import.rs +++ b/crates/brk_computer/src/blocks/size/import.rs @@ -1,32 +1,29 @@ use brk_error::Result; -use brk_indexer::Indexer; use brk_types::Version; -use vecdb::{Database, ReadableCloneableVec}; +use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightCumFull, ComputedHeightDerivedCumFull}, + internal::{ComputedFromHeightCumulativeFull, ComputedHeightDerivedCumulativeFull}, }; impl Vecs { pub(crate) fn forced_import( db: &Database, version: Version, - indexer: &Indexer, indexes: &indexes::Vecs, ) -> Result { Ok(Self { - vbytes: ComputedFromHeightCumFull::forced_import( + vbytes: ComputedFromHeightCumulativeFull::forced_import( db, "block_vbytes", version, indexes, )?, - size: ComputedHeightDerivedCumFull::forced_import( + size: ComputedHeightDerivedCumulativeFull::forced_import( db, "block_size", - indexer.vecs.blocks.total_size.read_only_boxed_clone(), version, indexes, )?, diff --git a/crates/brk_computer/src/blocks/size/vecs.rs b/crates/brk_computer/src/blocks/size/vecs.rs index d31d8a69d..4de52b37d 100644 --- a/crates/brk_computer/src/blocks/size/vecs.rs +++ b/crates/brk_computer/src/blocks/size/vecs.rs @@ -2,10 +2,10 @@ use brk_traversable::Traversable; use brk_types::StoredU64; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightCumFull, ComputedHeightDerivedCumFull}; +use crate::internal::{ComputedFromHeightCumulativeFull, ComputedHeightDerivedCumulativeFull}; #[derive(Traversable)] pub struct Vecs { - pub vbytes: ComputedFromHeightCumFull, - pub size: ComputedHeightDerivedCumFull, + pub vbytes: ComputedFromHeightCumulativeFull, + pub size: ComputedHeightDerivedCumulativeFull, } diff --git a/crates/brk_computer/src/blocks/weight/import.rs b/crates/brk_computer/src/blocks/weight/import.rs index 513282a06..108a449e0 100644 --- a/crates/brk_computer/src/blocks/weight/import.rs +++ b/crates/brk_computer/src/blocks/weight/import.rs @@ -1,25 +1,22 @@ use brk_error::Result; -use brk_indexer::Indexer; use brk_types::Version; -use vecdb::{Database, ReadableCloneableVec}; +use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightLast, ComputedHeightDerivedCumFull, RollingDistribution}, + internal::{ComputedFromHeightLast, ComputedHeightDerivedCumulativeFull, RollingDistribution}, }; impl Vecs { pub(crate) fn forced_import( db: &Database, version: Version, - indexer: &Indexer, indexes: &indexes::Vecs, ) -> Result { - let weight = ComputedHeightDerivedCumFull::forced_import( + let weight = ComputedHeightDerivedCumulativeFull::forced_import( db, "block_weight", - indexer.vecs.blocks.weight.read_only_boxed_clone(), version, indexes, )?; diff --git a/crates/brk_computer/src/blocks/weight/vecs.rs b/crates/brk_computer/src/blocks/weight/vecs.rs index e07b91c7a..e196c1a4e 100644 --- a/crates/brk_computer/src/blocks/weight/vecs.rs +++ b/crates/brk_computer/src/blocks/weight/vecs.rs @@ -3,12 +3,12 @@ use brk_types::{StoredF32, Weight}; use vecdb::{Rw, StorageMode}; use crate::internal::{ - ComputedFromHeightLast, ComputedHeightDerivedCumFull, RollingDistribution, + ComputedFromHeightLast, ComputedHeightDerivedCumulativeFull, RollingDistribution, }; #[derive(Traversable)] pub struct Vecs { - pub weight: ComputedHeightDerivedCumFull, + pub weight: ComputedHeightDerivedCumulativeFull, pub fullness: ComputedFromHeightLast, pub fullness_rolling: RollingDistribution, } diff --git a/crates/brk_computer/src/cointime/activity/import.rs b/crates/brk_computer/src/cointime/activity/import.rs index d549f6539..3eca49bfe 100644 --- a/crates/brk_computer/src/cointime/activity/import.rs +++ b/crates/brk_computer/src/cointime/activity/import.rs @@ -5,19 +5,19 @@ use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightCumSum, ComputedFromHeightLast}, + internal::{ComputedFromHeightCumulativeSum, ComputedFromHeightLast}, }; impl Vecs { pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result { Ok(Self { - coinblocks_created: ComputedFromHeightCumSum::forced_import( + coinblocks_created: ComputedFromHeightCumulativeSum::forced_import( db, "coinblocks_created", version, indexes, )?, - coinblocks_stored: ComputedFromHeightCumSum::forced_import( + coinblocks_stored: ComputedFromHeightCumulativeSum::forced_import( db, "coinblocks_stored", version, diff --git a/crates/brk_computer/src/cointime/activity/vecs.rs b/crates/brk_computer/src/cointime/activity/vecs.rs index 71a98691b..2282aed54 100644 --- a/crates/brk_computer/src/cointime/activity/vecs.rs +++ b/crates/brk_computer/src/cointime/activity/vecs.rs @@ -2,12 +2,12 @@ use brk_traversable::Traversable; use brk_types::StoredF64; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightCumSum, ComputedFromHeightLast}; +use crate::internal::{ComputedFromHeightCumulativeSum, ComputedFromHeightLast}; #[derive(Traversable)] pub struct Vecs { - pub coinblocks_created: ComputedFromHeightCumSum, - pub coinblocks_stored: ComputedFromHeightCumSum, + pub coinblocks_created: ComputedFromHeightCumulativeSum, + pub coinblocks_stored: ComputedFromHeightCumulativeSum, pub liveliness: ComputedFromHeightLast, pub vaultedness: ComputedFromHeightLast, pub activity_to_vaultedness_ratio: ComputedFromHeightLast, diff --git a/crates/brk_computer/src/cointime/cap/compute.rs b/crates/brk_computer/src/cointime/cap/compute.rs index 8e6f71425..4faf90a02 100644 --- a/crates/brk_computer/src/cointime/cap/compute.rs +++ b/crates/brk_computer/src/cointime/cap/compute.rs @@ -36,7 +36,7 @@ impl Vecs { self.thermo_cap.height.compute_transform( starting_indexes.height, - &*mining.rewards.subsidy.usd.height_cumulative, + &mining.rewards.subsidy.usd.cumulative.height, |(i, v, ..)| (i, v), exit, )?; diff --git a/crates/brk_computer/src/cointime/import.rs b/crates/brk_computer/src/cointime/import.rs index 2be42cf25..40bfd808a 100644 --- a/crates/brk_computer/src/cointime/import.rs +++ b/crates/brk_computer/src/cointime/import.rs @@ -9,14 +9,13 @@ use super::{ ActivityVecs, AdjustedVecs, CapVecs, DB_NAME, PricingVecs, ReserveRiskVecs, SupplyVecs, VERSION, ValueVecs, Vecs, }; -use crate::{indexes, prices}; +use crate::indexes; impl Vecs { pub(crate) fn forced_import( parent_path: &Path, parent_version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { let db = Database::open(&parent_path.join(DB_NAME))?; db.set_min_len(PAGE_SIZE * 1_000_000)?; @@ -24,7 +23,7 @@ impl Vecs { let version = parent_version + VERSION; let v1 = version + Version::ONE; let activity = ActivityVecs::forced_import(&db, version, indexes)?; - let supply = SupplyVecs::forced_import(&db, v1, indexes, prices)?; + let supply = SupplyVecs::forced_import(&db, v1, indexes)?; let value = ValueVecs::forced_import(&db, v1, indexes)?; let cap = CapVecs::forced_import(&db, v1, indexes)?; let pricing = PricingVecs::forced_import(&db, version, indexes)?; diff --git a/crates/brk_computer/src/cointime/pricing/compute.rs b/crates/brk_computer/src/cointime/pricing/compute.rs index 832ccfd68..8e0c5bb15 100644 --- a/crates/brk_computer/src/cointime/pricing/compute.rs +++ b/crates/brk_computer/src/cointime/pricing/compute.rs @@ -32,9 +32,10 @@ impl Vecs { .metrics .realized .realized_price + .usd .height; - self.vaulted_price.height.compute_divide( + self.vaulted_price.usd.height.compute_divide( starting_indexes.height, realized_price, &activity.vaultedness.height, @@ -46,10 +47,10 @@ impl Vecs { prices, starting_indexes, exit, - Some(&self.vaulted_price.height), + Some(&self.vaulted_price.usd.height), )?; - self.active_price.height.compute_multiply( + self.active_price.usd.height.compute_multiply( starting_indexes.height, realized_price, &activity.liveliness.height, @@ -61,10 +62,10 @@ impl Vecs { prices, starting_indexes, exit, - Some(&self.active_price.height), + Some(&self.active_price.usd.height), )?; - self.true_market_mean.height.compute_divide( + self.true_market_mean.usd.height.compute_divide( starting_indexes.height, &cap.investor_cap.height, &supply.active_supply.btc.height, @@ -76,11 +77,11 @@ impl Vecs { prices, starting_indexes, exit, - Some(&self.true_market_mean.height), + Some(&self.true_market_mean.usd.height), )?; // cointime_price = cointime_cap / circulating_supply - self.cointime_price.height.compute_divide( + self.cointime_price.usd.height.compute_divide( starting_indexes.height, &cap.cointime_cap.height, circulating_supply, @@ -92,7 +93,7 @@ impl Vecs { prices, starting_indexes, exit, - Some(&self.cointime_price.height), + Some(&self.cointime_price.usd.height), )?; Ok(()) diff --git a/crates/brk_computer/src/cointime/pricing/import.rs b/crates/brk_computer/src/cointime/pricing/import.rs index c617152da..b9b2732db 100644 --- a/crates/brk_computer/src/cointime/pricing/import.rs +++ b/crates/brk_computer/src/cointime/pricing/import.rs @@ -5,7 +5,7 @@ use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightRatio, PriceFromHeight}, + internal::{ComputedFromHeightRatio, Price}, }; impl Vecs { @@ -14,43 +14,43 @@ impl Vecs { version: Version, indexes: &indexes::Vecs, ) -> Result { - let vaulted_price = PriceFromHeight::forced_import(db, "vaulted_price", version, indexes)?; + let vaulted_price = Price::forced_import(db, "vaulted_price", version, indexes)?; let vaulted_price_ratio = ComputedFromHeightRatio::forced_import( db, "vaulted_price", - Some(&vaulted_price), + Some(&vaulted_price.usd), version, indexes, true, )?; - let active_price = PriceFromHeight::forced_import(db, "active_price", version, indexes)?; + let active_price = Price::forced_import(db, "active_price", version, indexes)?; let active_price_ratio = ComputedFromHeightRatio::forced_import( db, "active_price", - Some(&active_price), + Some(&active_price.usd), version, indexes, true, )?; let true_market_mean = - PriceFromHeight::forced_import(db, "true_market_mean", version, indexes)?; + Price::forced_import(db, "true_market_mean", version, indexes)?; let true_market_mean_ratio = ComputedFromHeightRatio::forced_import( db, "true_market_mean", - Some(&true_market_mean), + Some(&true_market_mean.usd), version, indexes, true, )?; let cointime_price = - PriceFromHeight::forced_import(db, "cointime_price", version, indexes)?; + Price::forced_import(db, "cointime_price", version, indexes)?; let cointime_price_ratio = ComputedFromHeightRatio::forced_import( db, "cointime_price", - Some(&cointime_price), + Some(&cointime_price.usd), version, indexes, true, diff --git a/crates/brk_computer/src/cointime/supply/import.rs b/crates/brk_computer/src/cointime/supply/import.rs index df1bb5a1f..66032e405 100644 --- a/crates/brk_computer/src/cointime/supply/import.rs +++ b/crates/brk_computer/src/cointime/supply/import.rs @@ -3,14 +3,13 @@ use brk_types::Version; use vecdb::Database; use super::Vecs; -use crate::{indexes, internal::ValueFromHeightLast, prices}; +use crate::{indexes, internal::ValueFromHeightLast}; impl Vecs { pub(crate) fn forced_import( db: &Database, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { Ok(Self { vaulted_supply: ValueFromHeightLast::forced_import( @@ -18,14 +17,12 @@ impl Vecs { "vaulted_supply", version, indexes, - prices, )?, active_supply: ValueFromHeightLast::forced_import( db, "active_supply", version, indexes, - prices, )?, }) } diff --git a/crates/brk_computer/src/cointime/value/import.rs b/crates/brk_computer/src/cointime/value/import.rs index 6a863399f..734418927 100644 --- a/crates/brk_computer/src/cointime/value/import.rs +++ b/crates/brk_computer/src/cointime/value/import.rs @@ -3,30 +3,30 @@ use brk_types::Version; use vecdb::Database; use super::Vecs; -use crate::{indexes, internal::ComputedFromHeightCumSum}; +use crate::{indexes, internal::ComputedFromHeightCumulativeSum}; impl Vecs { pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result { Ok(Self { - cointime_value_destroyed: ComputedFromHeightCumSum::forced_import( + cointime_value_destroyed: ComputedFromHeightCumulativeSum::forced_import( db, "cointime_value_destroyed", version, indexes, )?, - cointime_value_created: ComputedFromHeightCumSum::forced_import( + cointime_value_created: ComputedFromHeightCumulativeSum::forced_import( db, "cointime_value_created", version, indexes, )?, - cointime_value_stored: ComputedFromHeightCumSum::forced_import( + cointime_value_stored: ComputedFromHeightCumulativeSum::forced_import( db, "cointime_value_stored", version, indexes, )?, - vocdd: ComputedFromHeightCumSum::forced_import( + vocdd: ComputedFromHeightCumulativeSum::forced_import( db, "vocdd", version + Version::ONE, diff --git a/crates/brk_computer/src/cointime/value/vecs.rs b/crates/brk_computer/src/cointime/value/vecs.rs index e1b4c5ab8..214b3a1b9 100644 --- a/crates/brk_computer/src/cointime/value/vecs.rs +++ b/crates/brk_computer/src/cointime/value/vecs.rs @@ -2,12 +2,12 @@ use brk_traversable::Traversable; use brk_types::StoredF64; use vecdb::{Rw, StorageMode}; -use crate::internal::ComputedFromHeightCumSum; +use crate::internal::ComputedFromHeightCumulativeSum; #[derive(Traversable)] pub struct Vecs { - pub cointime_value_destroyed: ComputedFromHeightCumSum, - pub cointime_value_created: ComputedFromHeightCumSum, - pub cointime_value_stored: ComputedFromHeightCumSum, - pub vocdd: ComputedFromHeightCumSum, + pub cointime_value_destroyed: ComputedFromHeightCumulativeSum, + pub cointime_value_created: ComputedFromHeightCumulativeSum, + pub cointime_value_stored: ComputedFromHeightCumulativeSum, + pub vocdd: ComputedFromHeightCumulativeSum, } diff --git a/crates/brk_computer/src/distribution/address/growth_rate.rs b/crates/brk_computer/src/distribution/address/growth_rate.rs index 4a07529f2..bf57891fb 100644 --- a/crates/brk_computer/src/distribution/address/growth_rate.rs +++ b/crates/brk_computer/src/distribution/address/growth_rate.rs @@ -1,75 +1,113 @@ //! Growth rate: new_addr_count / addr_count (global + per-type) -use brk_cohort::{ByAddressType, zip2_by_addresstype}; +use brk_cohort::ByAddressType; use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, StoredF32, StoredU64, Version}; -use vecdb::ReadableCloneableVec; +use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode}; use crate::{ indexes, - internal::{LazyBinaryComputedFromHeightDistribution, RatioU64F32}, + internal::{ComputedFromHeightDistribution, WindowStarts}, }; use super::{AddrCountsVecs, NewAddrCountVecs}; -/// Growth rate by type - lazy ratio with distribution stats -pub type GrowthRateByType = - ByAddressType>; - /// Growth rate: new_addr_count / addr_count (global + per-type) -#[derive(Clone, Traversable)] -pub struct GrowthRateVecs { - pub all: LazyBinaryComputedFromHeightDistribution, +#[derive(Traversable)] +pub struct GrowthRateVecs { + pub all: ComputedFromHeightDistribution, #[traversable(flatten)] - pub by_addresstype: GrowthRateByType, + pub by_addresstype: ByAddressType>, } impl GrowthRateVecs { pub(crate) fn forced_import( + db: &Database, version: Version, indexes: &indexes::Vecs, - new_addr_count: &NewAddrCountVecs, - addr_count: &AddrCountsVecs, ) -> Result { - let all = make_growth_rate( + let all = ComputedFromHeightDistribution::forced_import( + db, "growth_rate", version, indexes, - &new_addr_count.all.height, - &addr_count.all.count.height, - ); + )?; - let by_addresstype: GrowthRateByType = zip2_by_addresstype( - &new_addr_count.by_addresstype, - &addr_count.by_addresstype, - |name, new, addr| { - Ok(make_growth_rate( + let by_addresstype: ByAddressType> = + ByAddressType::new_with_name(|name| { + ComputedFromHeightDistribution::forced_import( + db, &format!("{name}_growth_rate"), version, indexes, - &new.height, - &addr.count.height, - )) - }, - )?; + ) + })?; Ok(Self { all, by_addresstype }) } + + pub(crate) fn compute( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + new_addr_count: &NewAddrCountVecs, + addr_count: &AddrCountsVecs, + exit: &Exit, + ) -> Result<()> { + self.all.compute(max_from, windows, exit, |target| { + compute_ratio( + target, + max_from, + &new_addr_count.all.height, + &addr_count.all.count.height, + exit, + ) + })?; + + for ((_, growth), ((_, new), (_, addr))) in self + .by_addresstype + .iter_mut() + .zip( + new_addr_count + .by_addresstype + .iter() + .zip(addr_count.by_addresstype.iter()), + ) + { + growth.compute(max_from, windows, exit, |target| { + compute_ratio( + target, + max_from, + &new.height, + &addr.count.height, + exit, + ) + })?; + } + + Ok(()) + } } -fn make_growth_rate( - name: &str, - version: Version, - indexes: &indexes::Vecs, - new: &V1, - addr: &V2, -) -> LazyBinaryComputedFromHeightDistribution -where - V1: ReadableCloneableVec, - V2: ReadableCloneableVec, -{ - LazyBinaryComputedFromHeightDistribution::::forced_import::< - RatioU64F32, - >(name, version, new.read_only_boxed_clone(), addr.read_only_boxed_clone(), indexes) +fn compute_ratio( + target: &mut EagerVec>, + max_from: Height, + numerator: &impl ReadableVec, + denominator: &impl ReadableVec, + exit: &Exit, +) -> Result<()> { + target.compute_transform2( + max_from, + numerator, + denominator, + |(h, num, den, ..)| { + let n = *num as f64; + let d = *den as f64; + let ratio = if d == 0.0 { 0.0 } else { n / d }; + (h, StoredF32::from(ratio)) + }, + exit, + )?; + Ok(()) } diff --git a/crates/brk_computer/src/distribution/address/indexes/any.rs b/crates/brk_computer/src/distribution/address/indexes/any.rs index 34b005a82..b7710f934 100644 --- a/crates/brk_computer/src/distribution/address/indexes/any.rs +++ b/crates/brk_computer/src/distribution/address/indexes/any.rs @@ -11,8 +11,8 @@ use brk_types::{ use rayon::prelude::*; use rustc_hash::FxHashMap; use vecdb::{ - AnyStoredVec, AnyVec, BytesVec, Database, ReadableVec, WritableVec, ImportOptions, ImportableVec, - Reader, Rw, Stamp, StorageMode, + AnyStoredVec, AnyVec, BytesVec, Database, ImportOptions, ImportableVec, ReadableVec, Reader, + Rw, Stamp, StorageMode, WritableVec, }; use super::super::AddressTypeToTypeIndexMap; diff --git a/crates/brk_computer/src/distribution/address/new_addr_count.rs b/crates/brk_computer/src/distribution/address/new_addr_count.rs index 8310faf94..94fad6a0f 100644 --- a/crates/brk_computer/src/distribution/address/new_addr_count.rs +++ b/crates/brk_computer/src/distribution/address/new_addr_count.rs @@ -1,25 +1,23 @@ //! New address count: delta of total_addr_count (global + per-type) +//! New address count: delta of total_addr_count (global + per-type) + use brk_cohort::{ByAddressType, zip_by_addresstype}; use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{StoredU64, Version}; +use brk_types::{Height, StoredU64, Version}; use vecdb::{Database, Exit, Ident, Rw, StorageMode}; -use crate::{ComputeIndexes, indexes, internal::LazyComputedFromHeightFull}; +use crate::{indexes, internal::{LazyComputedFromHeightFull, WindowStarts}}; use super::TotalAddrCountVecs; -/// New addresses by type - identity transform with stored day1 stats -/// The delta is computed at the compute step, not lazily -pub type NewAddrCountByType = ByAddressType>; - /// New address count per block (global + per-type) #[derive(Traversable)] pub struct NewAddrCountVecs { pub all: LazyComputedFromHeightFull, #[traversable(flatten)] - pub by_addresstype: NewAddrCountByType, + pub by_addresstype: ByAddressType>, } impl NewAddrCountVecs { @@ -37,7 +35,7 @@ impl NewAddrCountVecs { indexes, )?; - let by_addresstype: NewAddrCountByType = + let by_addresstype: ByAddressType> = zip_by_addresstype(&total_addr_count.by_addresstype, |name, total| { LazyComputedFromHeightFull::forced_import::( db, @@ -54,14 +52,15 @@ impl NewAddrCountVecs { }) } - pub(crate) fn compute_cumulative( + pub(crate) fn compute( &mut self, - starting_indexes: &ComputeIndexes, + max_from: Height, + windows: &WindowStarts<'_>, exit: &Exit, ) -> Result<()> { - self.all.compute_cumulative(starting_indexes, exit)?; + self.all.compute(max_from, windows, exit)?; for vecs in self.by_addresstype.values_mut() { - vecs.compute_cumulative(starting_indexes, exit)?; + vecs.compute(max_from, windows, exit)?; } Ok(()) } diff --git a/crates/brk_computer/src/distribution/address/total_addr_count.rs b/crates/brk_computer/src/distribution/address/total_addr_count.rs index fd66c2aea..4c984056b 100644 --- a/crates/brk_computer/src/distribution/address/total_addr_count.rs +++ b/crates/brk_computer/src/distribution/address/total_addr_count.rs @@ -1,57 +1,85 @@ //! Total address count: addr_count + empty_addr_count (global + per-type) -use brk_cohort::{ByAddressType, zip2_by_addresstype}; +use brk_cohort::ByAddressType; use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{StoredU64, Version}; -use vecdb::ReadableCloneableVec; +use brk_types::{Height, StoredU64, Version}; +use vecdb::{Database, Exit, Rw, StorageMode}; -use crate::{indexes, internal::{LazyBinaryComputedFromHeightLast, U64Plus}}; +use crate::{indexes, internal::ComputedFromHeightLast}; use super::AddrCountsVecs; -/// Total addresses by type - lazy sum with all derived indexes -pub type TotalAddrCountByType = - ByAddressType>; - /// Total address count (global + per-type) with all derived indexes -#[derive(Clone, Traversable)] -pub struct TotalAddrCountVecs { - pub all: LazyBinaryComputedFromHeightLast, +#[derive(Traversable)] +pub struct TotalAddrCountVecs { + pub all: ComputedFromHeightLast, #[traversable(flatten)] - pub by_addresstype: TotalAddrCountByType, + pub by_addresstype: ByAddressType>, } impl TotalAddrCountVecs { pub(crate) fn forced_import( + db: &Database, version: Version, indexes: &indexes::Vecs, - addr_count: &AddrCountsVecs, - empty_addr_count: &AddrCountsVecs, ) -> Result { - let all = LazyBinaryComputedFromHeightLast::forced_import::( + let all = ComputedFromHeightLast::forced_import( + db, "total_addr_count", version, - addr_count.all.count.height.read_only_boxed_clone(), - empty_addr_count.all.count.height.read_only_boxed_clone(), indexes, - ); + )?; - let by_addresstype: TotalAddrCountByType = zip2_by_addresstype( - &addr_count.by_addresstype, - &empty_addr_count.by_addresstype, - |name, addr, empty| { - Ok(LazyBinaryComputedFromHeightLast::forced_import::( + let by_addresstype: ByAddressType> = ByAddressType::new_with_name( + |name| { + ComputedFromHeightLast::forced_import( + db, &format!("{name}_total_addr_count"), version, - addr.count.height.read_only_boxed_clone(), - empty.count.height.read_only_boxed_clone(), indexes, - )) + ) }, )?; Ok(Self { all, by_addresstype }) } + /// Eagerly compute total = addr_count + empty_addr_count. + pub(crate) fn compute( + &mut self, + max_from: Height, + addr_count: &AddrCountsVecs, + empty_addr_count: &AddrCountsVecs, + exit: &Exit, + ) -> Result<()> { + self.all.height.compute_transform2( + max_from, + &addr_count.all.count.height, + &empty_addr_count.all.count.height, + |(h, a, b, ..)| (h, StoredU64::from(*a + *b)), + exit, + )?; + + for ((_, total), ((_, addr), (_, empty))) in self + .by_addresstype + .iter_mut() + .zip( + addr_count + .by_addresstype + .iter() + .zip(empty_addr_count.by_addresstype.iter()), + ) + { + total.height.compute_transform2( + max_from, + &addr.count.height, + &empty.count.height, + |(h, a, b, ..)| (h, StoredU64::from(*a + *b)), + exit, + )?; + } + + Ok(()) + } } diff --git a/crates/brk_computer/src/distribution/block/cache/address.rs b/crates/brk_computer/src/distribution/block/cache/address.rs index 090bc9843..716b308fd 100644 --- a/crates/brk_computer/src/distribution/block/cache/address.rs +++ b/crates/brk_computer/src/distribution/block/cache/address.rs @@ -1,24 +1,24 @@ use brk_cohort::ByAddressType; use brk_error::Result; -use brk_types::{AnyAddressDataIndexEnum, FundedAddressData, OutputType, TypeIndex}; +use brk_types::{ + AnyAddressDataIndexEnum, EmptyAddressData, FundedAddressData, OutputType, TxIndex, TypeIndex, +}; +use smallvec::SmallVec; use crate::distribution::{ address::{AddressTypeToTypeIndexMap, AddressesDataVecs, AnyAddressIndexesVecs}, compute::VecsReaders, }; -use super::super::cohort::{ - EmptyAddressDataWithSource, FundedAddressDataWithSource, TxIndexVec, WithAddressDataSource, - update_tx_counts, -}; +use super::super::cohort::{WithAddressDataSource, update_tx_counts}; use super::lookup::AddressLookup; /// Cache for address data within a flush interval. pub struct AddressCache { /// Addresses with non-zero balance - funded: AddressTypeToTypeIndexMap, + funded: AddressTypeToTypeIndexMap>, /// Addresses that became empty (zero balance) - empty: AddressTypeToTypeIndexMap, + empty: AddressTypeToTypeIndexMap>, } impl Default for AddressCache { @@ -49,7 +49,7 @@ impl AddressCache { /// Merge address data into funded cache. #[inline] - pub(crate) fn merge_funded(&mut self, data: AddressTypeToTypeIndexMap) { + pub(crate) fn merge_funded(&mut self, data: AddressTypeToTypeIndexMap>) { self.funded.merge_mut(data); } @@ -63,7 +63,7 @@ impl AddressCache { } /// Update transaction counts for addresses. - pub(crate) fn update_tx_counts(&mut self, txindex_vecs: AddressTypeToTypeIndexMap) { + pub(crate) fn update_tx_counts(&mut self, txindex_vecs: AddressTypeToTypeIndexMap>) { update_tx_counts(&mut self.funded, &mut self.empty, txindex_vecs); } @@ -71,8 +71,8 @@ impl AddressCache { pub(crate) fn take( &mut self, ) -> ( - AddressTypeToTypeIndexMap, - AddressTypeToTypeIndexMap, + AddressTypeToTypeIndexMap>, + AddressTypeToTypeIndexMap>, ) { ( std::mem::take(&mut self.empty), @@ -93,7 +93,7 @@ pub(crate) fn load_uncached_address_data( vr: &VecsReaders, any_address_indexes: &AnyAddressIndexesVecs, addresses_data: &AddressesDataVecs, -) -> Result> { +) -> Result>> { // Check if this is a new address (typeindex >= first for this height) let first = *first_addressindexes.get(address_type).unwrap(); if first <= typeindex { diff --git a/crates/brk_computer/src/distribution/block/cache/lookup.rs b/crates/brk_computer/src/distribution/block/cache/lookup.rs index 458dd8437..d089e5a49 100644 --- a/crates/brk_computer/src/distribution/block/cache/lookup.rs +++ b/crates/brk_computer/src/distribution/block/cache/lookup.rs @@ -1,10 +1,8 @@ -use brk_types::{FundedAddressData, OutputType, TypeIndex}; +use brk_types::{EmptyAddressData, FundedAddressData, OutputType, TypeIndex}; use crate::distribution::address::AddressTypeToTypeIndexMap; -use super::super::cohort::{ - EmptyAddressDataWithSource, FundedAddressDataWithSource, WithAddressDataSource, -}; +use super::super::cohort::WithAddressDataSource; /// Tracking status of an address - determines cohort update strategy. #[derive(Clone, Copy)] @@ -19,8 +17,8 @@ pub enum TrackingStatus { /// Context for looking up and storing address data during block processing. pub struct AddressLookup<'a> { - pub funded: &'a mut AddressTypeToTypeIndexMap, - pub empty: &'a mut AddressTypeToTypeIndexMap, + pub funded: &'a mut AddressTypeToTypeIndexMap>, + pub empty: &'a mut AddressTypeToTypeIndexMap>, } impl<'a> AddressLookup<'a> { @@ -28,7 +26,7 @@ impl<'a> AddressLookup<'a> { &mut self, output_type: OutputType, type_index: TypeIndex, - ) -> (&mut FundedAddressDataWithSource, TrackingStatus) { + ) -> (&mut WithAddressDataSource, TrackingStatus) { use std::collections::hash_map::Entry; let map = self.funded.get_mut(output_type).unwrap(); @@ -83,7 +81,7 @@ impl<'a> AddressLookup<'a> { &mut self, output_type: OutputType, type_index: TypeIndex, - ) -> &mut FundedAddressDataWithSource { + ) -> &mut WithAddressDataSource { self.funded .get_mut(output_type) .unwrap() diff --git a/crates/brk_computer/src/distribution/block/cohort/address_updates.rs b/crates/brk_computer/src/distribution/block/cohort/address_updates.rs index c1646e5cf..02847c69e 100644 --- a/crates/brk_computer/src/distribution/block/cohort/address_updates.rs +++ b/crates/brk_computer/src/distribution/block/cohort/address_updates.rs @@ -7,7 +7,7 @@ use vecdb::AnyVec; use crate::distribution::{AddressTypeToTypeIndexMap, AddressesDataVecs}; -use super::with_source::{EmptyAddressDataWithSource, FundedAddressDataWithSource}; +use super::with_source::WithAddressDataSource; /// Process funded address data updates. /// @@ -17,7 +17,7 @@ use super::with_source::{EmptyAddressDataWithSource, FundedAddressDataWithSource /// - Transition empty -> funded: delete from empty, push to funded pub(crate) fn process_funded_addresses( addresses_data: &mut AddressesDataVecs, - funded_updates: AddressTypeToTypeIndexMap, + funded_updates: AddressTypeToTypeIndexMap>, ) -> Result> { let total: usize = funded_updates.iter().map(|(_, m)| m.len()).sum(); @@ -28,13 +28,13 @@ pub(crate) fn process_funded_addresses( for (address_type, items) in funded_updates.into_iter() { for (typeindex, source) in items { match source { - FundedAddressDataWithSource::New(data) => { + WithAddressDataSource::New(data) => { pushes.push((address_type, typeindex, data)); } - FundedAddressDataWithSource::FromFunded(index, data) => { + WithAddressDataSource::FromFunded(index, data) => { updates.push((index, data)); } - FundedAddressDataWithSource::FromEmpty(empty_index, data) => { + WithAddressDataSource::FromEmpty(empty_index, data) => { deletes.push(empty_index); pushes.push((address_type, typeindex, data)); } @@ -88,7 +88,7 @@ pub(crate) fn process_funded_addresses( /// - Transition funded -> empty: delete from funded, push to empty pub(crate) fn process_empty_addresses( addresses_data: &mut AddressesDataVecs, - empty_updates: AddressTypeToTypeIndexMap, + empty_updates: AddressTypeToTypeIndexMap>, ) -> Result> { let total: usize = empty_updates.iter().map(|(_, m)| m.len()).sum(); @@ -99,13 +99,13 @@ pub(crate) fn process_empty_addresses( for (address_type, items) in empty_updates.into_iter() { for (typeindex, source) in items { match source { - EmptyAddressDataWithSource::New(data) => { + WithAddressDataSource::New(data) => { pushes.push((address_type, typeindex, data)); } - EmptyAddressDataWithSource::FromEmpty(index, data) => { + WithAddressDataSource::FromEmpty(index, data) => { updates.push((index, data)); } - EmptyAddressDataWithSource::FromFunded(funded_index, data) => { + WithAddressDataSource::FromFunded(funded_index, data) => { deletes.push(funded_index); pushes.push((address_type, typeindex, data)); } diff --git a/crates/brk_computer/src/distribution/block/cohort/tx_counts.rs b/crates/brk_computer/src/distribution/block/cohort/tx_counts.rs index d75437460..0ed5f261d 100644 --- a/crates/brk_computer/src/distribution/block/cohort/tx_counts.rs +++ b/crates/brk_computer/src/distribution/block/cohort/tx_counts.rs @@ -1,6 +1,9 @@ +use brk_types::{EmptyAddressData, FundedAddressData, TxIndex}; +use smallvec::SmallVec; + use crate::distribution::address::AddressTypeToTypeIndexMap; -use super::with_source::{EmptyAddressDataWithSource, FundedAddressDataWithSource, TxIndexVec}; +use super::with_source::WithAddressDataSource; /// Update tx_count for addresses based on unique transactions they participated in. /// @@ -11,9 +14,9 @@ use super::with_source::{EmptyAddressDataWithSource, FundedAddressDataWithSource /// Addresses are looked up in funded_cache first, then empty_cache. /// NOTE: This should be called AFTER merging parallel-fetched address data into funded_cache. pub(crate) fn update_tx_counts( - funded_cache: &mut AddressTypeToTypeIndexMap, - empty_cache: &mut AddressTypeToTypeIndexMap, - mut txindex_vecs: AddressTypeToTypeIndexMap, + funded_cache: &mut AddressTypeToTypeIndexMap>, + empty_cache: &mut AddressTypeToTypeIndexMap>, + mut txindex_vecs: AddressTypeToTypeIndexMap>, ) { // First, deduplicate txindex_vecs for addresses that appear multiple times in a block for (_, map) in txindex_vecs.iter_mut() { diff --git a/crates/brk_computer/src/distribution/block/cohort/with_source.rs b/crates/brk_computer/src/distribution/block/cohort/with_source.rs index 5c95f5937..a02ef4513 100644 --- a/crates/brk_computer/src/distribution/block/cohort/with_source.rs +++ b/crates/brk_computer/src/distribution/block/cohort/with_source.rs @@ -1,16 +1,4 @@ -use brk_types::{ - EmptyAddressData, EmptyAddressIndex, FundedAddressData, FundedAddressIndex, TxIndex, -}; -use smallvec::SmallVec; - -/// Funded address data with source tracking for flush operations. -pub type FundedAddressDataWithSource = WithAddressDataSource; - -/// Empty address data with source tracking for flush operations. -pub type EmptyAddressDataWithSource = WithAddressDataSource; - -/// SmallVec for transaction indexes - most addresses have few transactions per block. -pub type TxIndexVec = SmallVec<[TxIndex; 4]>; +use brk_types::{EmptyAddressData, EmptyAddressIndex, FundedAddressData, FundedAddressIndex}; /// Address data wrapped with its source location for flush operations. /// diff --git a/crates/brk_computer/src/distribution/block/utxo/inputs.rs b/crates/brk_computer/src/distribution/block/utxo/inputs.rs index 79bee78b6..f4f6458a7 100644 --- a/crates/brk_computer/src/distribution/block/utxo/inputs.rs +++ b/crates/brk_computer/src/distribution/block/utxo/inputs.rs @@ -1,8 +1,9 @@ use brk_cohort::ByAddressType; use brk_error::Result; -use brk_types::{Height, OutputType, Sats, TxIndex, TypeIndex}; +use brk_types::{FundedAddressData, Height, OutputType, Sats, TxIndex, TypeIndex}; use rayon::prelude::*; use rustc_hash::FxHashMap; +use smallvec::SmallVec; use crate::distribution::{ address::{AddressTypeToTypeIndexMap, AddressesDataVecs, AnyAddressIndexesVecs}, @@ -14,7 +15,7 @@ use crate::distribution::address::HeightToAddressTypeToVec; use super::super::{ cache::{AddressCache, load_uncached_address_data}, - cohort::{FundedAddressDataWithSource, TxIndexVec}, + cohort::WithAddressDataSource, }; /// Result of processing inputs for a block. @@ -24,9 +25,9 @@ pub struct InputsResult { /// Per-height, per-address-type sent data: (typeindex, value) for each address. pub sent_data: HeightToAddressTypeToVec<(TypeIndex, Sats)>, /// Address data looked up during processing, keyed by (address_type, typeindex). - pub address_data: AddressTypeToTypeIndexMap, + pub address_data: AddressTypeToTypeIndexMap>, /// Transaction indexes per address for tx_count tracking. - pub txindex_vecs: AddressTypeToTypeIndexMap, + pub txindex_vecs: AddressTypeToTypeIndexMap>, } /// Process inputs (spent UTXOs) for a block. @@ -101,9 +102,9 @@ pub(crate) fn process_inputs( ); let mut sent_data = HeightToAddressTypeToVec::with_capacity(estimated_unique_heights); let mut address_data = - AddressTypeToTypeIndexMap::::with_capacity(estimated_per_type); + AddressTypeToTypeIndexMap::>::with_capacity(estimated_per_type); let mut txindex_vecs = - AddressTypeToTypeIndexMap::::with_capacity(estimated_per_type); + AddressTypeToTypeIndexMap::>::with_capacity(estimated_per_type); for (prev_height, value, output_type, addr_info) in items { height_to_sent diff --git a/crates/brk_computer/src/distribution/block/utxo/outputs.rs b/crates/brk_computer/src/distribution/block/utxo/outputs.rs index 9d50f05e0..fdd5a1a79 100644 --- a/crates/brk_computer/src/distribution/block/utxo/outputs.rs +++ b/crates/brk_computer/src/distribution/block/utxo/outputs.rs @@ -1,6 +1,7 @@ use brk_cohort::ByAddressType; use brk_error::Result; -use brk_types::{Sats, TxIndex, TypeIndex}; +use brk_types::{FundedAddressData, Sats, TxIndex, TypeIndex}; +use smallvec::SmallVec; use crate::distribution::{ address::{ @@ -12,7 +13,7 @@ use crate::distribution::{ use super::super::{ cache::{AddressCache, load_uncached_address_data}, - cohort::{FundedAddressDataWithSource, TxIndexVec}, + cohort::WithAddressDataSource, }; /// Result of processing outputs for a block. @@ -22,9 +23,9 @@ pub struct OutputsResult { /// Per-address-type received data: (typeindex, value) for each address. pub received_data: AddressTypeToVec<(TypeIndex, Sats)>, /// Address data looked up during processing, keyed by (address_type, typeindex). - pub address_data: AddressTypeToTypeIndexMap, + pub address_data: AddressTypeToTypeIndexMap>, /// Transaction indexes per address for tx_count tracking. - pub txindex_vecs: AddressTypeToTypeIndexMap, + pub txindex_vecs: AddressTypeToTypeIndexMap>, } /// Process outputs (new UTXOs) for a block. @@ -51,9 +52,9 @@ pub(crate) fn process_outputs( let mut transacted = Transacted::default(); let mut received_data = AddressTypeToVec::with_capacity(estimated_per_type); let mut address_data = - AddressTypeToTypeIndexMap::::with_capacity(estimated_per_type); + AddressTypeToTypeIndexMap::>::with_capacity(estimated_per_type); let mut txindex_vecs = - AddressTypeToTypeIndexMap::::with_capacity(estimated_per_type); + AddressTypeToTypeIndexMap::>::with_capacity(estimated_per_type); // Single pass: read from pre-collected vecs and accumulate for (local_idx, txoutdata) in txoutdata_vec.iter().enumerate() { diff --git a/crates/brk_computer/src/distribution/cohorts/address/groups.rs b/crates/brk_computer/src/distribution/cohorts/address/groups.rs index 4f0b926a9..85340ea6e 100644 --- a/crates/brk_computer/src/distribution/cohorts/address/groups.rs +++ b/crates/brk_computer/src/distribution/cohorts/address/groups.rs @@ -5,14 +5,14 @@ use brk_cohort::{ }; use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Dollars, Height, Version}; +use brk_types::{Dollars, Height, Sats, Version}; use derive_more::{Deref, DerefMut}; use rayon::prelude::*; use vecdb::{AnyStoredVec, Database, Exit, ReadableVec, Rw, StorageMode}; use crate::{ComputeIndexes, blocks, distribution::DynCohortVecs, indexes, prices}; -use crate::distribution::metrics::{CohortMetricsBase, SupplyMetrics}; +use crate::distribution::metrics::CohortMetricsBase; use super::{super::traits::CohortVecs, vecs::AddressCohortVecs}; @@ -24,16 +24,11 @@ pub struct AddressCohorts(AddressGroups Result { let v = version + VERSION; @@ -43,7 +38,7 @@ impl AddressCohorts { has_state: bool| -> Result { let sp = if has_state { Some(states_path) } else { None }; - AddressCohortVecs::forced_import(db, filter, name, v, indexes, prices, sp, all_supply) + AddressCohortVecs::forced_import(db, filter, name, v, indexes, sp) }; let full = |f: Filter, name: &'static str| create(f, name, true); @@ -135,16 +130,18 @@ impl AddressCohorts { } /// Second phase of post-processing: compute relative metrics. - pub(crate) fn compute_rest_part2( + pub(crate) fn compute_rest_part2( &mut self, blocks: &blocks::Vecs, prices: &prices::Vecs, starting_indexes: &ComputeIndexes, height_to_market_cap: &HM, + all_supply_sats: &AS, exit: &Exit, ) -> Result<()> where HM: ReadableVec + Sync, + AS: ReadableVec + Sync, { self.0.par_iter_mut().try_for_each(|v| { v.compute_rest_part2( @@ -152,6 +149,7 @@ impl AddressCohorts { prices, starting_indexes, height_to_market_cap, + all_supply_sats, exit, ) }) diff --git a/crates/brk_computer/src/distribution/cohorts/address/vecs.rs b/crates/brk_computer/src/distribution/cohorts/address/vecs.rs index f2ddfbdac..a1bc6b8d2 100644 --- a/crates/brk_computer/src/distribution/cohorts/address/vecs.rs +++ b/crates/brk_computer/src/distribution/cohorts/address/vecs.rs @@ -3,7 +3,7 @@ use std::path::Path; use brk_cohort::{CohortContext, Filter, Filtered}; use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Cents, Dollars, Height, StoredF64, StoredU64, Version}; +use brk_types::{Cents, Dollars, Height, Sats, StoredF64, StoredU64, Version}; use rayon::prelude::*; use vecdb::{AnyStoredVec, AnyVec, Database, Exit, WritableVec, ReadableVec, Rw, StorageMode}; @@ -15,7 +15,7 @@ use crate::{ prices, }; -use crate::distribution::metrics::{BasicCohortMetrics, CohortMetricsBase, ImportConfig, SupplyMetrics}; +use crate::distribution::metrics::{BasicCohortMetrics, CohortMetricsBase, ImportConfig}; use super::super::traits::{CohortVecs, DynCohortVecs}; @@ -41,19 +41,13 @@ pub struct AddressCohortVecs { impl AddressCohortVecs { /// Import address cohort from database. - /// - /// `all_supply` is the supply metrics from the "all" cohort, used as global - /// sources for `*_rel_to_market_cap` ratios. - #[allow(clippy::too_many_arguments)] pub(crate) fn forced_import( db: &Database, filter: Filter, name: &str, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, states_path: Option<&Path>, - all_supply: &SupplyMetrics, ) -> Result { let full_name = CohortContext::Address.full_name(&filter, name); @@ -64,7 +58,6 @@ impl AddressCohortVecs { context: CohortContext::Address, version, indexes, - prices, }; Ok(Self { @@ -73,7 +66,7 @@ impl AddressCohortVecs { state: states_path .map(|path| Box::new(AddressCohortState::new(path, &full_name))), - metrics: BasicCohortMetrics::forced_import(&cfg, all_supply)?, + metrics: BasicCohortMetrics::forced_import(&cfg)?, addr_count: ComputedFromHeightLast::forced_import( db, @@ -287,6 +280,7 @@ impl CohortVecs for AddressCohortVecs { prices: &prices::Vecs, starting_indexes: &ComputeIndexes, height_to_market_cap: &impl ReadableVec, + all_supply_sats: &impl ReadableVec, exit: &Exit, ) -> Result<()> { self.metrics.compute_rest_part2( @@ -294,6 +288,7 @@ impl CohortVecs for AddressCohortVecs { prices, starting_indexes, height_to_market_cap, + all_supply_sats, exit, )?; Ok(()) diff --git a/crates/brk_computer/src/distribution/cohorts/traits.rs b/crates/brk_computer/src/distribution/cohorts/traits.rs index f975d39d3..9576c6c56 100644 --- a/crates/brk_computer/src/distribution/cohorts/traits.rs +++ b/crates/brk_computer/src/distribution/cohorts/traits.rs @@ -1,5 +1,5 @@ use brk_error::Result; -use brk_types::{Cents, Dollars, Height, Version}; +use brk_types::{Cents, Dollars, Height, Sats, Version}; use vecdb::{Exit, ReadableVec}; use crate::{ComputeIndexes, blocks, prices}; @@ -75,6 +75,7 @@ pub trait CohortVecs: DynCohortVecs { prices: &prices::Vecs, starting_indexes: &ComputeIndexes, height_to_market_cap: &impl ReadableVec, + all_supply_sats: &impl ReadableVec, exit: &Exit, ) -> Result<()>; } diff --git a/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs b/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs index 61c29f196..e6d7a7c84 100644 --- a/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs +++ b/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs @@ -11,7 +11,7 @@ use brk_types::{ StoredF32, Timestamp, Version, }; use rayon::prelude::*; -use vecdb::{AnyStoredVec, Database, Exit, ReadableVec, Rw, StorageMode, VecIndex, WritableVec}; +use vecdb::{AnyStoredVec, Database, Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode, VecIndex, WritableVec}; use crate::{ ComputeIndexes, blocks, @@ -24,7 +24,7 @@ use crate::{ use crate::distribution::metrics::{ AdjustedCohortMetrics, AllCohortMetrics, BasicCohortMetrics, CohortMetricsBase, ExtendedAdjustedCohortMetrics, ExtendedCohortMetrics, ImportConfig, PeakRegretCohortMetrics, - RealizedBase, SupplyMetrics, + SupplyMetrics, }; use super::vecs::UTXOCohortVecs; @@ -68,7 +68,6 @@ impl UTXOCohorts { db: &Database, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, states_path: &Path, ) -> Result { let v = version + VERSION; @@ -82,7 +81,6 @@ impl UTXOCohorts { context: CohortContext::Utxo, version: v + Version::ONE, indexes, - prices, }; let all_supply = SupplyMetrics::forced_import(&all_cfg)?; @@ -90,7 +88,6 @@ impl UTXOCohorts { // age_range: ExtendedCohortMetrics with full state let age_range = { - let s = &all_supply; ByAgeRange::try_new(&|f: Filter, name: &'static str| -> Result<_> { let full_name = CohortContext::Utxo.full_name(&f, name); let cfg = ImportConfig { @@ -100,12 +97,11 @@ impl UTXOCohorts { context: CohortContext::Utxo, version: v, indexes, - prices, }; let state = Some(Box::new(UTXOCohortState::new(states_path, &full_name))); Ok(UTXOCohortVecs::new( state, - ExtendedCohortMetrics::forced_import(&cfg, s)?, + ExtendedCohortMetrics::forced_import(&cfg)?, )) })? }; @@ -121,12 +117,11 @@ impl UTXOCohorts { context: CohortContext::Utxo, version: v, indexes, - prices, }; let state = Some(Box::new(UTXOCohortState::new(states_path, &full_name))); Ok(UTXOCohortVecs::new( state, - BasicCohortMetrics::forced_import(&cfg, &all_supply)?, + BasicCohortMetrics::forced_import(&cfg)?, )) }; @@ -135,18 +130,13 @@ impl UTXOCohorts { let year = ByYear::try_new(&basic_separate)?; let type_ = BySpendableType::try_new(&basic_separate)?; - // Phase 3: Get up_to_1h realized for adjusted computation. - let up_to_1h_realized: &RealizedBase = &age_range.up_to_1h.metrics.realized; - - // Phase 4: Import "all" cohort with pre-imported supply. + // Phase 3: Import "all" cohort with pre-imported supply. let all = UTXOCohortVecs::new( None, - AllCohortMetrics::forced_import_with_supply(&all_cfg, all_supply, up_to_1h_realized)?, + AllCohortMetrics::forced_import_with_supply(&all_cfg, all_supply)?, ); - let all_supply_ref = &all.metrics.supply; - - // Phase 5: Import aggregate cohorts. + // Phase 4: Import aggregate cohorts. // sth: ExtendedAdjustedCohortMetrics let sth = { @@ -159,14 +149,11 @@ impl UTXOCohorts { context: CohortContext::Utxo, version: v, indexes, - prices, }; UTXOCohortVecs::new( None, ExtendedAdjustedCohortMetrics::forced_import( &cfg, - all_supply_ref, - up_to_1h_realized, )?, ) }; @@ -182,17 +169,15 @@ impl UTXOCohorts { context: CohortContext::Utxo, version: v, indexes, - prices, }; UTXOCohortVecs::new( None, - ExtendedCohortMetrics::forced_import(&cfg, all_supply_ref)?, + ExtendedCohortMetrics::forced_import(&cfg)?, ) }; // max_age: AdjustedCohortMetrics (adjusted + peak_regret) let max_age = { - let s = all_supply_ref; ByMaxAge::try_new(&|f: Filter, name: &'static str| -> Result<_> { let full_name = CohortContext::Utxo.full_name(&f, name); let cfg = ImportConfig { @@ -202,18 +187,16 @@ impl UTXOCohorts { context: CohortContext::Utxo, version: v, indexes, - prices, }; Ok(UTXOCohortVecs::new( None, - AdjustedCohortMetrics::forced_import(&cfg, s, up_to_1h_realized)?, + AdjustedCohortMetrics::forced_import(&cfg)?, )) })? }; // min_age: PeakRegretCohortMetrics let min_age = { - let s = all_supply_ref; ByMinAge::try_new(&|f: Filter, name: &'static str| -> Result<_> { let full_name = CohortContext::Utxo.full_name(&f, name); let cfg = ImportConfig { @@ -223,11 +206,10 @@ impl UTXOCohorts { context: CohortContext::Utxo, version: v, indexes, - prices, }; Ok(UTXOCohortVecs::new( None, - PeakRegretCohortMetrics::forced_import(&cfg, s)?, + PeakRegretCohortMetrics::forced_import(&cfg)?, )) })? }; @@ -243,11 +225,10 @@ impl UTXOCohorts { context: CohortContext::Utxo, version: v, indexes, - prices, }; Ok(UTXOCohortVecs::new( None, - BasicCohortMetrics::forced_import(&cfg, all_supply_ref)?, + BasicCohortMetrics::forced_import(&cfg)?, )) }; @@ -647,18 +628,32 @@ impl UTXOCohorts { where HM: ReadableVec + Sync, { + // Get up_to_1h value sources for adjusted computation (cloned to avoid borrow conflicts). + let up_to_1h_value_created = self.age_range.up_to_1h.metrics.realized.value_created.height.read_only_clone(); + let up_to_1h_value_destroyed = self.age_range.up_to_1h.metrics.realized.value_destroyed.height.read_only_clone(); + + // "all" cohort computed first (no all_supply_sats needed). self.all.metrics.compute_rest_part2( blocks, prices, starting_indexes, height_to_market_cap, + &up_to_1h_value_created, + &up_to_1h_value_destroyed, exit, )?; + + // Clone all_supply_sats for non-all cohorts. + let all_supply_sats = self.all.metrics.supply.total.sats.height.read_only_clone(); + self.sth.metrics.compute_rest_part2( blocks, prices, starting_indexes, height_to_market_cap, + &up_to_1h_value_created, + &up_to_1h_value_destroyed, + &all_supply_sats, exit, )?; self.lth.metrics.compute_rest_part2( @@ -666,6 +661,7 @@ impl UTXOCohorts { prices, starting_indexes, height_to_market_cap, + &all_supply_sats, exit, )?; self.age_range.par_iter_mut().try_for_each(|v| { @@ -674,6 +670,7 @@ impl UTXOCohorts { prices, starting_indexes, height_to_market_cap, + &all_supply_sats, exit, ) })?; @@ -683,6 +680,9 @@ impl UTXOCohorts { prices, starting_indexes, height_to_market_cap, + &up_to_1h_value_created, + &up_to_1h_value_destroyed, + &all_supply_sats, exit, ) })?; @@ -692,6 +692,7 @@ impl UTXOCohorts { prices, starting_indexes, height_to_market_cap, + &all_supply_sats, exit, ) })?; @@ -701,6 +702,7 @@ impl UTXOCohorts { prices, starting_indexes, height_to_market_cap, + &all_supply_sats, exit, ) })?; @@ -710,6 +712,7 @@ impl UTXOCohorts { prices, starting_indexes, height_to_market_cap, + &all_supply_sats, exit, ) })?; @@ -719,6 +722,7 @@ impl UTXOCohorts { prices, starting_indexes, height_to_market_cap, + &all_supply_sats, exit, ) })?; @@ -728,6 +732,7 @@ impl UTXOCohorts { prices, starting_indexes, height_to_market_cap, + &all_supply_sats, exit, ) })?; @@ -737,6 +742,7 @@ impl UTXOCohorts { prices, starting_indexes, height_to_market_cap, + &all_supply_sats, exit, ) })?; @@ -746,6 +752,7 @@ impl UTXOCohorts { prices, starting_indexes, height_to_market_cap, + &all_supply_sats, exit, ) })?; diff --git a/crates/brk_computer/src/distribution/compute/aggregates.rs b/crates/brk_computer/src/distribution/compute/aggregates.rs index 83237ee19..4007b8c01 100644 --- a/crates/brk_computer/src/distribution/compute/aggregates.rs +++ b/crates/brk_computer/src/distribution/compute/aggregates.rs @@ -75,6 +75,7 @@ where prices, starting_indexes, height_to_market_cap, + &utxo_cohorts.all.metrics.supply.total.sats.height, exit, )?; diff --git a/crates/brk_computer/src/distribution/compute/block_loop.rs b/crates/brk_computer/src/distribution/compute/block_loop.rs index 26fea5912..5e62be68f 100644 --- a/crates/brk_computer/src/distribution/compute/block_loop.rs +++ b/crates/brk_computer/src/distribution/compute/block_loop.rs @@ -65,10 +65,10 @@ pub(crate) fn process_blocks( let height_to_first_txoutindex = &indexer.vecs.outputs.first_txoutindex; let height_to_first_txinindex = &indexer.vecs.inputs.first_txinindex; - // From transactions and inputs/outputs (via .height or .height.sum_cum.sum patterns): + // From transactions and inputs/outputs (via .height or .height.sum_cumulative.sum patterns): let height_to_tx_count = &transactions.count.tx_count.height; - let height_to_output_count = &outputs.count.total_count.sum_cum.sum.0; - let height_to_input_count = &inputs.count.height.sum_cum.sum.0; + let height_to_output_count = &outputs.count.total_count.sum_cumulative.sum.0; + let height_to_input_count = &inputs.count.height.sum_cumulative.sum.0; // From blocks: let height_to_timestamp = &blocks.time.timestamp_monotonic; let height_to_date = &blocks.time.date; diff --git a/crates/brk_computer/src/distribution/compute/write.rs b/crates/brk_computer/src/distribution/compute/write.rs index a6c9162cf..f94429119 100644 --- a/crates/brk_computer/src/distribution/compute/write.rs +++ b/crates/brk_computer/src/distribution/compute/write.rs @@ -1,17 +1,14 @@ use std::time::Instant; use brk_error::Result; -use brk_types::Height; +use brk_types::{EmptyAddressData, FundedAddressData, Height}; use rayon::prelude::*; use tracing::info; use vecdb::{AnyStoredVec, WritableVec, Stamp}; use crate::distribution::{ Vecs, - block::{ - EmptyAddressDataWithSource, FundedAddressDataWithSource, process_empty_addresses, - process_funded_addresses, - }, + block::{WithAddressDataSource, process_empty_addresses, process_funded_addresses}, state::BlockState, }; @@ -28,8 +25,8 @@ use super::super::address::{AddressTypeToTypeIndexMap, AddressesDataVecs, AnyAdd pub(crate) fn process_address_updates( addresses_data: &mut AddressesDataVecs, address_indexes: &mut AnyAddressIndexesVecs, - empty_updates: AddressTypeToTypeIndexMap, - funded_updates: AddressTypeToTypeIndexMap, + empty_updates: AddressTypeToTypeIndexMap>, + funded_updates: AddressTypeToTypeIndexMap>, ) -> Result<()> { info!("Processing address updates..."); diff --git a/crates/brk_computer/src/distribution/metrics/activity.rs b/crates/brk_computer/src/distribution/metrics/activity.rs index 1e489007d..6062a01db 100644 --- a/crates/brk_computer/src/distribution/metrics/activity.rs +++ b/crates/brk_computer/src/distribution/metrics/activity.rs @@ -6,7 +6,7 @@ use vecdb::{AnyStoredVec, AnyVec, EagerVec, Exit, ImportableVec, PcoVec, Rw, Sto use crate::{ ComputeIndexes, blocks, - internal::{ComputedFromHeightCumSum, LazyComputedValueFromHeightCum, ValueEmaFromHeight}, + internal::{ComputedFromHeightCumulativeSum, LazyComputedValueFromHeightCumulative, ValueEmaFromHeight}, }; use super::ImportConfig; @@ -15,7 +15,7 @@ use super::ImportConfig; #[derive(Traversable)] pub struct ActivityMetrics { /// Total satoshis sent at each height + derived indexes - pub sent: LazyComputedValueFromHeightCum, + pub sent: LazyComputedValueFromHeightCumulative, /// 14-day EMA of sent supply (sats, btc, usd) pub sent_14d_ema: ValueEmaFromHeight, @@ -27,22 +27,21 @@ pub struct ActivityMetrics { pub satdays_destroyed: M::Stored>>, /// Coin-blocks destroyed (in BTC rather than sats) - pub coinblocks_destroyed: ComputedFromHeightCumSum, + pub coinblocks_destroyed: ComputedFromHeightCumulativeSum, /// Coin-days destroyed (in BTC rather than sats) - pub coindays_destroyed: ComputedFromHeightCumSum, + pub coindays_destroyed: ComputedFromHeightCumulativeSum, } impl ActivityMetrics { /// Import activity metrics from database. pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { Ok(Self { - sent: LazyComputedValueFromHeightCum::forced_import( + sent: LazyComputedValueFromHeightCumulative::forced_import( cfg.db, &cfg.name("sent"), cfg.version, cfg.indexes, - cfg.prices, )?, sent_14d_ema: ValueEmaFromHeight::forced_import( @@ -64,14 +63,14 @@ impl ActivityMetrics { cfg.version, )?, - coinblocks_destroyed: ComputedFromHeightCumSum::forced_import( + coinblocks_destroyed: ComputedFromHeightCumulativeSum::forced_import( cfg.db, &cfg.name("coinblocks_destroyed"), cfg.version, cfg.indexes, )?, - coindays_destroyed: ComputedFromHeightCumSum::forced_import( + coindays_destroyed: ComputedFromHeightCumulativeSum::forced_import( cfg.db, &cfg.name("coindays_destroyed"), cfg.version, @@ -165,8 +164,6 @@ impl ActivityMetrics { ) -> Result<()> { let window_starts = blocks.count.window_starts(); - self.sent.compute_cumulative(starting_indexes.height, exit)?; - // 14-day rolling average of sent (sats and dollars) self.sent_14d_ema.compute_rolling_average( starting_indexes.height, diff --git a/crates/brk_computer/src/distribution/metrics/cohort/adjusted.rs b/crates/brk_computer/src/distribution/metrics/cohort/adjusted.rs index 626b95a6d..33eef3e5d 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/adjusted.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/adjusted.rs @@ -1,7 +1,7 @@ use brk_cohort::Filter; use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Cents, Dollars, Height, Version}; +use brk_types::{Cents, Dollars, Height, Sats, Version}; use rayon::prelude::*; use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode}; @@ -25,7 +25,7 @@ pub struct AdjustedCohortMetrics { pub realized: Box>, pub cost_basis: Box>, pub unrealized: Box>, - pub relative: Box, + pub relative: Box>, } impl CohortMetricsBase for AdjustedCohortMetrics { @@ -73,21 +73,12 @@ impl CohortMetricsBase for AdjustedCohortMetrics { impl AdjustedCohortMetrics { pub(crate) fn forced_import( cfg: &ImportConfig, - all_supply: &SupplyMetrics, - up_to_1h: &RealizedBase, ) -> Result { let supply = SupplyMetrics::forced_import(cfg)?; let unrealized = UnrealizedWithPeakRegret::forced_import(cfg)?; - let realized = RealizedWithAdjusted::forced_import(cfg, up_to_1h)?; + let realized = RealizedWithAdjusted::forced_import(cfg)?; - let relative = RelativeWithPeakRegret::forced_import( - cfg, - &unrealized.base, - &supply, - all_supply, - &realized.base, - &unrealized.peak_regret_ext.peak_regret, - ); + let relative = RelativeWithPeakRegret::forced_import(cfg)?; Ok(Self { filter: cfg.filter.clone(), @@ -101,12 +92,16 @@ impl AdjustedCohortMetrics { }) } + #[allow(clippy::too_many_arguments)] pub(crate) fn compute_rest_part2( &mut self, blocks: &blocks::Vecs, prices: &prices::Vecs, starting_indexes: &ComputeIndexes, height_to_market_cap: &impl ReadableVec, + up_to_1h_value_created: &impl ReadableVec, + up_to_1h_value_destroyed: &impl ReadableVec, + all_supply_sats: &impl ReadableVec, exit: &Exit, ) -> Result<()> { self.realized.compute_rest_part2( @@ -115,8 +110,23 @@ impl AdjustedCohortMetrics { starting_indexes, &self.supply.total.btc.height, height_to_market_cap, + up_to_1h_value_created, + up_to_1h_value_destroyed, exit, - ) + )?; + + self.relative.compute( + starting_indexes.height, + &self.unrealized.base, + &self.realized.base, + &self.supply.total.sats.height, + height_to_market_cap, + all_supply_sats, + &self.unrealized.peak_regret_ext.peak_regret.height, + exit, + )?; + + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/cohort/all.rs b/crates/brk_computer/src/distribution/metrics/cohort/all.rs index 7fef6464d..bf72791c7 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/all.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/all.rs @@ -26,23 +26,49 @@ pub struct AllCohortMetrics { pub realized: Box>, pub cost_basis: Box>, pub unrealized: Box>, - pub relative: Box, + pub relative: Box>, } impl CohortMetricsBase for AllCohortMetrics { - fn filter(&self) -> &Filter { &self.filter } - fn supply(&self) -> &SupplyMetrics { &self.supply } - fn supply_mut(&mut self) -> &mut SupplyMetrics { &mut self.supply } - fn outputs(&self) -> &OutputsMetrics { &self.outputs } - fn outputs_mut(&mut self) -> &mut OutputsMetrics { &mut self.outputs } - fn activity(&self) -> &ActivityMetrics { &self.activity } - fn activity_mut(&mut self) -> &mut ActivityMetrics { &mut self.activity } - fn realized_base(&self) -> &RealizedBase { &self.realized } - fn realized_base_mut(&mut self) -> &mut RealizedBase { &mut self.realized } - fn unrealized_base(&self) -> &UnrealizedBase { &self.unrealized } - fn unrealized_base_mut(&mut self) -> &mut UnrealizedBase { &mut self.unrealized } - fn cost_basis_base(&self) -> &CostBasisBase { &self.cost_basis } - fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase { &mut self.cost_basis } + fn filter(&self) -> &Filter { + &self.filter + } + fn supply(&self) -> &SupplyMetrics { + &self.supply + } + fn supply_mut(&mut self) -> &mut SupplyMetrics { + &mut self.supply + } + fn outputs(&self) -> &OutputsMetrics { + &self.outputs + } + fn outputs_mut(&mut self) -> &mut OutputsMetrics { + &mut self.outputs + } + fn activity(&self) -> &ActivityMetrics { + &self.activity + } + fn activity_mut(&mut self) -> &mut ActivityMetrics { + &mut self.activity + } + fn realized_base(&self) -> &RealizedBase { + &self.realized + } + fn realized_base_mut(&mut self) -> &mut RealizedBase { + &mut self.realized + } + fn unrealized_base(&self) -> &UnrealizedBase { + &self.unrealized + } + fn unrealized_base_mut(&mut self) -> &mut UnrealizedBase { + &mut self.unrealized + } + fn cost_basis_base(&self) -> &CostBasisBase { + &self.cost_basis + } + fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase { + &mut self.cost_basis + } fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> { self.supply.validate_computed_versions(base_version)?; self.activity.validate_computed_versions(base_version)?; @@ -50,14 +76,21 @@ impl CohortMetricsBase for AllCohortMetrics { Ok(()) } fn compute_then_truncate_push_unrealized_states( - &mut self, height: Height, height_price: Cents, state: &mut CohortState, + &mut self, + height: Height, + height_price: Cents, + state: &mut CohortState, ) -> Result<()> { state.apply_pending(); self.cost_basis.truncate_push_minmax(height, state)?; let (height_unrealized_state, _) = state.compute_unrealized_states(height_price, None); - self.unrealized.base.truncate_push(height, &height_unrealized_state)?; + self.unrealized + .base + .truncate_push(height, &height_unrealized_state)?; let spot = height_price.to_dollars(); - self.cost_basis.extended.truncate_push_percentiles(height, state, spot)?; + self.cost_basis + .extended + .truncate_push_percentiles(height, state, spot)?; Ok(()) } fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> { @@ -82,18 +115,11 @@ impl AllCohortMetrics { pub(crate) fn forced_import_with_supply( cfg: &ImportConfig, supply: SupplyMetrics, - up_to_1h: &RealizedBase, ) -> Result { let unrealized = UnrealizedWithPeakRegret::forced_import(cfg)?; - let realized = RealizedWithExtendedAdjusted::forced_import(cfg, up_to_1h)?; + let realized = RealizedWithExtendedAdjusted::forced_import(cfg)?; - let relative = RelativeForAll::forced_import( - cfg, - &unrealized.base, - &supply, - &realized.base, - &unrealized.peak_regret_ext.peak_regret, - ); + let relative = RelativeForAll::forced_import(cfg)?; Ok(Self { filter: cfg.filter.clone(), @@ -107,12 +133,15 @@ impl AllCohortMetrics { }) } + #[allow(clippy::too_many_arguments)] pub(crate) fn compute_rest_part2( &mut self, blocks: &blocks::Vecs, prices: &prices::Vecs, starting_indexes: &ComputeIndexes, height_to_market_cap: &impl ReadableVec, + up_to_1h_value_created: &impl ReadableVec, + up_to_1h_value_destroyed: &impl ReadableVec, exit: &Exit, ) -> Result<()> { self.realized.compute_rest_part2( @@ -121,8 +150,21 @@ impl AllCohortMetrics { starting_indexes, &self.supply.total.btc.height, height_to_market_cap, + up_to_1h_value_created, + up_to_1h_value_destroyed, exit, - ) - } + )?; + self.relative.compute( + starting_indexes.height, + &self.unrealized.base, + &self.realized.base, + &self.supply.total.sats.height, + height_to_market_cap, + &self.unrealized.peak_regret_ext.peak_regret.height, + exit, + )?; + + Ok(()) + } } diff --git a/crates/brk_computer/src/distribution/metrics/cohort/basic.rs b/crates/brk_computer/src/distribution/metrics/cohort/basic.rs index 0afca9dac..653d8dde0 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/basic.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/basic.rs @@ -1,15 +1,15 @@ use brk_cohort::Filter; use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Cents, Dollars, Height, Version}; +use brk_types::{Cents, Dollars, Height, Sats, Version}; use rayon::prelude::*; use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode}; use crate::{ComputeIndexes, blocks, distribution::state::CohortState, prices}; use crate::distribution::metrics::{ - ActivityMetrics, CohortMetricsBase, CostBasisBase, ImportConfig, OutputsMetrics, RealizedBase, - RelativeWithRelToAll, SupplyMetrics, UnrealizedBase, + ActivityMetrics, CohortMetricsBase, CostBasisBase, ImportConfig, OutputsMetrics, + RealizedBase, RelativeWithRelToAll, SupplyMetrics, UnrealizedBase, }; /// Basic cohort metrics: no extensions, with relative (rel_to_all). @@ -24,7 +24,7 @@ pub struct BasicCohortMetrics { pub realized: Box>, pub cost_basis: Box>, pub unrealized: Box>, - pub relative: Box, + pub relative: Box>, } impl CohortMetricsBase for BasicCohortMetrics { @@ -70,15 +70,12 @@ impl CohortMetricsBase for BasicCohortMetrics { impl BasicCohortMetrics { pub(crate) fn forced_import( cfg: &ImportConfig, - all_supply: &SupplyMetrics, ) -> Result { let supply = SupplyMetrics::forced_import(cfg)?; let unrealized = UnrealizedBase::forced_import(cfg)?; let realized = RealizedBase::forced_import(cfg)?; - let relative = RelativeWithRelToAll::forced_import( - cfg, &unrealized, &supply, all_supply, &realized, - ); + let relative = RelativeWithRelToAll::forced_import(cfg)?; Ok(Self { filter: cfg.filter.clone(), @@ -102,6 +99,7 @@ impl BasicCohortMetrics { prices: &prices::Vecs, starting_indexes: &ComputeIndexes, height_to_market_cap: &impl ReadableVec, + all_supply_sats: &impl ReadableVec, exit: &Exit, ) -> Result<()> { self.realized.compute_rest_part2_base( @@ -111,7 +109,19 @@ impl BasicCohortMetrics { &self.supply.total.btc.height, height_to_market_cap, exit, - ) + )?; + + self.relative.compute( + starting_indexes.height, + &self.unrealized, + &self.realized, + &self.supply.total.sats.height, + height_to_market_cap, + all_supply_sats, + exit, + )?; + + Ok(()) } pub(crate) fn compute_from_stateful( diff --git a/crates/brk_computer/src/distribution/metrics/cohort/extended.rs b/crates/brk_computer/src/distribution/metrics/cohort/extended.rs index ee50e0bb3..39835ba9e 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/extended.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/extended.rs @@ -1,7 +1,7 @@ use brk_cohort::Filter; use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Cents, Dollars, Height, Version}; +use brk_types::{Cents, Dollars, Height, Sats, Version}; use rayon::prelude::*; use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode}; @@ -25,7 +25,7 @@ pub struct ExtendedCohortMetrics { pub realized: Box>, pub cost_basis: Box>, pub unrealized: Box>, - pub relative: Box, + pub relative: Box>, } impl CohortMetricsBase for ExtendedCohortMetrics { @@ -77,20 +77,12 @@ impl CohortMetricsBase for ExtendedCohortMetrics { impl ExtendedCohortMetrics { pub(crate) fn forced_import( cfg: &ImportConfig, - all_supply: &SupplyMetrics, ) -> Result { let supply = SupplyMetrics::forced_import(cfg)?; let unrealized = UnrealizedWithPeakRegret::forced_import(cfg)?; let realized = RealizedWithExtended::forced_import(cfg)?; - let relative = RelativeWithExtended::forced_import( - cfg, - &unrealized.base, - &supply, - all_supply, - &realized.base, - &unrealized.peak_regret_ext.peak_regret, - ); + let relative = RelativeWithExtended::forced_import(cfg)?; Ok(Self { filter: cfg.filter.clone(), @@ -110,6 +102,7 @@ impl ExtendedCohortMetrics { prices: &prices::Vecs, starting_indexes: &ComputeIndexes, height_to_market_cap: &impl ReadableVec, + all_supply_sats: &impl ReadableVec, exit: &Exit, ) -> Result<()> { self.realized.compute_rest_part2( @@ -119,7 +112,21 @@ impl ExtendedCohortMetrics { &self.supply.total.btc.height, height_to_market_cap, exit, - ) + )?; + + self.relative.compute( + starting_indexes.height, + &self.unrealized.base, + &self.realized.base, + &self.supply.total.sats.height, + height_to_market_cap, + all_supply_sats, + &self.supply.total.usd.height, + &self.unrealized.peak_regret_ext.peak_regret.height, + exit, + )?; + + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/cohort/extended_adjusted.rs b/crates/brk_computer/src/distribution/metrics/cohort/extended_adjusted.rs index 3bce565a4..f84bbc686 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/extended_adjusted.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/extended_adjusted.rs @@ -1,7 +1,7 @@ use brk_cohort::Filter; use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Cents, Dollars, Height, Version}; +use brk_types::{Cents, Dollars, Height, Sats, Version}; use rayon::prelude::*; use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode}; @@ -25,7 +25,7 @@ pub struct ExtendedAdjustedCohortMetrics { pub realized: Box>, pub cost_basis: Box>, pub unrealized: Box>, - pub relative: Box, + pub relative: Box>, } impl CohortMetricsBase for ExtendedAdjustedCohortMetrics { @@ -76,21 +76,12 @@ impl CohortMetricsBase for ExtendedAdjustedCohortMetrics { impl ExtendedAdjustedCohortMetrics { pub(crate) fn forced_import( cfg: &ImportConfig, - all_supply: &SupplyMetrics, - up_to_1h: &RealizedBase, ) -> Result { let supply = SupplyMetrics::forced_import(cfg)?; let unrealized = UnrealizedWithPeakRegret::forced_import(cfg)?; - let realized = RealizedWithExtendedAdjusted::forced_import(cfg, up_to_1h)?; + let realized = RealizedWithExtendedAdjusted::forced_import(cfg)?; - let relative = RelativeWithExtended::forced_import( - cfg, - &unrealized.base, - &supply, - all_supply, - &realized.base, - &unrealized.peak_regret_ext.peak_regret, - ); + let relative = RelativeWithExtended::forced_import(cfg)?; Ok(Self { filter: cfg.filter.clone(), @@ -104,12 +95,16 @@ impl ExtendedAdjustedCohortMetrics { }) } + #[allow(clippy::too_many_arguments)] pub(crate) fn compute_rest_part2( &mut self, blocks: &blocks::Vecs, prices: &prices::Vecs, starting_indexes: &ComputeIndexes, height_to_market_cap: &impl ReadableVec, + up_to_1h_value_created: &impl ReadableVec, + up_to_1h_value_destroyed: &impl ReadableVec, + all_supply_sats: &impl ReadableVec, exit: &Exit, ) -> Result<()> { self.realized.compute_rest_part2( @@ -118,8 +113,24 @@ impl ExtendedAdjustedCohortMetrics { starting_indexes, &self.supply.total.btc.height, height_to_market_cap, + up_to_1h_value_created, + up_to_1h_value_destroyed, exit, - ) + )?; + + self.relative.compute( + starting_indexes.height, + &self.unrealized.base, + &self.realized.base, + &self.supply.total.sats.height, + height_to_market_cap, + all_supply_sats, + &self.supply.total.usd.height, + &self.unrealized.peak_regret_ext.peak_regret.height, + exit, + )?; + + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/cohort/peak_regret.rs b/crates/brk_computer/src/distribution/metrics/cohort/peak_regret.rs index 34eea86e2..7bf115e54 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/peak_regret.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/peak_regret.rs @@ -1,7 +1,7 @@ use brk_cohort::Filter; use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Cents, Dollars, Height, Version}; +use brk_types::{Cents, Dollars, Height, Sats, Version}; use rayon::prelude::*; use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode}; @@ -24,7 +24,7 @@ pub struct PeakRegretCohortMetrics { pub realized: Box>, pub cost_basis: Box>, pub unrealized: Box>, - pub relative: Box, + pub relative: Box>, } impl CohortMetricsBase for PeakRegretCohortMetrics { @@ -72,20 +72,12 @@ impl CohortMetricsBase for PeakRegretCohortMetrics { impl PeakRegretCohortMetrics { pub(crate) fn forced_import( cfg: &ImportConfig, - all_supply: &SupplyMetrics, ) -> Result { let supply = SupplyMetrics::forced_import(cfg)?; let unrealized = UnrealizedWithPeakRegret::forced_import(cfg)?; let realized = RealizedBase::forced_import(cfg)?; - let relative = RelativeWithPeakRegret::forced_import( - cfg, - &unrealized.base, - &supply, - all_supply, - &realized, - &unrealized.peak_regret_ext.peak_regret, - ); + let relative = RelativeWithPeakRegret::forced_import(cfg)?; Ok(Self { filter: cfg.filter.clone(), @@ -105,6 +97,7 @@ impl PeakRegretCohortMetrics { prices: &prices::Vecs, starting_indexes: &ComputeIndexes, height_to_market_cap: &impl ReadableVec, + all_supply_sats: &impl ReadableVec, exit: &Exit, ) -> Result<()> { self.realized.compute_rest_part2_base( @@ -114,7 +107,20 @@ impl PeakRegretCohortMetrics { &self.supply.total.btc.height, height_to_market_cap, exit, - ) + )?; + + self.relative.compute( + starting_indexes.height, + &self.unrealized.base, + &self.realized, + &self.supply.total.sats.height, + height_to_market_cap, + all_supply_sats, + &self.unrealized.peak_regret_ext.peak_regret.height, + exit, + )?; + + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/config.rs b/crates/brk_computer/src/distribution/metrics/config.rs index 77101868a..7f50238ac 100644 --- a/crates/brk_computer/src/distribution/metrics/config.rs +++ b/crates/brk_computer/src/distribution/metrics/config.rs @@ -2,7 +2,7 @@ use brk_cohort::{CohortContext, Filter}; use brk_types::Version; use vecdb::Database; -use crate::{indexes, prices}; +use crate::indexes; /// Configuration for importing metrics. pub struct ImportConfig<'a> { @@ -12,7 +12,6 @@ pub struct ImportConfig<'a> { pub context: CohortContext, pub version: Version, pub indexes: &'a indexes::Vecs, - pub prices: &'a prices::Vecs, } impl<'a> ImportConfig<'a> { diff --git a/crates/brk_computer/src/distribution/metrics/cost_basis/base.rs b/crates/brk_computer/src/distribution/metrics/cost_basis/base.rs index 7e3230e15..a328e0312 100644 --- a/crates/brk_computer/src/distribution/metrics/cost_basis/base.rs +++ b/crates/brk_computer/src/distribution/metrics/cost_basis/base.rs @@ -6,7 +6,7 @@ use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec}; use crate::{ ComputeIndexes, distribution::state::CohortState, - internal::{ComputedFromHeightLast, Price, PriceFromHeight}, + internal::{ComputedFromHeightLast, Price}, }; use crate::distribution::metrics::ImportConfig; @@ -24,13 +24,13 @@ pub struct CostBasisBase { impl CostBasisBase { pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { Ok(Self { - min: PriceFromHeight::forced_import( + min: Price::forced_import( cfg.db, &cfg.name("min_cost_basis"), cfg.version, cfg.indexes, )?, - max: PriceFromHeight::forced_import( + max: Price::forced_import( cfg.db, &cfg.name("max_cost_basis"), cfg.version, @@ -40,7 +40,7 @@ impl CostBasisBase { } pub(crate) fn min_stateful_height_len(&self) -> usize { - self.min.height.len().min(self.max.height.len()) + self.min.usd.height.len().min(self.max.usd.height.len()) } pub(crate) fn truncate_push_minmax( @@ -48,14 +48,14 @@ impl CostBasisBase { height: Height, state: &CohortState, ) -> Result<()> { - self.min.height.truncate_push( + self.min.usd.height.truncate_push( height, state .cost_basis_data_first_key_value() .map(|(cents, _)| cents.into()) .unwrap_or(Dollars::NAN), )?; - self.max.height.truncate_push( + self.max.usd.height.truncate_push( height, state .cost_basis_data_last_key_value() @@ -67,8 +67,8 @@ impl CostBasisBase { pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> { vec![ - &mut self.min.height as &mut dyn AnyStoredVec, - &mut self.max.height, + &mut self.min.usd.height as &mut dyn AnyStoredVec, + &mut self.max.usd.height, ] } @@ -78,14 +78,14 @@ impl CostBasisBase { others: &[&Self], exit: &Exit, ) -> Result<()> { - self.min.height.compute_min_of_others( + self.min.usd.height.compute_min_of_others( starting_indexes.height, - &others.iter().map(|v| &v.min.height).collect::>(), + &others.iter().map(|v| &v.min.usd.height).collect::>(), exit, )?; - self.max.height.compute_max_of_others( + self.max.usd.height.compute_max_of_others( starting_indexes.height, - &others.iter().map(|v| &v.max.height).collect::>(), + &others.iter().map(|v| &v.max.usd.height).collect::>(), exit, )?; Ok(()) diff --git a/crates/brk_computer/src/distribution/metrics/cost_basis/extended.rs b/crates/brk_computer/src/distribution/metrics/cost_basis/extended.rs index 7cc64931b..5e26233a1 100644 --- a/crates/brk_computer/src/distribution/metrics/cost_basis/extended.rs +++ b/crates/brk_computer/src/distribution/metrics/cost_basis/extended.rs @@ -100,14 +100,14 @@ impl CostBasisExtended { .vecs .iter_mut() .flatten() - .map(|v| &mut v.height as &mut dyn AnyStoredVec), + .map(|v| &mut v.usd.height as &mut dyn AnyStoredVec), ); vecs.extend( self.invested_capital .vecs .iter_mut() .flatten() - .map(|v| &mut v.height as &mut dyn AnyStoredVec), + .map(|v| &mut v.usd.height as &mut dyn AnyStoredVec), ); vecs.push(&mut self.spot_cost_basis_percentile.height); vecs.push(&mut self.spot_invested_capital_percentile.height); diff --git a/crates/brk_computer/src/distribution/metrics/mod.rs b/crates/brk_computer/src/distribution/metrics/mod.rs index aad927ecc..2eae6d64b 100644 --- a/crates/brk_computer/src/distribution/metrics/mod.rs +++ b/crates/brk_computer/src/distribution/metrics/mod.rs @@ -152,13 +152,24 @@ pub trait CohortMetricsBase: Send + Sync { starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { + self.supply_mut() + .compute(prices, starting_indexes.height, exit)?; self.supply_mut() .compute_rest_part1(blocks, starting_indexes, exit)?; self.outputs_mut() .compute_rest(blocks, starting_indexes, exit)?; + self.activity_mut() + .sent + .compute(prices, starting_indexes.height, exit)?; self.activity_mut() .compute_rest_part1(blocks, starting_indexes, exit)?; + self.realized_base_mut() + .sent_in_profit + .compute(prices, starting_indexes.height, exit)?; + self.realized_base_mut() + .sent_in_loss + .compute(prices, starting_indexes.height, exit)?; self.realized_base_mut() .compute_rest_part1(starting_indexes, exit)?; diff --git a/crates/brk_computer/src/distribution/metrics/realized/adjusted.rs b/crates/brk_computer/src/distribution/metrics/realized/adjusted.rs index b51df1802..5a45fd077 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/adjusted.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/adjusted.rs @@ -1,26 +1,21 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Dollars, StoredF64, Version}; -use vecdb::{Exit, Ident, ReadableCloneableVec, Rw, StorageMode}; +use brk_types::{Dollars, Height, StoredF64, Version}; +use vecdb::{Exit, Ident, ReadableCloneableVec, ReadableVec, Rw, StorageMode}; use crate::{ ComputeIndexes, blocks, - internal::{ - ComputedFromHeightLast, DollarsMinus, LazyBinaryFromHeightLast, - LazyFromHeightLast, Ratio64, - }, + internal::{ComputedFromHeightLast, LazyFromHeightLast, Ratio64}, }; use crate::distribution::metrics::ImportConfig; -use super::RealizedBase; - /// Adjusted realized metrics (only for adjusted cohorts: all, sth, max_age). #[derive(Traversable)] pub struct RealizedAdjusted { - // === Adjusted Value (lazy: cohort - up_to_1h) === - pub adjusted_value_created: LazyBinaryFromHeightLast, - pub adjusted_value_destroyed: LazyBinaryFromHeightLast, + // === Adjusted Value (computed: cohort - up_to_1h) === + pub adjusted_value_created: ComputedFromHeightLast, + pub adjusted_value_destroyed: ComputedFromHeightLast, // === Adjusted Value Created/Destroyed Rolling Sums === pub adjusted_value_created_24h: ComputedFromHeightLast, @@ -34,10 +29,10 @@ pub struct RealizedAdjusted { // === Adjusted SOPR (rolling window ratios) === pub adjusted_sopr: LazyFromHeightLast, - pub adjusted_sopr_24h: LazyBinaryFromHeightLast, - pub adjusted_sopr_7d: LazyBinaryFromHeightLast, - pub adjusted_sopr_30d: LazyBinaryFromHeightLast, - pub adjusted_sopr_1y: LazyBinaryFromHeightLast, + pub adjusted_sopr_24h: ComputedFromHeightLast, + pub adjusted_sopr_7d: ComputedFromHeightLast, + pub adjusted_sopr_30d: ComputedFromHeightLast, + pub adjusted_sopr_1y: ComputedFromHeightLast, pub adjusted_sopr_24h_7d_ema: ComputedFromHeightLast, pub adjusted_sopr_7d_ema: LazyFromHeightLast, pub adjusted_sopr_24h_30d_ema: ComputedFromHeightLast, @@ -45,35 +40,32 @@ pub struct RealizedAdjusted { } impl RealizedAdjusted { - pub(crate) fn forced_import( - cfg: &ImportConfig, - base: &RealizedBase, - up_to_1h: &RealizedBase, - ) -> Result { + pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { let v1 = Version::ONE; macro_rules! import_rolling { ($name:expr) => { - ComputedFromHeightLast::forced_import(cfg.db, &cfg.name($name), cfg.version + v1, cfg.indexes)? + ComputedFromHeightLast::forced_import( + cfg.db, + &cfg.name($name), + cfg.version + v1, + cfg.indexes, + )? }; } - let adjusted_value_created = LazyBinaryFromHeightLast::from_both_binary_block::< - DollarsMinus, Dollars, Dollars, Dollars, Dollars, - >( + let adjusted_value_created = ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("adjusted_value_created"), cfg.version, - &base.value_created, - &up_to_1h.value_created, - ); - let adjusted_value_destroyed = LazyBinaryFromHeightLast::from_both_binary_block::< - DollarsMinus, Dollars, Dollars, Dollars, Dollars, - >( + cfg.indexes, + )?; + let adjusted_value_destroyed = ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("adjusted_value_destroyed"), cfg.version, - &base.value_destroyed, - &up_to_1h.value_destroyed, - ); + cfg.indexes, + )?; let adjusted_value_created_24h = import_rolling!("adjusted_value_created_24h"); let adjusted_value_created_7d = import_rolling!("adjusted_value_created_7d"); @@ -84,31 +76,50 @@ impl RealizedAdjusted { let adjusted_value_destroyed_30d = import_rolling!("adjusted_value_destroyed_30d"); let adjusted_value_destroyed_1y = import_rolling!("adjusted_value_destroyed_1y"); - let adjusted_sopr_24h = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("adjusted_sopr_24h"), cfg.version + v1, &adjusted_value_created_24h, &adjusted_value_destroyed_24h, - ); - let adjusted_sopr_7d = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("adjusted_sopr_7d"), cfg.version + v1, &adjusted_value_created_7d, &adjusted_value_destroyed_7d, - ); - let adjusted_sopr_30d = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("adjusted_sopr_30d"), cfg.version + v1, &adjusted_value_created_30d, &adjusted_value_destroyed_30d, - ); - let adjusted_sopr_1y = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("adjusted_sopr_1y"), cfg.version + v1, &adjusted_value_created_1y, &adjusted_value_destroyed_1y, - ); - let adjusted_sopr = LazyFromHeightLast::from_binary::( - &cfg.name("adjusted_sopr"), cfg.version + v1, &adjusted_sopr_24h, + let adjusted_sopr_24h = ComputedFromHeightLast::forced_import( + cfg.db, + &cfg.name("adjusted_sopr_24h"), + cfg.version + v1, + cfg.indexes, + )?; + let adjusted_sopr_7d = ComputedFromHeightLast::forced_import( + cfg.db, + &cfg.name("adjusted_sopr_7d"), + cfg.version + v1, + cfg.indexes, + )?; + let adjusted_sopr_30d = ComputedFromHeightLast::forced_import( + cfg.db, + &cfg.name("adjusted_sopr_30d"), + cfg.version + v1, + cfg.indexes, + )?; + let adjusted_sopr_1y = ComputedFromHeightLast::forced_import( + cfg.db, + &cfg.name("adjusted_sopr_1y"), + cfg.version + v1, + cfg.indexes, + )?; + let adjusted_sopr = LazyFromHeightLast::from_computed::( + &cfg.name("adjusted_sopr"), + cfg.version + v1, + adjusted_sopr_24h.height.read_only_boxed_clone(), + &adjusted_sopr_24h, ); let adjusted_sopr_24h_7d_ema = import_rolling!("adjusted_sopr_24h_7d_ema"); let adjusted_sopr_7d_ema = LazyFromHeightLast::from_computed::( - &cfg.name("adjusted_sopr_7d_ema"), cfg.version + v1, - adjusted_sopr_24h_7d_ema.height.read_only_boxed_clone(), &adjusted_sopr_24h_7d_ema, + &cfg.name("adjusted_sopr_7d_ema"), + cfg.version + v1, + adjusted_sopr_24h_7d_ema.height.read_only_boxed_clone(), + &adjusted_sopr_24h_7d_ema, ); let adjusted_sopr_24h_30d_ema = import_rolling!("adjusted_sopr_24h_30d_ema"); let adjusted_sopr_30d_ema = LazyFromHeightLast::from_computed::( - &cfg.name("adjusted_sopr_30d_ema"), cfg.version + v1, - adjusted_sopr_24h_30d_ema.height.read_only_boxed_clone(), &adjusted_sopr_24h_30d_ema, + &cfg.name("adjusted_sopr_30d_ema"), + cfg.version + v1, + adjusted_sopr_24h_30d_ema.height.read_only_boxed_clone(), + &adjusted_sopr_24h_30d_ema, ); Ok(RealizedAdjusted { @@ -134,36 +145,137 @@ impl RealizedAdjusted { }) } + #[allow(clippy::too_many_arguments)] pub(crate) fn compute_rest_part2_adj( &mut self, blocks: &blocks::Vecs, starting_indexes: &ComputeIndexes, + base_value_created: &impl ReadableVec, + base_value_destroyed: &impl ReadableVec, + up_to_1h_value_created: &impl ReadableVec, + up_to_1h_value_destroyed: &impl ReadableVec, exit: &Exit, ) -> Result<()> { + // Compute adjusted_value_created = base.value_created - up_to_1h.value_created + self.adjusted_value_created.height.compute_subtract( + starting_indexes.height, + base_value_created, + up_to_1h_value_created, + exit, + )?; + self.adjusted_value_destroyed.height.compute_subtract( + starting_indexes.height, + base_value_destroyed, + up_to_1h_value_destroyed, + exit, + )?; + // Adjusted value created/destroyed rolling sums - self.adjusted_value_created_24h.height.compute_rolling_sum(starting_indexes.height, &blocks.count.height_24h_ago, &self.adjusted_value_created.height, exit)?; - self.adjusted_value_created_7d.height.compute_rolling_sum(starting_indexes.height, &blocks.count.height_1w_ago, &self.adjusted_value_created.height, exit)?; - self.adjusted_value_created_30d.height.compute_rolling_sum(starting_indexes.height, &blocks.count.height_1m_ago, &self.adjusted_value_created.height, exit)?; - self.adjusted_value_created_1y.height.compute_rolling_sum(starting_indexes.height, &blocks.count.height_1y_ago, &self.adjusted_value_created.height, exit)?; - - self.adjusted_value_destroyed_24h.height.compute_rolling_sum(starting_indexes.height, &blocks.count.height_24h_ago, &self.adjusted_value_destroyed.height, exit)?; - self.adjusted_value_destroyed_7d.height.compute_rolling_sum(starting_indexes.height, &blocks.count.height_1w_ago, &self.adjusted_value_destroyed.height, exit)?; - self.adjusted_value_destroyed_30d.height.compute_rolling_sum(starting_indexes.height, &blocks.count.height_1m_ago, &self.adjusted_value_destroyed.height, exit)?; - self.adjusted_value_destroyed_1y.height.compute_rolling_sum(starting_indexes.height, &blocks.count.height_1y_ago, &self.adjusted_value_destroyed.height, exit)?; - - // Adjusted SOPR EMAs - self.adjusted_sopr_24h_7d_ema.height.compute_rolling_average( + self.adjusted_value_created_24h.height.compute_rolling_sum( + starting_indexes.height, + &blocks.count.height_24h_ago, + &self.adjusted_value_created.height, + exit, + )?; + self.adjusted_value_created_7d.height.compute_rolling_sum( starting_indexes.height, &blocks.count.height_1w_ago, - &self.adjusted_sopr.height, + &self.adjusted_value_created.height, exit, )?; - self.adjusted_sopr_24h_30d_ema.height.compute_rolling_average( + self.adjusted_value_created_30d.height.compute_rolling_sum( starting_indexes.height, &blocks.count.height_1m_ago, - &self.adjusted_sopr.height, + &self.adjusted_value_created.height, exit, )?; + self.adjusted_value_created_1y.height.compute_rolling_sum( + starting_indexes.height, + &blocks.count.height_1y_ago, + &self.adjusted_value_created.height, + exit, + )?; + + self.adjusted_value_destroyed_24h + .height + .compute_rolling_sum( + starting_indexes.height, + &blocks.count.height_24h_ago, + &self.adjusted_value_destroyed.height, + exit, + )?; + self.adjusted_value_destroyed_7d + .height + .compute_rolling_sum( + starting_indexes.height, + &blocks.count.height_1w_ago, + &self.adjusted_value_destroyed.height, + exit, + )?; + self.adjusted_value_destroyed_30d + .height + .compute_rolling_sum( + starting_indexes.height, + &blocks.count.height_1m_ago, + &self.adjusted_value_destroyed.height, + exit, + )?; + self.adjusted_value_destroyed_1y + .height + .compute_rolling_sum( + starting_indexes.height, + &blocks.count.height_1y_ago, + &self.adjusted_value_destroyed.height, + exit, + )?; + + // SOPR ratios from rolling sums + self.adjusted_sopr_24h + .compute_binary::( + starting_indexes.height, + &self.adjusted_value_created_24h.height, + &self.adjusted_value_destroyed_24h.height, + exit, + )?; + self.adjusted_sopr_7d + .compute_binary::( + starting_indexes.height, + &self.adjusted_value_created_7d.height, + &self.adjusted_value_destroyed_7d.height, + exit, + )?; + self.adjusted_sopr_30d + .compute_binary::( + starting_indexes.height, + &self.adjusted_value_created_30d.height, + &self.adjusted_value_destroyed_30d.height, + exit, + )?; + self.adjusted_sopr_1y + .compute_binary::( + starting_indexes.height, + &self.adjusted_value_created_1y.height, + &self.adjusted_value_destroyed_1y.height, + exit, + )?; + + // Adjusted SOPR EMAs + self.adjusted_sopr_24h_7d_ema + .height + .compute_rolling_average( + starting_indexes.height, + &blocks.count.height_1w_ago, + &self.adjusted_sopr.height, + exit, + )?; + self.adjusted_sopr_24h_30d_ema + .height + .compute_rolling_average( + starting_indexes.height, + &blocks.count.height_1m_ago, + &self.adjusted_sopr.height, + exit, + )?; Ok(()) } diff --git a/crates/brk_computer/src/distribution/metrics/realized/base.rs b/crates/brk_computer/src/distribution/metrics/realized/base.rs index 92d03a78a..a0a8c907a 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/base.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/base.rs @@ -1,24 +1,21 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{ - Bitcoin, Cents, CentsSats, CentsSquaredSats, Dollars, Height, StoredF32, StoredF64, - Version, + Bitcoin, Cents, CentsSats, CentsSquaredSats, Dollars, Height, StoredF32, StoredF64, Version, }; use vecdb::{ - AnyStoredVec, AnyVec, BytesVec, Exit, WritableVec, Ident, ImportableVec, - ReadableCloneableVec, ReadableVec, Negate, Rw, StorageMode, + AnyStoredVec, AnyVec, BytesVec, Exit, Ident, ImportableVec, Negate, ReadableCloneableVec, + ReadableVec, Rw, StorageMode, WritableVec, }; use crate::{ ComputeIndexes, blocks, distribution::state::RealizedState, internal::{ - CentsUnsignedToDollars, ComputedFromHeightCum, ComputedFromHeightLast, - ComputedFromHeightRatio, DollarsPlus, - DollarsSquaredDivide, LazyBinaryFromHeightLast, - LazyBinaryPriceFromHeight, LazyComputedValueFromHeightCum, LazyFromHeightLast, - LazyPriceFromCents, PercentageDollarsF32, Price, PriceFromHeight, - Ratio64, StoredF32Identity, ValueEmaFromHeight, + CentsUnsignedToDollars, ComputedFromHeightCumulative, ComputedFromHeightLast, + ComputedFromHeightRatio, DollarsPlus, LazyComputedValueFromHeightCumulative, LazyFromHeightLast, + PercentageDollarsF32, Price, Ratio64, + StoredF32Identity, ValueEmaFromHeight, }, prices, }; @@ -37,12 +34,12 @@ pub struct RealizedBase { // === Investor Price === pub investor_price_cents: ComputedFromHeightLast, - pub investor_price: LazyPriceFromCents, + pub investor_price: Price>, pub investor_price_extra: ComputedFromHeightRatio, // === Floor/Ceiling Price Bands === - pub lower_price_band: LazyBinaryPriceFromHeight, - pub upper_price_band: LazyBinaryPriceFromHeight, + pub lower_price_band: Price>, + pub upper_price_band: Price>, // === Raw values for aggregation === pub cap_raw: M::Stored>, @@ -52,21 +49,19 @@ pub struct RealizedBase { pub mvrv: LazyFromHeightLast, // === Realized Profit/Loss === - pub realized_profit: ComputedFromHeightCum, + pub realized_profit: ComputedFromHeightCumulative, pub realized_profit_7d_ema: ComputedFromHeightLast, - pub realized_loss: ComputedFromHeightCum, + pub realized_loss: ComputedFromHeightCumulative, pub realized_loss_7d_ema: ComputedFromHeightLast, pub neg_realized_loss: LazyFromHeightLast, - pub net_realized_pnl: ComputedFromHeightCum, + pub net_realized_pnl: ComputedFromHeightCumulative, pub net_realized_pnl_7d_ema: ComputedFromHeightLast, pub realized_value: ComputedFromHeightLast, - // === Realized vs Realized Cap Ratios (lazy) === - pub realized_profit_rel_to_realized_cap: - LazyBinaryFromHeightLast, - pub realized_loss_rel_to_realized_cap: LazyBinaryFromHeightLast, - pub net_realized_pnl_rel_to_realized_cap: - LazyBinaryFromHeightLast, + // === Realized vs Realized Cap Ratios === + pub realized_profit_rel_to_realized_cap: ComputedFromHeightLast, + pub realized_loss_rel_to_realized_cap: ComputedFromHeightLast, + pub net_realized_pnl_rel_to_realized_cap: ComputedFromHeightLast, // === Total Realized PnL === pub total_realized_pnl: LazyFromHeightLast, @@ -77,9 +72,9 @@ pub struct RealizedBase { pub loss_value_created: ComputedFromHeightLast, pub loss_value_destroyed: ComputedFromHeightLast, - // === Value Created/Destroyed Totals (lazy) === - pub value_created: LazyBinaryFromHeightLast, - pub value_destroyed: LazyBinaryFromHeightLast, + // === Value Created/Destroyed Totals === + pub value_created: ComputedFromHeightLast, + pub value_destroyed: ComputedFromHeightLast, // === Capitulation/Profit Flow (lazy aliases) === pub capitulation_flow: LazyFromHeightLast, @@ -97,10 +92,10 @@ pub struct RealizedBase { // === SOPR (rolling window ratios) === pub sopr: LazyFromHeightLast, - pub sopr_24h: LazyBinaryFromHeightLast, - pub sopr_7d: LazyBinaryFromHeightLast, - pub sopr_30d: LazyBinaryFromHeightLast, - pub sopr_1y: LazyBinaryFromHeightLast, + pub sopr_24h: ComputedFromHeightLast, + pub sopr_7d: ComputedFromHeightLast, + pub sopr_30d: ComputedFromHeightLast, + pub sopr_1y: ComputedFromHeightLast, pub sopr_24h_7d_ema: ComputedFromHeightLast, pub sopr_7d_ema: LazyFromHeightLast, pub sopr_24h_30d_ema: ComputedFromHeightLast, @@ -114,10 +109,10 @@ pub struct RealizedBase { // === Sell Side Risk (rolling window ratios) === pub sell_side_risk_ratio: LazyFromHeightLast, - pub sell_side_risk_ratio_24h: LazyBinaryFromHeightLast, - pub sell_side_risk_ratio_7d: LazyBinaryFromHeightLast, - pub sell_side_risk_ratio_30d: LazyBinaryFromHeightLast, - pub sell_side_risk_ratio_1y: LazyBinaryFromHeightLast, + pub sell_side_risk_ratio_24h: ComputedFromHeightLast, + pub sell_side_risk_ratio_7d: ComputedFromHeightLast, + pub sell_side_risk_ratio_30d: ComputedFromHeightLast, + pub sell_side_risk_ratio_1y: ComputedFromHeightLast, pub sell_side_risk_ratio_24h_7d_ema: ComputedFromHeightLast, pub sell_side_risk_ratio_7d_ema: LazyFromHeightLast, pub sell_side_risk_ratio_24h_30d_ema: ComputedFromHeightLast, @@ -125,17 +120,19 @@ pub struct RealizedBase { // === Net Realized PnL Deltas === pub net_realized_pnl_cumulative_30d_delta: ComputedFromHeightLast, - pub net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap: ComputedFromHeightLast, - pub net_realized_pnl_cumulative_30d_delta_rel_to_market_cap: ComputedFromHeightLast, + pub net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap: + ComputedFromHeightLast, + pub net_realized_pnl_cumulative_30d_delta_rel_to_market_cap: + ComputedFromHeightLast, // === Peak Regret === - pub peak_regret: ComputedFromHeightCum, - pub peak_regret_rel_to_realized_cap: LazyBinaryFromHeightLast, + pub peak_regret: ComputedFromHeightCumulative, + pub peak_regret_rel_to_realized_cap: ComputedFromHeightLast, // === Sent in Profit/Loss === - pub sent_in_profit: LazyComputedValueFromHeightCum, + pub sent_in_profit: LazyComputedValueFromHeightCumulative, pub sent_in_profit_14d_ema: ValueEmaFromHeight, - pub sent_in_loss: LazyComputedValueFromHeightCum, + pub sent_in_loss: LazyComputedValueFromHeightCumulative, pub sent_in_loss_14d_ema: ValueEmaFromHeight, } @@ -162,7 +159,7 @@ impl RealizedBase { &realized_cap_cents, ); - let realized_profit = ComputedFromHeightCum::forced_import( + let realized_profit = ComputedFromHeightCumulative::forced_import( cfg.db, &cfg.name("realized_profit"), cfg.version, @@ -176,7 +173,7 @@ impl RealizedBase { cfg.indexes, )?; - let realized_loss = ComputedFromHeightCum::forced_import( + let realized_loss = ComputedFromHeightCumulative::forced_import( cfg.db, &cfg.name("realized_loss"), cfg.version, @@ -190,14 +187,14 @@ impl RealizedBase { cfg.indexes, )?; - let neg_realized_loss = LazyFromHeightLast::from_computed::( + let neg_realized_loss = LazyFromHeightLast::from_height_source::( &cfg.name("neg_realized_loss"), cfg.version + v1, realized_loss.height.read_only_boxed_clone(), - &realized_loss, + cfg.indexes, ); - let net_realized_pnl = ComputedFromHeightCum::forced_import( + let net_realized_pnl = ComputedFromHeightCumulative::forced_import( cfg.db, &cfg.name("net_realized_pnl"), cfg.version, @@ -211,7 +208,7 @@ impl RealizedBase { cfg.indexes, )?; - let peak_regret = ComputedFromHeightCum::forced_import( + let peak_regret = ComputedFromHeightCumulative::forced_import( cfg.db, &cfg.name("realized_peak_regret"), cfg.version + v2, @@ -232,31 +229,28 @@ impl RealizedBase { &realized_value, ); - let realized_profit_rel_to_realized_cap = - LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( - &cfg.name("realized_profit_rel_to_realized_cap"), - cfg.version + v1, - &realized_profit, - &realized_cap, - ); + let realized_profit_rel_to_realized_cap = ComputedFromHeightLast::forced_import( + cfg.db, + &cfg.name("realized_profit_rel_to_realized_cap"), + cfg.version + v1, + cfg.indexes, + )?; - let realized_loss_rel_to_realized_cap = - LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( - &cfg.name("realized_loss_rel_to_realized_cap"), - cfg.version + v1, - &realized_loss, - &realized_cap, - ); + let realized_loss_rel_to_realized_cap = ComputedFromHeightLast::forced_import( + cfg.db, + &cfg.name("realized_loss_rel_to_realized_cap"), + cfg.version + v1, + cfg.indexes, + )?; - let net_realized_pnl_rel_to_realized_cap = - LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( - &cfg.name("net_realized_pnl_rel_to_realized_cap"), - cfg.version + v1, - &net_realized_pnl, - &realized_cap, - ); + let net_realized_pnl_rel_to_realized_cap = ComputedFromHeightLast::forced_import( + cfg.db, + &cfg.name("net_realized_pnl_rel_to_realized_cap"), + cfg.version + v1, + cfg.indexes, + )?; - let realized_price = PriceFromHeight::forced_import( + let realized_price = Price::forced_import( cfg.db, &cfg.name("realized_price"), cfg.version + v1, @@ -270,7 +264,7 @@ impl RealizedBase { cfg.indexes, )?; - let investor_price = LazyPriceFromCents::from_computed::( + let investor_price = Price::from_computed::( &cfg.name("investor_price"), cfg.version, &investor_price_cents, @@ -279,27 +273,24 @@ impl RealizedBase { let investor_price_extra = ComputedFromHeightRatio::forced_import_from_lazy( cfg.db, &cfg.name("investor_price"), - &investor_price.usd, cfg.version, cfg.indexes, extended, )?; - let lower_price_band = - LazyBinaryPriceFromHeight::from_price_and_lazy_price::( - &cfg.name("lower_price_band"), - cfg.version, - &realized_price, - &investor_price, - ); + let lower_price_band = Price::forced_import( + cfg.db, + &cfg.name("lower_price_band"), + cfg.version, + cfg.indexes, + )?; - let upper_price_band = - LazyBinaryPriceFromHeight::from_lazy_price_and_price::( - &cfg.name("upper_price_band"), - cfg.version, - &investor_price, - &realized_price, - ); + let upper_price_band = Price::forced_import( + cfg.db, + &cfg.name("upper_price_band"), + cfg.version, + cfg.indexes, + )?; let cap_raw = BytesVec::forced_import(cfg.db, &cfg.name("cap_raw"), cfg.version)?; let investor_cap_raw = @@ -330,18 +321,18 @@ impl RealizedBase { cfg.indexes, )?; - let value_created = LazyBinaryFromHeightLast::from_computed_last::( + let value_created = ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("value_created"), cfg.version, - &profit_value_created, - &loss_value_created, - ); - let value_destroyed = LazyBinaryFromHeightLast::from_computed_last::( + cfg.indexes, + )?; + let value_destroyed = ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("value_destroyed"), cfg.version, - &profit_value_destroyed, - &loss_value_destroyed, - ); + cfg.indexes, + )?; let capitulation_flow = LazyFromHeightLast::from_computed::( &cfg.name("capitulation_flow"), @@ -359,7 +350,7 @@ impl RealizedBase { let realized_price_extra = ComputedFromHeightRatio::forced_import( cfg.db, &cfg.name("realized_price"), - Some(&realized_price), + Some(&realized_price.usd), cfg.version + v1, cfg.indexes, extended, @@ -375,7 +366,12 @@ impl RealizedBase { // === Rolling sum intermediates === macro_rules! import_rolling { ($name:expr) => { - ComputedFromHeightLast::forced_import(cfg.db, &cfg.name($name), cfg.version + v1, cfg.indexes)? + ComputedFromHeightLast::forced_import( + cfg.db, + &cfg.name($name), + cfg.version + v1, + cfg.indexes, + )? }; } @@ -393,69 +389,70 @@ impl RealizedBase { let realized_value_30d = import_rolling!("realized_value_30d"); let realized_value_1y = import_rolling!("realized_value_1y"); - // === Rolling window lazy ratios === - let sopr_24h = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("sopr_24h"), cfg.version + v1, &value_created_24h, &value_destroyed_24h, - ); - let sopr_7d = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("sopr_7d"), cfg.version + v1, &value_created_7d, &value_destroyed_7d, - ); - let sopr_30d = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("sopr_30d"), cfg.version + v1, &value_created_30d, &value_destroyed_30d, - ); - let sopr_1y = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("sopr_1y"), cfg.version + v1, &value_created_1y, &value_destroyed_1y, - ); - let sopr = LazyFromHeightLast::from_binary::( - &cfg.name("sopr"), cfg.version + v1, &sopr_24h, + // === Rolling window stored ratios === + let sopr_24h = import_rolling!("sopr_24h"); + let sopr_7d = import_rolling!("sopr_7d"); + let sopr_30d = import_rolling!("sopr_30d"); + let sopr_1y = import_rolling!("sopr_1y"); + let sopr = LazyFromHeightLast::from_computed::( + &cfg.name("sopr"), + cfg.version + v1, + sopr_24h.height.read_only_boxed_clone(), + &sopr_24h, ); - let sell_side_risk_ratio_24h = LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( - &cfg.name("sell_side_risk_ratio_24h"), cfg.version + v1, &realized_value_24h, &realized_cap, - ); - let sell_side_risk_ratio_7d = LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( - &cfg.name("sell_side_risk_ratio_7d"), cfg.version + v1, &realized_value_7d, &realized_cap, - ); - let sell_side_risk_ratio_30d = LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( - &cfg.name("sell_side_risk_ratio_30d"), cfg.version + v1, &realized_value_30d, &realized_cap, - ); - let sell_side_risk_ratio_1y = LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( - &cfg.name("sell_side_risk_ratio_1y"), cfg.version + v1, &realized_value_1y, &realized_cap, - ); - let sell_side_risk_ratio = LazyFromHeightLast::from_binary::( - &cfg.name("sell_side_risk_ratio"), cfg.version + v1, &sell_side_risk_ratio_24h, + let sell_side_risk_ratio_24h = import_rolling!("sell_side_risk_ratio_24h"); + let sell_side_risk_ratio_7d = import_rolling!("sell_side_risk_ratio_7d"); + let sell_side_risk_ratio_30d = import_rolling!("sell_side_risk_ratio_30d"); + let sell_side_risk_ratio_1y = import_rolling!("sell_side_risk_ratio_1y"); + let sell_side_risk_ratio = LazyFromHeightLast::from_computed::( + &cfg.name("sell_side_risk_ratio"), + cfg.version + v1, + sell_side_risk_ratio_24h.height.read_only_boxed_clone(), + &sell_side_risk_ratio_24h, ); // === EMA imports + identity aliases === let sopr_24h_7d_ema = import_rolling!("sopr_24h_7d_ema"); let sopr_7d_ema = LazyFromHeightLast::from_computed::( - &cfg.name("sopr_7d_ema"), cfg.version + v1, - sopr_24h_7d_ema.height.read_only_boxed_clone(), &sopr_24h_7d_ema, + &cfg.name("sopr_7d_ema"), + cfg.version + v1, + sopr_24h_7d_ema.height.read_only_boxed_clone(), + &sopr_24h_7d_ema, ); let sopr_24h_30d_ema = import_rolling!("sopr_24h_30d_ema"); let sopr_30d_ema = LazyFromHeightLast::from_computed::( - &cfg.name("sopr_30d_ema"), cfg.version + v1, - sopr_24h_30d_ema.height.read_only_boxed_clone(), &sopr_24h_30d_ema, + &cfg.name("sopr_30d_ema"), + cfg.version + v1, + sopr_24h_30d_ema.height.read_only_boxed_clone(), + &sopr_24h_30d_ema, ); let sell_side_risk_ratio_24h_7d_ema = import_rolling!("sell_side_risk_ratio_24h_7d_ema"); let sell_side_risk_ratio_7d_ema = LazyFromHeightLast::from_computed::( - &cfg.name("sell_side_risk_ratio_7d_ema"), cfg.version + v1, - sell_side_risk_ratio_24h_7d_ema.height.read_only_boxed_clone(), &sell_side_risk_ratio_24h_7d_ema, + &cfg.name("sell_side_risk_ratio_7d_ema"), + cfg.version + v1, + sell_side_risk_ratio_24h_7d_ema + .height + .read_only_boxed_clone(), + &sell_side_risk_ratio_24h_7d_ema, ); let sell_side_risk_ratio_24h_30d_ema = import_rolling!("sell_side_risk_ratio_24h_30d_ema"); let sell_side_risk_ratio_30d_ema = LazyFromHeightLast::from_computed::( - &cfg.name("sell_side_risk_ratio_30d_ema"), cfg.version + v1, - sell_side_risk_ratio_24h_30d_ema.height.read_only_boxed_clone(), &sell_side_risk_ratio_24h_30d_ema, + &cfg.name("sell_side_risk_ratio_30d_ema"), + cfg.version + v1, + sell_side_risk_ratio_24h_30d_ema + .height + .read_only_boxed_clone(), + &sell_side_risk_ratio_24h_30d_ema, ); - let peak_regret_rel_to_realized_cap = - LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( - &cfg.name("peak_regret_rel_to_realized_cap"), - cfg.version + v1, - &peak_regret, - &realized_cap, - ); + let peak_regret_rel_to_realized_cap = ComputedFromHeightLast::forced_import( + cfg.db, + &cfg.name("peak_regret_rel_to_realized_cap"), + cfg.version + v1, + cfg.indexes, + )?; Ok(Self { realized_cap_cents, @@ -548,12 +545,11 @@ impl RealizedBase { )?, peak_regret, peak_regret_rel_to_realized_cap, - sent_in_profit: LazyComputedValueFromHeightCum::forced_import( + sent_in_profit: LazyComputedValueFromHeightCumulative::forced_import( cfg.db, &cfg.name("sent_in_profit"), cfg.version, cfg.indexes, - cfg.prices, )?, sent_in_profit_14d_ema: ValueEmaFromHeight::forced_import( cfg.db, @@ -561,12 +557,11 @@ impl RealizedBase { cfg.version, cfg.indexes, )?, - sent_in_loss: LazyComputedValueFromHeightCum::forced_import( + sent_in_loss: LazyComputedValueFromHeightCumulative::forced_import( cfg.db, &cfg.name("sent_in_loss"), cfg.version, cfg.indexes, - cfg.prices, )?, sent_in_loss_14d_ema: ValueEmaFromHeight::forced_import( cfg.db, @@ -804,9 +799,9 @@ impl RealizedBase { exit: &Exit, ) -> Result<()> { self.realized_profit - .compute_cumulative(starting_indexes.height, exit)?; + .compute_rest(starting_indexes.height, exit)?; self.realized_loss - .compute_cumulative(starting_indexes.height, exit)?; + .compute_rest(starting_indexes.height, exit)?; self.net_realized_pnl .compute(starting_indexes.height, exit, |vec| { @@ -827,12 +822,7 @@ impl RealizedBase { )?; self.peak_regret - .compute_cumulative(starting_indexes.height, exit)?; - - self.sent_in_profit - .compute_cumulative(starting_indexes.height, exit)?; - self.sent_in_loss - .compute_cumulative(starting_indexes.height, exit)?; + .compute_rest(starting_indexes.height, exit)?; Ok(()) } @@ -848,7 +838,7 @@ impl RealizedBase { height_to_market_cap: &impl ReadableVec, exit: &Exit, ) -> Result<()> { - self.realized_price.height.compute_divide( + self.realized_price.usd.height.compute_divide( starting_indexes.height, &self.realized_cap.height, height_to_supply, @@ -860,7 +850,12 @@ impl RealizedBase { prices, starting_indexes, exit, - Some(&self.realized_price.height), + Some(&self.realized_price.usd.height), + )?; + self.realized_price_extra.compute_usd_bands( + starting_indexes, + &self.realized_price.usd.height, + exit, )?; self.investor_price_extra.compute_rest( @@ -868,7 +863,12 @@ impl RealizedBase { prices, starting_indexes, exit, - Some(&self.investor_price.height), + Some(&self.investor_price.usd.height), + )?; + self.investor_price_extra.compute_usd_bands( + starting_indexes, + &self.investor_price.usd.height, + exit, )?; self.realized_cap_30d_delta.height.compute_rolling_change( @@ -878,39 +878,160 @@ impl RealizedBase { exit, )?; + // Compute value_created/destroyed from stored components + self.value_created + .compute_binary::( + starting_indexes.height, + &self.profit_value_created.height, + &self.loss_value_created.height, + exit, + )?; + self.value_destroyed + .compute_binary::( + starting_indexes.height, + &self.profit_value_destroyed.height, + &self.loss_value_destroyed.height, + exit, + )?; + // === Rolling sum intermediates === macro_rules! rolling_sum { ($target:expr, $window:expr, $source:expr) => { $target.height.compute_rolling_sum( - starting_indexes.height, $window, $source, exit, + starting_indexes.height, + $window, + $source, + exit, )? }; } - rolling_sum!(self.value_created_24h, &blocks.count.height_24h_ago, &self.value_created.height); - rolling_sum!(self.value_created_7d, &blocks.count.height_1w_ago, &self.value_created.height); - rolling_sum!(self.value_created_30d, &blocks.count.height_1m_ago, &self.value_created.height); - rolling_sum!(self.value_created_1y, &blocks.count.height_1y_ago, &self.value_created.height); - rolling_sum!(self.value_destroyed_24h, &blocks.count.height_24h_ago, &self.value_destroyed.height); - rolling_sum!(self.value_destroyed_7d, &blocks.count.height_1w_ago, &self.value_destroyed.height); - rolling_sum!(self.value_destroyed_30d, &blocks.count.height_1m_ago, &self.value_destroyed.height); - rolling_sum!(self.value_destroyed_1y, &blocks.count.height_1y_ago, &self.value_destroyed.height); + rolling_sum!( + self.value_created_24h, + &blocks.count.height_24h_ago, + &self.value_created.height + ); + rolling_sum!( + self.value_created_7d, + &blocks.count.height_1w_ago, + &self.value_created.height + ); + rolling_sum!( + self.value_created_30d, + &blocks.count.height_1m_ago, + &self.value_created.height + ); + rolling_sum!( + self.value_created_1y, + &blocks.count.height_1y_ago, + &self.value_created.height + ); + rolling_sum!( + self.value_destroyed_24h, + &blocks.count.height_24h_ago, + &self.value_destroyed.height + ); + rolling_sum!( + self.value_destroyed_7d, + &blocks.count.height_1w_ago, + &self.value_destroyed.height + ); + rolling_sum!( + self.value_destroyed_30d, + &blocks.count.height_1m_ago, + &self.value_destroyed.height + ); + rolling_sum!( + self.value_destroyed_1y, + &blocks.count.height_1y_ago, + &self.value_destroyed.height + ); // Realized value rolling sums - rolling_sum!(self.realized_value_24h, &blocks.count.height_24h_ago, &self.realized_value.height); - rolling_sum!(self.realized_value_7d, &blocks.count.height_1w_ago, &self.realized_value.height); - rolling_sum!(self.realized_value_30d, &blocks.count.height_1m_ago, &self.realized_value.height); - rolling_sum!(self.realized_value_1y, &blocks.count.height_1y_ago, &self.realized_value.height); + rolling_sum!( + self.realized_value_24h, + &blocks.count.height_24h_ago, + &self.realized_value.height + ); + rolling_sum!( + self.realized_value_7d, + &blocks.count.height_1w_ago, + &self.realized_value.height + ); + rolling_sum!( + self.realized_value_30d, + &blocks.count.height_1m_ago, + &self.realized_value.height + ); + rolling_sum!( + self.realized_value_1y, + &blocks.count.height_1y_ago, + &self.realized_value.height + ); - // 7d rolling averages - self.realized_profit_7d_ema - .height - .compute_rolling_average( + // Compute SOPR from rolling sums + self.sopr_24h.compute_binary::( + starting_indexes.height, + &self.value_created_24h.height, + &self.value_destroyed_24h.height, + exit, + )?; + self.sopr_7d.compute_binary::( + starting_indexes.height, + &self.value_created_7d.height, + &self.value_destroyed_7d.height, + exit, + )?; + self.sopr_30d.compute_binary::( + starting_indexes.height, + &self.value_created_30d.height, + &self.value_destroyed_30d.height, + exit, + )?; + self.sopr_1y.compute_binary::( + starting_indexes.height, + &self.value_created_1y.height, + &self.value_destroyed_1y.height, + exit, + )?; + + // Compute sell-side risk ratios + self.sell_side_risk_ratio_24h + .compute_binary::( starting_indexes.height, - &blocks.count.height_1w_ago, - &self.realized_profit.height, + &self.realized_value_24h.height, + &self.realized_cap.height, exit, )?; + self.sell_side_risk_ratio_7d + .compute_binary::( + starting_indexes.height, + &self.realized_value_7d.height, + &self.realized_cap.height, + exit, + )?; + self.sell_side_risk_ratio_30d + .compute_binary::( + starting_indexes.height, + &self.realized_value_30d.height, + &self.realized_cap.height, + exit, + )?; + self.sell_side_risk_ratio_1y + .compute_binary::( + starting_indexes.height, + &self.realized_value_1y.height, + &self.realized_cap.height, + exit, + )?; + + // 7d rolling averages + self.realized_profit_7d_ema.height.compute_rolling_average( + starting_indexes.height, + &blocks.count.height_1w_ago, + &self.realized_profit.height, + exit, + )?; self.realized_loss_7d_ema.height.compute_rolling_average( starting_indexes.height, &blocks.count.height_1w_ago, @@ -974,6 +1095,36 @@ impl RealizedBase { exit, )?; + // Realized profit/loss/net relative to realized cap + self.realized_profit_rel_to_realized_cap + .compute_binary::( + starting_indexes.height, + &self.realized_profit.height, + &self.realized_cap.height, + exit, + )?; + self.realized_loss_rel_to_realized_cap + .compute_binary::( + starting_indexes.height, + &self.realized_loss.height, + &self.realized_cap.height, + exit, + )?; + self.net_realized_pnl_rel_to_realized_cap + .compute_binary::( + starting_indexes.height, + &self.net_realized_pnl.height, + &self.realized_cap.height, + exit, + )?; + self.peak_regret_rel_to_realized_cap + .compute_binary::( + starting_indexes.height, + &self.peak_regret.height, + &self.realized_cap.height, + exit, + )?; + // Net realized PnL cumulative 30d delta self.net_realized_pnl_cumulative_30d_delta .height diff --git a/crates/brk_computer/src/distribution/metrics/realized/extended.rs b/crates/brk_computer/src/distribution/metrics/realized/extended.rs index c18500114..43929706c 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/extended.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/extended.rs @@ -6,7 +6,7 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode}; use crate::{ ComputeIndexes, blocks, internal::{ - ComputedFromHeightLast, LazyBinaryFromHeightLast, Ratio64, + ComputedFromHeightLast, Ratio64, }, }; @@ -29,11 +29,11 @@ pub struct RealizedExtended { pub realized_loss_30d: ComputedFromHeightLast, pub realized_loss_1y: ComputedFromHeightLast, - // === Realized Profit to Loss Ratio (lazy from rolling sums) === - pub realized_profit_to_loss_ratio_24h: LazyBinaryFromHeightLast, - pub realized_profit_to_loss_ratio_7d: LazyBinaryFromHeightLast, - pub realized_profit_to_loss_ratio_30d: LazyBinaryFromHeightLast, - pub realized_profit_to_loss_ratio_1y: LazyBinaryFromHeightLast, + // === Realized Profit to Loss Ratio (from rolling sums) === + pub realized_profit_to_loss_ratio_24h: ComputedFromHeightLast, + pub realized_profit_to_loss_ratio_7d: ComputedFromHeightLast, + pub realized_profit_to_loss_ratio_30d: ComputedFromHeightLast, + pub realized_profit_to_loss_ratio_1y: ComputedFromHeightLast, } impl RealizedExtended { @@ -46,28 +46,6 @@ impl RealizedExtended { }; } - let realized_profit_24h = import_rolling!("realized_profit_24h"); - let realized_profit_7d = import_rolling!("realized_profit_7d"); - let realized_profit_30d = import_rolling!("realized_profit_30d"); - let realized_profit_1y = import_rolling!("realized_profit_1y"); - let realized_loss_24h = import_rolling!("realized_loss_24h"); - let realized_loss_7d = import_rolling!("realized_loss_7d"); - let realized_loss_30d = import_rolling!("realized_loss_30d"); - let realized_loss_1y = import_rolling!("realized_loss_1y"); - - let realized_profit_to_loss_ratio_24h = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("realized_profit_to_loss_ratio_24h"), cfg.version + v1, &realized_profit_24h, &realized_loss_24h, - ); - let realized_profit_to_loss_ratio_7d = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("realized_profit_to_loss_ratio_7d"), cfg.version + v1, &realized_profit_7d, &realized_loss_7d, - ); - let realized_profit_to_loss_ratio_30d = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("realized_profit_to_loss_ratio_30d"), cfg.version + v1, &realized_profit_30d, &realized_loss_30d, - ); - let realized_profit_to_loss_ratio_1y = LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("realized_profit_to_loss_ratio_1y"), cfg.version + v1, &realized_profit_1y, &realized_loss_1y, - ); - Ok(RealizedExtended { realized_cap_rel_to_own_market_cap: ComputedFromHeightLast::forced_import( cfg.db, @@ -75,18 +53,18 @@ impl RealizedExtended { cfg.version, cfg.indexes, )?, - realized_profit_24h, - realized_profit_7d, - realized_profit_30d, - realized_profit_1y, - realized_loss_24h, - realized_loss_7d, - realized_loss_30d, - realized_loss_1y, - realized_profit_to_loss_ratio_24h, - realized_profit_to_loss_ratio_7d, - realized_profit_to_loss_ratio_30d, - realized_profit_to_loss_ratio_1y, + realized_profit_24h: import_rolling!("realized_profit_24h"), + realized_profit_7d: import_rolling!("realized_profit_7d"), + realized_profit_30d: import_rolling!("realized_profit_30d"), + realized_profit_1y: import_rolling!("realized_profit_1y"), + realized_loss_24h: import_rolling!("realized_loss_24h"), + realized_loss_7d: import_rolling!("realized_loss_7d"), + realized_loss_30d: import_rolling!("realized_loss_30d"), + realized_loss_1y: import_rolling!("realized_loss_1y"), + realized_profit_to_loss_ratio_24h: import_rolling!("realized_profit_to_loss_ratio_24h"), + realized_profit_to_loss_ratio_7d: import_rolling!("realized_profit_to_loss_ratio_7d"), + realized_profit_to_loss_ratio_30d: import_rolling!("realized_profit_to_loss_ratio_30d"), + realized_profit_to_loss_ratio_1y: import_rolling!("realized_profit_to_loss_ratio_1y"), }) } @@ -116,6 +94,20 @@ impl RealizedExtended { exit, )?; + // Realized profit to loss ratios + self.realized_profit_to_loss_ratio_24h.compute_binary::( + starting_indexes.height, &self.realized_profit_24h.height, &self.realized_loss_24h.height, exit, + )?; + self.realized_profit_to_loss_ratio_7d.compute_binary::( + starting_indexes.height, &self.realized_profit_7d.height, &self.realized_loss_7d.height, exit, + )?; + self.realized_profit_to_loss_ratio_30d.compute_binary::( + starting_indexes.height, &self.realized_profit_30d.height, &self.realized_loss_30d.height, exit, + )?; + self.realized_profit_to_loss_ratio_1y.compute_binary::( + starting_indexes.height, &self.realized_profit_1y.height, &self.realized_loss_1y.height, exit, + )?; + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/realized/with_adjusted.rs b/crates/brk_computer/src/distribution/metrics/realized/with_adjusted.rs index 651946149..63b10f435 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/with_adjusted.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/with_adjusted.rs @@ -23,9 +23,9 @@ pub struct RealizedWithAdjusted { } impl RealizedWithAdjusted { - pub(crate) fn forced_import(cfg: &ImportConfig, up_to_1h: &RealizedBase) -> Result { + pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { let base = RealizedBase::forced_import(cfg)?; - let adjusted = RealizedAdjusted::forced_import(cfg, &base, up_to_1h)?; + let adjusted = RealizedAdjusted::forced_import(cfg)?; Ok(Self { base, adjusted }) } @@ -37,6 +37,8 @@ impl RealizedWithAdjusted { starting_indexes: &ComputeIndexes, height_to_supply: &impl ReadableVec, height_to_market_cap: &impl ReadableVec, + up_to_1h_value_created: &impl ReadableVec, + up_to_1h_value_destroyed: &impl ReadableVec, exit: &Exit, ) -> Result<()> { self.base.compute_rest_part2_base( @@ -51,6 +53,10 @@ impl RealizedWithAdjusted { self.adjusted.compute_rest_part2_adj( blocks, starting_indexes, + &self.base.value_created.height, + &self.base.value_destroyed.height, + up_to_1h_value_created, + up_to_1h_value_destroyed, exit, )?; diff --git a/crates/brk_computer/src/distribution/metrics/realized/with_extended_adjusted.rs b/crates/brk_computer/src/distribution/metrics/realized/with_extended_adjusted.rs index b823c005d..fcd0ff0eb 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/with_extended_adjusted.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/with_extended_adjusted.rs @@ -25,10 +25,10 @@ pub struct RealizedWithExtendedAdjusted { } impl RealizedWithExtendedAdjusted { - pub(crate) fn forced_import(cfg: &ImportConfig, up_to_1h: &RealizedBase) -> Result { + pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { let base = RealizedBase::forced_import(cfg)?; let extended = RealizedExtended::forced_import(cfg)?; - let adjusted = RealizedAdjusted::forced_import(cfg, &base, up_to_1h)?; + let adjusted = RealizedAdjusted::forced_import(cfg)?; Ok(Self { base, extended, @@ -44,6 +44,8 @@ impl RealizedWithExtendedAdjusted { starting_indexes: &ComputeIndexes, height_to_supply: &impl ReadableVec, height_to_market_cap: &impl ReadableVec, + up_to_1h_value_created: &impl ReadableVec, + up_to_1h_value_destroyed: &impl ReadableVec, exit: &Exit, ) -> Result<()> { self.base.compute_rest_part2_base( @@ -66,6 +68,10 @@ impl RealizedWithExtendedAdjusted { self.adjusted.compute_rest_part2_adj( blocks, starting_indexes, + &self.base.value_created.height, + &self.base.value_destroyed.height, + up_to_1h_value_created, + up_to_1h_value_destroyed, exit, )?; diff --git a/crates/brk_computer/src/distribution/metrics/relative/base.rs b/crates/brk_computer/src/distribution/metrics/relative/base.rs index 32866d3ac..2bd294dcf 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/base.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/base.rs @@ -1,131 +1,117 @@ +use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Cents, Dollars, Sats, StoredF32, StoredF64, Version}; +use brk_types::{Dollars, Height, Sats, StoredF32, StoredF64, Version}; +use vecdb::{Exit, ReadableVec, Rw, StorageMode}; use crate::internal::{ - LazyBinaryComputedFromHeightLast, LazyBinaryFromHeightLast, LazyFromHeightLast, + ComputedFromHeightLast, NegPercentageDollarsF32, PercentageDollarsF32, PercentageSatsF64, }; -use crate::distribution::metrics::{ImportConfig, SupplyMetrics, UnrealizedBase}; +use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase}; /// Base relative metrics (always computed when relative is enabled). /// All fields are non-Optional - market_cap and realized_cap are always /// available when relative metrics are enabled. -#[derive(Clone, Traversable)] -pub struct RelativeBase { +#[derive(Traversable)] +pub struct RelativeBase { // === Supply in Profit/Loss Relative to Own Supply === - pub supply_in_profit_rel_to_own_supply: LazyBinaryFromHeightLast, - pub supply_in_loss_rel_to_own_supply: LazyBinaryFromHeightLast, + pub supply_in_profit_rel_to_own_supply: ComputedFromHeightLast, + pub supply_in_loss_rel_to_own_supply: ComputedFromHeightLast, // === Unrealized vs Market Cap === - pub unrealized_profit_rel_to_market_cap: LazyBinaryFromHeightLast, - pub unrealized_loss_rel_to_market_cap: LazyBinaryFromHeightLast, - pub neg_unrealized_loss_rel_to_market_cap: - LazyBinaryFromHeightLast, - pub net_unrealized_pnl_rel_to_market_cap: - LazyBinaryFromHeightLast, - pub nupl: LazyBinaryFromHeightLast, + pub unrealized_profit_rel_to_market_cap: ComputedFromHeightLast, + pub unrealized_loss_rel_to_market_cap: ComputedFromHeightLast, + pub neg_unrealized_loss_rel_to_market_cap: ComputedFromHeightLast, + pub net_unrealized_pnl_rel_to_market_cap: ComputedFromHeightLast, + pub nupl: ComputedFromHeightLast, // === Invested Capital in Profit/Loss as % of Realized Cap === - pub invested_capital_in_profit_pct: LazyBinaryFromHeightLast, - pub invested_capital_in_loss_pct: LazyBinaryFromHeightLast, + pub invested_capital_in_profit_pct: ComputedFromHeightLast, + pub invested_capital_in_loss_pct: ComputedFromHeightLast, } impl RelativeBase { - /// Import base relative metrics. - /// - /// `market_cap` is either `all_supply.total.usd` (for non-"all" cohorts) - /// or `supply.total.usd` (for the "all" cohort itself). - pub(crate) fn forced_import( - cfg: &ImportConfig, - unrealized: &UnrealizedBase, - supply: &SupplyMetrics, - market_cap: &LazyBinaryComputedFromHeightLast, - realized_cap: &LazyFromHeightLast, - ) -> Self { + pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { let v1 = Version::ONE; let v2 = Version::new(2); - Self { - supply_in_profit_rel_to_own_supply: - LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("supply_in_profit_rel_to_own_supply"), - cfg.version + v1, - &unrealized.supply_in_profit.sats, - &supply.total.sats, - ), - supply_in_loss_rel_to_own_supply: - LazyBinaryFromHeightLast::from_computed_last::( - &cfg.name("supply_in_loss_rel_to_own_supply"), - cfg.version + v1, - &unrealized.supply_in_loss.sats, - &supply.total.sats, - ), + Ok(Self { + supply_in_profit_rel_to_own_supply: ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("supply_in_profit_rel_to_own_supply"), cfg.version + v1, cfg.indexes, + )?, + supply_in_loss_rel_to_own_supply: ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("supply_in_loss_rel_to_own_supply"), cfg.version + v1, cfg.indexes, + )?, + unrealized_profit_rel_to_market_cap: ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("unrealized_profit_rel_to_market_cap"), cfg.version + v2, cfg.indexes, + )?, + unrealized_loss_rel_to_market_cap: ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("unrealized_loss_rel_to_market_cap"), cfg.version + v2, cfg.indexes, + )?, + neg_unrealized_loss_rel_to_market_cap: ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("neg_unrealized_loss_rel_to_market_cap"), cfg.version + v2, cfg.indexes, + )?, + net_unrealized_pnl_rel_to_market_cap: ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("net_unrealized_pnl_rel_to_market_cap"), cfg.version + v2, cfg.indexes, + )?, + nupl: ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("nupl"), cfg.version + v2, cfg.indexes, + )?, + invested_capital_in_profit_pct: ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("invested_capital_in_profit_pct"), cfg.version, cfg.indexes, + )?, + invested_capital_in_loss_pct: ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("invested_capital_in_loss_pct"), cfg.version, cfg.indexes, + )?, + }) + } - unrealized_profit_rel_to_market_cap: - LazyBinaryFromHeightLast::from_block_last_and_lazy_binary_computed_block_last::< - PercentageDollarsF32, _, _, - >( - &cfg.name("unrealized_profit_rel_to_market_cap"), - cfg.version + v2, - &unrealized.unrealized_profit, - market_cap, - ), - unrealized_loss_rel_to_market_cap: - LazyBinaryFromHeightLast::from_block_last_and_lazy_binary_computed_block_last::< - PercentageDollarsF32, _, _, - >( - &cfg.name("unrealized_loss_rel_to_market_cap"), - cfg.version + v2, - &unrealized.unrealized_loss, - market_cap, - ), - neg_unrealized_loss_rel_to_market_cap: - LazyBinaryFromHeightLast::from_block_last_and_lazy_binary_computed_block_last::< - NegPercentageDollarsF32, _, _, - >( - &cfg.name("neg_unrealized_loss_rel_to_market_cap"), - cfg.version + v2, - &unrealized.unrealized_loss, - market_cap, - ), - net_unrealized_pnl_rel_to_market_cap: - LazyBinaryFromHeightLast::from_binary_block_and_lazy_binary_block_last::< - PercentageDollarsF32, _, _, _, _, - >( - &cfg.name("net_unrealized_pnl_rel_to_market_cap"), - cfg.version + v2, - &unrealized.net_unrealized_pnl, - market_cap, - ), - nupl: - LazyBinaryFromHeightLast::from_binary_block_and_lazy_binary_block_last::< - PercentageDollarsF32, _, _, _, _, - >( - &cfg.name("nupl"), - cfg.version + v2, - &unrealized.net_unrealized_pnl, - market_cap, - ), - - invested_capital_in_profit_pct: - LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::< - PercentageDollarsF32, _, - >( - &cfg.name("invested_capital_in_profit_pct"), - cfg.version, - &unrealized.invested_capital_in_profit, - realized_cap, - ), - invested_capital_in_loss_pct: - LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::< - PercentageDollarsF32, _, - >( - &cfg.name("invested_capital_in_loss_pct"), - cfg.version, - &unrealized.invested_capital_in_loss, - realized_cap, - ), - } + pub(crate) fn compute( + &mut self, + max_from: Height, + unrealized: &UnrealizedBase, + realized: &RealizedBase, + supply_total_sats: &impl ReadableVec, + market_cap: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + self.supply_in_profit_rel_to_own_supply + .compute_binary::( + max_from, &unrealized.supply_in_profit.sats.height, supply_total_sats, exit, + )?; + self.supply_in_loss_rel_to_own_supply + .compute_binary::( + max_from, &unrealized.supply_in_loss.sats.height, supply_total_sats, exit, + )?; + self.unrealized_profit_rel_to_market_cap + .compute_binary::( + max_from, &unrealized.unrealized_profit.height, market_cap, exit, + )?; + self.unrealized_loss_rel_to_market_cap + .compute_binary::( + max_from, &unrealized.unrealized_loss.height, market_cap, exit, + )?; + self.neg_unrealized_loss_rel_to_market_cap + .compute_binary::( + max_from, &unrealized.unrealized_loss.height, market_cap, exit, + )?; + self.net_unrealized_pnl_rel_to_market_cap + .compute_binary::( + max_from, &unrealized.net_unrealized_pnl.height, market_cap, exit, + )?; + self.nupl + .compute_binary::( + max_from, &unrealized.net_unrealized_pnl.height, market_cap, exit, + )?; + self.invested_capital_in_profit_pct + .compute_binary::( + max_from, &unrealized.invested_capital_in_profit.height, &realized.realized_cap.height, exit, + )?; + self.invested_capital_in_loss_pct + .compute_binary::( + max_from, &unrealized.invested_capital_in_loss.height, &realized.realized_cap.height, exit, + )?; + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/relative/extended_own_market_cap.rs b/crates/brk_computer/src/distribution/metrics/relative/extended_own_market_cap.rs index 721212289..e8b625654 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/extended_own_market_cap.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/extended_own_market_cap.rs @@ -1,71 +1,88 @@ +use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Dollars, Sats, StoredF32, Version}; +use brk_types::{Dollars, Height, StoredF32}; +use vecdb::{Exit, ReadableVec, Rw, StorageMode}; use crate::internal::{ - LazyBinaryComputedFromHeightLast, LazyBinaryFromHeightLast, - NegPercentageDollarsF32, PercentageDollarsF32, + ComputedFromHeightLast, NegPercentageDollarsF32, PercentageDollarsF32, }; use crate::distribution::metrics::{ImportConfig, UnrealizedBase}; /// Extended relative metrics for own market cap (extended && rel_to_all). -#[derive(Clone, Traversable)] -pub struct RelativeExtendedOwnMarketCap { +#[derive(Traversable)] +pub struct RelativeExtendedOwnMarketCap { pub unrealized_profit_rel_to_own_market_cap: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, pub unrealized_loss_rel_to_own_market_cap: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, pub neg_unrealized_loss_rel_to_own_market_cap: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, pub net_unrealized_pnl_rel_to_own_market_cap: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, } impl RelativeExtendedOwnMarketCap { pub(crate) fn forced_import( cfg: &ImportConfig, - unrealized: &UnrealizedBase, - own_market_cap: &LazyBinaryComputedFromHeightLast, - ) -> Self { - let v2 = Version::new(2); + ) -> Result { + let v2 = brk_types::Version::new(2); - Self { + Ok(Self { unrealized_profit_rel_to_own_market_cap: - LazyBinaryFromHeightLast::from_block_last_and_lazy_binary_computed_block_last::< - PercentageDollarsF32, _, _, - >( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("unrealized_profit_rel_to_own_market_cap"), cfg.version + v2, - &unrealized.unrealized_profit, - own_market_cap, - ), + cfg.indexes, + )?, unrealized_loss_rel_to_own_market_cap: - LazyBinaryFromHeightLast::from_block_last_and_lazy_binary_computed_block_last::< - PercentageDollarsF32, _, _, - >( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("unrealized_loss_rel_to_own_market_cap"), cfg.version + v2, - &unrealized.unrealized_loss, - own_market_cap, - ), + cfg.indexes, + )?, neg_unrealized_loss_rel_to_own_market_cap: - LazyBinaryFromHeightLast::from_block_last_and_lazy_binary_computed_block_last::< - NegPercentageDollarsF32, _, _, - >( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("neg_unrealized_loss_rel_to_own_market_cap"), cfg.version + v2, - &unrealized.unrealized_loss, - own_market_cap, - ), + cfg.indexes, + )?, net_unrealized_pnl_rel_to_own_market_cap: - LazyBinaryFromHeightLast::from_binary_block_and_lazy_binary_block_last::< - PercentageDollarsF32, _, _, _, _, - >( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("net_unrealized_pnl_rel_to_own_market_cap"), cfg.version + v2, - &unrealized.net_unrealized_pnl, - own_market_cap, - ), - } + cfg.indexes, + )?, + }) + } + + pub(crate) fn compute( + &mut self, + max_from: Height, + unrealized: &UnrealizedBase, + own_market_cap: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + self.unrealized_profit_rel_to_own_market_cap + .compute_binary::( + max_from, &unrealized.unrealized_profit.height, own_market_cap, exit, + )?; + self.unrealized_loss_rel_to_own_market_cap + .compute_binary::( + max_from, &unrealized.unrealized_loss.height, own_market_cap, exit, + )?; + self.neg_unrealized_loss_rel_to_own_market_cap + .compute_binary::( + max_from, &unrealized.unrealized_loss.height, own_market_cap, exit, + )?; + self.net_unrealized_pnl_rel_to_own_market_cap + .compute_binary::( + max_from, &unrealized.net_unrealized_pnl.height, own_market_cap, exit, + )?; + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/relative/extended_own_pnl.rs b/crates/brk_computer/src/distribution/metrics/relative/extended_own_pnl.rs index 67caa2e3c..cd296d670 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/extended_own_pnl.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/extended_own_pnl.rs @@ -1,62 +1,88 @@ +use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Dollars, StoredF32, Version}; +use brk_types::{Dollars, Height, StoredF32}; +use vecdb::{Exit, Rw, StorageMode}; use crate::internal::{ - LazyBinaryFromHeightLast, NegPercentageDollarsF32, PercentageDollarsF32, + ComputedFromHeightLast, NegPercentageDollarsF32, PercentageDollarsF32, }; use crate::distribution::metrics::{ImportConfig, UnrealizedBase}; /// Extended relative metrics for own total unrealized PnL (extended only). -#[derive(Clone, Traversable)] -pub struct RelativeExtendedOwnPnl { +#[derive(Traversable)] +pub struct RelativeExtendedOwnPnl { pub unrealized_profit_rel_to_own_total_unrealized_pnl: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, pub unrealized_loss_rel_to_own_total_unrealized_pnl: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, pub neg_unrealized_loss_rel_to_own_total_unrealized_pnl: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, pub net_unrealized_pnl_rel_to_own_total_unrealized_pnl: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, } impl RelativeExtendedOwnPnl { pub(crate) fn forced_import( cfg: &ImportConfig, - unrealized: &UnrealizedBase, - ) -> Self { - let v1 = Version::ONE; - let v2 = Version::new(2); + ) -> Result { + let v1 = brk_types::Version::ONE; + let v2 = brk_types::Version::new(2); - Self { + Ok(Self { unrealized_profit_rel_to_own_total_unrealized_pnl: - LazyBinaryFromHeightLast::from_block_last_and_binary_block::( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"), cfg.version + v1, - &unrealized.unrealized_profit, - &unrealized.total_unrealized_pnl, - ), + cfg.indexes, + )?, unrealized_loss_rel_to_own_total_unrealized_pnl: - LazyBinaryFromHeightLast::from_block_last_and_binary_block::( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"), cfg.version + v1, - &unrealized.unrealized_loss, - &unrealized.total_unrealized_pnl, - ), + cfg.indexes, + )?, neg_unrealized_loss_rel_to_own_total_unrealized_pnl: - LazyBinaryFromHeightLast::from_block_last_and_binary_block::( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"), cfg.version + v1, - &unrealized.unrealized_loss, - &unrealized.total_unrealized_pnl, - ), + cfg.indexes, + )?, net_unrealized_pnl_rel_to_own_total_unrealized_pnl: - LazyBinaryFromHeightLast::from_both_binary_block::( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"), cfg.version + v2, - &unrealized.net_unrealized_pnl, - &unrealized.total_unrealized_pnl, - ), - } + cfg.indexes, + )?, + }) + } + + pub(crate) fn compute( + &mut self, + max_from: Height, + unrealized: &UnrealizedBase, + exit: &Exit, + ) -> Result<()> { + self.unrealized_profit_rel_to_own_total_unrealized_pnl + .compute_binary::( + max_from, &unrealized.unrealized_profit.height, &unrealized.total_unrealized_pnl.height, exit, + )?; + self.unrealized_loss_rel_to_own_total_unrealized_pnl + .compute_binary::( + max_from, &unrealized.unrealized_loss.height, &unrealized.total_unrealized_pnl.height, exit, + )?; + self.neg_unrealized_loss_rel_to_own_total_unrealized_pnl + .compute_binary::( + max_from, &unrealized.unrealized_loss.height, &unrealized.total_unrealized_pnl.height, exit, + )?; + self.net_unrealized_pnl_rel_to_own_total_unrealized_pnl + .compute_binary::( + max_from, &unrealized.net_unrealized_pnl.height, &unrealized.total_unrealized_pnl.height, exit, + )?; + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/relative/for_all.rs b/crates/brk_computer/src/distribution/metrics/relative/for_all.rs index afb66d3af..87e932d3c 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/for_all.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/for_all.rs @@ -1,43 +1,58 @@ -use brk_types::Dollars; +use brk_error::Result; use brk_traversable::Traversable; +use brk_types::{Dollars, Height, Sats}; use derive_more::{Deref, DerefMut}; +use vecdb::{Exit, ReadableVec, Rw, StorageMode}; -use crate::internal::ComputedFromHeightLast; - -use crate::distribution::metrics::{ImportConfig, RealizedBase, SupplyMetrics, UnrealizedBase}; +use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase}; use super::{RelativeBase, RelativeExtendedOwnPnl, RelativePeakRegret}; /// Relative metrics for the "all" cohort (base + own_pnl + peak_regret, NO rel_to_all). -#[derive(Clone, Deref, DerefMut, Traversable)] +#[derive(Deref, DerefMut, Traversable)] #[traversable(merge)] -pub struct RelativeForAll { +pub struct RelativeForAll { #[deref] #[deref_mut] #[traversable(flatten)] - pub base: RelativeBase, + pub base: RelativeBase, #[traversable(flatten)] - pub extended_own_pnl: RelativeExtendedOwnPnl, + pub extended_own_pnl: RelativeExtendedOwnPnl, #[traversable(flatten)] - pub peak_regret: RelativePeakRegret, + pub peak_regret: RelativePeakRegret, } impl RelativeForAll { - pub(crate) fn forced_import( - cfg: &ImportConfig, + pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { + Ok(Self { + base: RelativeBase::forced_import(cfg)?, + extended_own_pnl: RelativeExtendedOwnPnl::forced_import(cfg)?, + peak_regret: RelativePeakRegret::forced_import(cfg)?, + }) + } + + #[allow(clippy::too_many_arguments)] + pub(crate) fn compute( + &mut self, + max_from: Height, unrealized: &UnrealizedBase, - supply: &SupplyMetrics, - realized_base: &RealizedBase, - peak_regret: &ComputedFromHeightLast, - ) -> Self { - // For the "all" cohort, market_cap = own market cap - let market_cap = &supply.total.usd; - Self { - base: RelativeBase::forced_import( - cfg, unrealized, supply, market_cap, &realized_base.realized_cap, - ), - extended_own_pnl: RelativeExtendedOwnPnl::forced_import(cfg, unrealized), - peak_regret: RelativePeakRegret::forced_import(cfg, peak_regret, market_cap), - } + realized: &RealizedBase, + supply_total_sats: &impl ReadableVec, + market_cap: &impl ReadableVec, + peak_regret_val: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + self.base.compute( + max_from, + unrealized, + realized, + supply_total_sats, + market_cap, + exit, + )?; + self.extended_own_pnl.compute(max_from, unrealized, exit)?; + self.peak_regret + .compute(max_from, peak_regret_val, market_cap, exit)?; + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/relative/peak_regret.rs b/crates/brk_computer/src/distribution/metrics/relative/peak_regret.rs index 3acb04391..51f3d9227 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/peak_regret.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/peak_regret.rs @@ -1,36 +1,46 @@ +use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Dollars, Sats, StoredF32}; +use brk_types::{Dollars, Height, StoredF32}; +use vecdb::{Exit, ReadableVec, Rw, StorageMode}; use crate::internal::{ - ComputedFromHeightLast, LazyBinaryComputedFromHeightLast, LazyBinaryFromHeightLast, - PercentageDollarsF32, + ComputedFromHeightLast, PercentageDollarsF32, }; use crate::distribution::metrics::ImportConfig; /// Peak regret relative metric. -#[derive(Clone, Traversable)] -pub struct RelativePeakRegret { +#[derive(Traversable)] +pub struct RelativePeakRegret { pub unrealized_peak_regret_rel_to_market_cap: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, } impl RelativePeakRegret { pub(crate) fn forced_import( cfg: &ImportConfig, - peak_regret: &ComputedFromHeightLast, - market_cap: &LazyBinaryComputedFromHeightLast, - ) -> Self { - Self { + ) -> Result { + Ok(Self { unrealized_peak_regret_rel_to_market_cap: - LazyBinaryFromHeightLast::from_block_last_and_lazy_binary_computed_block_last::< - PercentageDollarsF32, _, _, - >( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("unrealized_peak_regret_rel_to_market_cap"), cfg.version, - peak_regret, - market_cap, - ), - } + cfg.indexes, + )?, + }) + } + + pub(crate) fn compute( + &mut self, + max_from: Height, + peak_regret: &impl ReadableVec, + market_cap: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + self.unrealized_peak_regret_rel_to_market_cap + .compute_binary::( + max_from, peak_regret, market_cap, exit, + ) } } diff --git a/crates/brk_computer/src/distribution/metrics/relative/to_all.rs b/crates/brk_computer/src/distribution/metrics/relative/to_all.rs index 4842f46c7..f6487c011 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/to_all.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/to_all.rs @@ -1,53 +1,72 @@ +use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Sats, StoredF64, Version}; +use brk_types::{Height, Sats, StoredF64}; +use vecdb::{Exit, ReadableVec, Rw, StorageMode}; -use crate::internal::{LazyBinaryFromHeightLast, PercentageSatsF64}; +use crate::internal::{ComputedFromHeightLast, PercentageSatsF64}; -use crate::distribution::metrics::{ImportConfig, SupplyMetrics, UnrealizedBase}; +use crate::distribution::metrics::{ImportConfig, UnrealizedBase}; /// Relative-to-all metrics (not present for the "all" cohort itself). -#[derive(Clone, Traversable)] -pub struct RelativeToAll { +#[derive(Traversable)] +pub struct RelativeToAll { pub supply_rel_to_circulating_supply: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, pub supply_in_profit_rel_to_circulating_supply: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, pub supply_in_loss_rel_to_circulating_supply: - LazyBinaryFromHeightLast, + ComputedFromHeightLast, } impl RelativeToAll { pub(crate) fn forced_import( cfg: &ImportConfig, - unrealized: &UnrealizedBase, - supply: &SupplyMetrics, - all_supply: &SupplyMetrics, - ) -> Self { - let v1 = Version::ONE; - let gs = &all_supply.total.sats; - - Self { + ) -> Result { + Ok(Self { supply_rel_to_circulating_supply: - LazyBinaryFromHeightLast::from_computed_last::( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("supply_rel_to_circulating_supply"), - cfg.version + v1, - &supply.total.sats, - gs, - ), + cfg.version + brk_types::Version::ONE, + cfg.indexes, + )?, supply_in_profit_rel_to_circulating_supply: - LazyBinaryFromHeightLast::from_computed_last::( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("supply_in_profit_rel_to_circulating_supply"), - cfg.version + v1, - &unrealized.supply_in_profit.sats, - gs, - ), + cfg.version + brk_types::Version::ONE, + cfg.indexes, + )?, supply_in_loss_rel_to_circulating_supply: - LazyBinaryFromHeightLast::from_computed_last::( + ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("supply_in_loss_rel_to_circulating_supply"), - cfg.version + v1, - &unrealized.supply_in_loss.sats, - gs, - ), - } + cfg.version + brk_types::Version::ONE, + cfg.indexes, + )?, + }) + } + + pub(crate) fn compute( + &mut self, + max_from: Height, + unrealized: &UnrealizedBase, + supply_total_sats: &impl ReadableVec, + all_supply_sats: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + self.supply_rel_to_circulating_supply + .compute_binary::( + max_from, supply_total_sats, all_supply_sats, exit, + )?; + self.supply_in_profit_rel_to_circulating_supply + .compute_binary::( + max_from, &unrealized.supply_in_profit.sats.height, all_supply_sats, exit, + )?; + self.supply_in_loss_rel_to_circulating_supply + .compute_binary::( + max_from, &unrealized.supply_in_loss.sats.height, all_supply_sats, exit, + )?; + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/relative/with_extended.rs b/crates/brk_computer/src/distribution/metrics/relative/with_extended.rs index c0f516421..88690267d 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/with_extended.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/with_extended.rs @@ -1,10 +1,10 @@ -use brk_types::Dollars; +use brk_error::Result; use brk_traversable::Traversable; +use brk_types::{Dollars, Height, Sats}; use derive_more::{Deref, DerefMut}; +use vecdb::{Exit, ReadableVec, Rw, StorageMode}; -use crate::internal::ComputedFromHeightLast; - -use crate::distribution::metrics::{ImportConfig, RealizedBase, SupplyMetrics, UnrealizedBase}; +use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase}; use super::{ RelativeBase, RelativeExtendedOwnMarketCap, RelativeExtendedOwnPnl, @@ -13,44 +13,52 @@ use super::{ /// Full extended relative metrics (base + rel_to_all + own_market_cap + own_pnl + peak_regret). /// Used by: sth, lth, age_range cohorts. -#[derive(Clone, Deref, DerefMut, Traversable)] +#[derive(Deref, DerefMut, Traversable)] #[traversable(merge)] -pub struct RelativeWithExtended { +pub struct RelativeWithExtended { #[deref] #[deref_mut] #[traversable(flatten)] - pub base: RelativeBase, + pub base: RelativeBase, #[traversable(flatten)] - pub rel_to_all: RelativeToAll, + pub rel_to_all: RelativeToAll, #[traversable(flatten)] - pub extended_own_market_cap: RelativeExtendedOwnMarketCap, + pub extended_own_market_cap: RelativeExtendedOwnMarketCap, #[traversable(flatten)] - pub extended_own_pnl: RelativeExtendedOwnPnl, + pub extended_own_pnl: RelativeExtendedOwnPnl, #[traversable(flatten)] - pub peak_regret: RelativePeakRegret, + pub peak_regret: RelativePeakRegret, } impl RelativeWithExtended { - pub(crate) fn forced_import( - cfg: &ImportConfig, + pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { + Ok(Self { + base: RelativeBase::forced_import(cfg)?, + rel_to_all: RelativeToAll::forced_import(cfg)?, + extended_own_market_cap: RelativeExtendedOwnMarketCap::forced_import(cfg)?, + extended_own_pnl: RelativeExtendedOwnPnl::forced_import(cfg)?, + peak_regret: RelativePeakRegret::forced_import(cfg)?, + }) + } + + #[allow(clippy::too_many_arguments)] + pub(crate) fn compute( + &mut self, + max_from: Height, unrealized: &UnrealizedBase, - supply: &SupplyMetrics, - all_supply: &SupplyMetrics, - realized_base: &RealizedBase, - peak_regret: &ComputedFromHeightLast, - ) -> Self { - let market_cap = &all_supply.total.usd; - let own_market_cap = &supply.total.usd; - Self { - base: RelativeBase::forced_import( - cfg, unrealized, supply, market_cap, &realized_base.realized_cap, - ), - rel_to_all: RelativeToAll::forced_import(cfg, unrealized, supply, all_supply), - extended_own_market_cap: RelativeExtendedOwnMarketCap::forced_import( - cfg, unrealized, own_market_cap, - ), - extended_own_pnl: RelativeExtendedOwnPnl::forced_import(cfg, unrealized), - peak_regret: RelativePeakRegret::forced_import(cfg, peak_regret, market_cap), - } + realized: &RealizedBase, + supply_total_sats: &impl ReadableVec, + market_cap: &impl ReadableVec, + all_supply_sats: &impl ReadableVec, + own_market_cap: &impl ReadableVec, + peak_regret_val: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + self.base.compute(max_from, unrealized, realized, supply_total_sats, market_cap, exit)?; + self.rel_to_all.compute(max_from, unrealized, supply_total_sats, all_supply_sats, exit)?; + self.extended_own_market_cap.compute(max_from, unrealized, own_market_cap, exit)?; + self.extended_own_pnl.compute(max_from, unrealized, exit)?; + self.peak_regret.compute(max_from, peak_regret_val, market_cap, exit)?; + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/relative/with_peak_regret.rs b/crates/brk_computer/src/distribution/metrics/relative/with_peak_regret.rs index 9dfae9e5d..f5f5647d8 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/with_peak_regret.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/with_peak_regret.rs @@ -1,44 +1,66 @@ -use brk_types::Dollars; +use brk_error::Result; use brk_traversable::Traversable; +use brk_types::{Dollars, Height, Sats}; use derive_more::{Deref, DerefMut}; +use vecdb::{Exit, ReadableVec, Rw, StorageMode}; -use crate::internal::ComputedFromHeightLast; - -use crate::distribution::metrics::{ImportConfig, RealizedBase, SupplyMetrics, UnrealizedBase}; +use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase}; use super::{RelativeBase, RelativePeakRegret, RelativeToAll}; /// Relative metrics with rel_to_all + peak_regret (no extended). /// Used by: max_age, min_age cohorts. -#[derive(Clone, Deref, DerefMut, Traversable)] +#[derive(Deref, DerefMut, Traversable)] #[traversable(merge)] -pub struct RelativeWithPeakRegret { +pub struct RelativeWithPeakRegret { #[deref] #[deref_mut] #[traversable(flatten)] - pub base: RelativeBase, + pub base: RelativeBase, #[traversable(flatten)] - pub rel_to_all: RelativeToAll, + pub rel_to_all: RelativeToAll, #[traversable(flatten)] - pub peak_regret: RelativePeakRegret, + pub peak_regret: RelativePeakRegret, } impl RelativeWithPeakRegret { - pub(crate) fn forced_import( - cfg: &ImportConfig, + pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { + Ok(Self { + base: RelativeBase::forced_import(cfg)?, + rel_to_all: RelativeToAll::forced_import(cfg)?, + peak_regret: RelativePeakRegret::forced_import(cfg)?, + }) + } + + #[allow(clippy::too_many_arguments)] + pub(crate) fn compute( + &mut self, + max_from: Height, unrealized: &UnrealizedBase, - supply: &SupplyMetrics, - all_supply: &SupplyMetrics, - realized_base: &RealizedBase, - peak_regret: &ComputedFromHeightLast, - ) -> Self { - let market_cap = &all_supply.total.usd; - Self { - base: RelativeBase::forced_import( - cfg, unrealized, supply, market_cap, &realized_base.realized_cap, - ), - rel_to_all: RelativeToAll::forced_import(cfg, unrealized, supply, all_supply), - peak_regret: RelativePeakRegret::forced_import(cfg, peak_regret, market_cap), - } + realized: &RealizedBase, + supply_total_sats: &impl ReadableVec, + market_cap: &impl ReadableVec, + all_supply_sats: &impl ReadableVec, + peak_regret_val: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + self.base.compute( + max_from, + unrealized, + realized, + supply_total_sats, + market_cap, + exit, + )?; + self.rel_to_all.compute( + max_from, + unrealized, + supply_total_sats, + all_supply_sats, + exit, + )?; + self.peak_regret + .compute(max_from, peak_regret_val, market_cap, exit)?; + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/relative/with_rel_to_all.rs b/crates/brk_computer/src/distribution/metrics/relative/with_rel_to_all.rs index c1170c3d8..07c606fe6 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/with_rel_to_all.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/with_rel_to_all.rs @@ -1,37 +1,60 @@ +use brk_error::Result; use brk_traversable::Traversable; +use brk_types::{Dollars, Height, Sats}; use derive_more::{Deref, DerefMut}; +use vecdb::{Exit, ReadableVec, Rw, StorageMode}; -use crate::distribution::metrics::{ImportConfig, RealizedBase, SupplyMetrics, UnrealizedBase}; +use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase}; use super::{RelativeBase, RelativeToAll}; /// Relative metrics with rel_to_all (no extended, no peak_regret). /// Used by: epoch, year, type, amount, address cohorts. -#[derive(Clone, Deref, DerefMut, Traversable)] +#[derive(Deref, DerefMut, Traversable)] #[traversable(merge)] -pub struct RelativeWithRelToAll { +pub struct RelativeWithRelToAll { #[deref] #[deref_mut] #[traversable(flatten)] - pub base: RelativeBase, + pub base: RelativeBase, #[traversable(flatten)] - pub rel_to_all: RelativeToAll, + pub rel_to_all: RelativeToAll, } impl RelativeWithRelToAll { - pub(crate) fn forced_import( - cfg: &ImportConfig, + pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { + Ok(Self { + base: RelativeBase::forced_import(cfg)?, + rel_to_all: RelativeToAll::forced_import(cfg)?, + }) + } + + #[allow(clippy::too_many_arguments)] + pub(crate) fn compute( + &mut self, + max_from: Height, unrealized: &UnrealizedBase, - supply: &SupplyMetrics, - all_supply: &SupplyMetrics, - realized_base: &RealizedBase, - ) -> Self { - let market_cap = &all_supply.total.usd; - Self { - base: RelativeBase::forced_import( - cfg, unrealized, supply, market_cap, &realized_base.realized_cap, - ), - rel_to_all: RelativeToAll::forced_import(cfg, unrealized, supply, all_supply), - } + realized: &RealizedBase, + supply_total_sats: &impl ReadableVec, + market_cap: &impl ReadableVec, + all_supply_sats: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + self.base.compute( + max_from, + unrealized, + realized, + supply_total_sats, + market_cap, + exit, + )?; + self.rel_to_all.compute( + max_from, + unrealized, + supply_total_sats, + all_supply_sats, + exit, + )?; + Ok(()) } } diff --git a/crates/brk_computer/src/distribution/metrics/supply.rs b/crates/brk_computer/src/distribution/metrics/supply.rs index 9661e3012..8a87d522f 100644 --- a/crates/brk_computer/src/distribution/metrics/supply.rs +++ b/crates/brk_computer/src/distribution/metrics/supply.rs @@ -2,13 +2,13 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, Sats, Version}; -use crate::{ComputeIndexes, blocks}; +use crate::{ComputeIndexes, blocks, prices}; use rayon::prelude::*; use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec}; use crate::internal::{ - HalfPriceTimesSats, HalveDollars, HalveSats, HalveSatsToBitcoin, - LazyBinaryValueFromHeightLast, ValueChangeFromHeight, ValueFromHeightLast, + HalveDollars, HalveSats, HalveSatsToBitcoin, + LazyValueFromHeightLast, ValueChangeFromHeight, ValueFromHeightLast, }; use super::ImportConfig; @@ -17,7 +17,7 @@ use super::ImportConfig; #[derive(Traversable)] pub struct SupplyMetrics { pub total: ValueFromHeightLast, - pub halved: LazyBinaryValueFromHeightLast, + pub halved: LazyValueFromHeightLast, /// 30-day change in supply (net position change) - sats, btc, usd pub _30d_change: ValueChangeFromHeight, } @@ -30,15 +30,13 @@ impl SupplyMetrics { &cfg.name("supply"), cfg.version, cfg.indexes, - cfg.prices, )?; - let supply_halved = LazyBinaryValueFromHeightLast::from_block_source::< + let supply_halved = LazyValueFromHeightLast::from_block_source::< HalveSats, HalveSatsToBitcoin, - HalfPriceTimesSats, HalveDollars, - >(&cfg.name("supply_halved"), &supply, cfg.prices, cfg.version); + >(&cfg.name("supply_halved"), &supply, cfg.version); let _30d_change = ValueChangeFromHeight::forced_import( cfg.db, @@ -67,7 +65,21 @@ impl SupplyMetrics { /// Returns a parallel iterator over all vecs for parallel writing. pub(crate) fn par_iter_mut(&mut self) -> impl ParallelIterator { - vec![&mut self.total.sats.height as &mut dyn AnyStoredVec].into_par_iter() + vec![ + &mut self.total.sats.height as &mut dyn AnyStoredVec, + &mut self.total.usd.height as &mut dyn AnyStoredVec, + ] + .into_par_iter() + } + + /// Eagerly compute USD height values from sats × price. + pub(crate) fn compute( + &mut self, + prices: &prices::Vecs, + max_from: Height, + exit: &Exit, + ) -> Result<()> { + self.total.compute(prices, max_from, exit) } /// Validate computed versions against base version. diff --git a/crates/brk_computer/src/distribution/metrics/unrealized/base.rs b/crates/brk_computer/src/distribution/metrics/unrealized/base.rs index dae624805..ae7227f82 100644 --- a/crates/brk_computer/src/distribution/metrics/unrealized/base.rs +++ b/crates/brk_computer/src/distribution/metrics/unrealized/base.rs @@ -10,8 +10,7 @@ use crate::{ ComputeIndexes, distribution::state::UnrealizedState, internal::{ - ComputedFromHeightLast, DollarsMinus, DollarsPlus, LazyBinaryFromHeightLast, - LazyFromHeightLast, ValueFromHeightLast, + ComputedFromHeightLast, LazyFromHeightLast, ValueFromHeightLast, }, prices, }; @@ -48,8 +47,8 @@ pub struct UnrealizedBase { pub neg_unrealized_loss: LazyFromHeightLast, // === Net and Total === - pub net_unrealized_pnl: LazyBinaryFromHeightLast, - pub total_unrealized_pnl: LazyBinaryFromHeightLast, + pub net_unrealized_pnl: ComputedFromHeightLast, + pub total_unrealized_pnl: ComputedFromHeightLast, } impl UnrealizedBase { @@ -59,14 +58,12 @@ impl UnrealizedBase { &cfg.name("supply_in_profit"), cfg.version, cfg.indexes, - cfg.prices, )?; let supply_in_loss = ValueFromHeightLast::forced_import( cfg.db, &cfg.name("supply_in_loss"), cfg.version, cfg.indexes, - cfg.prices, )?; let unrealized_profit = ComputedFromHeightLast::forced_import( @@ -142,18 +139,18 @@ impl UnrealizedBase { &unrealized_loss, ); - let net_unrealized_pnl = LazyBinaryFromHeightLast::from_computed_last::( + let net_unrealized_pnl = ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("net_unrealized_pnl"), cfg.version, - &unrealized_profit, - &unrealized_loss, - ); - let total_unrealized_pnl = LazyBinaryFromHeightLast::from_computed_last::( + cfg.indexes, + )?; + let total_unrealized_pnl = ComputedFromHeightLast::forced_import( + cfg.db, &cfg.name("total_unrealized_pnl"), cfg.version, - &unrealized_profit, - &unrealized_loss, - ); + cfg.indexes, + )?; Ok(Self { supply_in_profit, @@ -240,7 +237,9 @@ impl UnrealizedBase { pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> { vec![ &mut self.supply_in_profit.sats.height as &mut dyn AnyStoredVec, - &mut self.supply_in_loss.sats.height, + &mut self.supply_in_profit.usd.height as &mut dyn AnyStoredVec, + &mut self.supply_in_loss.sats.height as &mut dyn AnyStoredVec, + &mut self.supply_in_loss.usd.height as &mut dyn AnyStoredVec, &mut self.unrealized_profit.height, &mut self.unrealized_loss.height, &mut self.invested_capital_in_profit.height, @@ -419,6 +418,19 @@ impl UnrealizedBase { exit, )?; + self.net_unrealized_pnl.height.compute_subtract( + starting_indexes.height, + &self.unrealized_profit.height, + &self.unrealized_loss.height, + exit, + )?; + self.total_unrealized_pnl.height.compute_add( + starting_indexes.height, + &self.unrealized_profit.height, + &self.unrealized_loss.height, + exit, + )?; + Ok(()) } diff --git a/crates/brk_computer/src/distribution/vecs.rs b/crates/brk_computer/src/distribution/vecs.rs index ce1e94b5e..415413d19 100644 --- a/crates/brk_computer/src/distribution/vecs.rs +++ b/crates/brk_computer/src/distribution/vecs.rs @@ -9,8 +9,8 @@ use brk_types::{ }; use tracing::{debug, info}; use vecdb::{ - AnyVec, BytesVec, Database, Exit, WritableVec, ImportableVec, ReadableCloneableVec, - ReadableVec, Rw, StorageMode, LazyVecFrom1, PAGE_SIZE, Stamp, + AnyVec, BytesVec, Database, Exit, ImportableVec, LazyVecFrom1, PAGE_SIZE, ReadOnlyClone, + ReadableCloneableVec, ReadableVec, Rw, Stamp, StorageMode, WritableVec, }; use crate::{ @@ -50,12 +50,12 @@ pub struct Vecs { pub empty_addr_count: AddrCountsVecs, pub address_activity: AddressActivityVecs, - /// Total addresses ever seen (addr_count + empty_addr_count) - lazy, global + per-type - pub total_addr_count: TotalAddrCountVecs, + /// Total addresses ever seen (addr_count + empty_addr_count) - stored, global + per-type + pub total_addr_count: TotalAddrCountVecs, /// New addresses per block (delta of total) - lazy height, stored day1 stats, global + per-type pub new_addr_count: NewAddrCountVecs, - /// Growth rate (new / addr_count) - lazy ratio with distribution stats, global + per-type - pub growth_rate: GrowthRateVecs, + /// Growth rate (new / addr_count) - stored ratio with distribution stats, global + per-type + pub growth_rate: GrowthRateVecs, pub fundedaddressindex: LazyVecFrom1, @@ -70,7 +70,6 @@ impl Vecs { parent: &Path, parent_version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { let db_path = parent.join(super::DB_NAME); let states_path = db_path.join("states"); @@ -81,17 +80,9 @@ impl Vecs { let version = parent_version + VERSION; - let utxo_cohorts = UTXOCohorts::forced_import(&db, version, indexes, prices, &states_path)?; + let utxo_cohorts = UTXOCohorts::forced_import(&db, version, indexes, &states_path)?; - // Create address cohorts with reference to utxo "all" cohort's supply for global ratios - let address_cohorts = AddressCohorts::forced_import( - &db, - version, - indexes, - prices, - &states_path, - &utxo_cohorts.all.metrics.supply, - )?; + let address_cohorts = AddressCohorts::forced_import(&db, version, indexes, &states_path)?; // Create address data BytesVecs first so we can also use them for identity mappings let fundedaddressindex_to_fundedaddressdata = BytesVec::forced_import_with( @@ -123,21 +114,15 @@ impl Vecs { let address_activity = AddressActivityVecs::forced_import(&db, "address_activity", version, indexes)?; - // Lazy total = addr_count + empty_addr_count (global + per-type, with all derived indexes) - let total_addr_count = TotalAddrCountVecs::forced_import( - version, - indexes, - &addr_count, - &empty_addr_count, - )?; + // Stored total = addr_count + empty_addr_count (global + per-type, with all derived indexes) + let total_addr_count = TotalAddrCountVecs::forced_import(&db, version, indexes)?; // Lazy delta of total (global + per-type) let new_addr_count = NewAddrCountVecs::forced_import(&db, version, indexes, &total_addr_count)?; // Growth rate: new / addr_count (global + per-type) - let growth_rate = - GrowthRateVecs::forced_import(version, indexes, &new_addr_count, &addr_count)?; + let growth_rate = GrowthRateVecs::forced_import(&db, version, indexes)?; let this = Self { supply_state: BytesVec::forced_import_with( @@ -359,14 +344,38 @@ impl Vecs { self.empty_addr_count .compute_rest(blocks, starting_indexes, exit)?; - // 6d. Compute new_addr_count cumulative (height is lazy delta) + // 6c. Compute total_addr_count = addr_count + empty_addr_count + self.total_addr_count.compute( + starting_indexes.height, + &self.addr_count, + &self.empty_addr_count, + exit, + )?; + + // 6d. Compute new_addr_count cumulative + rolling (height is lazy delta) + let window_starts = blocks.count.window_starts(); self.new_addr_count - .compute_cumulative(starting_indexes, exit)?; + .compute(starting_indexes.height, &window_starts, exit)?; + + // 6e. Compute growth_rate = new_addr_count / addr_count + self.growth_rate.compute( + starting_indexes.height, + &window_starts, + &self.new_addr_count, + &self.addr_count, + exit, + )?; // 7. Compute rest part2 (relative metrics) - let supply_metrics = &self.utxo_cohorts.all.metrics.supply; - - let height_to_market_cap = supply_metrics.total.usd.height.clone(); + let height_to_market_cap = self + .utxo_cohorts + .all + .metrics + .supply + .total + .usd + .height + .read_only_clone(); aggregates::compute_rest_part2( &mut self.utxo_cohorts, @@ -400,5 +409,4 @@ impl Vecs { .min(Height::from(self.empty_addr_count.min_stateful_height())) .min(Height::from(self.address_activity.min_stateful_height())) } - } diff --git a/crates/brk_computer/src/inputs/count/compute.rs b/crates/brk_computer/src/inputs/count/compute.rs index 263eab2a3..386108009 100644 --- a/crates/brk_computer/src/inputs/count/compute.rs +++ b/crates/brk_computer/src/inputs/count/compute.rs @@ -27,7 +27,7 @@ impl Vecs { self.rolling.compute( starting_indexes.height, &window_starts, - self.height.sum_cum.sum.inner(), + self.height.sum_cumulative.sum.inner(), exit, )?; diff --git a/crates/brk_computer/src/internal/block_windows.rs b/crates/brk_computer/src/internal/block_windows.rs new file mode 100644 index 000000000..f12f340dd --- /dev/null +++ b/crates/brk_computer/src/internal/block_windows.rs @@ -0,0 +1,15 @@ +//! Base generic struct with 2 type parameters — one per rolling window duration. +//! +//! Foundation for tx-derived rolling window types (1h, 24h — actual time-based). + +use brk_traversable::Traversable; + +#[derive(Clone, Traversable)] +#[traversable(merge)] +pub struct BlockWindows { + #[traversable(rename = "1h")] + pub _1h: A, + #[traversable(rename = "24h")] + pub _24h: B, +} + diff --git a/crates/brk_computer/src/internal/compute.rs b/crates/brk_computer/src/internal/compute.rs index e63cbef9b..5b417bb31 100644 --- a/crates/brk_computer/src/internal/compute.rs +++ b/crates/brk_computer/src/internal/compute.rs @@ -314,3 +314,108 @@ where Ok(()) } + +/// Compute distribution stats from windowed ranges of a source vec. +/// +/// For each index `i`, reads all source items from groups `window_starts[i]..=i` +/// and computes average, min, max, median, and percentiles across the full window. +#[allow(clippy::too_many_arguments)] +pub(crate) fn compute_aggregations_windowed( + max_from: I, + source: &impl ReadableVec, + first_indexes: &impl ReadableVec, + count_indexes: &impl ReadableVec, + window_starts: &impl ReadableVec, + exit: &Exit, + min: &mut EagerVec>, + max: &mut EagerVec>, + average: &mut EagerVec>, + median: &mut EagerVec>, + pct10: &mut EagerVec>, + pct25: &mut EagerVec>, + pct75: &mut EagerVec>, + pct90: &mut EagerVec>, +) -> Result<()> +where + I: VecIndex, + T: ComputedVecValue + JsonSchema, + A: VecIndex + VecValue + CheckedSub, +{ + let combined_version = + source.version() + first_indexes.version() + count_indexes.version() + window_starts.version(); + + let mut idx = max_from; + for vec in [&mut *min, &mut *max, &mut *average, &mut *median, &mut *pct10, &mut *pct25, &mut *pct75, &mut *pct90] { + idx = validate_and_start(vec, combined_version, idx)?; + } + let index = idx; + + let start = index.to_usize(); + let fi_len = first_indexes.len(); + + let first_indexes_batch: Vec = first_indexes.collect_range_at(start, fi_len); + let count_indexes_batch: Vec = count_indexes.collect_range_at(start, fi_len); + let window_starts_batch: Vec = window_starts.collect_range_at(start, fi_len); + + let zero = T::from(0_usize); + + first_indexes_batch + .iter() + .zip(count_indexes_batch.iter()) + .zip(window_starts_batch.iter()) + .enumerate() + .try_for_each(|(j, ((fi, ci), ws))| -> Result<()> { + let idx = start + j; + let window_start_offset = ws.to_usize(); + + // Last tx index (exclusive) of current block + let count = u64::from(*ci) as usize; + let range_end_usize = fi.to_usize() + count; + + // First tx index of the window start block + let range_start_usize = if window_start_offset >= start { + first_indexes_batch[window_start_offset - start].to_usize() + } else { + first_indexes + .collect_one_at(window_start_offset) + .unwrap() + .to_usize() + }; + + let effective_count = range_end_usize.saturating_sub(range_start_usize); + + if effective_count == 0 { + for vec in [&mut *min, &mut *max, &mut *average, &mut *median, &mut *pct10, &mut *pct25, &mut *pct75, &mut *pct90] { + vec.truncate_push_at(idx, zero)?; + } + } else { + let mut values: Vec = + source.collect_range_at(range_start_usize, range_end_usize); + + // Compute sum before sorting + let len = values.len(); + let sum_val = values.iter().copied().fold(T::from(0), |a, b| a + b); + let avg = sum_val / len; + + values.sort_unstable(); + + max.truncate_push_at(idx, *values.last().unwrap())?; + pct90.truncate_push_at(idx, get_percentile(&values, 0.90))?; + pct75.truncate_push_at(idx, get_percentile(&values, 0.75))?; + median.truncate_push_at(idx, get_percentile(&values, 0.50))?; + pct25.truncate_push_at(idx, get_percentile(&values, 0.25))?; + pct10.truncate_push_at(idx, get_percentile(&values, 0.10))?; + min.truncate_push_at(idx, *values.first().unwrap())?; + average.truncate_push_at(idx, avg)?; + } + + Ok(()) + })?; + + let _lock = exit.lock(); + for vec in [min, max, average, median, pct10, pct25, pct75, pct90] { + vec.write()?; + } + + Ok(()) +} diff --git a/crates/brk_computer/src/internal/eager_indexes.rs b/crates/brk_computer/src/internal/eager_indexes.rs index 022e05cc1..285373828 100644 --- a/crates/brk_computer/src/internal/eager_indexes.rs +++ b/crates/brk_computer/src/internal/eager_indexes.rs @@ -7,43 +7,44 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, + Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Minute1, Minute5, + Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10, }; use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw, StorageMode, VecIndex}; - -use crate::{ - indexes, - indexes_from, - internal::{ComputedVecValue, Indexes, NumericValue}, - ComputeIndexes, +use vecdb::{ + Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw, StorageMode, VecIndex, }; -pub type EagerIndexesInner = Indexes< - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, - ::Stored>>, ->; +use crate::{ + ComputeIndexes, indexes, indexes_from, + internal::{ComputedVecValue, Indexes, NumericValue}, +}; #[derive(Deref, DerefMut, Traversable)] #[traversable(transparent)] -pub struct EagerIndexes(pub EagerIndexesInner) +pub struct EagerIndexes( + #[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: ComputedVecValue + PartialOrd + JsonSchema; diff --git a/crates/brk_computer/src/internal/lazy_eager_indexes.rs b/crates/brk_computer/src/internal/lazy_eager_indexes.rs index 8f3475dc2..f96389f85 100644 --- a/crates/brk_computer/src/internal/lazy_eager_indexes.rs +++ b/crates/brk_computer/src/internal/lazy_eager_indexes.rs @@ -5,8 +5,8 @@ use brk_traversable::Traversable; use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Minute1, Minute10, Minute30, Minute5, Month1, - Month3, Month6, Version, Week1, Year1, Year10, Hour1, Hour4, Hour12, + Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour4, Hour12, Minute1, Minute5, Minute10, + Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10, }; use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; @@ -17,29 +17,30 @@ use crate::{ internal::{ComputedVecValue, EagerIndexes, Indexes}, }; -pub type LazyEagerIndexesInner = Indexes< - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, - LazyVecFrom1, ->; - #[derive(Clone, Deref, DerefMut, Traversable)] #[traversable(transparent)] -pub struct LazyEagerIndexes(pub LazyEagerIndexesInner) +pub struct LazyEagerIndexes( + #[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: ComputedVecValue + PartialOrd + JsonSchema, S: ComputedVecValue; diff --git a/crates/brk_computer/src/internal/mod.rs b/crates/brk_computer/src/internal/mod.rs index 5092153b7..5069e6584 100644 --- a/crates/brk_computer/src/internal/mod.rs +++ b/crates/brk_computer/src/internal/mod.rs @@ -1,3 +1,4 @@ +mod block_windows; mod compute; mod distribution_stats; mod eager_indexes; @@ -8,6 +9,7 @@ mod single; mod traits; mod windows; +pub(crate) use block_windows::*; pub(crate) use compute::*; pub(crate) use distribution_stats::*; pub(crate) use eager_indexes::*; diff --git a/crates/brk_computer/src/internal/multi/from_height/binary_last.rs b/crates/brk_computer/src/internal/multi/from_height/binary_last.rs deleted file mode 100644 index 4d17863e6..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/binary_last.rs +++ /dev/null @@ -1,309 +0,0 @@ -//! Lazy binary transform from two SumCum sources, producing Last (cumulative) ratios only. - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2}; - -use crate::{ - indexes_from, - internal::{ - ComputedFromHeightLast, ComputedFromHeightSumCum, ComputedHeightDerivedLast, - ComputedVecValue, LazyBinaryComputedFromHeightLast, LazyBinaryHeightDerivedLast, - LazyBinaryTransformLast, LazyFromHeightLast, NumericValue, - }, -}; - -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryFromHeightLast -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - pub height: LazyVecFrom2, - #[deref] - #[deref_mut] - pub rest: Box>, -} - -const VERSION: Version = Version::ZERO; - -/// Helper macro: given two deref-able sources whose `.$p` fields implement -/// `ReadableCloneableVec`, build all 17 period fields of a `LazyBinaryHeightDerivedLast`. -macro_rules! build_rest { - ($name:expr, $v:expr, $source1:expr, $source2:expr) => {{ - macro_rules! period { - ($p:ident) => { - LazyBinaryTransformLast::from_vecs::( - $name, - $v, - $source1.$p.read_only_boxed_clone(), - $source2.$p.read_only_boxed_clone(), - ) - }; - } - Box::new(LazyBinaryHeightDerivedLast(indexes_from!(period))) - }}; -} - -impl LazyBinaryFromHeightLast -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_computed_sum_cum>( - name: &str, - version: Version, - source1: &ComputedFromHeightSumCum, - source2: &ComputedFromHeightSumCum, - ) -> Self - where - S1T: PartialOrd, - S2T: PartialOrd, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::( - name, - v, - source1.height_cumulative.read_only_boxed_clone(), - source2.height_cumulative.read_only_boxed_clone(), - ), - rest: Box::new(LazyBinaryHeightDerivedLast::from_computed_sum_cum::(name, v, source1, source2)), - } - } - - pub(crate) fn from_computed_last>( - name: &str, - version: Version, - source1: &ComputedFromHeightLast, - source2: &ComputedFromHeightLast, - ) -> Self - where - S1T: NumericValue, - S2T: NumericValue, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::( - name, - v, - source1.height.read_only_boxed_clone(), - source2.height.read_only_boxed_clone(), - ), - rest: Box::new(LazyBinaryHeightDerivedLast::from_computed_last::(name, v, source1, source2)), - } - } - - pub(crate) fn from_block_last_and_lazy_block_last( - name: &str, - version: Version, - source1: &ComputedFromHeightLast, - source2: &LazyFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S1T: NumericValue, - S2SourceT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::( - name, - v, - source1.height.read_only_boxed_clone(), - source2.height.read_only_boxed_clone(), - ), - rest: Box::new(LazyBinaryHeightDerivedLast::from_block_last_and_lazy_block_last::( - name, v, source1, source2, - )), - } - } - - pub(crate) fn from_lazy_block_last_and_block_last( - name: &str, - version: Version, - source1: &LazyFromHeightLast, - source2: &ComputedFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S2T: NumericValue, - S1SourceT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::( - name, - v, - source1.height.read_only_boxed_clone(), - source2.height.read_only_boxed_clone(), - ), - rest: Box::new(LazyBinaryHeightDerivedLast::from_lazy_block_last_and_block_last::( - name, v, source1, source2, - )), - } - } - - /// Create from a ComputedFromHeightLast and a LazyBinaryFromHeightLast. - pub(crate) fn from_block_last_and_binary_block( - name: &str, - version: Version, - source1: &ComputedFromHeightLast, - source2: &LazyBinaryFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S1T: NumericValue, - S2aT: ComputedVecValue + JsonSchema, - S2bT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::( - name, - v, - source1.height.read_only_boxed_clone(), - source2.height.read_only_boxed_clone(), - ), - rest: build_rest!(name, v, source1, source2), - } - } - - /// Create from two LazyBinaryFromHeightLast sources. - pub(crate) fn from_both_binary_block( - name: &str, - version: Version, - source1: &LazyBinaryFromHeightLast, - source2: &LazyBinaryFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S1aT: ComputedVecValue + JsonSchema, - S1bT: ComputedVecValue + JsonSchema, - S2aT: ComputedVecValue + JsonSchema, - S2bT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::( - name, - v, - source1.height.read_only_boxed_clone(), - source2.height.read_only_boxed_clone(), - ), - rest: build_rest!(name, v, source1, source2), - } - } - - /// Create from separate height sources and two `ComputedHeightDerivedLast` structs. - pub(crate) fn from_height_and_derived_last>( - name: &str, - version: Version, - height_source1: ReadableBoxedVec, - height_source2: ReadableBoxedVec, - derived1: &ComputedHeightDerivedLast, - derived2: &ComputedHeightDerivedLast, - ) -> Self - where - S1T: NumericValue, - S2T: NumericValue, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::(name, v, height_source1, height_source2), - rest: build_rest!(name, v, derived1, derived2), - } - } - - /// Create from a ComputedFromHeightLast and a LazyBinaryComputedFromHeightLast. - pub(crate) fn from_block_last_and_lazy_binary_computed_block_last( - name: &str, - version: Version, - source1: &ComputedFromHeightLast, - source2: &LazyBinaryComputedFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S1T: NumericValue, - S2aT: ComputedVecValue + JsonSchema, - S2bT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::( - name, - v, - source1.height.read_only_boxed_clone(), - source2.height.read_only_boxed_clone(), - ), - rest: build_rest!(name, v, source1, source2), - } - } - - /// Create from two LazyBinaryComputedFromHeightLast sources. - pub(crate) fn from_both_lazy_binary_computed_block_last( - name: &str, - version: Version, - source1: &LazyBinaryComputedFromHeightLast, - source2: &LazyBinaryComputedFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S1aT: ComputedVecValue + JsonSchema, - S1bT: ComputedVecValue + JsonSchema, - S2aT: ComputedVecValue + JsonSchema, - S2bT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::( - name, - v, - source1.height.read_only_boxed_clone(), - source2.height.read_only_boxed_clone(), - ), - rest: build_rest!(name, v, source1, source2), - } - } - - /// Create from a LazyBinaryFromHeightLast and a LazyBinaryComputedFromHeightLast. - pub(crate) fn from_binary_block_and_lazy_binary_block_last( - name: &str, - version: Version, - source1: &LazyBinaryFromHeightLast, - source2: &LazyBinaryComputedFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S1aT: ComputedVecValue + JsonSchema, - S1bT: ComputedVecValue + JsonSchema, - S2aT: ComputedVecValue + JsonSchema, - S2bT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - Self { - height: LazyVecFrom2::transformed::( - name, - v, - source1.height.read_only_boxed_clone(), - source2.height.read_only_boxed_clone(), - ), - rest: build_rest!(name, v, source1, source2), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/cum.rs b/crates/brk_computer/src/internal/multi/from_height/cumulative.rs similarity index 52% rename from crates/brk_computer/src/internal/multi/from_height/cum.rs rename to crates/brk_computer/src/internal/multi/from_height/cumulative.rs index c12e88469..8e68bba28 100644 --- a/crates/brk_computer/src/internal/multi/from_height/cum.rs +++ b/crates/brk_computer/src/internal/multi/from_height/cumulative.rs @@ -1,38 +1,34 @@ -//! ComputedFromHeightCum - stored height + LazyLast + cumulative (from height). +//! ComputedFromHeightCumulative - stored height + LazyAggVec + cumulative (from height). //! -//! Like ComputedFromHeightCumSum but without RollingWindows. +//! Like ComputedFromHeightCumulativeSum but without RollingWindows. //! Used for distribution metrics where rolling is optional per cohort. -//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views. +//! Cumulative gets its own ComputedFromHeightLast so it has LazyAggVec index views. use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode}; +use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode}; use crate::{ indexes, internal::{ComputedFromHeightLast, NumericValue}, }; -#[derive(Deref, DerefMut, Traversable)] +#[derive(Traversable)] #[traversable(merge)] -pub struct ComputedFromHeightCum +pub struct ComputedFromHeightCumulative where T: NumericValue + JsonSchema, { - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub last: ComputedFromHeightLast, + pub height: M::Stored>>, #[traversable(flatten)] pub cumulative: ComputedFromHeightLast, } const VERSION: Version = Version::ZERO; -impl ComputedFromHeightCum +impl ComputedFromHeightCumulative where T: NumericValue + JsonSchema, { @@ -44,15 +40,11 @@ where ) -> Result { let v = version + VERSION; - let last = ComputedFromHeightLast::forced_import(db, name, v, indexes)?; - let cumulative = ComputedFromHeightLast::forced_import( - db, - &format!("{name}_cumulative"), - v, - indexes, - )?; + let height: EagerVec> = EagerVec::forced_import(db, name, v)?; + let cumulative = + ComputedFromHeightLast::forced_import(db, &format!("{name}_cumulative"), v, indexes)?; - Ok(Self { last, cumulative }) + Ok(Self { height, cumulative }) } /// Compute height data via closure, then cumulative only (no rolling). @@ -65,25 +57,18 @@ where where T: Default, { - compute_height(&mut self.last.height)?; - self.cumulative - .height - .compute_cumulative(max_from, &self.last.height, exit)?; - Ok(()) + compute_height(&mut self.height)?; + self.compute_rest(max_from, exit) } /// Compute cumulative from already-filled height vec. - pub(crate) fn compute_cumulative( - &mut self, - max_from: Height, - exit: &Exit, - ) -> Result<()> + pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> where T: Default, { self.cumulative .height - .compute_cumulative(max_from, &self.last.height, exit)?; + .compute_cumulative(max_from, &self.height, exit)?; Ok(()) } } diff --git a/crates/brk_computer/src/internal/multi/from_height/cum_rolling_full.rs b/crates/brk_computer/src/internal/multi/from_height/cumulative_rolling_full.rs similarity index 62% rename from crates/brk_computer/src/internal/multi/from_height/cum_rolling_full.rs rename to crates/brk_computer/src/internal/multi/from_height/cumulative_rolling_full.rs index 712513e52..c57e9bd23 100644 --- a/crates/brk_computer/src/internal/multi/from_height/cum_rolling_full.rs +++ b/crates/brk_computer/src/internal/multi/from_height/cumulative_rolling_full.rs @@ -1,32 +1,28 @@ -//! ComputedFromHeightCumFull - stored height + LazyLast + cumulative (from height) + RollingFull. +//! ComputedFromHeightCumulativeFull - stored height + LazyAggVec + cumulative (from height) + RollingFull. //! //! For metrics with stored per-block data, cumulative sums, and rolling windows. -//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views too. +//! Cumulative gets its own ComputedFromHeightLast so it has LazyAggVec index views too. use std::ops::SubAssign; use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode}; +use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode}; use crate::{ indexes, internal::{ComputedFromHeightLast, NumericValue, RollingFull, WindowStarts}, }; -#[derive(Deref, DerefMut, Traversable)] +#[derive(Traversable)] #[traversable(merge)] -pub struct ComputedFromHeightCumFull +pub struct ComputedFromHeightCumulativeFull where T: NumericValue + JsonSchema, { - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub last: ComputedFromHeightLast, + pub height: M::Stored>>, #[traversable(flatten)] pub cumulative: ComputedFromHeightLast, #[traversable(flatten)] @@ -35,7 +31,7 @@ where const VERSION: Version = Version::ZERO; -impl ComputedFromHeightCumFull +impl ComputedFromHeightCumulativeFull where T: NumericValue + JsonSchema, { @@ -47,17 +43,13 @@ where ) -> Result { let v = version + VERSION; - let last = ComputedFromHeightLast::forced_import(db, name, v, indexes)?; - let cumulative = ComputedFromHeightLast::forced_import( - db, - &format!("{name}_cumulative"), - v, - indexes, - )?; + let height: EagerVec> = EagerVec::forced_import(db, name, v)?; + let cumulative = + ComputedFromHeightLast::forced_import(db, &format!("{name}_cumulative"), v, indexes)?; let rolling = RollingFull::forced_import(db, name, v, indexes)?; Ok(Self { - last, + height, cumulative, rolling, }) @@ -75,12 +67,12 @@ where T: From + Default + SubAssign + Copy + Ord, f64: From, { - compute_height(&mut self.last.height)?; + compute_height(&mut self.height)?; self.cumulative .height - .compute_cumulative(max_from, &self.last.height, exit)?; + .compute_cumulative(max_from, &self.height, exit)?; self.rolling - .compute(max_from, windows, &self.last.height, exit)?; + .compute(max_from, windows, &self.height, exit)?; Ok(()) } } diff --git a/crates/brk_computer/src/internal/multi/from_height/cum_rolling_sum.rs b/crates/brk_computer/src/internal/multi/from_height/cumulative_rolling_sum.rs similarity index 59% rename from crates/brk_computer/src/internal/multi/from_height/cum_rolling_sum.rs rename to crates/brk_computer/src/internal/multi/from_height/cumulative_rolling_sum.rs index b62b00ecb..0ed09bdf7 100644 --- a/crates/brk_computer/src/internal/multi/from_height/cum_rolling_sum.rs +++ b/crates/brk_computer/src/internal/multi/from_height/cumulative_rolling_sum.rs @@ -1,33 +1,29 @@ -//! ComputedFromHeightCumSum - stored height + LazyLast + cumulative (from height) + RollingWindows (sum). +//! ComputedFromHeightCumulativeSum - stored height + LazyAggVec + cumulative (from height) + RollingWindows (sum). //! -//! Like ComputedFromHeightCumFull but with rolling sum only (no distribution). +//! Like ComputedFromHeightCumulativeFull but with rolling sum only (no distribution). //! Used for count metrics where distribution stats aren't meaningful. -//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views too. +//! Cumulative gets its own ComputedFromHeightLast so it has LazyAggVec index views too. use std::ops::SubAssign; use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode}; +use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode}; use crate::{ indexes, internal::{ComputedFromHeightLast, NumericValue, RollingWindows, WindowStarts}, }; -#[derive(Deref, DerefMut, Traversable)] +#[derive(Traversable)] #[traversable(merge)] -pub struct ComputedFromHeightCumSum +pub struct ComputedFromHeightCumulativeSum where T: NumericValue + JsonSchema, { - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub last: ComputedFromHeightLast, + pub height: M::Stored>>, #[traversable(flatten)] pub cumulative: ComputedFromHeightLast, #[traversable(flatten)] @@ -36,7 +32,7 @@ where const VERSION: Version = Version::ZERO; -impl ComputedFromHeightCumSum +impl ComputedFromHeightCumulativeSum where T: NumericValue + JsonSchema, { @@ -48,17 +44,13 @@ where ) -> Result { let v = version + VERSION; - let last = ComputedFromHeightLast::forced_import(db, name, v, indexes)?; - let cumulative = ComputedFromHeightLast::forced_import( - db, - &format!("{name}_cumulative"), - v, - indexes, - )?; + let height: EagerVec> = EagerVec::forced_import(db, name, v)?; + let cumulative = + ComputedFromHeightLast::forced_import(db, &format!("{name}_cumulative"), v, indexes)?; let rolling = RollingWindows::forced_import(db, name, v, indexes)?; Ok(Self { - last, + height, cumulative, rolling, }) @@ -75,12 +67,12 @@ where where T: Default + SubAssign, { - compute_height(&mut self.last.height)?; + compute_height(&mut self.height)?; self.cumulative .height - .compute_cumulative(max_from, &self.last.height, exit)?; + .compute_cumulative(max_from, &self.height, exit)?; self.rolling - .compute_rolling_sum(max_from, windows, &self.last.height, exit)?; + .compute_rolling_sum(max_from, windows, &self.height, exit)?; Ok(()) } } diff --git a/crates/brk_computer/src/internal/multi/from_height/distribution.rs b/crates/brk_computer/src/internal/multi/from_height/distribution.rs index 071a3941a..0964c2a71 100644 --- a/crates/brk_computer/src/internal/multi/from_height/distribution.rs +++ b/crates/brk_computer/src/internal/multi/from_height/distribution.rs @@ -1,5 +1,6 @@ //! ComputedFromHeight using Distribution aggregation (no sum/cumulative). //! +//! Stored height data + LazyAggVec index views + rolling distribution windows. //! Use for block-based metrics where sum/cumulative would be misleading //! (e.g., activity counts that can't be deduplicated across blocks). @@ -7,25 +8,22 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{Database, EagerVec, ImportableVec, PcoVec, ReadableCloneableVec, Rw, StorageMode}; +use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode}; use crate::indexes; -use crate::internal::{ComputedHeightDerivedDistribution, ComputedVecValue, NumericValue}; +use crate::internal::{ComputedVecValue, NumericValue, RollingDistribution, WindowStarts}; -#[derive(Deref, DerefMut, Traversable)] +#[derive(Traversable)] #[traversable(merge)] pub struct ComputedFromHeightDistribution where T: ComputedVecValue + PartialOrd + JsonSchema, { - #[traversable(rename = "base")] pub height: M::Stored>>, - #[deref] - #[deref_mut] - pub rest: Box>, + #[traversable(flatten)] + pub rolling: RollingDistribution, } const VERSION: Version = Version::ZERO; @@ -43,14 +41,26 @@ where let v = version + VERSION; let height: EagerVec> = EagerVec::forced_import(db, name, v)?; + let rolling = RollingDistribution::forced_import(db, name, v, indexes)?; - let rest = ComputedHeightDerivedDistribution::forced_import( - name, - height.read_only_boxed_clone(), - v, - indexes, - ); + Ok(Self { height, rolling }) + } - Ok(Self { height, rest: Box::new(rest) }) + /// Compute height data via closure, then rolling distribution. + pub(crate) fn compute( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + exit: &Exit, + compute_height: impl FnOnce(&mut EagerVec>) -> Result<()>, + ) -> Result<()> + where + T: Copy + Ord + From + Default, + f64: From, + { + compute_height(&mut self.height)?; + self.rolling + .compute_distribution(max_from, windows, &self.height, exit)?; + Ok(()) } } diff --git a/crates/brk_computer/src/internal/multi/from_height/full.rs b/crates/brk_computer/src/internal/multi/from_height/full.rs deleted file mode 100644 index efd95342e..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/full.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! ComputedFromHeight with full stats aggregation. - -use brk_error::Result; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{Database, EagerVec, ImportableVec, PcoVec, ReadableCloneableVec, Rw, StorageMode}; - -use crate::indexes; - -use crate::internal::{ComputedHeightDerivedFull, ComputedVecValue, NumericValue}; - -#[derive(Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct ComputedFromHeightFull -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - #[traversable(rename = "base")] - pub height: M::Stored>>, - #[deref] - #[deref_mut] - pub rest: Box>, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedFromHeightFull -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let v = version + VERSION; - - let height: EagerVec> = EagerVec::forced_import(db, name, v)?; - - let rest = ComputedHeightDerivedFull::forced_import( - db, - name, - height.read_only_boxed_clone(), - v, - indexes, - )?; - - Ok(Self { - height, - rest: Box::new(rest), - }) - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/last.rs b/crates/brk_computer/src/internal/multi/from_height/last.rs index 0a4e23258..74b7d23f2 100644 --- a/crates/brk_computer/src/internal/multi/from_height/last.rs +++ b/crates/brk_computer/src/internal/multi/from_height/last.rs @@ -6,7 +6,10 @@ use brk_traversable::Traversable; use brk_types::{Height, Version}; use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{Database, EagerVec, ImportableVec, PcoVec, ReadableCloneableVec, Rw, StorageMode}; +use vecdb::{ + BinaryTransform, Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableCloneableVec, + ReadableVec, Rw, StorageMode, VecValue, +}; use crate::indexes; @@ -41,9 +44,39 @@ where let height: EagerVec> = EagerVec::forced_import(db, name, v)?; - let rest = - ComputedHeightDerivedLast::forced_import(name, height.read_only_boxed_clone(), v, indexes); + let rest = ComputedHeightDerivedLast::forced_import( + name, + height.read_only_boxed_clone(), + v, + indexes, + ); - Ok(Self { height, rest: Box::new(rest) }) + Ok(Self { + height, + rest: Box::new(rest), + }) + } + + /// Eagerly compute this vec as a binary transform of two sources. + pub(crate) fn compute_binary( + &mut self, + max_from: Height, + source1: &impl ReadableVec, + source2: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + S1T: VecValue, + S2T: VecValue, + F: BinaryTransform, + { + self.height.compute_transform2( + max_from, + source1, + source2, + |(h, s1, s2, ..)| (h, F::apply(s1, s2)), + exit, + )?; + Ok(()) } } diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_distribution.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_distribution.rs deleted file mode 100644 index 63a377d09..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_distribution.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! LazyBinaryComputedFromHeightDistribution - lazy binary transform with distribution stats. -//! -//! Height-level values are lazy: `transform(source1[h], source2[h])`. -//! Uses Distribution aggregation (no sum/cumulative) - appropriate for ratios. - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2}; - -use crate::{ - indexes, - internal::{ComputedHeightDerivedDistribution, ComputedVecValue, NumericValue}, -}; - -const VERSION: Version = Version::ZERO; - -/// Lazy binary transform at height with distribution stats (no sum/cumulative). -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryComputedFromHeightDistribution -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - #[traversable(rename = "base")] - pub height: LazyVecFrom2, - #[deref] - #[deref_mut] - pub rest: Box>, -} - -impl LazyBinaryComputedFromHeightDistribution -where - T: NumericValue + JsonSchema, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn forced_import>( - name: &str, - version: Version, - source1: ReadableBoxedVec, - source2: ReadableBoxedVec, - indexes: &indexes::Vecs, - ) -> Self { - let v = version + VERSION; - - let height = LazyVecFrom2::transformed::(name, v, source1, source2); - - let rest = ComputedHeightDerivedDistribution::forced_import( - name, - height.read_only_boxed_clone(), - v, - indexes, - ); - - Self { height, rest: Box::new(rest) } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_full.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_full.rs deleted file mode 100644 index ba010320a..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_full.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! LazyBinaryComputedFromHeightFull - block full with lazy binary transform at height level. -//! -//! Height-level values are lazy: `transform(source1[h], source2[h])`. -//! Cumulative, day1 stats, and difficultyepoch are stored since they -//! require aggregation across heights. - -use brk_error::Result; -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, Database, Exit, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2, Rw, StorageMode}; - -use crate::{ - ComputeIndexes, indexes, - internal::{ComputedHeightDerivedFull, ComputedVecValue, NumericValue}, -}; - -const VERSION: Version = Version::ZERO; - -/// Block full aggregation with lazy binary transform at height + computed derived indexes. -#[derive(Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryComputedFromHeightFull -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - #[traversable(rename = "base")] - pub height: LazyVecFrom2, - #[deref] - #[deref_mut] - pub rest: Box>, -} - -impl LazyBinaryComputedFromHeightFull -where - T: NumericValue + JsonSchema, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn forced_import>( - db: &Database, - name: &str, - version: Version, - source1: ReadableBoxedVec, - source2: ReadableBoxedVec, - indexes: &indexes::Vecs, - ) -> Result { - let v = version + VERSION; - - let height = LazyVecFrom2::transformed::(name, v, source1, source2); - - let rest = - ComputedHeightDerivedFull::forced_import(db, name, height.read_only_boxed_clone(), v, indexes)?; - - Ok(Self { height, rest: Box::new(rest) }) - } - - pub(crate) fn compute_cumulative( - &mut self, - starting_indexes: &ComputeIndexes, - exit: &Exit, - ) -> Result<()> { - self.rest - .compute_cumulative(starting_indexes, &self.height, exit) - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_last.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_last.rs deleted file mode 100644 index 2f0e689ac..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_last.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! LazyBinaryComputedFromHeightLast - block last with lazy binary transform at height level. -//! -//! Height-level value is lazy: `transform(source1[h], source2[h])`. -//! Day1 last is stored since it requires finding the last value within each date -//! (which may span multiple heights with varying prices). - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2}; - -use crate::{ - indexes, - internal::{ComputedHeightDerivedLast, ComputedVecValue, NumericValue}, -}; - -const VERSION: Version = Version::ZERO; - -/// Block last aggregation with lazy binary transform at height + computed derived indexes. -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryComputedFromHeightLast -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - pub height: LazyVecFrom2, - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub rest: Box>, -} - -impl LazyBinaryComputedFromHeightLast -where - T: NumericValue + JsonSchema, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn forced_import>( - name: &str, - version: Version, - source1: ReadableBoxedVec, - source2: ReadableBoxedVec, - indexes: &indexes::Vecs, - ) -> Self { - let v = version + VERSION; - - let height = LazyVecFrom2::transformed::(name, v, source1, source2); - - let rest = - ComputedHeightDerivedLast::forced_import(name, height.read_only_boxed_clone(), v, indexes); - - Self { height, rest: Box::new(rest) } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_sum_cum.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_sum_cum.rs deleted file mode 100644 index 384840843..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_binary_computed_sum_cum.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! LazyBinaryComputedFromHeightSumCum - block sum_cum with lazy binary transform at height level. -//! -//! Height-level sum is lazy: `transform(source1[h], source2[h])`. -//! Cumulative and day1 stats are stored since they require aggregation -//! across heights. - -use brk_error::Result; -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, Database, Exit, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2, Rw, StorageMode}; - -use crate::{ - ComputeIndexes, - indexes, - internal::{ComputedHeightDerivedSumCum, ComputedVecValue, NumericValue}, -}; - -const VERSION: Version = Version::ZERO; - -/// Block sum_cum aggregation with lazy binary transform at height + computed derived indexes. -#[derive(Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryComputedFromHeightSumCum -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - #[traversable(rename = "sum")] - pub height: LazyVecFrom2, - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub rest: Box>, -} - -impl LazyBinaryComputedFromHeightSumCum -where - T: NumericValue + JsonSchema, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn forced_import>( - db: &Database, - name: &str, - version: Version, - source1: ReadableBoxedVec, - source2: ReadableBoxedVec, - indexes: &indexes::Vecs, - ) -> Result { - let v = version + VERSION; - - let height = LazyVecFrom2::transformed::(name, v, source1, source2); - - let rest = - ComputedHeightDerivedSumCum::forced_import(db, name, height.read_only_boxed_clone(), v, indexes)?; - - Ok(Self { height, rest: Box::new(rest) }) - } - - pub(crate) fn compute_cumulative( - &mut self, - starting_indexes: &ComputeIndexes, - exit: &Exit, - ) -> Result<()> { - self.rest - .derive_from(starting_indexes, &self.height, exit) - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_computed_full.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_computed_full.rs index 3ece1eca6..3ac777941 100644 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_computed_full.rs +++ b/crates/brk_computer/src/internal/multi/from_height/lazy_computed_full.rs @@ -1,33 +1,34 @@ -//! LazyComputedFromHeightFull - block full with lazy height transform. +//! LazyComputedFromHeightCumulativeFull - block full with lazy height transform + cumulative + rolling. + +use std::ops::SubAssign; use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, Version}; use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{Database, Exit, ReadableCloneableVec, LazyVecFrom1, UnaryTransform, Rw, StorageMode}; +use vecdb::{Database, Exit, LazyVecFrom1, ReadableCloneableVec, Rw, StorageMode, UnaryTransform}; use crate::{ - ComputeIndexes, indexes, - internal::{ComputedVecValue, ComputedHeightDerivedFull, NumericValue}, + internal::{ComputedHeightDerivedCumulativeFull, ComputedVecValue, NumericValue, WindowStarts}, }; const VERSION: Version = Version::ZERO; -/// Block full aggregation with lazy height transform + computed derived indexes. +/// Block full aggregation with lazy height transform + cumulative + rolling windows. #[derive(Deref, DerefMut, Traversable)] #[traversable(merge)] pub struct LazyComputedFromHeightFull where - T: ComputedVecValue + PartialOrd + JsonSchema, + T: NumericValue + JsonSchema, S: ComputedVecValue, { #[traversable(rename = "base")] pub height: LazyVecFrom1, #[deref] #[deref_mut] - pub rest: Box>, + pub rest: Box>, } impl LazyComputedFromHeightFull @@ -46,18 +47,29 @@ where let height = LazyVecFrom1::transformed::(name, v, source.read_only_boxed_clone()); - let rest = - ComputedHeightDerivedFull::forced_import(db, name, height.read_only_boxed_clone(), v, indexes)?; + let rest = ComputedHeightDerivedCumulativeFull::forced_import( + db, + name, + v, + indexes, + )?; - Ok(Self { height, rest: Box::new(rest) }) + Ok(Self { + height, + rest: Box::new(rest), + }) } - pub(crate) fn compute_cumulative( + pub(crate) fn compute( &mut self, - starting_indexes: &ComputeIndexes, + max_from: Height, + windows: &WindowStarts<'_>, exit: &Exit, - ) -> Result<()> { - self.rest - .compute_cumulative(starting_indexes, &self.height, exit) + ) -> Result<()> + where + T: From + Default + SubAssign + Copy + Ord, + f64: From, + { + self.rest.compute(max_from, windows, &self.height, exit) } } diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_computed_sum_cum.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_computed_sum_cum.rs deleted file mode 100644 index 2dadaff3a..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_computed_sum_cum.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! LazyComputedFromHeightSumCum - block sum+cumulative with lazy height transform. -//! -//! Use this when you need: -//! - Lazy height (binary transform from two sources) -//! - Stored cumulative and day1 aggregates -//! - Lazy coarser period lookups - -use brk_error::Result; -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{Database, Exit, ReadableCloneableVec, LazyVecFrom2, Rw, StorageMode}; - -use crate::{indexes, ComputeIndexes}; - -use crate::internal::{ComputedVecValue, ComputedHeightDerivedSumCum, NumericValue}; - -/// Block sum+cumulative with lazy binary height transform + computed derived indexes. -/// -/// Height is a lazy binary transform (e.g., mask × source, or price × sats). -/// Cumulative and day1 are stored (computed from lazy height). -/// Coarser periods are lazy lookups. -#[derive(Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyComputedFromHeightSumCum -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue, -{ - #[traversable(rename = "sum")] - pub height: LazyVecFrom2, - #[deref] - #[deref_mut] - pub rest: Box>, -} - -const VERSION: Version = Version::ZERO; - -impl LazyComputedFromHeightSumCum -where - T: NumericValue + JsonSchema, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - height: LazyVecFrom2, - ) -> Result { - let v = version + VERSION; - - let rest = ComputedHeightDerivedSumCum::forced_import( - db, - name, - height.read_only_boxed_clone(), - v, - indexes, - )?; - - Ok(Self { height, rest: Box::new(rest) }) - } - - pub(crate) fn compute_cumulative( - &mut self, - starting_indexes: &ComputeIndexes, - exit: &Exit, - ) -> Result<()> { - self.rest - .derive_from(starting_indexes, &self.height, exit) - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_full.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_full.rs deleted file mode 100644 index 0b0cd70fb..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_full.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! Lazy unary transform from height with Full aggregation. - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform}; - -use crate::internal::{ - ComputedFromHeightFull, ComputedVecValue, LazyHeightDerivedFull, - NumericValue, -}; -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyFromHeightFull -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - #[traversable(rename = "base")] - pub height: LazyVecFrom1, - #[deref] - #[deref_mut] - pub rest: Box>, -} - -const VERSION: Version = Version::ZERO; - -impl LazyFromHeightFull -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_computed>( - name: &str, - version: Version, - height_source: ReadableBoxedVec, - source: &ComputedFromHeightFull, - ) -> Self - where - S1T: NumericValue, - { - let v = version + VERSION; - Self { - height: LazyVecFrom1::transformed::(name, v, height_source), - rest: Box::new(LazyHeightDerivedFull::from_derived_computed::(name, v, &source.rest)), - } - } - -} diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_last.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_last.rs index 18eabe537..41c527bcf 100644 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_last.rs +++ b/crates/brk_computer/src/internal/multi/from_height/lazy_last.rs @@ -6,10 +6,11 @@ use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; use vecdb::{ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom1, UnaryTransform}; -use crate::internal::{ - ComputedFromHeightLast, - ComputedVecValue, LazyBinaryComputedFromHeightLast, LazyBinaryFromHeightLast, - LazyHeightDerivedLast, NumericValue, +use crate::{ + indexes, + internal::{ + ComputedFromHeightLast, ComputedVecValue, LazyHeightDerivedLast, NumericValue, + }, }; #[derive(Clone, Deref, DerefMut, Traversable)] #[traversable(merge)] @@ -48,22 +49,19 @@ where } } - pub(crate) fn from_lazy_binary_computed( + pub(crate) fn from_height_source>( name: &str, version: Version, height_source: ReadableBoxedVec, - source: &LazyBinaryComputedFromHeightLast, + indexes: &indexes::Vecs, ) -> Self where - F: UnaryTransform, S1T: NumericValue, - S1aT: ComputedVecValue + JsonSchema, - S1bT: ComputedVecValue + JsonSchema, { let v = version + VERSION; Self { - height: LazyVecFrom1::transformed::(name, v, height_source), - rest: Box::new(LazyHeightDerivedLast::from_derived_computed::(name, v, &source.rest)), + height: LazyVecFrom1::transformed::(name, v, height_source.clone()), + rest: Box::new(LazyHeightDerivedLast::from_height_source::(name, v, height_source, indexes)), } } @@ -84,21 +82,4 @@ where } } - /// Create by unary-transforming a LazyBinaryFromHeightLast source. - pub(crate) fn from_binary( - name: &str, - version: Version, - source: &LazyBinaryFromHeightLast, - ) -> Self - where - F: UnaryTransform, - S1aT: ComputedVecValue + JsonSchema, - S1bT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - Self { - height: LazyVecFrom1::transformed::(name, v, source.height.read_only_boxed_clone()), - rest: Box::new(LazyHeightDerivedLast::from_binary::(name, v, &source.rest)), - } - } } diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_sum_cum.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_sum_cum.rs deleted file mode 100644 index 16d074539..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_sum_cum.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! Lazy unary transform from height with SumCum aggregation. - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform}; - -use crate::internal::{ - ComputedFromHeightSumCum, ComputedHeightDerivedSumCum, ComputedVecValue, - LazyHeightDerivedSumCum, NumericValue, -}; -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyFromHeightSumCum -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - #[traversable(rename = "sum")] - pub height: LazyVecFrom1, - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub rest: Box>, -} - -const VERSION: Version = Version::ZERO; - -impl LazyFromHeightSumCum -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_computed>( - name: &str, - version: Version, - height_source: ReadableBoxedVec, - source: &ComputedFromHeightSumCum, - ) -> Self - where - S1T: NumericValue, - { - let v = version + VERSION; - Self { - height: LazyVecFrom1::transformed::(name, v, height_source), - rest: Box::new(LazyHeightDerivedSumCum::from_derived_computed::(name, v, &source.rest)), - } - } - - pub(crate) fn from_derived>( - name: &str, - version: Version, - height_source: ReadableBoxedVec, - source: &ComputedHeightDerivedSumCum, - ) -> Self - where - S1T: NumericValue, - { - let v = version + VERSION; - Self { - height: LazyVecFrom1::transformed::(name, v, height_source), - rest: Box::new(LazyHeightDerivedSumCum::from_derived_computed::(name, v, source)), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/lazy_value.rs b/crates/brk_computer/src/internal/multi/from_height/lazy_value.rs deleted file mode 100644 index c942c10ec..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/lazy_value.rs +++ /dev/null @@ -1,54 +0,0 @@ -use brk_traversable::Traversable; -use brk_types::{Bitcoin, Dollars, Height, Sats, Version}; -use derive_more::{Deref, DerefMut}; -use vecdb::{BinaryTransform, ReadableBoxedVec, LazyVecFrom1, LazyVecFrom2, UnaryTransform}; - -use crate::internal::LazyDerivedValuesHeight; - -const VERSION: Version = Version::ZERO; - -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyFromHeightValue { - #[traversable(rename = "sats")] - pub sats: LazyVecFrom1, - #[deref] - #[deref_mut] - pub rest: LazyDerivedValuesHeight, -} - -impl LazyFromHeightValue { - pub(crate) fn from_sources( - name: &str, - sats_source: ReadableBoxedVec, - price_source: ReadableBoxedVec, - version: Version, - ) -> Self - where - SatsTransform: UnaryTransform, - BitcoinTransform: UnaryTransform, - DollarsTransform: BinaryTransform, - { - let v = version + VERSION; - - let sats = LazyVecFrom1::transformed::(name, v, sats_source.clone()); - - let btc = LazyVecFrom1::transformed::( - &format!("{name}_btc"), - v, - sats_source.clone(), - ); - - let usd = LazyVecFrom2::transformed::( - &format!("{name}_usd"), - v, - price_source, - sats_source, - ); - - Self { - sats, - rest: LazyDerivedValuesHeight { btc, usd }, - } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/mod.rs b/crates/brk_computer/src/internal/multi/from_height/mod.rs index a579f8d3c..b1136b1ee 100644 --- a/crates/brk_computer/src/internal/multi/from_height/mod.rs +++ b/crates/brk_computer/src/internal/multi/from_height/mod.rs @@ -1,67 +1,41 @@ -mod binary_last; mod constant; -mod cum; -mod cum_rolling_full; -mod cum_rolling_sum; +mod cumulative; +mod cumulative_rolling_full; +mod cumulative_rolling_sum; mod distribution; -mod full; mod last; -mod lazy_binary_computed_distribution; -mod lazy_binary_computed_full; -mod lazy_binary_computed_last; -mod lazy_binary_computed_sum_cum; mod lazy_computed_full; -mod lazy_computed_sum_cum; -mod lazy_full; mod lazy_last; -mod lazy_sum_cum; -mod lazy_value; mod percentiles; mod price; mod ratio; mod stddev; mod stored_value_last; -mod sum_cum; mod value_change; mod value_ema; mod value_full; mod value_last; -mod value_lazy_binary_last; -mod value_lazy_computed_cum; +mod value_lazy_computed_cumulative; mod value_lazy_last; -mod value_lazy_sum_cum; -mod value_sum_cum; +mod value_sum_cumulative; -pub use binary_last::*; pub use constant::*; -pub use cum::*; -pub use cum_rolling_full::*; -pub use cum_rolling_sum::*; +pub use cumulative::*; +pub use cumulative_rolling_full::*; +pub use cumulative_rolling_sum::*; pub use distribution::*; -pub use full::*; pub use last::*; -pub use lazy_binary_computed_distribution::*; -pub use lazy_binary_computed_full::*; -pub use lazy_binary_computed_last::*; -pub use lazy_binary_computed_sum_cum::*; pub use lazy_computed_full::*; -pub use lazy_computed_sum_cum::*; -pub use lazy_full::*; pub use lazy_last::*; -pub use lazy_sum_cum::*; -pub use lazy_value::*; pub use percentiles::*; pub use price::*; pub use ratio::*; pub use stddev::*; pub use stored_value_last::*; -pub use sum_cum::*; pub use value_change::*; pub use value_ema::*; pub use value_full::*; pub use value_last::*; -pub use value_lazy_binary_last::*; -pub use value_lazy_computed_cum::*; +pub use value_lazy_computed_cumulative::*; pub use value_lazy_last::*; -pub use value_lazy_sum_cum::*; -pub use value_sum_cum::*; +pub use value_sum_cumulative::*; diff --git a/crates/brk_computer/src/internal/multi/from_height/percentiles.rs b/crates/brk_computer/src/internal/multi/from_height/percentiles.rs index a45b89f51..bdac6c148 100644 --- a/crates/brk_computer/src/internal/multi/from_height/percentiles.rs +++ b/crates/brk_computer/src/internal/multi/from_height/percentiles.rs @@ -1,12 +1,10 @@ use brk_error::Result; use brk_traversable::{Traversable, TreeNode}; use brk_types::{Dollars, Height, StoredF32, Version}; -use vecdb::{ - AnyExportableVec, Database, ReadOnlyClone, Ro, Rw, StorageMode, WritableVec, -}; +use vecdb::{AnyExportableVec, Database, ReadOnlyClone, Ro, Rw, StorageMode, WritableVec}; use crate::indexes; -use crate::internal::{ComputedFromHeightLast, Price, PriceFromHeight}; +use crate::internal::{ComputedFromHeightLast, Price}; pub const PERCENTILES: [u8; 19] = [ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, @@ -15,7 +13,10 @@ pub const PERCENTILES_LEN: usize = PERCENTILES.len(); /// Compute spot percentile rank by interpolating within percentile bands. /// Returns a value between 0 and 100 indicating where spot sits in the distribution. -pub(crate) fn compute_spot_percentile_rank(percentile_prices: &[Dollars; PERCENTILES_LEN], spot: Dollars) -> StoredF32 { +pub(crate) fn compute_spot_percentile_rank( + percentile_prices: &[Dollars; PERCENTILES_LEN], + spot: Dollars, +) -> StoredF32 { if spot.is_nan() || percentile_prices[0].is_nan() { return StoredF32::NAN; } @@ -83,7 +84,8 @@ impl PercentilesVecs { let vecs = PERCENTILES.map(|p| { compute.then(|| { let metric_name = format!("{prefix}_pct{p:02}"); - PriceFromHeight::forced_import(db, &metric_name, version + VERSION, indexes).unwrap() + Price::forced_import(db, &metric_name, version + VERSION, indexes) + .unwrap() }) }); @@ -98,7 +100,7 @@ impl PercentilesVecs { ) -> Result<()> { for (i, vec) in self.vecs.iter_mut().enumerate() { if let Some(v) = vec { - v.height.truncate_push(height, percentile_prices[i])?; + v.usd.height.truncate_push(height, percentile_prices[i])?; } } Ok(()) @@ -107,7 +109,7 @@ impl PercentilesVecs { /// Validate computed versions or reset if mismatched. pub(crate) fn validate_computed_version_or_reset(&mut self, version: Version) -> Result<()> { for vec in self.vecs.iter_mut().flatten() { - vec.height.validate_computed_version_or_reset(version)?; + vec.usd.height.validate_computed_version_or_reset(version)?; } Ok(()) } @@ -118,7 +120,10 @@ impl ReadOnlyClone for PercentilesVecs { fn read_only_clone(&self) -> Self::ReadOnly { PercentilesVecs { - vecs: self.vecs.each_ref().map(|v| v.as_ref().map(|p| p.read_only_clone())), + vecs: self + .vecs + .each_ref() + .map(|v| v.as_ref().map(|p| p.read_only_clone())), } } } diff --git a/crates/brk_computer/src/internal/multi/from_height/price.rs b/crates/brk_computer/src/internal/multi/from_height/price.rs index 481c4e87e..e5704925a 100644 --- a/crates/brk_computer/src/internal/multi/from_height/price.rs +++ b/crates/brk_computer/src/internal/multi/from_height/price.rs @@ -5,35 +5,25 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Cents, Dollars, SatsFract, Version}; -use derive_more::{Deref, DerefMut}; +use brk_types::{Dollars, SatsFract, Version}; use schemars::JsonSchema; -use vecdb::{BinaryTransform, Database, ReadableCloneableVec, UnaryTransform}; +use vecdb::{Database, ReadableCloneableVec, UnaryTransform}; -use super::{ComputedFromHeightLast, LazyBinaryFromHeightLast, LazyFromHeightLast}; +use super::{ComputedFromHeightLast, LazyFromHeightLast}; use crate::{ indexes, internal::{ComputedVecValue, DollarsToSatsFract, NumericValue}, }; /// Generic price metric with both USD and sats representations. -/// -/// Derefs to the usd metric, so existing code works unchanged. -/// Access `.sats` for the sats exchange rate version. -#[derive(Clone, Deref, DerefMut, Traversable)] +#[derive(Clone, Traversable)] #[traversable(merge)] pub struct Price { - #[deref] - #[deref_mut] #[traversable(flatten)] pub usd: U, pub sats: LazyFromHeightLast, } -// --- PriceFromHeight --- - -pub type PriceFromHeight = Price>; - impl Price> { pub(crate) fn forced_import( db: &Database, @@ -42,28 +32,16 @@ impl Price> { indexes: &indexes::Vecs, ) -> Result { let usd = ComputedFromHeightLast::forced_import(db, name, version, indexes)?; - Ok(Self::from_computed(name, version, usd)) - } - - pub(crate) fn from_computed( - name: &str, - version: Version, - usd: ComputedFromHeightLast, - ) -> Self { let sats = LazyFromHeightLast::from_computed::( &format!("{name}_sats"), version, usd.height.read_only_boxed_clone(), &usd, ); - Self { usd, sats } + Ok(Self { usd, sats }) } } -// --- LazyPriceFromHeight --- - -pub type LazyPriceFromHeight = Price>; - impl Price> where ST: ComputedVecValue + NumericValue + JsonSchema + 'static, @@ -88,106 +66,3 @@ where } } -// --- LazyPriceFromCents --- - -pub type LazyPriceFromCents = Price>; - -// --- LazyBinaryPriceFromHeight --- - -pub type LazyBinaryPriceFromHeight = Price>; - -impl Price> { - /// Create from a PriceFromHeight (source1) and a LazyPriceFromCents (source2). - pub(crate) fn from_price_and_lazy_price>( - name: &str, - version: Version, - source1: &PriceFromHeight, - source2: &LazyPriceFromCents, - ) -> Self { - let usd = LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( - name, - version, - &source1.usd, - &source2.usd, - ); - - let sats = LazyFromHeightLast::from_binary::( - &format!("{name}_sats"), - version, - &usd, - ); - - Self { usd, sats } - } - - /// Create from a LazyPriceFromCents (source1) and a PriceFromHeight (source2). - pub(crate) fn from_lazy_price_and_price>( - name: &str, - version: Version, - source1: &LazyPriceFromCents, - source2: &PriceFromHeight, - ) -> Self { - let usd = LazyBinaryFromHeightLast::from_lazy_block_last_and_block_last::( - name, - version, - &source1.usd, - &source2.usd, - ); - - let sats = LazyFromHeightLast::from_binary::( - &format!("{name}_sats"), - version, - &usd, - ); - - Self { usd, sats } - } -} - -// --- Price bands (for stddev/ratio) --- - -impl Price> -where - S2T: ComputedVecValue + NumericValue + JsonSchema, -{ - /// Create a price band from a computed price and a computed band. - pub(crate) fn from_computed_price_and_band>( - name: &str, - version: Version, - price: &ComputedFromHeightLast, - band: &ComputedFromHeightLast, - ) -> Self { - let usd = LazyBinaryFromHeightLast::from_computed_last::(name, version, price, band); - - let sats = LazyFromHeightLast::from_binary::( - &format!("{name}_sats"), - version, - &usd, - ); - - Self { usd, sats } - } - - /// Create a price band from a lazy price and a computed band. - pub(crate) fn from_lazy_price_and_band, S1T>( - name: &str, - version: Version, - price: &LazyFromHeightLast, - band: &ComputedFromHeightLast, - ) -> Self - where - S1T: ComputedVecValue + JsonSchema, - { - let usd = LazyBinaryFromHeightLast::from_lazy_block_last_and_block_last::( - name, version, price, band, - ); - - let sats = LazyFromHeightLast::from_binary::( - &format!("{name}_sats"), - version, - &usd, - ); - - Self { usd, sats } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/ratio.rs b/crates/brk_computer/src/internal/multi/from_height/ratio.rs index cc369e004..db4a6999b 100644 --- a/crates/brk_computer/src/internal/multi/from_height/ratio.rs +++ b/crates/brk_computer/src/internal/multi/from_height/ratio.rs @@ -1,23 +1,19 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Dollars, Height, StoredF32, Version}; -use schemars::JsonSchema; use vecdb::{ - AnyStoredVec, AnyVec, Database, EagerVec, Exit, WritableVec, ReadableVec, - PcoVec, Rw, StorageMode, VecIndex, + AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex, + WritableVec, }; use crate::{ - blocks, indexes, ComputeIndexes, - internal::{ - ComputedFromHeightStdDev, ComputedVecValue, LazyBinaryFromHeightLast, - LazyFromHeightLast, Price, PriceTimesRatio, StandardDeviationVecsOptions, - }, + ComputeIndexes, blocks, indexes, + internal::{ComputedFromHeightStdDev, Price, StandardDeviationVecsOptions}, prices, utils::get_percentile, }; -use super::{ComputedFromHeightLast, PriceFromHeight}; +use super::ComputedFromHeightLast; #[derive(Traversable)] pub struct ComputedFromHeightRatio { @@ -32,12 +28,12 @@ pub struct ComputedFromHeightRatio { pub ratio_pct5: Option>, pub ratio_pct2: Option>, pub ratio_pct1: Option>, - pub ratio_pct99_usd: Option>>, - pub ratio_pct98_usd: Option>>, - pub ratio_pct95_usd: Option>>, - pub ratio_pct5_usd: Option>>, - pub ratio_pct2_usd: Option>>, - pub ratio_pct1_usd: Option>>, + pub ratio_pct99_usd: Option>>, + pub ratio_pct98_usd: Option>>, + pub ratio_pct95_usd: Option>>, + pub ratio_pct5_usd: Option>>, + pub ratio_pct2_usd: Option>>, + pub ratio_pct1_usd: Option>>, pub ratio_sd: Option>, pub ratio_4y_sd: Option>, @@ -74,10 +70,7 @@ impl ComputedFromHeightRatio { // Only compute price internally when metric_price is None let price = metric_price .is_none() - .then(|| PriceFromHeight::forced_import(db, name, v, indexes).unwrap()); - - // Use provided metric_price, falling back to internally computed price - let effective_price = metric_price.or(price.as_ref().map(|p| &p.usd)); + .then(|| Price::forced_import(db, name, v, indexes).unwrap()); macro_rules! import_sd { ($suffix:expr, $days:expr) => { @@ -88,7 +81,6 @@ impl ComputedFromHeightRatio { v, indexes, StandardDeviationVecsOptions::default().add_all(), - effective_price, ) .unwrap() }; @@ -103,14 +95,19 @@ impl ComputedFromHeightRatio { macro_rules! lazy_usd { ($ratio:expr, $suffix:expr) => { - effective_price.zip($ratio.as_ref()).map(|(mp, r)| { - Price::from_computed_price_and_band::( - &format!("{name}_{}", $suffix), - v, - mp, - r, - ) - }) + if !extended { + None + } else { + $ratio.as_ref().map(|_| { + Price::forced_import( + db, + &format!("{name}_{}", $suffix), + v, + indexes, + ) + .unwrap() + }) + } }; } @@ -138,10 +135,9 @@ impl ComputedFromHeightRatio { }) } - pub(crate) fn forced_import_from_lazy( + pub(crate) fn forced_import_from_lazy( db: &Database, name: &str, - metric_price: &LazyFromHeightLast, version: Version, indexes: &indexes::Vecs, extended: bool, @@ -169,7 +165,6 @@ impl ComputedFromHeightRatio { v, indexes, StandardDeviationVecsOptions::default().add_all(), - Some(metric_price), ) .unwrap() }; @@ -184,13 +179,9 @@ impl ComputedFromHeightRatio { macro_rules! lazy_usd { ($ratio:expr, $suffix:expr) => { - $ratio.as_ref().map(|r| { - Price::from_lazy_price_and_band::( - &format!("{name}_{}", $suffix), - v, - metric_price, - r, - ) + $ratio.as_ref().map(|_| { + Price::forced_import(db, &format!("{name}_{}", $suffix), v, indexes) + .unwrap() }) }; } @@ -398,6 +389,51 @@ impl ComputedFromHeightRatio { Ok(()) } + /// Compute USD ratio bands: usd_band = metric_price * ratio_percentile + pub(crate) fn compute_usd_bands( + &mut self, + starting_indexes: &ComputeIndexes, + metric_price: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + use crate::internal::PriceTimesRatio; + + macro_rules! compute_band { + ($usd_field:ident, $band_field:ident) => { + if let Some(usd) = self.$usd_field.as_mut() { + if let Some(band) = self.$band_field.as_ref() { + usd.usd + .compute_binary::( + starting_indexes.height, + metric_price, + &band.height, + exit, + )?; + } + } + }; + } + + compute_band!(ratio_pct99_usd, ratio_pct99); + compute_band!(ratio_pct98_usd, ratio_pct98); + compute_band!(ratio_pct95_usd, ratio_pct95); + compute_band!(ratio_pct5_usd, ratio_pct5); + compute_band!(ratio_pct2_usd, ratio_pct2); + compute_band!(ratio_pct1_usd, ratio_pct1); + + // Stddev USD bands + macro_rules! compute_sd_usd { + ($($field:ident),*) => { + $(if let Some(sd) = self.$field.as_mut() { + sd.compute_usd_bands(starting_indexes, metric_price, exit)?; + })* + }; + } + compute_sd_usd!(ratio_sd, ratio_4y_sd, ratio_2y_sd, ratio_1y_sd); + + Ok(()) + } + fn mut_ratio_vecs(&mut self) -> Vec<&mut EagerVec>> { macro_rules! collect_vecs { ($($field:ident),*) => {{ diff --git a/crates/brk_computer/src/internal/multi/from_height/stddev.rs b/crates/brk_computer/src/internal/multi/from_height/stddev.rs index ae747904d..89fc901e7 100644 --- a/crates/brk_computer/src/internal/multi/from_height/stddev.rs +++ b/crates/brk_computer/src/internal/multi/from_height/stddev.rs @@ -3,18 +3,14 @@ use std::mem; use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Dollars, Height, StoredF32, Version}; -use schemars::JsonSchema; use vecdb::{ - AnyStoredVec, AnyVec, Database, EagerVec, Exit, WritableVec, ReadableVec, - PcoVec, Rw, StorageMode, VecIndex, + AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex, + WritableVec, }; -use crate::{blocks, indexes, ComputeIndexes}; +use crate::{ComputeIndexes, blocks, indexes}; -use crate::internal::{ - ComputedFromHeightLast, ComputedVecValue, LazyBinaryFromHeightLast, LazyFromHeightLast, - Price, PriceTimesRatio, -}; +use crate::internal::{ComputedFromHeightLast, Price}; #[derive(Default)] pub struct StandardDeviationVecsOptions { @@ -67,19 +63,19 @@ pub struct ComputedFromHeightStdDev { pub m2_5sd: Option>, pub m3sd: Option>, - pub _0sd_usd: Option>>, - pub p0_5sd_usd: Option>>, - pub p1sd_usd: Option>>, - pub p1_5sd_usd: Option>>, - pub p2sd_usd: Option>>, - pub p2_5sd_usd: Option>>, - pub p3sd_usd: Option>>, - pub m0_5sd_usd: Option>>, - pub m1sd_usd: Option>>, - pub m1_5sd_usd: Option>>, - pub m2sd_usd: Option>>, - pub m2_5sd_usd: Option>>, - pub m3sd_usd: Option>>, + pub _0sd_usd: Option>>, + pub p0_5sd_usd: Option>>, + pub p1sd_usd: Option>>, + pub p1_5sd_usd: Option>>, + pub p2sd_usd: Option>>, + pub p2_5sd_usd: Option>>, + pub p3sd_usd: Option>>, + pub m0_5sd_usd: Option>>, + pub m1sd_usd: Option>>, + pub m1_5sd_usd: Option>>, + pub m2sd_usd: Option>>, + pub m2_5sd_usd: Option>>, + pub m3sd_usd: Option>>, } impl ComputedFromHeightStdDev { @@ -91,7 +87,6 @@ impl ComputedFromHeightStdDev { parent_version: Version, indexes: &indexes::Vecs, options: StandardDeviationVecsOptions, - metric_price: Option<&ComputedFromHeightLast>, ) -> Result { let version = parent_version + Version::TWO; @@ -121,23 +116,21 @@ impl ComputedFromHeightStdDev { let m2_5sd = options.bands().then(|| import!("m2_5sd")); let m3sd = options.bands().then(|| import!("m3sd")); - // Create USD bands using the metric price (the denominator of the ratio). - // This converts ratio bands back to USD: usd_band = metric_price * ratio_band + // Import USD price band vecs (computed eagerly at compute time) macro_rules! lazy_usd { ($band:expr, $suffix:expr) => { if !options.price_bands() { None - } else if let Some(mp) = metric_price { - $band.as_ref().map(|b| { - Price::from_computed_price_and_band::( + } else { + $band.as_ref().map(|_| { + Price::forced_import( + db, &format!("{name}_{}", $suffix), version, - mp, - b, + indexes, ) + .unwrap() }) - } else { - None } }; } @@ -177,15 +170,13 @@ impl ComputedFromHeightStdDev { }) } - #[allow(clippy::too_many_arguments)] - pub(crate) fn forced_import_from_lazy( + pub(crate) fn forced_import_from_lazy( db: &Database, name: &str, days: usize, parent_version: Version, indexes: &indexes::Vecs, options: StandardDeviationVecsOptions, - metric_price: Option<&LazyFromHeightLast>, ) -> Result { let version = parent_version + Version::TWO; @@ -216,21 +207,21 @@ impl ComputedFromHeightStdDev { let m3sd = options.bands().then(|| import!("m3sd")); // For lazy metric price, use from_lazy_block_last_and_block_last. - // PriceTimesRatio: BinaryTransform - // source1 = metric_price (Dollars, lazy), source2 = band (StoredF32, computed) macro_rules! lazy_usd { ($band:expr, $suffix:expr) => { - metric_price - .zip($band.as_ref()) - .filter(|_| options.price_bands()) - .map(|(mp, b)| { - Price::from_lazy_price_and_band::( + if !options.price_bands() { + None + } else { + $band.as_ref().map(|_| { + Price::forced_import( + db, &format!("{name}_{}", $suffix), version, - mp, - b, + indexes, ) + .unwrap() }) + } }; } @@ -277,29 +268,21 @@ impl ComputedFromHeightStdDev { // 1. Compute SMA using the appropriate lookback vec (or full-history SMA) if self.days != usize::MAX { let window_starts = blocks.count.start_vec(self.days); - self.sma - .as_mut() - .unwrap() - .height - .compute_rolling_average( - starting_indexes.height, - window_starts, - source, - exit, - )?; + self.sma.as_mut().unwrap().height.compute_rolling_average( + starting_indexes.height, + window_starts, + source, + exit, + )?; } else { // Full history SMA (days == usize::MAX) - self.sma - .as_mut() - .unwrap() - .height - .compute_sma_( - starting_indexes.height, - source, - self.days, - exit, - None, - )?; + self.sma.as_mut().unwrap().height.compute_sma_( + starting_indexes.height, + source, + self.days, + exit, + None, + )?; } let sma_opt: Option<&EagerVec>> = None; @@ -407,7 +390,8 @@ impl ComputedFromHeightStdDev { // This is the population SD of all daily values relative to the current SMA let sd = if n > 0 { let nf = n as f64; - let variance = welford_sum_sq / nf - 2.0 * avg_f64 * welford_sum / nf + avg_f64 * avg_f64; + let variance = + welford_sum_sq / nf - 2.0 * avg_f64 * welford_sum / nf + avg_f64 * avg_f64; StoredF32::from(variance.max(0.0).sqrt() as f32) } else { StoredF32::from(0.0_f32) @@ -471,6 +455,48 @@ impl ComputedFromHeightStdDev { Ok(()) } + /// Compute USD price bands: usd_band = metric_price * band_ratio + pub(crate) fn compute_usd_bands( + &mut self, + starting_indexes: &ComputeIndexes, + metric_price: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + use crate::internal::PriceTimesRatio; + + macro_rules! compute_band { + ($usd_field:ident, $band_field:ident) => { + if let Some(usd) = self.$usd_field.as_mut() { + if let Some(band) = self.$band_field.as_ref() { + usd.usd + .compute_binary::( + starting_indexes.height, + metric_price, + &band.height, + exit, + )?; + } + } + }; + } + + compute_band!(_0sd_usd, sma); + compute_band!(p0_5sd_usd, p0_5sd); + compute_band!(p1sd_usd, p1sd); + compute_band!(p1_5sd_usd, p1_5sd); + compute_band!(p2sd_usd, p2sd); + compute_band!(p2_5sd_usd, p2_5sd); + compute_band!(p3sd_usd, p3sd); + compute_band!(m0_5sd_usd, m0_5sd); + compute_band!(m1sd_usd, m1sd); + compute_band!(m1_5sd_usd, m1_5sd); + compute_band!(m2sd_usd, m2sd); + compute_band!(m2_5sd_usd, m2_5sd); + compute_band!(m3sd_usd, m3sd); + + Ok(()) + } + fn mut_stateful_computed( &mut self, ) -> impl Iterator> { diff --git a/crates/brk_computer/src/internal/multi/from_height/sum_cum.rs b/crates/brk_computer/src/internal/multi/from_height/sum_cum.rs deleted file mode 100644 index aa157cd19..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/sum_cum.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! ComputedFromHeight using SumCum aggregation. - -use brk_error::Result; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{ - Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableCloneableVec, Rw, StorageMode, -}; - -use crate::{ComputeIndexes, indexes}; - -use crate::internal::{ComputedHeightDerivedSumCum, ComputedVecValue, NumericValue}; - -#[derive(Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct ComputedFromHeightSumCum -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - #[traversable(rename = "sum")] - pub height: M::Stored>>, - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub rest: Box>, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedFromHeightSumCum -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let v = version + VERSION; - - let height: EagerVec> = EagerVec::forced_import(db, name, v)?; - - let rest = - ComputedHeightDerivedSumCum::forced_import(db, name, height.read_only_boxed_clone(), v, indexes)?; - - Ok(Self { height, rest: Box::new(rest) }) - } - - /// Compute height_cumulative from self.height. - pub(crate) fn compute_cumulative( - &mut self, - starting_indexes: &ComputeIndexes, - exit: &Exit, - ) -> Result<()> { - self.rest.derive_from(starting_indexes, &self.height, exit) - } - - pub(crate) fn compute( - &mut self, - starting_indexes: &ComputeIndexes, - exit: &Exit, - mut compute: impl FnMut(&mut EagerVec>) -> Result<()>, - ) -> Result<()> { - compute(&mut self.height)?; - self.compute_cumulative(starting_indexes, exit) - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/value_full.rs b/crates/brk_computer/src/internal/multi/from_height/value_full.rs index 2ebcc609b..b85cafd48 100644 --- a/crates/brk_computer/src/internal/multi/from_height/value_full.rs +++ b/crates/brk_computer/src/internal/multi/from_height/value_full.rs @@ -1,31 +1,27 @@ //! Value type for Full pattern from Height. //! -//! Height-level USD stats are lazy: `sats * price`. -//! Cumulative and day1 stats are stored since they require aggregation -//! across heights with varying prices. +//! Height-level USD stats are stored (eagerly computed from sats × price). +//! Uses CumFull: stored base + cumulative + rolling windows. use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Bitcoin, Dollars, Height, Sats, Version}; -use vecdb::{Database, EagerVec, Exit, ReadableCloneableVec, PcoVec, Rw, StorageMode}; +use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageMode}; use crate::{ - ComputeIndexes, indexes, - internal::{ - ComputedFromHeightFull, LazyBinaryComputedFromHeightFull, LazyFromHeightFull, - SatsTimesPrice, SatsToBitcoin, - }, + indexes, + internal::{ComputedFromHeightCumulativeFull, LazyFromHeightLast, SatsToBitcoin, WindowStarts}, prices, }; #[derive(Traversable)] pub struct ValueFromHeightFull { - pub sats: ComputedFromHeightFull, - pub btc: LazyFromHeightFull, - pub usd: LazyBinaryComputedFromHeightFull, + pub sats: ComputedFromHeightCumulativeFull, + pub btc: LazyFromHeightLast, + pub usd: ComputedFromHeightCumulativeFull, } -const VERSION: Version = Version::ONE; // Bumped for lazy height dollars +const VERSION: Version = Version::TWO; // Bumped for stored height dollars impl ValueFromHeightFull { pub(crate) fn forced_import( @@ -33,44 +29,45 @@ impl ValueFromHeightFull { name: &str, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { let v = version + VERSION; - let sats = ComputedFromHeightFull::forced_import(db, name, v, indexes)?; + let sats = ComputedFromHeightCumulativeFull::forced_import(db, name, v, indexes)?; - let btc = LazyFromHeightFull::from_computed::( + let btc = LazyFromHeightLast::from_height_source::( &format!("{name}_btc"), v, sats.height.read_only_boxed_clone(), - &sats, + indexes, ); - let usd = LazyBinaryComputedFromHeightFull::forced_import::( - db, - &format!("{name}_usd"), - v, - sats.height.read_only_boxed_clone(), - prices.usd.price.read_only_boxed_clone(), - indexes, - )?; + let usd = + ComputedFromHeightCumulativeFull::forced_import(db, &format!("{name}_usd"), v, indexes)?; - Ok(Self { - sats, - btc, - usd, - }) + Ok(Self { sats, btc, usd }) } pub(crate) fn compute( &mut self, - starting_indexes: &ComputeIndexes, + max_from: Height, + windows: &WindowStarts<'_>, + prices: &prices::Vecs, exit: &Exit, - mut compute: impl FnMut(&mut EagerVec>) -> Result<()>, + compute_sats: impl FnOnce(&mut EagerVec>) -> Result<()>, ) -> Result<()> { - compute(&mut self.sats.height)?; - self.sats.rest.compute_cumulative(starting_indexes, &self.sats.height, exit)?; - self.usd.compute_cumulative(starting_indexes, exit)?; - Ok(()) + self.sats.compute(max_from, windows, exit, compute_sats)?; + + self.usd.compute(max_from, windows, exit, |vec| { + Ok(vec.compute_transform2( + max_from, + &self.sats.height, + &prices.usd.price, + |(h, sats, price, ..)| { + let btc = *sats as f64 / 100_000_000.0; + (h, Dollars::from(*price * btc)) + }, + exit, + )?) + }) } } diff --git a/crates/brk_computer/src/internal/multi/from_height/value_last.rs b/crates/brk_computer/src/internal/multi/from_height/value_last.rs index a10107b9f..e6a901c2f 100644 --- a/crates/brk_computer/src/internal/multi/from_height/value_last.rs +++ b/crates/brk_computer/src/internal/multi/from_height/value_last.rs @@ -1,30 +1,26 @@ //! Value type for Last pattern from Height. //! -//! Height-level USD value is lazy: `sats * price`. +//! Height-level USD value is stored (eagerly computed from sats × price). //! Day1 last is stored since it requires finding the last value within each date. use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Bitcoin, Dollars, Sats, Version}; -use vecdb::{Database, ReadableCloneableVec, Rw, StorageMode}; +use brk_types::{Bitcoin, Dollars, Height, Sats, Version}; +use vecdb::{Database, Exit, ReadableCloneableVec, Rw, StorageMode}; use crate::{ - indexes, - internal::{ - ComputedFromHeightLast, LazyBinaryComputedFromHeightLast, LazyFromHeightLast, - SatsTimesPrice, SatsToBitcoin, - }, - prices, + indexes, prices, + internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin}, }; #[derive(Traversable)] pub struct ValueFromHeightLast { pub sats: ComputedFromHeightLast, pub btc: LazyFromHeightLast, - pub usd: LazyBinaryComputedFromHeightLast, + pub usd: ComputedFromHeightLast, } -const VERSION: Version = Version::ONE; // Bumped for lazy height dollars +const VERSION: Version = Version::TWO; // Bumped for stored height dollars impl ValueFromHeightLast { pub(crate) fn forced_import( @@ -32,7 +28,6 @@ impl ValueFromHeightLast { name: &str, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { let v = version + VERSION; @@ -45,13 +40,7 @@ impl ValueFromHeightLast { &sats, ); - let usd = LazyBinaryComputedFromHeightLast::forced_import::( - &format!("{name}_usd"), - v, - sats.height.read_only_boxed_clone(), - prices.usd.price.read_only_boxed_clone(), - indexes, - ); + let usd = ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), v, indexes)?; Ok(Self { sats, @@ -59,4 +48,24 @@ impl ValueFromHeightLast { usd, }) } + + /// Eagerly compute USD height values: sats[h] * price[h]. + pub(crate) fn compute( + &mut self, + prices: &prices::Vecs, + max_from: Height, + exit: &Exit, + ) -> Result<()> { + self.usd.height.compute_transform2( + max_from, + &self.sats.height, + &prices.usd.price, + |(h, sats, price, ..)| { + let btc = *sats as f64 / 100_000_000.0; + (h, Dollars::from(*price * btc)) + }, + exit, + )?; + Ok(()) + } } diff --git a/crates/brk_computer/src/internal/multi/from_height/value_lazy_binary_last.rs b/crates/brk_computer/src/internal/multi/from_height/value_lazy_binary_last.rs deleted file mode 100644 index f0e54665a..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/value_lazy_binary_last.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! Lazy binary value wrapper combining height (with price) + all derived last transforms. - -use brk_traversable::Traversable; -use brk_types::{Bitcoin, Dollars, Sats, Version}; -use derive_more::{Deref, DerefMut}; -use vecdb::{BinaryTransform, ReadableCloneableVec, UnaryTransform}; - -use super::LazyFromHeightValue; -use crate::internal::{LazyValueHeightDerivedLast, ValueFromHeightLast}; -use crate::prices; - -const VERSION: Version = Version::ZERO; - -/// Lazy binary value wrapper with height (using price binary transform) + all derived last transforms. -/// -/// Use this when the height-level dollars need a binary transform (e.g., price * sats) -/// rather than a unary transform from existing dollars. -/// -/// All coarser-than-height periods (minute1 through difficultyepoch) use unary transforms -/// on the pre-computed values from the source. -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(merge)] -pub struct LazyBinaryValueFromHeightLast { - #[traversable(flatten)] - pub height: LazyFromHeightValue, - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub rest: Box, -} - -impl LazyBinaryValueFromHeightLast { - pub(crate) fn from_block_source< - SatsTransform, - BitcoinTransform, - HeightDollarsTransform, - DateDollarsTransform, - >( - name: &str, - source: &ValueFromHeightLast, - prices: &prices::Vecs, - version: Version, - ) -> Self - where - SatsTransform: UnaryTransform, - BitcoinTransform: UnaryTransform, - HeightDollarsTransform: BinaryTransform, - DateDollarsTransform: UnaryTransform, - { - let v = version + VERSION; - - let price_source = prices.usd.price.read_only_boxed_clone(); - - let height = LazyFromHeightValue::from_sources::< - SatsTransform, - BitcoinTransform, - HeightDollarsTransform, - >(name, source.sats.height.read_only_boxed_clone(), price_source, v); - - let rest = - LazyValueHeightDerivedLast::from_block_source::( - name, source, v, - ); - - Self { height, rest: Box::new(rest) } - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_cum.rs b/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_cum.rs deleted file mode 100644 index f20daec75..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_cum.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Value type with stored sats height + cumulative, lazy btc + lazy dollars. -//! -//! Like LazyComputedValueFromHeightSumCum but with Cum (no old period aggregations). -//! - Sats: stored height + cumulative (ComputedFromHeightCum) -//! - BTC: lazy transform from sats (LazyFromHeightLast) -//! - USD: lazy binary (price × sats), LazyLast per index (no stored cumulative) - -use brk_error::Result; -use brk_traversable::Traversable; -use brk_types::{Bitcoin, Dollars, Height, Sats, Version}; -use vecdb::{Database, Exit, ReadableCloneableVec, Rw, StorageMode}; - -use crate::{ - indexes, - internal::{ - ComputedFromHeightCum, LazyBinaryComputedFromHeightLast, LazyFromHeightLast, - PriceTimesSats, SatsToBitcoin, - }, - prices, -}; - -/// Value wrapper with stored sats height + cumulative, lazy btc + lazy usd. -#[derive(Traversable)] -pub struct LazyComputedValueFromHeightCum { - pub sats: ComputedFromHeightCum, - pub btc: LazyFromHeightLast, - pub usd: LazyBinaryComputedFromHeightLast, -} - -const VERSION: Version = Version::ZERO; - -impl LazyComputedValueFromHeightCum { - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - prices: &prices::Vecs, - ) -> Result { - let v = version + VERSION; - - let sats = ComputedFromHeightCum::forced_import(db, name, v, indexes)?; - - let btc = LazyFromHeightLast::from_computed::( - &format!("{name}_btc"), - v, - sats.height.read_only_boxed_clone(), - &sats, - ); - - let usd = LazyBinaryComputedFromHeightLast::forced_import::( - &format!("{name}_usd"), - v, - prices.usd.price.read_only_boxed_clone(), - sats.height.read_only_boxed_clone(), - indexes, - ); - - Ok(Self { sats, btc, usd }) - } - - /// Compute cumulative from already-filled sats height vec. - pub(crate) fn compute_cumulative( - &mut self, - max_from: Height, - exit: &Exit, - ) -> Result<()> { - self.sats.compute_cumulative(max_from, exit)?; - Ok(()) - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_cumulative.rs b/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_cumulative.rs new file mode 100644 index 000000000..4bf1ecb32 --- /dev/null +++ b/crates/brk_computer/src/internal/multi/from_height/value_lazy_computed_cumulative.rs @@ -0,0 +1,72 @@ +//! Value type with stored sats height + cumulative, stored usd, lazy btc. +//! +//! - Sats: stored height + cumulative (ComputedFromHeightCumulative) +//! - BTC: lazy transform from sats (LazyFromHeightLast) +//! - USD: stored (eagerly computed from price × sats) + +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{Bitcoin, Dollars, Height, Sats, Version}; +use vecdb::{Database, Exit, ReadableCloneableVec, Rw, StorageMode}; + +use crate::{ + indexes, + internal::{ComputedFromHeightCumulative, ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin}, + prices, +}; + +/// Value wrapper with stored sats height + cumulative, lazy btc + stored usd. +#[derive(Traversable)] +pub struct LazyComputedValueFromHeightCumulative { + pub sats: ComputedFromHeightCumulative, + pub btc: LazyFromHeightLast, + pub usd: ComputedFromHeightLast, +} + +const VERSION: Version = Version::ONE; // Bumped for stored height dollars + +impl LazyComputedValueFromHeightCumulative { + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let v = version + VERSION; + + let sats = ComputedFromHeightCumulative::forced_import(db, name, v, indexes)?; + + let btc = LazyFromHeightLast::from_height_source::( + &format!("{name}_btc"), + v, + sats.height.read_only_boxed_clone(), + indexes, + ); + + let usd = ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), v, indexes)?; + + Ok(Self { sats, btc, usd }) + } + + /// Compute cumulative + USD from already-filled sats height vec. + pub(crate) fn compute( + &mut self, + prices: &prices::Vecs, + max_from: Height, + exit: &Exit, + ) -> Result<()> { + self.sats.compute_rest(max_from, exit)?; + + self.usd.height.compute_transform2( + max_from, + &prices.usd.price, + &self.sats.height, + |(h, price, sats, ..)| { + let btc = *sats as f64 / 100_000_000.0; + (h, Dollars::from(*price * btc)) + }, + exit, + )?; + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/multi/from_height/value_lazy_last.rs b/crates/brk_computer/src/internal/multi/from_height/value_lazy_last.rs index 80bd6efc9..287ff598f 100644 --- a/crates/brk_computer/src/internal/multi/from_height/value_lazy_last.rs +++ b/crates/brk_computer/src/internal/multi/from_height/value_lazy_last.rs @@ -1,13 +1,11 @@ //! Lazy value wrapper for ValueFromHeightLast - all transforms are lazy. use brk_traversable::Traversable; -use brk_types::{Dollars, Sats, Version}; +use brk_types::{Bitcoin, Dollars, Sats, Version}; use derive_more::{Deref, DerefMut}; use vecdb::UnaryTransform; -use crate::internal::{ - LazyValueHeight, LazyValueHeightDerivedLast, SatsToBitcoin, ValueFromHeightLast, -}; +use crate::internal::{LazyValueHeight, LazyValueHeightDerivedLast, ValueFromHeightLast}; const VERSION: Version = Version::ZERO; @@ -24,22 +22,23 @@ pub struct LazyValueFromHeightLast { } impl LazyValueFromHeightLast { - pub(crate) fn from_block_source( + pub(crate) fn from_block_source( name: &str, source: &ValueFromHeightLast, version: Version, ) -> Self where SatsTransform: UnaryTransform, + BitcoinTransform: UnaryTransform, DollarsTransform: UnaryTransform, { let v = version + VERSION; let height = - LazyValueHeight::from_block_source::(name, source, v); + LazyValueHeight::from_block_source::(name, source, v); let rest = - LazyValueHeightDerivedLast::from_block_source::( + LazyValueHeightDerivedLast::from_block_source::( name, source, v, ); diff --git a/crates/brk_computer/src/internal/multi/from_height/value_lazy_sum_cum.rs b/crates/brk_computer/src/internal/multi/from_height/value_lazy_sum_cum.rs deleted file mode 100644 index 8932dc3af..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/value_lazy_sum_cum.rs +++ /dev/null @@ -1,101 +0,0 @@ -//! Value type with lazy binary height + stored derived SumCum. -//! -//! Use this when the height-level sats is a lazy binary transform (e.g., mask × source). - -use brk_error::Result; -use brk_traversable::Traversable; -use brk_types::{Bitcoin, Dollars, Height, Sats, Version}; -use schemars::JsonSchema; -use vecdb::{ - BinaryTransform, Database, Exit, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2, Rw, - StorageMode, -}; - -use crate::{ - ComputeIndexes, indexes, - internal::{ - ComputedVecValue, LazyComputedFromHeightSumCum, LazyFromHeightSumCum, PriceTimesSats, - SatsToBitcoin, - }, - prices, -}; - -/// Value wrapper with lazy binary height + stored derived SumCum. -/// -/// Sats height is a lazy binary transform (e.g., mask × source). -/// Dollars height is also lazy (price × sats). -/// Cumulative and day1 are stored. -#[derive(Traversable)] -pub struct LazyValueFromHeightSumCum -where - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub sats: LazyComputedFromHeightSumCum, - pub btc: LazyFromHeightSumCum, - pub usd: LazyComputedFromHeightSumCum, -} - -const VERSION: Version = Version::ZERO; - -impl LazyValueFromHeightSumCum -where - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - source1: ReadableBoxedVec, - source2: ReadableBoxedVec, - prices: &prices::Vecs, - ) -> Result - where - F: BinaryTransform, - { - let v = version + VERSION; - - let sats_height = LazyVecFrom2::transformed::(name, v, source1, source2); - let sats = LazyComputedFromHeightSumCum::forced_import(db, name, v, indexes, sats_height)?; - - let btc = LazyFromHeightSumCum::from_derived::( - &format!("{name}_btc"), - v, - sats.height.read_only_boxed_clone(), - &sats.rest, - ); - - let usd_height = LazyVecFrom2::transformed::( - &format!("{name}_usd"), - v, - prices.usd.price.read_only_boxed_clone(), - sats.height.read_only_boxed_clone(), - ); - - let usd = LazyComputedFromHeightSumCum::forced_import( - db, - &format!("{name}_usd"), - v, - indexes, - usd_height, - )?; - - Ok(Self { - sats, - btc, - usd, - }) - } - - pub(crate) fn compute_cumulative( - &mut self, - starting_indexes: &ComputeIndexes, - exit: &Exit, - ) -> Result<()> { - self.sats.compute_cumulative(starting_indexes, exit)?; - self.usd.compute_cumulative(starting_indexes, exit)?; - Ok(()) - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/value_sum_cum.rs b/crates/brk_computer/src/internal/multi/from_height/value_sum_cum.rs deleted file mode 100644 index 2790fe51e..000000000 --- a/crates/brk_computer/src/internal/multi/from_height/value_sum_cum.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! Value type for SumCum pattern from Height. -//! -//! Height-level USD sum is lazy: `sats * price`. -//! Cumulative and day1 stats are stored since they require aggregation -//! across heights with varying prices. - -use brk_error::Result; -use brk_traversable::Traversable; -use brk_types::{Bitcoin, Dollars, Height, Sats, Version}; -use vecdb::{Database, EagerVec, Exit, ReadableCloneableVec, PcoVec, Rw, StorageMode}; - -use crate::{ - ComputeIndexes, - indexes, - internal::{ - ComputedFromHeightSumCum, LazyBinaryComputedFromHeightSumCum, LazyFromHeightSumCum, - SatsTimesPrice, SatsToBitcoin, - }, - prices, -}; - -#[derive(Traversable)] -pub struct ValueFromHeightSumCum { - pub sats: ComputedFromHeightSumCum, - pub btc: LazyFromHeightSumCum, - pub usd: LazyBinaryComputedFromHeightSumCum, -} - -const VERSION: Version = Version::ONE; // Bumped for lazy height dollars - -impl ValueFromHeightSumCum { - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - prices: &prices::Vecs, - ) -> Result { - let v = version + VERSION; - - let sats = ComputedFromHeightSumCum::forced_import(db, name, v, indexes)?; - - let btc = LazyFromHeightSumCum::from_computed::( - &format!("{name}_btc"), - v, - sats.height.read_only_boxed_clone(), - &sats, - ); - - let usd = LazyBinaryComputedFromHeightSumCum::forced_import::( - db, - &format!("{name}_usd"), - v, - sats.height.read_only_boxed_clone(), - prices.usd.price.read_only_boxed_clone(), - indexes, - )?; - - - Ok(Self { - sats, - btc, - usd, - }) - } - - pub(crate) fn compute( - &mut self, - starting_indexes: &ComputeIndexes, - exit: &Exit, - mut compute: impl FnMut(&mut EagerVec>) -> Result<()>, - ) -> Result<()> { - compute(&mut self.sats.height)?; - self.sats.compute_cumulative(starting_indexes, exit)?; - self.usd.compute_cumulative(starting_indexes, exit)?; - Ok(()) - } -} diff --git a/crates/brk_computer/src/internal/multi/from_height/value_sum_cumulative.rs b/crates/brk_computer/src/internal/multi/from_height/value_sum_cumulative.rs new file mode 100644 index 000000000..3b8d08f93 --- /dev/null +++ b/crates/brk_computer/src/internal/multi/from_height/value_sum_cumulative.rs @@ -0,0 +1,72 @@ +//! Value type for SumCumulative pattern from Height. +//! +//! Height-level USD sum is stored (eagerly computed from sats × price). +//! Uses CumSum: stored base + cumulative + rolling sum windows. + +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{Bitcoin, Dollars, Height, Sats, Version}; +use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageMode}; + +use crate::{ + indexes, prices, + internal::{ComputedFromHeightCumulativeSum, LazyFromHeightLast, SatsToBitcoin, WindowStarts}, +}; + +#[derive(Traversable)] +pub struct ValueFromHeightSumCumulative { + pub sats: ComputedFromHeightCumulativeSum, + pub btc: LazyFromHeightLast, + pub usd: ComputedFromHeightCumulativeSum, +} + +const VERSION: Version = Version::TWO; // Bumped for stored height dollars + +impl ValueFromHeightSumCumulative { + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let v = version + VERSION; + + let sats = ComputedFromHeightCumulativeSum::forced_import(db, name, v, indexes)?; + + let btc = LazyFromHeightLast::from_height_source::( + &format!("{name}_btc"), + v, + sats.height.read_only_boxed_clone(), + indexes, + ); + + let usd = + ComputedFromHeightCumulativeSum::forced_import(db, &format!("{name}_usd"), v, indexes)?; + + Ok(Self { sats, btc, usd }) + } + + pub(crate) fn compute( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + prices: &prices::Vecs, + exit: &Exit, + compute_sats: impl FnOnce(&mut EagerVec>) -> Result<()>, + ) -> Result<()> { + self.sats.compute(max_from, windows, exit, compute_sats)?; + + self.usd.compute(max_from, windows, exit, |vec| { + Ok(vec.compute_transform2( + max_from, + &self.sats.height, + &prices.usd.price, + |(h, sats, price, ..)| { + let btc = *sats as f64 / 100_000_000.0; + (h, Dollars::from(*price * btc)) + }, + exit, + )?) + }) + } +} diff --git a/crates/brk_computer/src/internal/multi/from_tx/lazy_distribution.rs b/crates/brk_computer/src/internal/multi/from_tx/distribution.rs similarity index 75% rename from crates/brk_computer/src/internal/multi/from_tx/lazy_distribution.rs rename to crates/brk_computer/src/internal/multi/from_tx/distribution.rs index 41306e8a0..1ec91271a 100644 --- a/crates/brk_computer/src/internal/multi/from_tx/lazy_distribution.rs +++ b/crates/brk_computer/src/internal/multi/from_tx/distribution.rs @@ -3,19 +3,16 @@ use brk_error::Result; use brk_indexer::Indexer; use brk_traversable::Traversable; -use brk_types::{TxIndex, Version}; -use derive_more::{Deref, DerefMut}; +use brk_types::TxIndex; use schemars::JsonSchema; -use vecdb::{Database, Exit, LazyVecFrom2, ReadableVec, Rw, StorageMode}; +use vecdb::{Database, Exit, LazyVecFrom2, ReadableVec, Rw, StorageMode, Version}; use crate::{ ComputeIndexes, indexes, - internal::{ComputedVecValue, TxDerivedDistribution, NumericValue}, + internal::{BlockWindowStarts, ComputedVecValue, NumericValue, TxDerivedDistribution}, }; -const VERSION: Version = Version::ZERO; - -#[derive(Deref, DerefMut, Traversable)] +#[derive(Traversable)] #[traversable(merge)] pub struct LazyFromTxDistribution where @@ -24,8 +21,6 @@ where S2: ComputedVecValue, { pub txindex: LazyVecFrom2, - #[deref] - #[deref_mut] #[traversable(flatten)] pub distribution: TxDerivedDistribution, } @@ -41,10 +36,8 @@ where name: &str, version: Version, txindex: LazyVecFrom2, - indexes: &indexes::Vecs, ) -> Result { - let v = version + VERSION; - let distribution = TxDerivedDistribution::forced_import(db, name, v, indexes)?; + let distribution = TxDerivedDistribution::forced_import(db, name, version)?; Ok(Self { txindex, distribution, @@ -56,12 +49,21 @@ where indexer: &Indexer, indexes: &indexes::Vecs, starting_indexes: &ComputeIndexes, + block_windows: &BlockWindowStarts<'_>, exit: &Exit, ) -> Result<()> where + T: Copy + Ord + From + Default, + f64: From, LazyVecFrom2: ReadableVec, { - self.distribution - .derive_from(indexer, indexes, starting_indexes, &self.txindex, exit) + self.distribution.derive_from( + indexer, + indexes, + starting_indexes, + block_windows, + &self.txindex, + exit, + ) } } diff --git a/crates/brk_computer/src/internal/multi/from_tx/mod.rs b/crates/brk_computer/src/internal/multi/from_tx/mod.rs index 2fe8bcd49..79fe1f2d5 100644 --- a/crates/brk_computer/src/internal/multi/from_tx/mod.rs +++ b/crates/brk_computer/src/internal/multi/from_tx/mod.rs @@ -1,3 +1,3 @@ -mod lazy_distribution; +mod distribution; -pub use lazy_distribution::*; +pub use distribution::*; diff --git a/crates/brk_computer/src/internal/multi/height_derived/binary_last.rs b/crates/brk_computer/src/internal/multi/height_derived/binary_last.rs deleted file mode 100644 index 0ebb4089b..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/binary_last.rs +++ /dev/null @@ -1,165 +0,0 @@ -//! Lazy binary transform for derived block with Last aggregation only. -//! -//! Newtype on `Indexes` with `LazyBinaryTransformLast` per field. - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30, - Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableCloneableVec}; - -use crate::{ - indexes_from, - internal::{ - ComputedFromHeightLast, ComputedFromHeightSumCum, ComputedVecValue, Indexes, - LazyBinaryTransformLast, LazyFromHeightLast, NumericValue, - }, -}; - -pub type LazyBinaryHeightDerivedLastInner = Indexes< - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, - LazyBinaryTransformLast, ->; - -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(transparent)] -pub struct LazyBinaryHeightDerivedLast( - pub LazyBinaryHeightDerivedLastInner, -) -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -impl LazyBinaryHeightDerivedLast -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_computed_sum_cum>( - name: &str, - version: Version, - source1: &ComputedFromHeightSumCum, - source2: &ComputedFromHeightSumCum, - ) -> Self - where - S1T: PartialOrd, - S2T: PartialOrd, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyBinaryTransformLast::from_vecs::( - name, - v, - source1.$p.cumulative.read_only_boxed_clone(), - source2.$p.cumulative.read_only_boxed_clone(), - ) - }; - } - - Self(indexes_from!(period)) - } - - pub(crate) fn from_computed_last>( - name: &str, - version: Version, - source1: &ComputedFromHeightLast, - source2: &ComputedFromHeightLast, - ) -> Self - where - S1T: NumericValue, - S2T: NumericValue, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyBinaryTransformLast::from_lazy_last::( - name, - v, - &source1.$p, - &source2.$p, - ) - }; - } - - Self(indexes_from!(period)) - } - - pub(crate) fn from_lazy_block_last_and_block_last( - name: &str, - version: Version, - source1: &LazyFromHeightLast, - source2: &ComputedFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S2T: NumericValue, - S1SourceT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyBinaryTransformLast::from_vecs::( - name, - v, - source1.$p.read_only_boxed_clone(), - source2.$p.read_only_boxed_clone(), - ) - }; - } - - Self(indexes_from!(period)) - } - - pub(crate) fn from_block_last_and_lazy_block_last( - name: &str, - version: Version, - source1: &ComputedFromHeightLast, - source2: &LazyFromHeightLast, - ) -> Self - where - F: BinaryTransform, - S1T: NumericValue, - S2SourceT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyBinaryTransformLast::from_vecs::( - name, - v, - source1.$p.read_only_boxed_clone(), - source2.$p.read_only_boxed_clone(), - ) - }; - } - - Self(indexes_from!(period)) - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/cum_full.rs b/crates/brk_computer/src/internal/multi/height_derived/cumulative_full.rs similarity index 60% rename from crates/brk_computer/src/internal/multi/height_derived/cum_full.rs rename to crates/brk_computer/src/internal/multi/height_derived/cumulative_full.rs index e4cad197f..bf177f33f 100644 --- a/crates/brk_computer/src/internal/multi/height_derived/cum_full.rs +++ b/crates/brk_computer/src/internal/multi/height_derived/cumulative_full.rs @@ -1,7 +1,7 @@ -//! ComputedHeightDerivedCumFull - LazyLast index views + cumulative (from height) + RollingFull. +//! ComputedHeightDerivedCumulativeFull - LazyAggVec index views + cumulative (from height) + RollingFull. //! //! For metrics derived from indexer sources (no stored height vec). -//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views too. +//! Cumulative gets its own ComputedFromHeightLast so it has LazyAggVec index views too. use std::ops::SubAssign; @@ -9,23 +9,19 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, Version}; use schemars::JsonSchema; -use vecdb::{Database, Exit, ReadableBoxedVec, ReadableVec, Rw, StorageMode}; +use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode}; use crate::{ indexes, - internal::{ - ComputedFromHeightLast, ComputedHeightDerivedLast, NumericValue, RollingFull, WindowStarts, - }, + internal::{ComputedFromHeightLast, NumericValue, RollingFull, WindowStarts}, }; #[derive(Traversable)] #[traversable(merge)] -pub struct ComputedHeightDerivedCumFull +pub struct ComputedHeightDerivedCumulativeFull where T: NumericValue + JsonSchema, { - #[traversable(flatten)] - pub last: ComputedHeightDerivedLast, #[traversable(flatten)] pub cumulative: ComputedFromHeightLast, #[traversable(flatten)] @@ -34,30 +30,23 @@ where const VERSION: Version = Version::ZERO; -impl ComputedHeightDerivedCumFull +impl ComputedHeightDerivedCumulativeFull where T: NumericValue + JsonSchema, { pub(crate) fn forced_import( db: &Database, name: &str, - height_source: ReadableBoxedVec, version: Version, indexes: &indexes::Vecs, ) -> Result { let v = version + VERSION; - let last = ComputedHeightDerivedLast::forced_import(name, height_source, v, indexes); - let cumulative = ComputedFromHeightLast::forced_import( - db, - &format!("{name}_cumulative"), - v, - indexes, - )?; + let cumulative = + ComputedFromHeightLast::forced_import(db, &format!("{name}_cumulative"), v, indexes)?; let rolling = RollingFull::forced_import(db, name, v, indexes)?; Ok(Self { - last, cumulative, rolling, }) @@ -77,7 +66,8 @@ where self.cumulative .height .compute_cumulative(max_from, height_source, exit)?; - self.rolling.compute(max_from, windows, height_source, exit)?; + self.rolling + .compute(max_from, windows, height_source, exit)?; Ok(()) } } diff --git a/crates/brk_computer/src/internal/multi/height_derived/distribution.rs b/crates/brk_computer/src/internal/multi/height_derived/distribution.rs deleted file mode 100644 index 9fdfc808a..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/distribution.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! ComputedHeightDerivedDistribution - lazy time periods + epochs. - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, ReadableCloneableVec}; - -use crate::{ - indexes, - internal::{ComputedVecValue, LazyDistribution, NumericValue}, -}; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct ComputedHeightDerivedDistribution -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - pub minute1: LazyDistribution, - pub minute5: LazyDistribution, - pub minute10: LazyDistribution, - pub minute30: LazyDistribution, - pub hour1: LazyDistribution, - pub hour4: LazyDistribution, - pub hour12: LazyDistribution, - pub day1: LazyDistribution, - pub day3: LazyDistribution, - pub week1: LazyDistribution, - pub month1: LazyDistribution, - pub month3: LazyDistribution, - pub month6: LazyDistribution, - pub year1: LazyDistribution, - pub year10: LazyDistribution, - pub halvingepoch: LazyDistribution, - pub difficultyepoch: LazyDistribution, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedHeightDerivedDistribution -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - name: &str, - height_source: ReadableBoxedVec, - version: Version, - indexes: &indexes::Vecs, - ) -> Self { - let v = version + VERSION; - - macro_rules! period { - ($idx:ident) => { - LazyDistribution::from_height_source( - name, - v, - height_source.clone(), - indexes.$idx.first_height.read_only_boxed_clone(), - ) - }; - } - - macro_rules! epoch { - ($idx:ident) => { - LazyDistribution::from_source( - name, - v, - height_source.clone(), - indexes.$idx.identity.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: epoch!(halvingepoch), - difficultyepoch: epoch!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/full.rs b/crates/brk_computer/src/internal/multi/height_derived/full.rs deleted file mode 100644 index f0b6c633f..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/full.rs +++ /dev/null @@ -1,140 +0,0 @@ -//! ComputedHeightDerivedFull - height_cumulative (stored) + lazy time periods + epochs. - -use brk_error::Result; - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{Database, Exit, ReadableBoxedVec, ReadableCloneableVec, ReadableVec, Rw, StorageMode}; - -use crate::{ - indexes, - internal::{ComputedVecValue, CumulativeVec, LazyFull, NumericValue}, - ComputeIndexes, -}; - -#[derive(Traversable)] -#[traversable(merge)] -pub struct ComputedHeightDerivedFull -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - #[traversable(rename = "cumulative")] - pub height_cumulative: CumulativeVec, - pub minute1: LazyFull, - pub minute5: LazyFull, - pub minute10: LazyFull, - pub minute30: LazyFull, - pub hour1: LazyFull, - pub hour4: LazyFull, - pub hour12: LazyFull, - pub day1: LazyFull, - pub day3: LazyFull, - pub week1: LazyFull, - pub month1: LazyFull, - pub month3: LazyFull, - pub month6: LazyFull, - pub year1: LazyFull, - pub year10: LazyFull, - pub halvingepoch: LazyFull, - pub difficultyepoch: LazyFull, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedHeightDerivedFull -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - db: &Database, - name: &str, - height_source: ReadableBoxedVec, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let v = version + VERSION; - let height_cumulative = CumulativeVec::forced_import(db, name, v)?; - - macro_rules! period { - ($idx:ident) => { - LazyFull::from_height_source( - name, - v, - height_source.clone(), - height_cumulative.read_only_boxed_clone(), - indexes.$idx.first_height.read_only_boxed_clone(), - ) - }; - } - - macro_rules! epoch { - ($idx:ident) => { - LazyFull::from_stats_aggregate( - name, - v, - height_source.clone(), - height_source.clone(), - height_source.clone(), - height_source.clone(), - height_cumulative.read_only_boxed_clone(), - height_source.clone(), - indexes.$idx.identity.read_only_boxed_clone(), - ) - }; - } - - let minute1 = period!(minute1); - let minute5 = period!(minute5); - let minute10 = period!(minute10); - let minute30 = period!(minute30); - let hour1 = period!(hour1); - let hour4 = period!(hour4); - let hour12 = period!(hour12); - let day1 = period!(day1); - let day3 = period!(day3); - let week1 = period!(week1); - let month1 = period!(month1); - let month3 = period!(month3); - let month6 = period!(month6); - let year1 = period!(year1); - let year10 = period!(year10); - let halvingepoch = epoch!(halvingepoch); - let difficultyepoch = epoch!(difficultyepoch); - - Ok(Self { - height_cumulative, - minute1, - minute5, - minute10, - minute30, - hour1, - hour4, - hour12, - day1, - day3, - week1, - month1, - month3, - month6, - year1, - year10, - halvingepoch, - difficultyepoch, - }) - } - - pub(crate) fn compute_cumulative( - &mut self, - starting_indexes: &ComputeIndexes, - height_source: &impl ReadableVec, - exit: &Exit, - ) -> Result<()> { - Ok(self.height_cumulative - .0 - .compute_cumulative(starting_indexes.height, height_source, exit)?) - } -} 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 7791cb69c..8b68e5bff 100644 --- a/crates/brk_computer/src/internal/multi/height_derived/last.rs +++ b/crates/brk_computer/src/internal/multi/height_derived/last.rs @@ -1,46 +1,43 @@ -//! ComputedHeightDerivedLast - lazy time periods + epochs (last value). -//! -//! Newtype on `Indexes` with `LazyLast` per field. +//! ComputedHeightDerivedLast — sparse time periods + dense epochs (last value). use brk_traversable::Traversable; use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, + Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Minute1, Minute5, + Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10, }; use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, ReadableCloneableVec}; +use vecdb::{LazyAggVec, ReadableBoxedVec, ReadableCloneableVec}; use crate::{ - indexes, - indexes_from, - internal::{ComputedVecValue, Indexes, LazyLast, NumericValue}, + indexes, indexes_from, + internal::{ComputedVecValue, Indexes, NumericValue}, }; -/// All 17 time-period/epoch `LazyLast` vecs, packed as a newtype on `Indexes`. -pub type ComputedHeightDerivedLastInner = Indexes< - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, - LazyLast, ->; - #[derive(Clone, Deref, DerefMut, Traversable)] #[traversable(transparent)] -pub struct ComputedHeightDerivedLast(pub ComputedHeightDerivedLastInner) +pub struct ComputedHeightDerivedLast( + #[allow(clippy::type_complexity)] + pub Indexes< + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, Height, Height, T>, + LazyAggVec, + LazyAggVec, + >, +) where T: ComputedVecValue + PartialOrd + JsonSchema; @@ -60,7 +57,7 @@ where macro_rules! period { ($idx:ident) => { - LazyLast::from_height_source( + LazyAggVec::sparse_from_first_index( name, v, height_source.clone(), @@ -71,7 +68,7 @@ where macro_rules! epoch { ($idx:ident) => { - LazyLast::from_source( + LazyAggVec::from_source( name, v, height_source.clone(), diff --git a/crates/brk_computer/src/internal/multi/height_derived/lazy_full.rs b/crates/brk_computer/src/internal/multi/height_derived/lazy_full.rs deleted file mode 100644 index d17fbccac..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/lazy_full.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! Lazy aggregated Full for block-level sources. - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30, - Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{ReadableCloneableVec, UnaryTransform}; - -use crate::internal::{ - ComputedHeightDerivedFull, ComputedVecValue, LazyTransformFull, NumericValue, -}; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct LazyHeightDerivedFull -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - pub minute1: LazyTransformFull, - pub minute5: LazyTransformFull, - pub minute10: LazyTransformFull, - pub minute30: LazyTransformFull, - pub hour1: LazyTransformFull, - pub hour4: LazyTransformFull, - pub hour12: LazyTransformFull, - pub day1: LazyTransformFull, - pub day3: LazyTransformFull, - pub week1: LazyTransformFull, - pub month1: LazyTransformFull, - pub month3: LazyTransformFull, - pub month6: LazyTransformFull, - pub year1: LazyTransformFull, - pub year10: LazyTransformFull, - pub halvingepoch: LazyTransformFull, - pub difficultyepoch: LazyTransformFull, -} - -const VERSION: Version = Version::ZERO; - -impl LazyHeightDerivedFull -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_derived_computed>( - name: &str, - version: Version, - source: &ComputedHeightDerivedFull, - ) -> Self - where - S1T: NumericValue, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyTransformFull::from_boxed::( - name, - v, - source.$p.average.read_only_boxed_clone(), - source.$p.min.read_only_boxed_clone(), - source.$p.max.read_only_boxed_clone(), - source.$p.percentiles.pct10.read_only_boxed_clone(), - source.$p.percentiles.pct25.read_only_boxed_clone(), - source.$p.percentiles.median.read_only_boxed_clone(), - source.$p.percentiles.pct75.read_only_boxed_clone(), - source.$p.percentiles.pct90.read_only_boxed_clone(), - source.$p.sum.read_only_boxed_clone(), - source.$p.cumulative.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/lazy_last.rs b/crates/brk_computer/src/internal/multi/height_derived/lazy_last.rs index 1a386761e..963d39b68 100644 --- a/crates/brk_computer/src/internal/multi/height_derived/lazy_last.rs +++ b/crates/brk_computer/src/internal/multi/height_derived/lazy_last.rs @@ -1,58 +1,92 @@ -//! Lazy aggregated Last for block-level sources. -//! -//! Newtype on `Indexes` with `LazyTransformLast` per field. +//! LazyHeightDerivedLast — unary transform of height-derived last values. + +use std::marker::PhantomData; use brk_traversable::Traversable; use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour4, Hour12, Minute1, Minute5, Minute10, - Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10, + Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Minute1, Minute5, + Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10, }; use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; -use vecdb::{ReadableCloneableVec, UnaryTransform}; +use vecdb::{LazyVecFrom1, ReadableBoxedVec, ReadableCloneableVec, UnaryTransform, VecIndex, VecValue}; use crate::{ - indexes_from, + indexes, indexes_from, internal::{ - ComputedFromHeightLast, ComputedHeightDerivedLast, ComputedVecValue, Indexes, - LazyBinaryHeightDerivedLast, LazyTransformLast, NumericValue, + ComputedFromHeightLast, ComputedHeightDerivedLast, ComputedVecValue, Indexes, NumericValue, }, }; +#[derive(Clone, Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct LazyTransformLast(pub LazyVecFrom1) +where + I: VecIndex, + T: VecValue + PartialOrd + JsonSchema, + S1T: VecValue; + +impl LazyTransformLast +where + I: VecIndex, + T: VecValue + PartialOrd + JsonSchema + 'static, + S1T: VecValue + JsonSchema, +{ + fn from_boxed>( + name: &str, + version: Version, + source: ReadableBoxedVec, + ) -> Self { + Self(LazyVecFrom1::transformed::(name, version, source)) + } +} + +struct MapOption(PhantomData); + +impl UnaryTransform, Option> for MapOption +where + F: UnaryTransform, +{ + #[inline(always)] + fn apply(value: Option) -> Option { + value.map(F::apply) + } +} + #[derive(Clone, Deref, DerefMut, Traversable)] #[traversable(transparent)] pub struct LazyHeightDerivedLast( #[allow(clippy::type_complexity)] pub Indexes< - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, - LazyTransformLast, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, + LazyTransformLast, Option>, LazyTransformLast, LazyTransformLast, >, ) where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue; + T: VecValue + PartialOrd + JsonSchema, + S1T: VecValue; const VERSION: Version = Version::ZERO; impl LazyHeightDerivedLast where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, + T: VecValue + PartialOrd + JsonSchema + 'static, + S1T: VecValue + PartialOrd + JsonSchema, { pub(crate) fn from_computed>( name: &str, @@ -62,15 +96,20 @@ where where S1T: NumericValue, { - let v = version + VERSION; + Self::from_derived_computed::(name, version, &source.rest) + } - macro_rules! period { - ($p:ident) => { - LazyTransformLast::from_boxed::(name, v, source.rest.$p.read_only_boxed_clone()) - }; - } - - Self(indexes_from!(period)) + pub(crate) fn from_height_source>( + name: &str, + version: Version, + height_source: ReadableBoxedVec, + indexes: &indexes::Vecs, + ) -> Self + where + S1T: NumericValue, + { + let derived = ComputedHeightDerivedLast::forced_import(name, height_source, version, indexes); + Self::from_derived_computed::(name, version, &derived) } pub(crate) fn from_derived_computed>( @@ -84,15 +123,24 @@ where let v = version + VERSION; macro_rules! period { + ($p:ident) => { + LazyTransformLast::from_boxed::>( + name, + v, + source.$p.read_only_boxed_clone(), + ) + }; + } + + macro_rules! epoch { ($p:ident) => { LazyTransformLast::from_boxed::(name, v, source.$p.read_only_boxed_clone()) }; } - Self(indexes_from!(period)) + Self(indexes_from!(period, epoch)) } - /// Create by unary-transforming a LazyHeightDerivedLast source. pub(crate) fn from_lazy( name: &str, version: Version, @@ -106,32 +154,20 @@ where macro_rules! period { ($p:ident) => { - LazyTransformLast::from_boxed::(name, v, source.$p.read_only_boxed_clone()) + LazyTransformLast::from_boxed::>( + name, + v, + source.$p.read_only_boxed_clone(), + ) }; } - Self(indexes_from!(period)) - } - - /// Create by unary-transforming a LazyBinaryHeightDerivedLast source. - pub(crate) fn from_binary( - name: &str, - version: Version, - source: &LazyBinaryHeightDerivedLast, - ) -> Self - where - F: UnaryTransform, - S1aT: ComputedVecValue + JsonSchema, - S1bT: ComputedVecValue + JsonSchema, - { - let v = version + VERSION; - - macro_rules! period { + macro_rules! epoch { ($p:ident) => { LazyTransformLast::from_boxed::(name, v, source.$p.read_only_boxed_clone()) }; } - Self(indexes_from!(period)) + Self(indexes_from!(period, epoch)) } } diff --git a/crates/brk_computer/src/internal/multi/height_derived/lazy_sum_cum.rs b/crates/brk_computer/src/internal/multi/height_derived/lazy_sum_cum.rs deleted file mode 100644 index c283ba99f..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/lazy_sum_cum.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! Lazy aggregated SumCum for block-level sources. - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30, - Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{ReadableCloneableVec, UnaryTransform}; - -use crate::internal::{ - ComputedHeightDerivedSumCum, ComputedVecValue, LazyTransformSumCum, NumericValue, -}; - -#[derive(Clone, Traversable)] -#[traversable(merge)] -pub struct LazyHeightDerivedSumCum -where - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - pub minute1: LazyTransformSumCum, - pub minute5: LazyTransformSumCum, - pub minute10: LazyTransformSumCum, - pub minute30: LazyTransformSumCum, - pub hour1: LazyTransformSumCum, - pub hour4: LazyTransformSumCum, - pub hour12: LazyTransformSumCum, - pub day1: LazyTransformSumCum, - pub day3: LazyTransformSumCum, - pub week1: LazyTransformSumCum, - pub month1: LazyTransformSumCum, - pub month3: LazyTransformSumCum, - pub month6: LazyTransformSumCum, - pub year1: LazyTransformSumCum, - pub year10: LazyTransformSumCum, - pub halvingepoch: LazyTransformSumCum, - pub difficultyepoch: LazyTransformSumCum, -} - -const VERSION: Version = Version::ZERO; - -impl LazyHeightDerivedSumCum -where - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_derived_computed>( - name: &str, - version: Version, - source: &ComputedHeightDerivedSumCum, - ) -> Self - where - S1T: NumericValue, - { - let v = version + VERSION; - - macro_rules! period { - ($p:ident) => { - LazyTransformSumCum::from_boxed_sum_raw::( - name, - v, - source.$p.sum.read_only_boxed_clone(), - source.$p.cumulative.read_only_boxed_clone(), - ) - }; - } - - Self { - minute1: period!(minute1), - minute5: period!(minute5), - minute10: period!(minute10), - minute30: period!(minute30), - hour1: period!(hour1), - hour4: period!(hour4), - hour12: period!(hour12), - day1: period!(day1), - day3: period!(day3), - week1: period!(week1), - month1: period!(month1), - month3: period!(month3), - month6: period!(month6), - year1: period!(year1), - year10: period!(year10), - halvingepoch: period!(halvingepoch), - difficultyepoch: period!(difficultyepoch), - } - } -} diff --git a/crates/brk_computer/src/internal/multi/height_derived/mod.rs b/crates/brk_computer/src/internal/multi/height_derived/mod.rs index 883b12b41..49a9212aa 100644 --- a/crates/brk_computer/src/internal/multi/height_derived/mod.rs +++ b/crates/brk_computer/src/internal/multi/height_derived/mod.rs @@ -1,21 +1,9 @@ -mod binary_last; -mod cum_full; -mod distribution; -mod full; +mod cumulative_full; mod last; -mod lazy_full; mod lazy_last; -mod lazy_sum_cum; -mod sum_cum; mod value_lazy_last; -pub use binary_last::*; -pub use cum_full::*; -pub use distribution::*; -pub use full::*; +pub use cumulative_full::*; pub use last::*; -pub use lazy_full::*; pub use lazy_last::*; -pub use lazy_sum_cum::*; -pub use sum_cum::*; pub use value_lazy_last::*; diff --git a/crates/brk_computer/src/internal/multi/height_derived/sum_cum.rs b/crates/brk_computer/src/internal/multi/height_derived/sum_cum.rs deleted file mode 100644 index 1cceed713..000000000 --- a/crates/brk_computer/src/internal/multi/height_derived/sum_cum.rs +++ /dev/null @@ -1,138 +0,0 @@ -//! ComputedHeightDerivedSumCum - height cumulative (stored) + lazy time periods + epochs. - -use brk_error::Result; - -use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10, -}; -use schemars::JsonSchema; -use vecdb::{Database, Exit, ReadableBoxedVec, ReadableCloneableVec, ReadableVec, Rw, StorageMode}; - -use crate::{ - indexes, - internal::{ComputedVecValue, CumulativeVec, LazySumCum, NumericValue}, - ComputeIndexes, -}; - -#[derive(Traversable)] -#[traversable(merge)] -pub struct ComputedHeightDerivedSumCum -where - T: ComputedVecValue + PartialOrd + JsonSchema, -{ - #[traversable(rename = "cumulative")] - pub height_cumulative: CumulativeVec, - pub minute1: LazySumCum, - pub minute5: LazySumCum, - pub minute10: LazySumCum, - pub minute30: LazySumCum, - pub hour1: LazySumCum, - pub hour4: LazySumCum, - pub hour12: LazySumCum, - pub day1: LazySumCum, - pub day3: LazySumCum, - pub week1: LazySumCum, - pub month1: LazySumCum, - pub month3: LazySumCum, - pub month6: LazySumCum, - pub year1: LazySumCum, - pub year10: LazySumCum, - pub halvingepoch: LazySumCum, - pub difficultyepoch: LazySumCum, -} - -const VERSION: Version = Version::ZERO; - -impl ComputedHeightDerivedSumCum -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - db: &Database, - name: &str, - height_source: ReadableBoxedVec, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let v = version + VERSION; - - let height_cumulative = CumulativeVec::forced_import(db, name, v)?; - - macro_rules! period { - ($idx:ident) => { - LazySumCum::from_height_sources_sum_raw( - name, - v, - height_source.clone(), - height_cumulative.read_only_boxed_clone(), - indexes.$idx.first_height.read_only_boxed_clone(), - ) - }; - } - - macro_rules! epoch { - ($idx:ident) => { - LazySumCum::from_sources_sum_raw( - name, - v, - height_source.clone(), - height_cumulative.read_only_boxed_clone(), - indexes.$idx.identity.read_only_boxed_clone(), - ) - }; - } - - let minute1 = period!(minute1); - let minute5 = period!(minute5); - let minute10 = period!(minute10); - let minute30 = period!(minute30); - let hour1 = period!(hour1); - let hour4 = period!(hour4); - let hour12 = period!(hour12); - let day1 = period!(day1); - let day3 = period!(day3); - let week1 = period!(week1); - let month1 = period!(month1); - let month3 = period!(month3); - let month6 = period!(month6); - let year1 = period!(year1); - let year10 = period!(year10); - let halvingepoch = epoch!(halvingepoch); - let difficultyepoch = epoch!(difficultyepoch); - - Ok(Self { - height_cumulative, - minute1, - minute5, - minute10, - minute30, - hour1, - hour4, - hour12, - day1, - day3, - week1, - month1, - month3, - month6, - year1, - year10, - halvingepoch, - difficultyepoch, - }) - } - - pub(crate) fn derive_from( - &mut self, - starting_indexes: &ComputeIndexes, - height_source: &impl ReadableVec, - exit: &Exit, - ) -> Result<()> { - self.height_cumulative - .0 - .compute_cumulative(starting_indexes.height, height_source, exit)?; - Ok(()) - } -} diff --git a/crates/brk_computer/src/internal/multi/tx_derived/distribution.rs b/crates/brk_computer/src/internal/multi/tx_derived/distribution.rs index 052f045d2..a4508ae41 100644 --- a/crates/brk_computer/src/internal/multi/tx_derived/distribution.rs +++ b/crates/brk_computer/src/internal/multi/tx_derived/distribution.rs @@ -1,19 +1,19 @@ -//! TxDerivedDistribution - computes TxIndex data to height Distribution + lazy time periods + epochs. +//! TxDerivedDistribution - per-block + rolling window distribution stats from tx-level data. +//! +//! Computes true distribution stats (average, min, max, median, percentiles) by reading +//! actual tx values for each scope: current block, last 1h, last 24h. use brk_error::Result; use brk_indexer::Indexer; use brk_traversable::Traversable; -use brk_types::{ - Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10, - Minute30, Minute5, Month1, Month3, Month6, TxIndex, Version, Week1, Year1, Year10, -}; +use brk_types::{Height, TxIndex}; use schemars::JsonSchema; -use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode}; +use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode, Version}; use crate::{ ComputeIndexes, indexes, - internal::{ComputedVecValue, Distribution, LazyDistribution, NumericValue}, + internal::{BlockRollingDistribution, BlockWindowStarts, ComputedVecValue, Distribution, NumericValue}, }; #[derive(Traversable)] @@ -22,28 +22,11 @@ pub struct TxDerivedDistribution where T: ComputedVecValue + PartialOrd + JsonSchema, { - pub height: Distribution, - pub minute1: LazyDistribution, - pub minute5: LazyDistribution, - pub minute10: LazyDistribution, - pub minute30: LazyDistribution, - pub hour1: LazyDistribution, - pub hour4: LazyDistribution, - pub hour12: LazyDistribution, - pub day1: LazyDistribution, - pub day3: LazyDistribution, - pub week1: LazyDistribution, - pub month1: LazyDistribution, - pub month3: LazyDistribution, - pub month6: LazyDistribution, - pub year1: LazyDistribution, - pub year10: LazyDistribution, - pub halvingepoch: LazyDistribution, - pub difficultyepoch: LazyDistribution, + pub block: Distribution, + #[traversable(flatten)] + pub rolling: BlockRollingDistribution, } -const VERSION: Version = Version::ZERO; - impl TxDerivedDistribution where T: NumericValue + JsonSchema, @@ -52,71 +35,11 @@ where db: &Database, name: &str, version: Version, - indexes: &indexes::Vecs, ) -> Result { - let height = Distribution::forced_import(db, name, version + VERSION)?; - let v = version + VERSION; + let block = Distribution::forced_import(db, name, version)?; + let rolling = BlockRollingDistribution::forced_import(db, name, version)?; - macro_rules! period { - ($idx:ident) => { - LazyDistribution::from_height_source( - name, - v, - height.boxed_average(), - indexes.$idx.first_height.read_only_boxed_clone(), - ) - }; - } - - macro_rules! epoch { - ($idx:ident) => { - LazyDistribution::from_source( - name, - v, - height.boxed_average(), - indexes.$idx.identity.read_only_boxed_clone(), - ) - }; - } - - let minute1 = period!(minute1); - let minute5 = period!(minute5); - let minute10 = period!(minute10); - let minute30 = period!(minute30); - let hour1 = period!(hour1); - let hour4 = period!(hour4); - let hour12 = period!(hour12); - let day1 = period!(day1); - let day3 = period!(day3); - let week1 = period!(week1); - let month1 = period!(month1); - let month3 = period!(month3); - let month6 = period!(month6); - let year1 = period!(year1); - let year10 = period!(year10); - let halvingepoch = epoch!(halvingepoch); - let difficultyepoch = epoch!(difficultyepoch); - - Ok(Self { - height, - minute1, - minute5, - minute10, - minute30, - hour1, - hour4, - hour12, - day1, - day3, - week1, - month1, - month3, - month6, - year1, - year10, - halvingepoch, - difficultyepoch, - }) + Ok(Self { block, rolling }) } pub(crate) fn derive_from( @@ -124,25 +47,46 @@ where indexer: &Indexer, indexes: &indexes::Vecs, starting_indexes: &ComputeIndexes, + block_windows: &BlockWindowStarts<'_>, txindex_source: &impl ReadableVec, exit: &Exit, - ) -> Result<()> { - self.derive_from_with_skip(indexer, indexes, starting_indexes, txindex_source, exit, 0) + ) -> Result<()> + where + T: Copy + Ord + From + Default, + f64: From, + { + self.derive_from_with_skip( + indexer, + indexes, + starting_indexes, + block_windows, + txindex_source, + exit, + 0, + ) } - /// Derive from source, skipping first N transactions per block from all calculations. + /// Derive from source, skipping first N transactions per block from per-block stats. /// /// Use `skip_count: 1` to exclude coinbase transactions from fee/feerate stats. + /// Rolling window distributions do NOT skip (negligible impact over many blocks). + #[allow(clippy::too_many_arguments)] pub(crate) fn derive_from_with_skip( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, starting_indexes: &ComputeIndexes, + block_windows: &BlockWindowStarts<'_>, txindex_source: &impl ReadableVec, exit: &Exit, skip_count: usize, - ) -> Result<()> { - self.height.compute_with_skip( + ) -> Result<()> + where + T: Copy + Ord + From + Default, + f64: From, + { + // Per-block distribution (supports skip for coinbase exclusion) + self.block.compute_with_skip( starting_indexes.height, txindex_source, &indexer.vecs.transactions.first_txindex, @@ -151,6 +95,26 @@ where skip_count, )?; + // 1h rolling: true distribution from all txs in last hour + self.rolling._1h.compute_from_window( + starting_indexes.height, + txindex_source, + &indexer.vecs.transactions.first_txindex, + &indexes.height.txindex_count, + block_windows._1h, + exit, + )?; + + // 24h rolling: true distribution from all txs in last 24 hours + self.rolling._24h.compute_from_window( + starting_indexes.height, + txindex_source, + &indexer.vecs.transactions.first_txindex, + &indexes.height.txindex_count, + block_windows._24h, + exit, + )?; + Ok(()) } } diff --git a/crates/brk_computer/src/internal/single/group/distribution.rs b/crates/brk_computer/src/internal/single/group/distribution.rs index 4016e503f..a95ae27d8 100644 --- a/crates/brk_computer/src/internal/single/group/distribution.rs +++ b/crates/brk_computer/src/internal/single/group/distribution.rs @@ -2,27 +2,40 @@ use brk_error::Result; use brk_traversable::Traversable; use schemars::JsonSchema; use vecdb::{ - Database, Exit, ReadableBoxedVec, ReadableVec, Ro, Rw, StorageMode, VecIndex, VecValue, Version, + Database, Exit, ReadableVec, Ro, Rw, StorageMode, VecIndex, VecValue, Version, }; -use crate::internal::ComputedVecValue; +use crate::internal::{ + AverageVec, ComputedVecValue, MaxVec, MedianVec, MinVec, Pct10Vec, Pct25Vec, Pct75Vec, + Pct90Vec, +}; -use super::{MinMaxAverage, Percentiles}; - -/// Distribution stats (average + minmax + percentiles) +/// Distribution stats (average + min + max + percentiles) — flat 8-field struct. #[derive(Traversable)] pub struct Distribution { + pub average: AverageVec, #[traversable(flatten)] - pub min_max_average: MinMaxAverage, + pub min: MinVec, #[traversable(flatten)] - pub percentiles: Percentiles, + pub max: MaxVec, + pub pct10: Pct10Vec, + pub pct25: Pct25Vec, + pub median: MedianVec, + pub pct75: Pct75Vec, + pub pct90: Pct90Vec, } impl Distribution { pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result { Ok(Self { - min_max_average: MinMaxAverage::forced_import(db, name, version)?, - percentiles: Percentiles::forced_import(db, name, version)?, + average: AverageVec::forced_import(db, name, version)?, + min: MinVec::forced_import(db, name, version)?, + max: MaxVec::forced_import(db, name, version)?, + pct10: Pct10Vec::forced_import(db, name, version)?, + pct25: Pct25Vec::forced_import(db, name, version)?, + median: MedianVec::forced_import(db, name, version)?, + pct75: Pct75Vec::forced_import(db, name, version)?, + pct90: Pct90Vec::forced_import(db, name, version)?, }) } @@ -50,28 +63,63 @@ impl Distribution { skip_count, None, // first None, // last - Some(&mut self.min_max_average.minmax.min.0), - Some(&mut self.min_max_average.minmax.max.0), - Some(&mut self.min_max_average.average.0), + Some(&mut self.min.0), + Some(&mut self.max.0), + Some(&mut self.average.0), None, // sum None, // cumulative - Some(&mut self.percentiles.median.0), - Some(&mut self.percentiles.pct10.0), - Some(&mut self.percentiles.pct25.0), - Some(&mut self.percentiles.pct75.0), - Some(&mut self.percentiles.pct90.0), + Some(&mut self.median.0), + Some(&mut self.pct10.0), + Some(&mut self.pct25.0), + Some(&mut self.pct75.0), + Some(&mut self.pct90.0), ) } - // Boxed accessors - pub(crate) fn boxed_average(&self) -> ReadableBoxedVec { - self.min_max_average.boxed_average() + /// Compute distribution stats from all items in a rolling window of groups. + /// + /// For each index `i`, reads all source items from groups `window_starts[i]..=i` + /// and computes distribution stats across the entire window. + pub(crate) fn compute_from_window( + &mut self, + max_from: I, + source: &impl ReadableVec, + first_indexes: &impl ReadableVec, + count_indexes: &impl ReadableVec, + window_starts: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + A: VecIndex + VecValue + brk_types::CheckedSub, + { + crate::internal::compute_aggregations_windowed( + max_from, + source, + first_indexes, + count_indexes, + window_starts, + exit, + &mut self.min.0, + &mut self.max.0, + &mut self.average.0, + &mut self.median.0, + &mut self.pct10.0, + &mut self.pct25.0, + &mut self.pct75.0, + &mut self.pct90.0, + ) } pub fn read_only_clone(&self) -> Distribution { Distribution { - min_max_average: self.min_max_average.read_only_clone(), - percentiles: self.percentiles.read_only_clone(), + average: self.average.read_only_clone(), + min: self.min.read_only_clone(), + max: self.max.read_only_clone(), + pct10: self.pct10.read_only_clone(), + pct25: self.pct25.read_only_clone(), + median: self.median.read_only_clone(), + pct75: self.pct75.read_only_clone(), + pct90: self.pct90.read_only_clone(), } } } diff --git a/crates/brk_computer/src/internal/single/group/full.rs b/crates/brk_computer/src/internal/single/group/full.rs index 2c4b8e108..499ddb955 100644 --- a/crates/brk_computer/src/internal/single/group/full.rs +++ b/crates/brk_computer/src/internal/single/group/full.rs @@ -1,30 +1,27 @@ use brk_error::Result; use brk_traversable::Traversable; use schemars::JsonSchema; -use vecdb::{ - Database, Exit, ReadableBoxedVec, ReadableCloneableVec, ReadableVec, Ro, Rw, StorageMode, - VecIndex, VecValue, Version, -}; +use vecdb::{Database, Exit, ReadableVec, Ro, Rw, StorageMode, VecIndex, VecValue, Version}; use crate::internal::ComputedVecValue; -use super::{Distribution, SumCum}; +use super::{Distribution, SumCumulative}; -/// Full stats aggregate: distribution + sum_cum +/// Full stats aggregate: distribution + sum_cumulative /// Matches the common full_stats() pattern: average + minmax + percentiles + sum + cumulative #[derive(Traversable)] pub struct Full { #[traversable(flatten)] pub distribution: Distribution, #[traversable(flatten)] - pub sum_cum: SumCum, + pub sum_cumulative: SumCumulative, } impl Full { pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result { Ok(Self { distribution: Distribution::forced_import(db, name, version)?, - sum_cum: SumCum::forced_import(db, name, version)?, + sum_cumulative: SumCumulative::forced_import(db, name, version)?, }) } @@ -52,27 +49,23 @@ impl Full { skip_count, None, // first None, // last - Some(&mut self.distribution.min_max_average.minmax.min.0), - Some(&mut self.distribution.min_max_average.minmax.max.0), - Some(&mut self.distribution.min_max_average.average.0), - Some(&mut self.sum_cum.sum.0), - Some(&mut self.sum_cum.cumulative.0), - Some(&mut self.distribution.percentiles.median.0), - Some(&mut self.distribution.percentiles.pct10.0), - Some(&mut self.distribution.percentiles.pct25.0), - Some(&mut self.distribution.percentiles.pct75.0), - Some(&mut self.distribution.percentiles.pct90.0), + Some(&mut self.distribution.min.0), + Some(&mut self.distribution.max.0), + Some(&mut self.distribution.average.0), + Some(&mut self.sum_cumulative.sum.0), + Some(&mut self.sum_cumulative.cumulative.0), + Some(&mut self.distribution.median.0), + Some(&mut self.distribution.pct10.0), + Some(&mut self.distribution.pct25.0), + Some(&mut self.distribution.pct75.0), + Some(&mut self.distribution.pct90.0), ) } - pub(crate) fn boxed_sum(&self) -> ReadableBoxedVec { - self.sum_cum.sum.0.read_only_boxed_clone() - } - pub fn read_only_clone(&self) -> Full { Full { distribution: self.distribution.read_only_clone(), - sum_cum: self.sum_cum.read_only_clone(), + sum_cumulative: self.sum_cumulative.read_only_clone(), } } } diff --git a/crates/brk_computer/src/internal/single/group/min_max.rs b/crates/brk_computer/src/internal/single/group/min_max.rs deleted file mode 100644 index af877b90b..000000000 --- a/crates/brk_computer/src/internal/single/group/min_max.rs +++ /dev/null @@ -1,31 +0,0 @@ -use brk_error::Result; -use brk_traversable::Traversable; -use schemars::JsonSchema; -use vecdb::{Database, Ro, Rw, StorageMode, VecIndex, Version}; - -use crate::internal::{ComputedVecValue, MaxVec, MinVec}; - -/// Min + Max -#[derive(Traversable)] -pub struct MinMax { - #[traversable(flatten)] - pub min: MinVec, - #[traversable(flatten)] - pub max: MaxVec, -} - -impl MinMax { - pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result { - Ok(Self { - min: MinVec::forced_import(db, name, version)?, - max: MaxVec::forced_import(db, name, version)?, - }) - } - - pub fn read_only_clone(&self) -> MinMax { - MinMax { - min: self.min.read_only_clone(), - max: self.max.read_only_clone(), - } - } -} diff --git a/crates/brk_computer/src/internal/single/group/min_max_average.rs b/crates/brk_computer/src/internal/single/group/min_max_average.rs deleted file mode 100644 index 9ae668dcc..000000000 --- a/crates/brk_computer/src/internal/single/group/min_max_average.rs +++ /dev/null @@ -1,39 +0,0 @@ -use brk_error::Result; -use brk_traversable::Traversable; -use schemars::JsonSchema; -use vecdb::{ - Database, ReadableBoxedVec, ReadableCloneableVec, Ro, Rw, StorageMode, VecIndex, Version, -}; - -use crate::internal::{AverageVec, ComputedVecValue}; - -use super::MinMax; - -/// Average + MinMax (for TxIndex day1 aggregation - no percentiles) -#[derive(Traversable)] -pub struct MinMaxAverage { - pub average: AverageVec, - #[traversable(flatten)] - pub minmax: MinMax, -} - -impl MinMaxAverage { - pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result { - Ok(Self { - average: AverageVec::forced_import(db, name, version)?, - minmax: MinMax::forced_import(db, name, version)?, - }) - } - - // Boxed accessors - pub(crate) fn boxed_average(&self) -> ReadableBoxedVec { - self.average.0.read_only_boxed_clone() - } - - pub fn read_only_clone(&self) -> MinMaxAverage { - MinMaxAverage { - average: self.average.read_only_clone(), - minmax: self.minmax.read_only_clone(), - } - } -} diff --git a/crates/brk_computer/src/internal/single/group/mod.rs b/crates/brk_computer/src/internal/single/group/mod.rs index d730abf80..084778e94 100644 --- a/crates/brk_computer/src/internal/single/group/mod.rs +++ b/crates/brk_computer/src/internal/single/group/mod.rs @@ -1,13 +1,7 @@ mod distribution; mod full; -mod min_max; -mod min_max_average; -mod percentiles; -mod sum_cum; +mod sum_cumulative; pub use distribution::*; pub use full::*; -pub use min_max::*; -pub use min_max_average::*; -pub use percentiles::*; -pub use sum_cum::*; +pub use sum_cumulative::*; diff --git a/crates/brk_computer/src/internal/single/group/percentiles.rs b/crates/brk_computer/src/internal/single/group/percentiles.rs deleted file mode 100644 index bbb06ed1d..000000000 --- a/crates/brk_computer/src/internal/single/group/percentiles.rs +++ /dev/null @@ -1,38 +0,0 @@ -use brk_error::Result; -use brk_traversable::Traversable; -use schemars::JsonSchema; -use vecdb::{Database, Ro, Rw, StorageMode, VecIndex, Version}; - -use crate::internal::{ComputedVecValue, MedianVec, Pct10Vec, Pct25Vec, Pct75Vec, Pct90Vec}; - -/// All percentiles (pct10, pct25, median, pct75, pct90) -#[derive(Traversable)] -pub struct Percentiles { - pub pct10: Pct10Vec, - pub pct25: Pct25Vec, - pub median: MedianVec, - pub pct75: Pct75Vec, - pub pct90: Pct90Vec, -} - -impl Percentiles { - pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result { - Ok(Self { - pct10: Pct10Vec::forced_import(db, name, version)?, - pct25: Pct25Vec::forced_import(db, name, version)?, - median: MedianVec::forced_import(db, name, version)?, - pct75: Pct75Vec::forced_import(db, name, version)?, - pct90: Pct90Vec::forced_import(db, name, version)?, - }) - } - - pub fn read_only_clone(&self) -> Percentiles { - Percentiles { - pct10: self.pct10.read_only_clone(), - pct25: self.pct25.read_only_clone(), - median: self.median.read_only_clone(), - pct75: self.pct75.read_only_clone(), - pct90: self.pct90.read_only_clone(), - } - } -} diff --git a/crates/brk_computer/src/internal/single/group/sum_cum.rs b/crates/brk_computer/src/internal/single/group/sum_cumulative.rs similarity index 75% rename from crates/brk_computer/src/internal/single/group/sum_cum.rs rename to crates/brk_computer/src/internal/single/group/sum_cumulative.rs index e4e98d96b..fbd4ae696 100644 --- a/crates/brk_computer/src/internal/single/group/sum_cum.rs +++ b/crates/brk_computer/src/internal/single/group/sum_cumulative.rs @@ -7,14 +7,14 @@ use crate::internal::{ComputedVecValue, CumulativeVec, SumVec}; /// Sum + Cumulative (12% of usage) #[derive(Traversable)] -pub struct SumCum { +pub struct SumCumulative { #[traversable(flatten)] pub sum: SumVec, #[traversable(flatten)] pub cumulative: CumulativeVec, } -impl SumCum { +impl SumCumulative { pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result { Ok(Self { sum: SumVec::forced_import(db, name, version)?, @@ -22,8 +22,8 @@ impl SumCum { }) } - pub fn read_only_clone(&self) -> SumCum { - SumCum { + pub fn read_only_clone(&self) -> SumCumulative { + SumCumulative { sum: self.sum.read_only_clone(), cumulative: self.cumulative.read_only_clone(), } diff --git a/crates/brk_computer/src/internal/single/height/derived_values.rs b/crates/brk_computer/src/internal/single/height/derived_values.rs deleted file mode 100644 index 2efc723fc..000000000 --- a/crates/brk_computer/src/internal/single/height/derived_values.rs +++ /dev/null @@ -1,9 +0,0 @@ -use brk_traversable::Traversable; -use brk_types::{Bitcoin, Dollars, Height, Sats}; -use vecdb::{LazyVecFrom1, LazyVecFrom2}; - -#[derive(Clone, Traversable)] -pub struct LazyDerivedValuesHeight { - pub btc: LazyVecFrom1, - pub usd: LazyVecFrom2, -} diff --git a/crates/brk_computer/src/internal/single/height/lazy_value.rs b/crates/brk_computer/src/internal/single/height/lazy_value.rs index 89909f5e4..1b10e030f 100644 --- a/crates/brk_computer/src/internal/single/height/lazy_value.rs +++ b/crates/brk_computer/src/internal/single/height/lazy_value.rs @@ -4,7 +4,7 @@ use brk_traversable::Traversable; use brk_types::{Bitcoin, Dollars, Height, Sats, Version}; use vecdb::{ReadableCloneableVec, LazyVecFrom1, UnaryTransform}; -use crate::internal::{SatsToBitcoin, ValueFromHeightLast}; +use crate::internal::ValueFromHeightLast; const VERSION: Version = Version::ZERO; @@ -19,13 +19,14 @@ pub struct LazyValueHeight { } impl LazyValueHeight { - pub(crate) fn from_block_source( + pub(crate) fn from_block_source( name: &str, source: &ValueFromHeightLast, version: Version, ) -> Self where SatsTransform: UnaryTransform, + BitcoinTransform: UnaryTransform, DollarsTransform: UnaryTransform, { let v = version + VERSION; @@ -33,7 +34,7 @@ impl LazyValueHeight { let sats = LazyVecFrom1::transformed::(name, v, source.sats.height.read_only_boxed_clone()); - let btc = LazyVecFrom1::transformed::( + let btc = LazyVecFrom1::transformed::( &format!("{name}_btc"), v, source.sats.height.read_only_boxed_clone(), diff --git a/crates/brk_computer/src/internal/single/height/mod.rs b/crates/brk_computer/src/internal/single/height/mod.rs index 04499ded4..66af87422 100644 --- a/crates/brk_computer/src/internal/single/height/mod.rs +++ b/crates/brk_computer/src/internal/single/height/mod.rs @@ -1,5 +1,3 @@ -mod derived_values; mod lazy_value; -pub use derived_values::*; pub use lazy_value::*; diff --git a/crates/brk_computer/src/internal/single/lazy/average.rs b/crates/brk_computer/src/internal/single/lazy/average.rs deleted file mode 100644 index f04dc31e4..000000000 --- a/crates/brk_computer/src/internal/single/lazy/average.rs +++ /dev/null @@ -1,156 +0,0 @@ -//! Lazy average-value aggregation. - -use std::sync::Arc; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -type ForEachRangeFn = - fn(usize, usize, &ReadableBoxedVec, &ReadableBoxedVec, &mut dyn FnMut(T)); - -pub struct LazyAverage -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - name: Arc, - version: Version, - source: ReadableBoxedVec, - mapping: ReadableBoxedVec, - for_each_range: ForEachRangeFn, -} - -impl_lazy_agg!(LazyAverage); - -impl LazyAverage -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - pub(crate) fn from_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - Self::from_source_inner(&format!("{name}_average"), version, source, len_source) - } - - fn from_source_inner( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - fn for_each_range< - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex + FromCoarserIndex, - S2T: VecValue, - >( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let mapping_len = mapping.len(); - let source_len = source.len(); - let to = to.min(mapping_len); - if from >= to { - return; - } - let mut cursor = Cursor::from_dyn(&**source); - cursor.advance(S1I::min_from(I::from(from))); - for i in from..to { - let start = S1I::min_from(I::from(i)); - let end = S1I::max_from(I::from(i), source_len) + 1; - let count = end.saturating_sub(start); - if count == 0 || cursor.remaining() == 0 { - continue; - } - let sum = cursor.fold(count, T::from(0), |s, v| s + v); - f(sum / count); - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: len_source, - for_each_range: for_each_range::, - } - } -} - -impl LazyAverage -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn from_height_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - Self::from_height_source_inner(&format!("{name}_average"), version, source, first_height) - } - - fn from_height_source_inner( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - fn for_each_range( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let map_end = (to + 1).min(mapping.len()); - let heights = mapping.collect_range_dyn(from, map_end); - let source_len = source.len(); - let Some(&first_h) = heights.first() else { - return; - }; - let mut cursor = Cursor::from_dyn(&**source); - cursor.advance(first_h.to_usize()); - for idx in 0..(to - from) { - let Some(&cur_h) = heights.get(idx) else { - continue; - }; - let first = cur_h.to_usize(); - let next_first = heights - .get(idx + 1) - .map(|h| h.to_usize()) - .unwrap_or(source_len); - let count = next_first.saturating_sub(first); - if count == 0 || cursor.remaining() == 0 { - continue; - } - let sum = cursor.fold(count, T::from(0), |s, v| s + v); - f(sum / count); - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: first_height, - for_each_range: for_each_range::, - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/cumulative.rs b/crates/brk_computer/src/internal/single/lazy/cumulative.rs deleted file mode 100644 index 6270964dd..000000000 --- a/crates/brk_computer/src/internal/single/lazy/cumulative.rs +++ /dev/null @@ -1,137 +0,0 @@ -//! Lazy cumulative-only aggregation (takes last value from cumulative source). - -use std::sync::Arc; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -type ForEachRangeFn = - fn(usize, usize, &ReadableBoxedVec, &ReadableBoxedVec, &mut dyn FnMut(T)); - -pub struct LazyCumulative -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - name: Arc, - version: Version, - source: ReadableBoxedVec, - mapping: ReadableBoxedVec, - for_each_range: ForEachRangeFn, -} - -impl_lazy_agg!(LazyCumulative); - -impl LazyCumulative -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - pub(crate) fn from_source( - name: &str, - version: Version, - cumulative_source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - fn for_each_range< - I: VecIndex, - T: VecValue, - S1I: VecIndex + FromCoarserIndex, - S2T: VecValue, - >( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let mapping_len = mapping.len(); - let source_len = source.len(); - let mut cursor = Cursor::from_dyn(&**source); - for i in from..to { - if i >= mapping_len { - break; - } - let target = S1I::max_from(I::from(i), source_len); - if cursor.position() <= target { - cursor.advance(target - cursor.position()); - if let Some(v) = cursor.next() { - f(v); - } - } else if let Some(v) = source.collect_one_at(target) { - f(v); - } - } - } - Self { - name: Arc::from(format!("{name}_cumulative")), - version: version + VERSION, - source: cumulative_source, - mapping: len_source, - for_each_range: for_each_range::, - } - } -} - -impl LazyCumulative -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - /// Create from a height-indexed cumulative source using an explicit first_height mapping. - /// Looks up cumulative value at last height of the day. - pub(crate) fn from_height_source( - name: &str, - version: Version, - cumulative_source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - fn for_each_range( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let map_end = (to + 1).min(mapping.len()); - let heights = mapping.collect_range_dyn(from, map_end); - let source_len = source.len(); - let mut cursor = Cursor::from_dyn(&**source); - for idx in 0..(to - from) { - let next_first = heights - .get(idx + 1) - .map(|h| h.to_usize()) - .unwrap_or(source_len); - if next_first == 0 { - continue; - } - let target = next_first - 1; - if cursor.position() <= target { - cursor.advance(target - cursor.position()); - if let Some(v) = cursor.next() { - f(v); - } - } else if let Some(v) = source.collect_one_at(target) { - f(v); - } - } - } - Self { - name: Arc::from(format!("{name}_cumulative")), - version: version + VERSION, - source: cumulative_source, - mapping: first_height, - for_each_range: for_each_range::, - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/distribution.rs b/crates/brk_computer/src/internal/single/lazy/distribution.rs deleted file mode 100644 index aab1af02d..000000000 --- a/crates/brk_computer/src/internal/single/lazy/distribution.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! Lazy distribution pattern (average, min, max + percentiles). - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use super::{LazyAggPercentiles, LazyAverage, LazyMax, LazyMin}; -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -#[derive(Clone, Traversable)] -pub struct LazyDistribution -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - #[traversable(flatten)] - pub average: LazyAverage, - #[traversable(flatten)] - pub min: LazyMin, - #[traversable(flatten)] - pub max: LazyMax, - #[traversable(flatten)] - pub percentiles: LazyAggPercentiles, -} - -impl LazyDistribution -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - pub(crate) fn from_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - let v = version + VERSION; - - Self { - average: LazyAverage::from_source(name, v, source.clone(), len_source.clone()), - min: LazyMin::from_source(name, v, source.clone(), len_source.clone()), - max: LazyMax::from_source(name, v, source.clone(), len_source.clone()), - percentiles: LazyAggPercentiles::from_source(name, v, source, len_source), - } - } -} - -impl LazyDistribution -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn from_height_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - let v = version + VERSION; - - Self { - average: LazyAverage::from_height_source(name, v, source.clone(), first_height.clone()), - min: LazyMin::from_height_source(name, v, source.clone(), first_height.clone()), - max: LazyMax::from_height_source(name, v, source.clone(), first_height.clone()), - percentiles: LazyAggPercentiles::from_height_source(name, v, source, first_height), - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/full.rs b/crates/brk_computer/src/internal/single/lazy/full.rs deleted file mode 100644 index 4bb7c9d1c..000000000 --- a/crates/brk_computer/src/internal/single/lazy/full.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! Lazy full stats aggregate (distribution + sum + cumulative). - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use super::{LazyAggPercentiles, LazyAverage, LazyCumulative, LazyMax, LazyMin, LazySum}; -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -#[derive(Clone, Traversable)] -pub struct LazyFull -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - #[traversable(flatten)] - pub average: LazyAverage, - #[traversable(flatten)] - pub min: LazyMin, - #[traversable(flatten)] - pub max: LazyMax, - #[traversable(flatten)] - pub percentiles: LazyAggPercentiles, - #[traversable(flatten)] - pub sum: LazySum, - #[traversable(flatten)] - pub cumulative: LazyCumulative, -} - -impl LazyFull -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - #[allow(clippy::too_many_arguments)] - pub(crate) fn from_stats_aggregate( - name: &str, - version: Version, - source_average: ReadableBoxedVec, - source_min: ReadableBoxedVec, - source_max: ReadableBoxedVec, - source_sum: ReadableBoxedVec, - source_cumulative: ReadableBoxedVec, - source_all: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - let v = version + VERSION; - - Self { - average: LazyAverage::from_source(name, v, source_average, len_source.clone()), - min: LazyMin::from_source(name, v, source_min, len_source.clone()), - max: LazyMax::from_source(name, v, source_max, len_source.clone()), - percentiles: LazyAggPercentiles::from_source(name, v, source_all, len_source.clone()), - sum: LazySum::from_source(name, v, source_sum, len_source.clone()), - cumulative: LazyCumulative::from_source(name, v, source_cumulative, len_source), - } - } -} - -impl LazyFull -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn from_height_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - height_cumulative: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - let v = version + VERSION; - - Self { - average: LazyAverage::from_height_source(name, v, source.clone(), first_height.clone()), - min: LazyMin::from_height_source(name, v, source.clone(), first_height.clone()), - max: LazyMax::from_height_source(name, v, source.clone(), first_height.clone()), - percentiles: LazyAggPercentiles::from_height_source(name, v, source.clone(), first_height.clone()), - sum: LazySum::from_height_source(name, v, source, first_height.clone()), - cumulative: LazyCumulative::from_height_source(name, v, height_cumulative, first_height), - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/last.rs b/crates/brk_computer/src/internal/single/lazy/last.rs deleted file mode 100644 index 740536336..000000000 --- a/crates/brk_computer/src/internal/single/lazy/last.rs +++ /dev/null @@ -1,137 +0,0 @@ -//! Lazy last-value aggregation. - -use std::sync::Arc; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -type ForEachRangeFn = - fn(usize, usize, &ReadableBoxedVec, &ReadableBoxedVec, &mut dyn FnMut(T)); - -pub struct LazyLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - name: Arc, - version: Version, - source: ReadableBoxedVec, - mapping: ReadableBoxedVec, - for_each_range: ForEachRangeFn, -} - -impl_lazy_agg!(LazyLast); - -impl LazyLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - pub(crate) fn from_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - fn for_each_range< - I: VecIndex, - T: VecValue, - S1I: VecIndex + FromCoarserIndex, - S2T: VecValue, - >( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let mapping_len = mapping.len(); - let source_len = source.len(); - let mut cursor = Cursor::from_dyn(&**source); - for i in from..to { - if i >= mapping_len { - break; - } - let target = S1I::max_from(I::from(i), source_len); - if cursor.position() <= target { - cursor.advance(target - cursor.position()); - if let Some(v) = cursor.next() { - f(v); - } - } else if let Some(v) = source.collect_one_at(target) { - f(v); - } - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: len_source, - for_each_range: for_each_range::, - } - } -} - -impl LazyLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - /// Create from a height-indexed source using an explicit first_height mapping. - /// For day1 d, looks up value at `first_height[d+1] - 1` (last height of the day). - pub(crate) fn from_height_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - fn for_each_range( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let map_end = (to + 1).min(mapping.len()); - let heights = mapping.collect_range_dyn(from, map_end); - let source_len = source.len(); - let mut cursor = Cursor::from_dyn(&**source); - for idx in 0..(to - from) { - let next_first = heights - .get(idx + 1) - .map(|h| h.to_usize()) - .unwrap_or(source_len); - if next_first == 0 { - continue; - } - let target = next_first - 1; - if cursor.position() <= target { - cursor.advance(target - cursor.position()); - if let Some(v) = cursor.next() { - f(v); - } - } else if let Some(v) = source.collect_one_at(target) { - f(v); - } - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: first_height, - for_each_range: for_each_range::, - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/max.rs b/crates/brk_computer/src/internal/single/lazy/max.rs deleted file mode 100644 index 2764614b5..000000000 --- a/crates/brk_computer/src/internal/single/lazy/max.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! Lazy max-value aggregation. - -use std::sync::Arc; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -type ForEachRangeFn = - fn(usize, usize, &ReadableBoxedVec, &ReadableBoxedVec, &mut dyn FnMut(T)); - -pub struct LazyMax -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - name: Arc, - version: Version, - source: ReadableBoxedVec, - mapping: ReadableBoxedVec, - for_each_range: ForEachRangeFn, -} - -impl_lazy_agg!(LazyMax); - -impl LazyMax -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - pub(crate) fn from_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - Self::from_source_inner(&format!("{name}_max"), version, source, len_source) - } - - fn from_source_inner( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - fn for_each_range< - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex + FromCoarserIndex, - S2T: VecValue, - >( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let mapping_len = mapping.len(); - let source_len = source.len(); - let to = to.min(mapping_len); - if from >= to { - return; - } - let mut cursor = Cursor::from_dyn(&**source); - cursor.advance(S1I::min_from(I::from(from))); - for i in from..to { - let start = S1I::min_from(I::from(i)); - let end = S1I::max_from(I::from(i), source_len) + 1; - let count = end.saturating_sub(start); - if count == 0 { - continue; - } - if let Some(first) = cursor.next() { - f(cursor.fold(count - 1, first, |m, v| if v > m { v } else { m })); - } - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: len_source, - for_each_range: for_each_range::, - } - } -} - -impl LazyMax -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn from_height_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - Self::from_height_source_inner(&format!("{name}_max"), version, source, first_height) - } - - fn from_height_source_inner( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - fn for_each_range( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let map_end = (to + 1).min(mapping.len()); - let heights = mapping.collect_range_dyn(from, map_end); - let source_len = source.len(); - let Some(&first_h) = heights.first() else { - return; - }; - let mut cursor = Cursor::from_dyn(&**source); - cursor.advance(first_h.to_usize()); - for idx in 0..(to - from) { - let Some(&cur_h) = heights.get(idx) else { - continue; - }; - let first = cur_h.to_usize(); - let next_first = heights - .get(idx + 1) - .map(|h| h.to_usize()) - .unwrap_or(source_len); - let count = next_first.saturating_sub(first); - if count == 0 { - continue; - } - if let Some(first_val) = cursor.next() { - f(cursor.fold(count - 1, first_val, |m, v| if v > m { v } else { m })); - } - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: first_height, - for_each_range: for_each_range::, - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/min.rs b/crates/brk_computer/src/internal/single/lazy/min.rs deleted file mode 100644 index 37055ebab..000000000 --- a/crates/brk_computer/src/internal/single/lazy/min.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! Lazy min-value aggregation. - -use std::sync::Arc; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -type ForEachRangeFn = - fn(usize, usize, &ReadableBoxedVec, &ReadableBoxedVec, &mut dyn FnMut(T)); - -pub struct LazyMin -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - name: Arc, - version: Version, - source: ReadableBoxedVec, - mapping: ReadableBoxedVec, - for_each_range: ForEachRangeFn, -} - -impl_lazy_agg!(LazyMin); - -impl LazyMin -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - pub(crate) fn from_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - Self::from_source_inner(&format!("{name}_min"), version, source, len_source) - } - - fn from_source_inner( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - fn for_each_range< - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex + FromCoarserIndex, - S2T: VecValue, - >( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let mapping_len = mapping.len(); - let source_len = source.len(); - let to = to.min(mapping_len); - if from >= to { - return; - } - let mut cursor = Cursor::from_dyn(&**source); - cursor.advance(S1I::min_from(I::from(from))); - for i in from..to { - let start = S1I::min_from(I::from(i)); - let end = S1I::max_from(I::from(i), source_len) + 1; - let count = end.saturating_sub(start); - if count == 0 { - continue; - } - if let Some(first) = cursor.next() { - f(cursor.fold(count - 1, first, |m, v| if v < m { v } else { m })); - } - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: len_source, - for_each_range: for_each_range::, - } - } -} - -impl LazyMin -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn from_height_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - Self::from_height_source_inner(&format!("{name}_min"), version, source, first_height) - } - - fn from_height_source_inner( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - fn for_each_range( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let map_end = (to + 1).min(mapping.len()); - let heights = mapping.collect_range_dyn(from, map_end); - let source_len = source.len(); - let Some(&first_h) = heights.first() else { - return; - }; - let mut cursor = Cursor::from_dyn(&**source); - cursor.advance(first_h.to_usize()); - for idx in 0..(to - from) { - let Some(&cur_h) = heights.get(idx) else { - continue; - }; - let first = cur_h.to_usize(); - let next_first = heights - .get(idx + 1) - .map(|h| h.to_usize()) - .unwrap_or(source_len); - let count = next_first.saturating_sub(first); - if count == 0 { - continue; - } - if let Some(first_val) = cursor.next() { - f(cursor.fold(count - 1, first_val, |m, v| if v < m { v } else { m })); - } - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: first_height, - for_each_range: for_each_range::, - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/mod.rs b/crates/brk_computer/src/internal/single/lazy/mod.rs deleted file mode 100644 index 81db81e6a..000000000 --- a/crates/brk_computer/src/internal/single/lazy/mod.rs +++ /dev/null @@ -1,204 +0,0 @@ -//! Lazy aggregation primitives (finer index → coarser index). -//! -//! These types implement GROUP BY: map each coarser output index to a range -//! in the finer source, then aggregate that range. They implement the vecdb -//! ReadableVec trait directly. - -/// Common trait implementations for lazy aggregation types. -/// -/// Provides: Clone, AnyVec, TypedVec, ReadableVec, Traversable. -/// The struct must have fields: name, version, source, mapping, for_each_range. -macro_rules! impl_lazy_agg { - ($name:ident) => { - impl Clone for $name - where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, - { - fn clone(&self) -> Self { - Self { - name: self.name.clone(), - version: self.version, - source: self.source.clone(), - mapping: self.mapping.clone(), - for_each_range: self.for_each_range, - } - } - } - - impl vecdb::AnyVec for $name - where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, - { - fn version(&self) -> Version { - self.version + self.source.version() + self.mapping.version() - } - fn name(&self) -> &str { - &self.name - } - fn index_type_to_string(&self) -> &'static str { - I::to_string() - } - fn len(&self) -> usize { - self.mapping.len() - } - #[inline] - fn value_type_to_size_of(&self) -> usize { - size_of::() - } - #[inline] - fn value_type_to_string(&self) -> &'static str { - vecdb::short_type_name::() - } - #[inline] - fn region_names(&self) -> Vec { - vec![] - } - } - - impl vecdb::TypedVec for $name - where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, - { - type I = I; - type T = T; - } - - impl vecdb::ReadableVec for $name - where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, - { - fn read_into_at(&self, from: usize, to: usize, buf: &mut Vec) { - let to = to.min(self.mapping.len()); - if from >= to { return; } - buf.reserve(to - from); - (self.for_each_range)(from, to, &self.source, &self.mapping, &mut |v| buf.push(v)); - } - - fn for_each_range_dyn_at(&self, from: usize, to: usize, f: &mut dyn FnMut(T)) { - let to = to.min(self.mapping.len()); - if from >= to { return; } - (self.for_each_range)(from, to, &self.source, &self.mapping, f); - } - - #[inline] - fn fold_range_at B>( - &self, - from: usize, - to: usize, - init: B, - mut f: F, - ) -> B - where - Self: Sized, - { - let to = to.min(self.mapping.len()); - if from >= to { return init; } - let mut acc = Some(init); - (self.for_each_range)(from, to, &self.source, &self.mapping, &mut |v| { - acc = Some(f(acc.take().unwrap(), v)); - }); - acc.unwrap() - } - - #[inline] - fn try_fold_range_at std::result::Result>( - &self, - from: usize, - to: usize, - init: B, - mut f: F, - ) -> std::result::Result - where - Self: Sized, - { - let to = to.min(self.mapping.len()); - if from >= to { return Ok(init); } - let mut acc: Option> = Some(Ok(init)); - (self.for_each_range)(from, to, &self.source, &self.mapping, &mut |v| { - if let Some(Ok(a)) = acc.take() { - acc = Some(f(a, v)); - } - }); - acc.unwrap() - } - - #[inline] - fn collect_one_at(&self, index: usize) -> Option { - if index >= self.mapping.len() { - return None; - } - let mut result = None; - (self.for_each_range)(index, index + 1, &self.source, &self.mapping, &mut |v| result = Some(v)); - result - } - } - - impl Traversable for $name - where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static, - S2T: VecValue, - { - fn iter_any_exportable(&self) -> impl Iterator { - std::iter::once(self as &dyn vecdb::AnyExportableVec) - } - - fn to_tree_node(&self) -> brk_types::TreeNode { - use vecdb::AnyVec; - let index_str = I::to_string(); - let index = brk_types::Index::try_from(index_str).ok(); - let indexes = index.into_iter().collect(); - let leaf = brk_types::MetricLeaf::new( - self.name().to_string(), - self.value_type_to_string().to_string(), - indexes, - ); - let schema = - schemars::SchemaGenerator::default().into_root_schema_for::(); - let schema_json = serde_json::to_value(schema).unwrap_or_default(); - brk_types::TreeNode::Leaf(brk_types::MetricLeafWithSchema::new( - leaf, - schema_json, - )) - } - } - }; -} - -mod average; -mod cumulative; -mod distribution; -mod full; -mod last; -mod max; -mod min; -mod percentile; -mod percentiles; -mod sparse_last; -mod sum; -mod sum_cum; - -pub use average::*; -pub use cumulative::*; -pub use distribution::*; -pub use full::*; -pub use last::*; -pub use max::*; -pub use min::*; -pub use percentile::*; -pub use percentiles::*; -pub use sum::*; -pub use sum_cum::*; diff --git a/crates/brk_computer/src/internal/single/lazy/percentile.rs b/crates/brk_computer/src/internal/single/lazy/percentile.rs deleted file mode 100644 index 2ca490a02..000000000 --- a/crates/brk_computer/src/internal/single/lazy/percentile.rs +++ /dev/null @@ -1,156 +0,0 @@ -//! Lazy percentile aggregation via const-generic fn pointers. - -use std::sync::Arc; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -type ForEachRangeFn = - fn(usize, usize, &ReadableBoxedVec, &ReadableBoxedVec, &mut dyn FnMut(T)); - -pub struct LazyPercentile -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - name: Arc, - version: Version, - source: ReadableBoxedVec, - mapping: ReadableBoxedVec, - for_each_range: ForEachRangeFn, -} - -impl_lazy_agg!(LazyPercentile); - -fn select_and_pick(values: &mut [T], pct: u8) -> Option { - if values.is_empty() { - return None; - } - let idx = (values.len() - 1) * pct as usize / 100; - values.select_nth_unstable_by(idx, |a, b| { - a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal) - }); - Some(values[idx]) -} - -impl LazyPercentile -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - pub(crate) fn from_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - fn for_each_range< - const PCT: u8, - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex + FromCoarserIndex, - S2T: VecValue, - >( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let mapping_len = mapping.len(); - let source_len = source.len(); - let to = to.min(mapping_len); - if from >= to { - return; - } - let mut cursor = Cursor::from_dyn(&**source); - cursor.advance(S1I::min_from(I::from(from))); - let mut values = Vec::new(); - for i in from..to { - let start = S1I::min_from(I::from(i)); - let end = S1I::max_from(I::from(i), source_len) + 1; - if end <= start { - continue; - } - values.clear(); - cursor.for_each(end - start, |v| values.push(v)); - if let Some(v) = select_and_pick(&mut values, PCT) { - f(v); - } - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: len_source, - for_each_range: for_each_range::, - } - } -} - -impl LazyPercentile -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn from_height_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - fn for_each_range( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let map_end = (to + 1).min(mapping.len()); - let heights = mapping.collect_range_dyn(from, map_end); - let source_len = source.len(); - let Some(&first_h) = heights.first() else { - return; - }; - let mut cursor = Cursor::from_dyn(&**source); - cursor.advance(first_h.to_usize()); - let mut values = Vec::new(); - for idx in 0..(to - from) { - let Some(&cur_h) = heights.get(idx) else { - continue; - }; - let first = cur_h.to_usize(); - let next_first = heights - .get(idx + 1) - .map(|h| h.to_usize()) - .unwrap_or(source_len); - if next_first <= first { - continue; - } - values.clear(); - cursor.for_each(next_first - first, |v| values.push(v)); - if let Some(v) = select_and_pick(&mut values, PCT) { - f(v); - } - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: first_height, - for_each_range: for_each_range::, - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/percentiles.rs b/crates/brk_computer/src/internal/single/lazy/percentiles.rs deleted file mode 100644 index 314a0c072..000000000 --- a/crates/brk_computer/src/internal/single/lazy/percentiles.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! Lazy percentiles composite (pct10, pct25, median, pct75, pct90). - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use crate::internal::ComputedVecValue; - -use super::LazyPercentile; - -const VERSION: Version = Version::ZERO; - -#[derive(Clone, Traversable)] -pub struct LazyAggPercentiles -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - pub pct10: LazyPercentile, - pub pct25: LazyPercentile, - pub median: LazyPercentile, - pub pct75: LazyPercentile, - pub pct90: LazyPercentile, -} - -impl LazyAggPercentiles -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - pub(crate) fn from_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - let v = version + VERSION; - - Self { - pct10: LazyPercentile::from_source::<10>(&format!("{name}_pct10"), v, source.clone(), len_source.clone()), - pct25: LazyPercentile::from_source::<25>(&format!("{name}_pct25"), v, source.clone(), len_source.clone()), - median: LazyPercentile::from_source::<50>(&format!("{name}_median"), v, source.clone(), len_source.clone()), - pct75: LazyPercentile::from_source::<75>(&format!("{name}_pct75"), v, source.clone(), len_source.clone()), - pct90: LazyPercentile::from_source::<90>(&format!("{name}_pct90"), v, source, len_source), - } - } -} - -impl LazyAggPercentiles -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn from_height_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - let v = version + VERSION; - - Self { - pct10: LazyPercentile::from_height_source::<10>(&format!("{name}_pct10"), v, source.clone(), first_height.clone()), - pct25: LazyPercentile::from_height_source::<25>(&format!("{name}_pct25"), v, source.clone(), first_height.clone()), - median: LazyPercentile::from_height_source::<50>(&format!("{name}_median"), v, source.clone(), first_height.clone()), - pct75: LazyPercentile::from_height_source::<75>(&format!("{name}_pct75"), v, source.clone(), first_height.clone()), - pct90: LazyPercentile::from_height_source::<90>(&format!("{name}_pct90"), v, source, first_height), - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/sparse_last.rs b/crates/brk_computer/src/internal/single/lazy/sparse_last.rs deleted file mode 100644 index d0836e229..000000000 --- a/crates/brk_computer/src/internal/single/lazy/sparse_last.rs +++ /dev/null @@ -1,279 +0,0 @@ -//! Sparse last-value aggregation for time-based periods. -//! -//! Unlike [`LazyLast`], which skips empty periods, `SparseLast` produces -//! `Option` for every period slot: `Some(v)` when blocks exist, `None` -//! when a time period contains no blocks. This preserves dense positional -//! mapping (slot i = period start + i) required for correct serialization. - -use std::sync::Arc; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{Cursor, ReadableBoxedVec, VecIndex}; - -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -/// Lazy last-value aggregation that emits `Option` for every time period. -/// -/// For periods containing blocks: `Some(last_value_in_period)`. -/// For empty periods (no blocks mined): `None`. -pub struct SparseLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, -{ - name: Arc, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, -} - -impl SparseLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn new( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - Self { - name: Arc::from(name), - version: version + VERSION, - source, - first_height, - } - } - - pub fn height_source(&self) -> &ReadableBoxedVec { - &self.source - } - - pub fn first_height(&self) -> &ReadableBoxedVec { - &self.first_height - } - - /// Dense iteration: calls `f` for every period in `[from, to)`, - /// including empty ones (with `None`). - fn for_each_impl( - from: usize, - to: usize, - source: &ReadableBoxedVec, - first_height: &ReadableBoxedVec, - f: &mut dyn FnMut(Option), - ) { - let map_end = (to + 1).min(first_height.len()); - let heights = first_height.collect_range_dyn(from, map_end); - let source_len = source.len(); - let mut cursor = Cursor::from_dyn(&**source); - - for idx in 0..(to - from) { - let current_first = heights[idx].to_usize(); - let next_first = heights - .get(idx + 1) - .map(|h| h.to_usize()) - .unwrap_or(source_len); - - // Empty period: no blocks belong to this time slot - if next_first == 0 || current_first >= next_first { - f(None); - continue; - } - - // Last height in this period - let target = next_first - 1; - - if cursor.position() <= target { - cursor.advance(target - cursor.position()); - match cursor.next() { - Some(v) => f(Some(v)), - None => f(None), - } - } else { - match source.collect_one_at(target) { - Some(v) => f(Some(v)), - None => f(None), - } - } - } - } -} - -impl Clone for SparseLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, -{ - fn clone(&self) -> Self { - Self { - name: self.name.clone(), - version: self.version, - source: self.source.clone(), - first_height: self.first_height.clone(), - } - } -} - -impl vecdb::AnyVec for SparseLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, -{ - fn version(&self) -> Version { - self.version + self.source.version() + self.first_height.version() - } - - fn name(&self) -> &str { - &self.name - } - - fn index_type_to_string(&self) -> &'static str { - I::to_string() - } - - fn len(&self) -> usize { - self.first_height.len() - } - - #[inline] - fn value_type_to_size_of(&self) -> usize { - size_of::>() - } - - #[inline] - fn value_type_to_string(&self) -> &'static str { - vecdb::short_type_name::() - } - - #[inline] - fn region_names(&self) -> Vec { - vec![] - } -} - -impl vecdb::TypedVec for SparseLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, -{ - type I = I; - type T = Option; -} - -impl vecdb::ReadableVec> for SparseLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, -{ - fn read_into_at(&self, from: usize, to: usize, buf: &mut Vec>) { - let to = to.min(self.first_height.len()); - if from >= to { - return; - } - buf.reserve(to - from); - Self::for_each_impl(from, to, &self.source, &self.first_height, &mut |v| { - buf.push(v) - }); - } - - fn for_each_range_dyn_at(&self, from: usize, to: usize, f: &mut dyn FnMut(Option)) { - let to = to.min(self.first_height.len()); - if from >= to { - return; - } - Self::for_each_impl(from, to, &self.source, &self.first_height, f); - } - - #[inline] - fn fold_range_at) -> B>( - &self, - from: usize, - to: usize, - init: B, - mut f: F, - ) -> B - where - Self: Sized, - { - let to = to.min(self.first_height.len()); - if from >= to { - return init; - } - let mut acc = Some(init); - Self::for_each_impl(from, to, &self.source, &self.first_height, &mut |v| { - acc = Some(f(acc.take().unwrap(), v)); - }); - acc.unwrap() - } - - #[inline] - fn try_fold_range_at) -> std::result::Result>( - &self, - from: usize, - to: usize, - init: B, - mut f: F, - ) -> std::result::Result - where - Self: Sized, - { - let to = to.min(self.first_height.len()); - if from >= to { - return Ok(init); - } - let mut acc: Option> = Some(Ok(init)); - Self::for_each_impl(from, to, &self.source, &self.first_height, &mut |v| { - if let Some(Ok(a)) = acc.take() { - acc = Some(f(a, v)); - } - }); - acc.unwrap() - } - - #[inline] - fn collect_one_at(&self, index: usize) -> Option> { - if index >= self.first_height.len() { - return None; - } - let current_first = self.first_height.collect_one_at(index)?.to_usize(); - let next_first = self - .first_height - .collect_one_at(index + 1) - .map(|h| h.to_usize()) - .unwrap_or(self.source.len()); - if next_first == 0 || current_first >= next_first { - return Some(None); - } - Some(self.source.collect_one_at(next_first - 1)) - } -} - -impl Traversable for SparseLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - fn iter_any_exportable(&self) -> impl Iterator { - std::iter::once(self as &dyn vecdb::AnyExportableVec) - } - - fn to_tree_node(&self) -> brk_types::TreeNode { - use vecdb::AnyVec; - let index_str = I::to_string(); - let index = brk_types::Index::try_from(index_str).ok(); - let indexes = index.into_iter().collect(); - let leaf = brk_types::MetricLeaf::new( - self.name().to_string(), - self.value_type_to_string().to_string(), - indexes, - ); - let schema = schemars::SchemaGenerator::default().into_root_schema_for::>(); - let schema_json = serde_json::to_value(schema).unwrap_or_default(); - brk_types::TreeNode::Leaf(brk_types::MetricLeafWithSchema::new(leaf, schema_json)) - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/sum.rs b/crates/brk_computer/src/internal/single/lazy/sum.rs deleted file mode 100644 index 3017ffaca..000000000 --- a/crates/brk_computer/src/internal/single/lazy/sum.rs +++ /dev/null @@ -1,172 +0,0 @@ -//! Lazy sum-value aggregation. - -use std::sync::Arc; - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use crate::internal::ComputedVecValue; - -const VERSION: Version = Version::ZERO; - -type ForEachRangeFn = - fn(usize, usize, &ReadableBoxedVec, &ReadableBoxedVec, &mut dyn FnMut(T)); - -pub struct LazySum -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - name: Arc, - version: Version, - source: ReadableBoxedVec, - mapping: ReadableBoxedVec, - for_each_range: ForEachRangeFn, -} - -impl_lazy_agg!(LazySum); - -impl LazySum -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - pub(crate) fn from_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - Self::from_source_inner(&format!("{name}_sum"), version, source, len_source) - } - - pub(crate) fn from_source_raw( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - Self::from_source_inner(name, version, source, len_source) - } - - fn from_source_inner( - name: &str, - version: Version, - source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - fn for_each_range< - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex + FromCoarserIndex, - S2T: VecValue, - >( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let mapping_len = mapping.len(); - let source_len = source.len(); - let to = to.min(mapping_len); - if from >= to { - return; - } - let mut cursor = Cursor::from_dyn(&**source); - cursor.advance(S1I::min_from(I::from(from))); - for i in from..to { - let start = S1I::min_from(I::from(i)); - let end = S1I::max_from(I::from(i), source_len) + 1; - let count = end.saturating_sub(start); - if count == 0 || cursor.remaining() == 0 { - continue; - } - f(cursor.fold(count, T::from(0), |s, v| s + v)); - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: len_source, - for_each_range: for_each_range::, - } - } -} - -impl LazySum -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn from_height_source( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - Self::from_height_source_inner(&format!("{name}_sum"), version, source, first_height) - } - - pub(crate) fn from_height_source_raw( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - Self::from_height_source_inner(name, version, source, first_height) - } - - fn from_height_source_inner( - name: &str, - version: Version, - source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - fn for_each_range( - from: usize, - to: usize, - source: &ReadableBoxedVec, - mapping: &ReadableBoxedVec, - f: &mut dyn FnMut(T), - ) { - let map_end = (to + 1).min(mapping.len()); - let heights = mapping.collect_range_dyn(from, map_end); - let source_len = source.len(); - let Some(&first_h) = heights.first() else { - return; - }; - let mut cursor = Cursor::from_dyn(&**source); - cursor.advance(first_h.to_usize()); - for idx in 0..(to - from) { - let Some(&cur_h) = heights.get(idx) else { - continue; - }; - let first = cur_h.to_usize(); - let next_first = heights - .get(idx + 1) - .map(|h| h.to_usize()) - .unwrap_or(source_len); - let count = next_first.saturating_sub(first); - if count == 0 || cursor.remaining() == 0 { - continue; - } - f(cursor.fold(count, T::from(0), |s, v| s + v)); - } - } - Self { - name: Arc::from(name), - version: version + VERSION, - source, - mapping: first_height, - for_each_range: for_each_range::, - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy/sum_cum.rs b/crates/brk_computer/src/internal/single/lazy/sum_cum.rs deleted file mode 100644 index 6bef68309..000000000 --- a/crates/brk_computer/src/internal/single/lazy/sum_cum.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! Lazy sum + cumulative aggregation. - -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue}; - -use crate::internal::{ComputedVecValue, LazyCumulative, LazySum}; - -const VERSION: Version = Version::ZERO; - -#[derive(Clone, Traversable)] -pub struct LazySumCum -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1I: VecIndex, - S2T: VecValue, -{ - #[traversable(flatten)] - pub sum: LazySum, - #[traversable(flatten)] - pub cumulative: LazyCumulative, -} - -impl LazySumCum -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1I: VecIndex + 'static + FromCoarserIndex, - S2T: VecValue, -{ - /// Create from sources without adding _sum suffix to sum vec. - pub(crate) fn from_sources_sum_raw( - name: &str, - version: Version, - sum_source: ReadableBoxedVec, - cumulative_source: ReadableBoxedVec, - len_source: ReadableBoxedVec, - ) -> Self { - Self { - sum: LazySum::from_source_raw(name, version + VERSION, sum_source, len_source.clone()), - cumulative: LazyCumulative::from_source( - name, - version + VERSION, - cumulative_source, - len_source, - ), - } - } -} - -impl LazySumCum -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, -{ - pub(crate) fn from_height_sources_sum_raw( - name: &str, - version: Version, - sum_source: ReadableBoxedVec, - cumulative_source: ReadableBoxedVec, - first_height: ReadableBoxedVec, - ) -> Self { - Self { - sum: LazySum::from_height_source_raw( - name, - version + VERSION, - sum_source, - first_height.clone(), - ), - cumulative: LazyCumulative::from_height_source( - name, - version + VERSION, - cumulative_source, - first_height, - ), - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy_transform/binary_last.rs b/crates/brk_computer/src/internal/single/lazy_transform/binary_last.rs deleted file mode 100644 index 44057d3b7..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/binary_last.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! Lazy binary transform for Last-only. - -use brk_traversable::Traversable; -use brk_types::Version; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2, VecIndex}; - -use crate::internal::{ComputedVecValue, LazyLast}; - -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(wrap = "last")] -pub struct LazyBinaryTransformLast(pub LazyVecFrom2) -where - I: VecIndex, - T: ComputedVecValue + JsonSchema, - S1T: ComputedVecValue, - S2T: ComputedVecValue; - -impl LazyBinaryTransformLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, - S2T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_lazy_last< - F: BinaryTransform, - S1I: VecIndex + 'static, - S2I: VecIndex + 'static, - S1S2T, - S2S2T, - >( - name: &str, - version: Version, - source1: &LazyLast, - source2: &LazyLast, - ) -> Self - where - S1S2T: vecdb::VecValue, - S2S2T: vecdb::VecValue, - { - Self(LazyVecFrom2::transformed::( - name, - version, - source1.read_only_boxed_clone(), - source2.read_only_boxed_clone(), - )) - } - - pub(crate) fn from_vecs>( - name: &str, - version: Version, - source1: ReadableBoxedVec, - source2: ReadableBoxedVec, - ) -> Self { - Self(LazyVecFrom2::transformed::( - name, version, source1, source2, - )) - } -} diff --git a/crates/brk_computer/src/internal/single/lazy_transform/full.rs b/crates/brk_computer/src/internal/single/lazy_transform/full.rs deleted file mode 100644 index 6df1e653f..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/full.rs +++ /dev/null @@ -1,64 +0,0 @@ -//! Lazy unary transform for Full. - -use brk_traversable::Traversable; -use brk_types::Version; -use schemars::JsonSchema; -use vecdb::{LazyVecFrom1, ReadableBoxedVec, UnaryTransform, VecIndex}; - -use crate::internal::ComputedVecValue; - -use super::LazyPercentiles; - -#[derive(Clone, Traversable)] -pub struct LazyTransformFull -where - I: VecIndex, - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - pub average: LazyVecFrom1, - pub min: LazyVecFrom1, - pub max: LazyVecFrom1, - #[traversable(flatten)] - pub percentiles: LazyPercentiles, - pub sum: LazyVecFrom1, - pub cumulative: LazyVecFrom1, -} - -impl LazyTransformFull -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - #[allow(clippy::too_many_arguments)] - pub(crate) fn from_boxed>( - name: &str, - version: Version, - average: ReadableBoxedVec, - min: ReadableBoxedVec, - max: ReadableBoxedVec, - pct10: ReadableBoxedVec, - pct25: ReadableBoxedVec, - median: ReadableBoxedVec, - pct75: ReadableBoxedVec, - pct90: ReadableBoxedVec, - sum: ReadableBoxedVec, - cumulative: ReadableBoxedVec, - ) -> Self { - Self { - average: LazyVecFrom1::transformed::(&format!("{name}_average"), version, average), - min: LazyVecFrom1::transformed::(&format!("{name}_min"), version, min), - max: LazyVecFrom1::transformed::(&format!("{name}_max"), version, max), - percentiles: LazyPercentiles::from_boxed::( - name, version, pct10, pct25, median, pct75, pct90, - ), - sum: LazyVecFrom1::transformed::(&format!("{name}_sum"), version, sum), - cumulative: LazyVecFrom1::transformed::( - &format!("{name}_cumulative"), - version, - cumulative, - ), - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy_transform/last.rs b/crates/brk_computer/src/internal/single/lazy_transform/last.rs deleted file mode 100644 index 2cd301e8c..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/last.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Lazy unary transform for Last-only - transforms last at one index level. - -use brk_traversable::Traversable; -use brk_types::Version; -use derive_more::{Deref, DerefMut}; -use schemars::JsonSchema; -use vecdb::{LazyVecFrom1, ReadableBoxedVec, UnaryTransform, VecIndex}; - -use crate::internal::ComputedVecValue; - -#[derive(Clone, Deref, DerefMut, Traversable)] -#[traversable(transparent)] -pub struct LazyTransformLast(pub LazyVecFrom1) -where - I: VecIndex, - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue; - -impl LazyTransformLast -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_boxed>( - name: &str, - version: Version, - last_source: ReadableBoxedVec, - ) -> Self { - Self(LazyVecFrom1::transformed::(name, version, last_source)) - } -} diff --git a/crates/brk_computer/src/internal/single/lazy_transform/mod.rs b/crates/brk_computer/src/internal/single/lazy_transform/mod.rs deleted file mode 100644 index 539d9771f..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -mod binary_last; -mod full; -mod last; -mod percentiles; -mod sum_cum; - -pub use binary_last::*; -pub use full::*; -pub use last::*; -pub use percentiles::*; -pub use sum_cum::*; diff --git a/crates/brk_computer/src/internal/single/lazy_transform/percentiles.rs b/crates/brk_computer/src/internal/single/lazy_transform/percentiles.rs deleted file mode 100644 index a47c6d40f..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/percentiles.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! Lazy unary transform for Percentiles. - -use brk_traversable::Traversable; -use brk_types::Version; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform, VecIndex}; - -use crate::internal::ComputedVecValue; - -#[derive(Clone, Traversable)] -pub struct LazyPercentiles -where - I: VecIndex, - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - pub pct10: LazyVecFrom1, - pub pct25: LazyVecFrom1, - pub median: LazyVecFrom1, - pub pct75: LazyVecFrom1, - pub pct90: LazyVecFrom1, -} - -impl LazyPercentiles -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - #[allow(clippy::too_many_arguments)] - pub(crate) fn from_boxed>( - name: &str, - version: Version, - pct10: ReadableBoxedVec, - pct25: ReadableBoxedVec, - median: ReadableBoxedVec, - pct75: ReadableBoxedVec, - pct90: ReadableBoxedVec, - ) -> Self { - Self { - pct10: LazyVecFrom1::transformed::(&format!("{name}_pct10"), version, pct10), - pct25: LazyVecFrom1::transformed::(&format!("{name}_pct25"), version, pct25), - median: LazyVecFrom1::transformed::(&format!("{name}_median"), version, median), - pct75: LazyVecFrom1::transformed::(&format!("{name}_pct75"), version, pct75), - pct90: LazyVecFrom1::transformed::(&format!("{name}_pct90"), version, pct90), - } - } -} diff --git a/crates/brk_computer/src/internal/single/lazy_transform/sum_cum.rs b/crates/brk_computer/src/internal/single/lazy_transform/sum_cum.rs deleted file mode 100644 index 73570d241..000000000 --- a/crates/brk_computer/src/internal/single/lazy_transform/sum_cum.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! Lazy unary transform for SumCum. - -use brk_traversable::Traversable; -use brk_types::Version; -use schemars::JsonSchema; -use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform, VecIndex}; - -use crate::internal::ComputedVecValue; - -#[derive(Clone, Traversable)] -pub struct LazyTransformSumCum -where - I: VecIndex, - T: ComputedVecValue + PartialOrd + JsonSchema, - S1T: ComputedVecValue, -{ - pub sum: LazyVecFrom1, - pub cumulative: LazyVecFrom1, -} - -impl LazyTransformSumCum -where - I: VecIndex, - T: ComputedVecValue + JsonSchema + 'static, - S1T: ComputedVecValue + JsonSchema, -{ - pub(crate) fn from_boxed_sum_raw>( - name: &str, - version: Version, - sum_source: ReadableBoxedVec, - cumulative_source: ReadableBoxedVec, - ) -> Self { - Self { - sum: LazyVecFrom1::transformed::(name, version, sum_source), - cumulative: LazyVecFrom1::transformed::( - &format!("{name}_cumulative"), - version, - cumulative_source, - ), - } - } -} diff --git a/crates/brk_computer/src/internal/single/mod.rs b/crates/brk_computer/src/internal/single/mod.rs index 7f10eb152..079ee2493 100644 --- a/crates/brk_computer/src/internal/single/mod.rs +++ b/crates/brk_computer/src/internal/single/mod.rs @@ -2,16 +2,12 @@ mod group; mod height; -mod lazy; -mod lazy_transform; mod rolling; mod transform; mod vec; pub use group::*; pub use height::*; -pub use lazy::*; -pub use lazy_transform::*; pub use rolling::*; pub use transform::*; pub use vec::*; diff --git a/crates/brk_computer/src/internal/single/rolling/block_windows.rs b/crates/brk_computer/src/internal/single/rolling/block_windows.rs new file mode 100644 index 000000000..d356db470 --- /dev/null +++ b/crates/brk_computer/src/internal/single/rolling/block_windows.rs @@ -0,0 +1,44 @@ +//! Block-count-based rolling window starts and distribution — 1h and 24h (actual time-based). +//! +//! Uses stored height-ago vecs (`height_1h_ago`, `height_24h_ago`) for accurate +//! time-based window starts. + +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::Height; +use derive_more::{Deref, DerefMut}; +use schemars::JsonSchema; +use vecdb::{Database, EagerVec, PcoVec, Rw, StorageMode, Version}; + +use crate::internal::{BlockWindows, ComputedVecValue, Distribution, NumericValue}; + +/// Rolling window start heights for tx-derived metrics (1h, 24h). +pub struct BlockWindowStarts<'a> { + pub _1h: &'a EagerVec>, + pub _24h: &'a EagerVec>, +} + +/// 2 rolling window distributions (1h, 24h), each with 8 distribution stat vecs. +#[derive(Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct BlockRollingDistribution( + pub BlockWindows>, +) +where + T: ComputedVecValue + PartialOrd + JsonSchema; + +impl BlockRollingDistribution +where + T: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + ) -> Result { + Ok(Self(BlockWindows { + _1h: Distribution::forced_import(db, &format!("{name}_1h"), version)?, + _24h: Distribution::forced_import(db, &format!("{name}_24h"), version)?, + })) + } +} diff --git a/crates/brk_computer/src/internal/single/rolling/distribution.rs b/crates/brk_computer/src/internal/single/rolling/distribution.rs index 11e20308e..0b2c1ac24 100644 --- a/crates/brk_computer/src/internal/single/rolling/distribution.rs +++ b/crates/brk_computer/src/internal/single/rolling/distribution.rs @@ -1,7 +1,7 @@ //! RollingDistribution - 8 distribution stats, each a RollingWindows. //! //! Computes average, min, max, p10, p25, median, p75, p90 rolling windows -//! from a single source vec. +//! from a single source vec in a single sorted-vec pass per window. use brk_error::Result; @@ -14,15 +14,13 @@ use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode}; use crate::{ indexes, internal::{ComputedVecValue, DistributionStats, NumericValue, RollingWindows, WindowStarts}, - traits::compute_rolling_percentiles_from_starts, + traits::compute_rolling_distribution_from_starts, }; /// 8 distribution stats × 4 windows = 32 stored height vecs, each with 17 index views. #[derive(Deref, DerefMut, Traversable)] #[traversable(transparent)] -pub struct RollingDistribution( - pub DistributionStats>, -) +pub struct RollingDistribution(pub DistributionStats>) where T: ComputedVecValue + PartialOrd + JsonSchema; @@ -53,9 +51,10 @@ where /// Compute all 8 distribution stats across all 4 windows from a single source. /// - /// - average: running sum / count (O(n) per window) - /// - min/max: deque-based (O(n) amortized per window) - /// - p10/p25/median/p75/p90: single-pass sorted vec per window + /// Uses a single sorted-vec pass per window that extracts all 8 stats: + /// - average: running sum / count + /// - min/max: first/last of sorted vec + /// - p10/p25/median/p75/p90: percentile interpolation from sorted vec pub(crate) fn compute_distribution( &mut self, max_from: Height, @@ -67,24 +66,14 @@ where T: Copy + Ord + From + Default, f64: From, { - // Average: O(n) per window using running sum - self.0 - .average - .compute_rolling_average(max_from, windows, source, exit)?; - - // Min/Max: O(n) amortized per window using deques - self.0 - .min - .compute_rolling_min(max_from, windows, source, exit)?; - self.0 - .max - .compute_rolling_max(max_from, windows, source, exit)?; - - // Percentiles + median: single-pass per window using sorted vec - compute_rolling_percentiles_from_starts( + // 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, @@ -92,10 +81,13 @@ where &mut self.0.p90._24h.height, exit, )?; - compute_rolling_percentiles_from_starts( + 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, @@ -103,10 +95,13 @@ where &mut self.0.p90._7d.height, exit, )?; - compute_rolling_percentiles_from_starts( + 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, @@ -114,10 +109,13 @@ where &mut self.0.p90._30d.height, exit, )?; - compute_rolling_percentiles_from_starts( + 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, diff --git a/crates/brk_computer/src/internal/single/rolling/mod.rs b/crates/brk_computer/src/internal/single/rolling/mod.rs index 8e62a242c..39b7bcd6c 100644 --- a/crates/brk_computer/src/internal/single/rolling/mod.rs +++ b/crates/brk_computer/src/internal/single/rolling/mod.rs @@ -1,8 +1,10 @@ +mod block_windows; mod distribution; mod full; mod value_windows; mod windows; +pub use block_windows::*; pub use distribution::*; pub use full::*; pub use value_windows::*; diff --git a/crates/brk_computer/src/internal/single/rolling/windows.rs b/crates/brk_computer/src/internal/single/rolling/windows.rs index 6540f5b04..3b4249345 100644 --- a/crates/brk_computer/src/internal/single/rolling/windows.rs +++ b/crates/brk_computer/src/internal/single/rolling/windows.rs @@ -1,7 +1,7 @@ //! RollingWindows - newtype on Windows with ComputedFromHeightLast per window duration. //! //! Each of the 4 windows (24h, 7d, 30d, 1y) contains a height-level stored vec -//! plus all 17 LazyLast index views. +//! plus all 17 LazyAggVec index views. use std::ops::SubAssign; @@ -82,97 +82,4 @@ where .compute_rolling_sum(max_from, windows._1y, source, exit)?; Ok(()) } - - pub(crate) fn compute_rolling_min( - &mut self, - max_from: Height, - windows: &WindowStarts<'_>, - source: &impl ReadableVec, - exit: &Exit, - ) -> Result<()> - where - A: vecdb::VecValue + Ord, - T: From, - { - use crate::traits::ComputeRollingMinFromStarts; - self.0 - ._24h - .height - .compute_rolling_min_from_starts(max_from, windows._24h, source, exit)?; - self.0 - ._7d - .height - .compute_rolling_min_from_starts(max_from, windows._7d, source, exit)?; - self.0 - ._30d - .height - .compute_rolling_min_from_starts(max_from, windows._30d, source, exit)?; - self.0 - ._1y - .height - .compute_rolling_min_from_starts(max_from, windows._1y, source, exit)?; - Ok(()) - } - - pub(crate) fn compute_rolling_max( - &mut self, - max_from: Height, - windows: &WindowStarts<'_>, - source: &impl ReadableVec, - exit: &Exit, - ) -> Result<()> - where - A: vecdb::VecValue + Ord, - T: From, - { - use crate::traits::ComputeRollingMaxFromStarts; - self.0 - ._24h - .height - .compute_rolling_max_from_starts(max_from, windows._24h, source, exit)?; - self.0 - ._7d - .height - .compute_rolling_max_from_starts(max_from, windows._7d, source, exit)?; - self.0 - ._30d - .height - .compute_rolling_max_from_starts(max_from, windows._30d, source, exit)?; - self.0 - ._1y - .height - .compute_rolling_max_from_starts(max_from, windows._1y, source, exit)?; - Ok(()) - } - - pub(crate) fn compute_rolling_average( - &mut self, - max_from: Height, - windows: &WindowStarts<'_>, - source: &impl ReadableVec, - exit: &Exit, - ) -> Result<()> - where - A: vecdb::VecValue, - f64: From + From, - T: From + Default, - { - self.0 - ._24h - .height - .compute_rolling_average(max_from, windows._24h, source, exit)?; - self.0 - ._7d - .height - .compute_rolling_average(max_from, windows._7d, source, exit)?; - self.0 - ._30d - .height - .compute_rolling_average(max_from, windows._30d, source, exit)?; - self.0 - ._1y - .height - .compute_rolling_average(max_from, windows._1y, source, exit)?; - Ok(()) - } } diff --git a/crates/brk_computer/src/internal/single/transform/close_price_times_sats.rs b/crates/brk_computer/src/internal/single/transform/close_price_times_sats.rs deleted file mode 100644 index f0e1e65e1..000000000 --- a/crates/brk_computer/src/internal/single/transform/close_price_times_sats.rs +++ /dev/null @@ -1,12 +0,0 @@ -use brk_types::{Bitcoin, Dollars, Sats}; -use vecdb::BinaryTransform; - -/// Dollars * Sats -> Dollars (price × sats / 1e8) -pub struct PriceTimesSats; - -impl BinaryTransform for PriceTimesSats { - #[inline(always)] - fn apply(price: Dollars, sats: Sats) -> Dollars { - price * Bitcoin::from(sats) - } -} diff --git a/crates/brk_computer/src/internal/single/transform/difference_f32.rs b/crates/brk_computer/src/internal/single/transform/difference_f32.rs deleted file mode 100644 index fe273eaa6..000000000 --- a/crates/brk_computer/src/internal/single/transform/difference_f32.rs +++ /dev/null @@ -1,12 +0,0 @@ -use brk_types::StoredF32; -use vecdb::BinaryTransform; - -/// (StoredF32, StoredF32) -> StoredF32 difference (a - b) -pub struct DifferenceF32; - -impl BinaryTransform for DifferenceF32 { - #[inline(always)] - fn apply(a: StoredF32, b: StoredF32) -> StoredF32 { - StoredF32::from(*a - *b) - } -} diff --git a/crates/brk_computer/src/internal/single/transform/dollar_minus.rs b/crates/brk_computer/src/internal/single/transform/dollar_minus.rs deleted file mode 100644 index 1294c391c..000000000 --- a/crates/brk_computer/src/internal/single/transform/dollar_minus.rs +++ /dev/null @@ -1,11 +0,0 @@ -use brk_types::Dollars; -use vecdb::BinaryTransform; - -pub struct DollarsMinus; - -impl BinaryTransform for DollarsMinus { - #[inline(always)] - fn apply(lhs: Dollars, rhs: Dollars) -> Dollars { - lhs - rhs - } -} diff --git a/crates/brk_computer/src/internal/single/transform/dollars_squared_divide.rs b/crates/brk_computer/src/internal/single/transform/dollars_squared_divide.rs deleted file mode 100644 index 8cb80373c..000000000 --- a/crates/brk_computer/src/internal/single/transform/dollars_squared_divide.rs +++ /dev/null @@ -1,18 +0,0 @@ -use brk_types::Dollars; -use vecdb::BinaryTransform; - -/// (Dollars, Dollars) -> Dollars: a² / b -pub struct DollarsSquaredDivide; - -impl BinaryTransform for DollarsSquaredDivide { - #[inline(always)] - fn apply(a: Dollars, b: Dollars) -> Dollars { - let af = f64::from(a); - let bf = f64::from(b); - if bf == 0.0 { - Dollars::NAN - } else { - Dollars::from(af * af / bf) - } - } -} diff --git a/crates/brk_computer/src/internal/single/transform/half_close_price_times_sats.rs b/crates/brk_computer/src/internal/single/transform/half_close_price_times_sats.rs deleted file mode 100644 index 0ff800da4..000000000 --- a/crates/brk_computer/src/internal/single/transform/half_close_price_times_sats.rs +++ /dev/null @@ -1,12 +0,0 @@ -use brk_types::{Bitcoin, Dollars, Sats}; -use vecdb::BinaryTransform; - -/// Dollars * Sats -> Dollars/2 (price × sats / 1e8 / 2) -pub struct HalfPriceTimesSats; - -impl BinaryTransform for HalfPriceTimesSats { - #[inline(always)] - fn apply(price: Dollars, sats: Sats) -> Dollars { - (price * Bitcoin::from(sats)).halved() - } -} diff --git a/crates/brk_computer/src/internal/single/transform/mod.rs b/crates/brk_computer/src/internal/single/transform/mod.rs index 41eb275b0..86de28222 100644 --- a/crates/brk_computer/src/internal/single/transform/mod.rs +++ b/crates/brk_computer/src/internal/single/transform/mod.rs @@ -1,17 +1,13 @@ mod block_count_target; mod cents_to_dollars; mod cents_to_sats; -mod close_price_times_sats; -mod difference_f32; + mod dollar_halve; mod dollar_identity; -mod dollar_minus; mod dollar_plus; mod dollar_times_tenths; -mod dollars_squared_divide; mod dollars_to_sats_fract; mod f32_identity; -mod half_close_price_times_sats; mod percentage_diff_close_dollars; mod percentage_dollars_f32; mod percentage_dollars_f32_neg; @@ -20,20 +16,17 @@ mod percentage_u32_f32; mod price_times_ratio; mod ratio32; mod ratio64; -mod ratio_f32; -mod ratio_u64_f32; + mod return_f32_tenths; mod return_i8; mod return_u16; -mod rsi_formula; + mod sat_halve; mod sat_halve_to_bitcoin; mod sat_identity; mod sat_mask; mod sat_to_bitcoin; -mod sats_times_close_price; mod u16_to_years; -mod u64_plus; mod volatility_sqrt30; mod volatility_sqrt365; mod volatility_sqrt7; @@ -41,39 +34,32 @@ mod volatility_sqrt7; pub use block_count_target::*; pub use cents_to_dollars::*; pub use cents_to_sats::*; -pub use close_price_times_sats::*; -pub use difference_f32::*; + pub use dollar_halve::*; pub use dollar_identity::*; -pub use dollar_minus::*; pub use dollar_plus::*; pub use dollar_times_tenths::*; -pub use dollars_squared_divide::*; pub use dollars_to_sats_fract::*; pub use f32_identity::*; -pub use half_close_price_times_sats::*; pub use percentage_diff_close_dollars::*; pub use percentage_dollars_f32::*; pub use percentage_dollars_f32_neg::*; pub use percentage_sats_f64::*; pub use percentage_u32_f32::*; pub use price_times_ratio::*; -pub use ratio_f32::*; -pub use ratio_u64_f32::*; + pub use ratio32::*; pub use ratio64::*; pub use return_f32_tenths::*; pub use return_i8::*; pub use return_u16::*; -pub use rsi_formula::*; + pub use sat_halve::*; pub use sat_halve_to_bitcoin::*; pub use sat_identity::*; pub use sat_mask::*; pub use sat_to_bitcoin::*; -pub use sats_times_close_price::*; pub use u16_to_years::*; -pub use u64_plus::*; pub use volatility_sqrt7::*; pub use volatility_sqrt30::*; pub use volatility_sqrt365::*; diff --git a/crates/brk_computer/src/internal/single/transform/ratio_f32.rs b/crates/brk_computer/src/internal/single/transform/ratio_f32.rs deleted file mode 100644 index 20610df51..000000000 --- a/crates/brk_computer/src/internal/single/transform/ratio_f32.rs +++ /dev/null @@ -1,16 +0,0 @@ -use brk_types::StoredF32; -use vecdb::BinaryTransform; - -/// (StoredF32, StoredF32) -> StoredF32 ratio (a / b) -pub struct RatioF32; - -impl BinaryTransform for RatioF32 { - #[inline(always)] - fn apply(a: StoredF32, b: StoredF32) -> StoredF32 { - if *b == 0.0 { - StoredF32::from(0.0) - } else { - StoredF32::from(*a / *b) - } - } -} diff --git a/crates/brk_computer/src/internal/single/transform/ratio_u64_f32.rs b/crates/brk_computer/src/internal/single/transform/ratio_u64_f32.rs deleted file mode 100644 index 3755d6fdc..000000000 --- a/crates/brk_computer/src/internal/single/transform/ratio_u64_f32.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! (StoredU64, StoredU64) -> StoredF32 ratio - -use brk_types::{StoredF32, StoredU64}; -use vecdb::BinaryTransform; - -/// (StoredU64, StoredU64) -> StoredF32 ratio (a/b) -pub struct RatioU64F32; - -impl BinaryTransform for RatioU64F32 { - #[inline(always)] - fn apply(numerator: StoredU64, denominator: StoredU64) -> StoredF32 { - let num: f64 = (*numerator) as f64; - let den: f64 = (*denominator) as f64; - if den == 0.0 { - StoredF32::from(0.0) - } else { - StoredF32::from(num / den) - } - } -} diff --git a/crates/brk_computer/src/internal/single/transform/rsi_formula.rs b/crates/brk_computer/src/internal/single/transform/rsi_formula.rs deleted file mode 100644 index 53f551350..000000000 --- a/crates/brk_computer/src/internal/single/transform/rsi_formula.rs +++ /dev/null @@ -1,17 +0,0 @@ -use brk_types::StoredF32; -use vecdb::BinaryTransform; - -/// (StoredF32, StoredF32) -> StoredF32 RSI formula: 100 * a / (a + b) -pub struct RsiFormula; - -impl BinaryTransform for RsiFormula { - #[inline(always)] - fn apply(average_gain: StoredF32, average_loss: StoredF32) -> StoredF32 { - let sum = *average_gain + *average_loss; - if sum == 0.0 { - StoredF32::from(50.0) - } else { - StoredF32::from(100.0 * *average_gain / sum) - } - } -} diff --git a/crates/brk_computer/src/internal/single/transform/sats_times_close_price.rs b/crates/brk_computer/src/internal/single/transform/sats_times_close_price.rs deleted file mode 100644 index 7474ee922..000000000 --- a/crates/brk_computer/src/internal/single/transform/sats_times_close_price.rs +++ /dev/null @@ -1,12 +0,0 @@ -use brk_types::{Bitcoin, Dollars, Sats}; -use vecdb::BinaryTransform; - -/// Sats * Dollars -> Dollars (sats / 1e8 × price) -pub struct SatsTimesPrice; - -impl BinaryTransform for SatsTimesPrice { - #[inline(always)] - fn apply(sats: Sats, price: Dollars) -> Dollars { - price * Bitcoin::from(sats) - } -} diff --git a/crates/brk_computer/src/internal/single/transform/u64_plus.rs b/crates/brk_computer/src/internal/single/transform/u64_plus.rs deleted file mode 100644 index 3b0ae40f6..000000000 --- a/crates/brk_computer/src/internal/single/transform/u64_plus.rs +++ /dev/null @@ -1,13 +0,0 @@ -use brk_types::StoredU64; -use vecdb::BinaryTransform; - -/// (StoredU64, StoredU64) -> StoredU64 addition -/// Used for computing total_addr_count = addr_count + empty_addr_count -pub struct U64Plus; - -impl BinaryTransform for U64Plus { - #[inline(always)] - fn apply(lhs: StoredU64, rhs: StoredU64) -> StoredU64 { - StoredU64::from(u64::from(lhs) + u64::from(rhs)) - } -} diff --git a/crates/brk_computer/src/internal/single/vec/cumulative.rs b/crates/brk_computer/src/internal/single/vec/cumulative.rs index 679e2045c..b8e6193af 100644 --- a/crates/brk_computer/src/internal/single/vec/cumulative.rs +++ b/crates/brk_computer/src/internal/single/vec/cumulative.rs @@ -3,8 +3,7 @@ use brk_traversable::Traversable; use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; use vecdb::{ - Database, EagerVec, ImportableVec, PcoVec, ReadableBoxedVec, ReadableCloneableVec, Ro, Rw, - StorageMode, StoredVec, VecIndex, Version, + Database, EagerVec, ImportableVec, PcoVec, Ro, Rw, StorageMode, StoredVec, VecIndex, Version, }; use crate::internal::ComputedVecValue; @@ -30,10 +29,6 @@ impl CumulativeVec { &self.0 } - pub(crate) fn read_only_boxed_clone(&self) -> ReadableBoxedVec { - self.0.read_only_boxed_clone() - } - pub fn read_only_clone(&self) -> CumulativeVec { CumulativeVec(StoredVec::read_only_clone(&self.0)) } diff --git a/crates/brk_computer/src/internal/single/vec/max.rs b/crates/brk_computer/src/internal/single/vec/max.rs index 1b0e60e71..83fa50339 100644 --- a/crates/brk_computer/src/internal/single/vec/max.rs +++ b/crates/brk_computer/src/internal/single/vec/max.rs @@ -16,7 +16,11 @@ pub struct MaxVec MaxVec { pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result { - Ok(Self(EagerVec::forced_import(db, &format!("{name}_max"), version)?)) + Ok(Self(EagerVec::forced_import( + db, + &format!("{name}_max"), + version, + )?)) } pub fn read_only_clone(&self) -> MaxVec { diff --git a/crates/brk_computer/src/internal/single/vec/min.rs b/crates/brk_computer/src/internal/single/vec/min.rs index 99a049094..68def91ee 100644 --- a/crates/brk_computer/src/internal/single/vec/min.rs +++ b/crates/brk_computer/src/internal/single/vec/min.rs @@ -16,7 +16,11 @@ pub struct MinVec MinVec { pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result { - Ok(Self(EagerVec::forced_import(db, &format!("{name}_min"), version)?)) + Ok(Self(EagerVec::forced_import( + db, + &format!("{name}_min"), + version, + )?)) } pub fn read_only_clone(&self) -> MinVec { diff --git a/crates/brk_computer/src/internal/single/vec/percentiles.rs b/crates/brk_computer/src/internal/single/vec/percentiles.rs index 90a2586dc..c62dbd88e 100644 --- a/crates/brk_computer/src/internal/single/vec/percentiles.rs +++ b/crates/brk_computer/src/internal/single/vec/percentiles.rs @@ -19,8 +19,16 @@ macro_rules! define_percentile_vec { ); impl $name { - pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result { - Ok(Self(EagerVec::forced_import(db, &format!("{name}_{}", $suffix), version)?)) + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + ) -> Result { + Ok(Self(EagerVec::forced_import( + db, + &format!("{name}_{}", $suffix), + version, + )?)) } pub fn read_only_clone(&self) -> $name { @@ -30,8 +38,28 @@ macro_rules! define_percentile_vec { }; } -define_percentile_vec!(Pct10Vec, "pct10", "10th percentile in an aggregation period"); -define_percentile_vec!(Pct25Vec, "pct25", "25th percentile in an aggregation period"); -define_percentile_vec!(MedianVec, "median", "Median (50th percentile) in an aggregation period"); -define_percentile_vec!(Pct75Vec, "pct75", "75th percentile in an aggregation period"); -define_percentile_vec!(Pct90Vec, "pct90", "90th percentile in an aggregation period"); +define_percentile_vec!( + Pct10Vec, + "pct10", + "10th percentile in an aggregation period" +); +define_percentile_vec!( + Pct25Vec, + "pct25", + "25th percentile in an aggregation period" +); +define_percentile_vec!( + MedianVec, + "median", + "Median (50th percentile) in an aggregation period" +); +define_percentile_vec!( + Pct75Vec, + "pct75", + "75th percentile in an aggregation period" +); +define_percentile_vec!( + Pct90Vec, + "pct90", + "90th percentile in an aggregation period" +); diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index a9e90a2a8..a3ef4c7c9 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -110,7 +110,7 @@ impl Computer { // Import mining module (separate database) let mining_handle = big_thread().spawn_scoped(s, || -> Result<_> { - Ok(Box::new(mining::Vecs::forced_import(&computed_path, VERSION, &indexes, &prices)?)) + Ok(Box::new(mining::Vecs::forced_import(&computed_path, VERSION, &indexes)?)) })?; // Import transactions module @@ -120,7 +120,6 @@ impl Computer { VERSION, indexer, &indexes, - &prices, )?)) })?; @@ -129,12 +128,11 @@ impl Computer { &computed_path, VERSION, &indexes, - &prices, )?)) })?; let cointime = Box::new( - cointime::Vecs::forced_import(&computed_path, VERSION, &indexes, &prices)? + cointime::Vecs::forced_import(&computed_path, VERSION, &indexes)? ); let blocks = blocks_handle.join().unwrap()?; @@ -142,15 +140,10 @@ impl Computer { let transactions = transactions_handle.join().unwrap()?; let scripts = scripts_handle.join().unwrap()?; - // pools depends on blocks, mining, and transactions for lazy dominance vecs let pools = Box::new(pools::Vecs::forced_import( &computed_path, VERSION, &indexes, - &prices, - &blocks, - &mining, - &transactions, )?); Ok((blocks, mining, transactions, scripts, pools, cointime)) @@ -163,26 +156,22 @@ impl Computer { // Threads inside let i = Instant::now(); let distribution = Box::new( - distribution::Vecs::forced_import(&computed_path, VERSION, &indexes, &prices)? + distribution::Vecs::forced_import(&computed_path, VERSION, &indexes)? ); info!("Imported distribution in {:?}", i.elapsed()); // Supply must be imported after distribution (references distribution's supply) let i = Instant::now(); let supply = Box::new( - supply::Vecs::forced_import(&computed_path, VERSION, &indexes, &prices, &distribution)? + supply::Vecs::forced_import(&computed_path, VERSION, &indexes, &distribution)? ); info!("Imported supply in {:?}", i.elapsed()); - // Market must be imported after distribution and transactions (for NVT indicator) let i = Instant::now(); let market = Box::new(market::Vecs::forced_import( &computed_path, VERSION, &indexes, - &prices, - &distribution, - &transactions, )?); info!("Imported market in {:?}", i.elapsed()); @@ -305,7 +294,7 @@ impl Computer { info!("Computing scripts..."); let i = Instant::now(); self.scripts - .compute(indexer, &self.blocks, &self.outputs, &starting_indexes, exit)?; + .compute(indexer, &self.blocks, &self.outputs, &self.prices, &starting_indexes, exit)?; info!("Computed scripts in {:?}", i.elapsed()); info!("Computing outputs..."); @@ -344,6 +333,7 @@ impl Computer { &self.indexes, &self.blocks, &self.transactions, + &self.prices, &starting_indexes, exit, )?; @@ -367,6 +357,9 @@ impl Computer { indexer, &self.indexes, &self.blocks, + &self.prices, + &self.mining, + &self.transactions, &starting_indexes_clone, exit, )?; @@ -404,6 +397,7 @@ impl Computer { &self.blocks, &self.mining, &self.distribution, + &self.transactions, &starting_indexes, exit, )?; @@ -418,6 +412,7 @@ impl Computer { &self.blocks, &self.mining, &self.transactions, + &self.prices, &self.distribution, &starting_indexes, exit, diff --git a/crates/brk_computer/src/market/ath/compute.rs b/crates/brk_computer/src/market/ath/compute.rs index 61630ddf9..86f4f544b 100644 --- a/crates/brk_computer/src/market/ath/compute.rs +++ b/crates/brk_computer/src/market/ath/compute.rs @@ -12,7 +12,7 @@ impl Vecs { starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.price_ath.height.compute_all_time_high( + self.price_ath.usd.height.compute_all_time_high( starting_indexes.height, &prices.usd.price, exit, @@ -21,7 +21,7 @@ impl Vecs { let mut prev = None; self.days_since_price_ath.height.compute_transform2( starting_indexes.height, - &self.price_ath.height, + &self.price_ath.usd.height, &prices.usd.price, |(i, ath, price, slf)| { if prev.is_none() { diff --git a/crates/brk_computer/src/market/ath/import.rs b/crates/brk_computer/src/market/ath/import.rs index ae6ba1d2f..a486217a0 100644 --- a/crates/brk_computer/src/market/ath/import.rs +++ b/crates/brk_computer/src/market/ath/import.rs @@ -1,15 +1,14 @@ use brk_error::Result; use brk_types::Version; -use vecdb::{Database, ReadableCloneableVec}; +use vecdb::Database; use super::Vecs; use crate::{ indexes, internal::{ - ComputedFromHeightLast, LazyBinaryFromHeightLast, LazyHeightDerivedLast, - PercentageDiffDollars, PriceFromHeight, StoredU16ToYears, + ComputedFromHeightLast, LazyHeightDerivedLast, + Price, StoredU16ToYears, }, - prices, }; impl Vecs { @@ -17,9 +16,8 @@ impl Vecs { db: &Database, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { - let price_ath = PriceFromHeight::forced_import(db, "price_ath", version, indexes)?; + let price_ath = Price::forced_import(db, "price_ath", version, indexes)?; let max_days_between_price_aths = ComputedFromHeightLast::forced_import( db, @@ -45,14 +43,7 @@ impl Vecs { ); let price_drawdown = - LazyBinaryFromHeightLast::from_height_and_derived_last::( - "price_drawdown", - version, - prices.usd.price.read_only_boxed_clone(), - price_ath.height.read_only_boxed_clone(), - &prices.usd.close, - &price_ath.rest, - ); + ComputedFromHeightLast::forced_import(db, "price_drawdown", version, indexes)?; Ok(Self { price_ath, diff --git a/crates/brk_computer/src/market/ath/vecs.rs b/crates/brk_computer/src/market/ath/vecs.rs index 7aa4dd3b8..b3bd8735e 100644 --- a/crates/brk_computer/src/market/ath/vecs.rs +++ b/crates/brk_computer/src/market/ath/vecs.rs @@ -2,15 +2,13 @@ use brk_traversable::Traversable; use brk_types::{Dollars, StoredF32, StoredU16}; use vecdb::{Rw, StorageMode}; -use crate::internal::{ - ComputedFromHeightLast, LazyBinaryFromHeightLast, LazyHeightDerivedLast, Price, -}; +use crate::internal::{ComputedFromHeightLast, LazyHeightDerivedLast, Price}; /// All-time high related metrics #[derive(Traversable)] pub struct Vecs { pub price_ath: Price>, - pub price_drawdown: LazyBinaryFromHeightLast, + pub price_drawdown: ComputedFromHeightLast, pub days_since_price_ath: ComputedFromHeightLast, pub years_since_price_ath: LazyHeightDerivedLast, pub max_days_between_price_aths: ComputedFromHeightLast, diff --git a/crates/brk_computer/src/market/compute.rs b/crates/brk_computer/src/market/compute.rs index 68fe242ac..e7d13618a 100644 --- a/crates/brk_computer/src/market/compute.rs +++ b/crates/brk_computer/src/market/compute.rs @@ -1,7 +1,7 @@ use brk_error::Result; use vecdb::Exit; -use crate::{ComputeIndexes, blocks, distribution, indexes, mining, prices}; +use crate::{ComputeIndexes, blocks, distribution, indexes, mining, prices, transactions}; use super::Vecs; @@ -14,6 +14,7 @@ impl Vecs { blocks: &blocks::Vecs, mining: &mining::Vecs, distribution: &distribution::Vecs, + transactions: &transactions::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { @@ -26,9 +27,11 @@ impl Vecs { // Returns metrics (depends on lookback) self.returns - .compute(indexes, blocks, starting_indexes, exit)?; + .compute(indexes, prices, blocks, &self.lookback, starting_indexes, exit)?; - // Volatility: all fields are lazy (derived from returns SD) + // Volatility (depends on returns) + self.volatility + .compute(&self.returns, starting_indexes.height, exit)?; // Range metrics (independent) self.range @@ -50,6 +53,8 @@ impl Vecs { prices, blocks, distribution, + transactions, + &self.moving_average, starting_indexes, exit, )?; diff --git a/crates/brk_computer/src/market/dca/by_class.rs b/crates/brk_computer/src/market/dca/by_class.rs index a9b0c3ba2..537728d9d 100644 --- a/crates/brk_computer/src/market/dca/by_class.rs +++ b/crates/brk_computer/src/market/dca/by_class.rs @@ -131,37 +131,4 @@ impl ByDcaClass { ] } - pub(crate) fn zip_ref<'a, U>(&'a self, other: &'a ByDcaClass) -> ByDcaClass<(&'a T, &'a U)> { - ByDcaClass { - _2015: (&self._2015, &other._2015), - _2016: (&self._2016, &other._2016), - _2017: (&self._2017, &other._2017), - _2018: (&self._2018, &other._2018), - _2019: (&self._2019, &other._2019), - _2020: (&self._2020, &other._2020), - _2021: (&self._2021, &other._2021), - _2022: (&self._2022, &other._2022), - _2023: (&self._2023, &other._2023), - _2024: (&self._2024, &other._2024), - _2025: (&self._2025, &other._2025), - _2026: (&self._2026, &other._2026), - } - } - - pub(crate) fn map U>(self, mut f: F) -> ByDcaClass { - ByDcaClass { - _2015: f(self._2015), - _2016: f(self._2016), - _2017: f(self._2017), - _2018: f(self._2018), - _2019: f(self._2019), - _2020: f(self._2020), - _2021: f(self._2021), - _2022: f(self._2022), - _2023: f(self._2023), - _2024: f(self._2024), - _2025: f(self._2025), - _2026: f(self._2026), - } - } } diff --git a/crates/brk_computer/src/market/dca/by_period.rs b/crates/brk_computer/src/market/dca/by_period.rs index 356e64666..83db063cc 100644 --- a/crates/brk_computer/src/market/dca/by_period.rs +++ b/crates/brk_computer/src/market/dca/by_period.rs @@ -172,40 +172,6 @@ impl ByDcaPeriod { .into_iter() } - pub(crate) fn zip_ref<'a, U>(&'a self, other: &'a ByDcaPeriod) -> ByDcaPeriod<(&'a T, &'a U)> { - ByDcaPeriod { - _1w: (&self._1w, &other._1w), - _1m: (&self._1m, &other._1m), - _3m: (&self._3m, &other._3m), - _6m: (&self._6m, &other._6m), - _1y: (&self._1y, &other._1y), - _2y: (&self._2y, &other._2y), - _3y: (&self._3y, &other._3y), - _4y: (&self._4y, &other._4y), - _5y: (&self._5y, &other._5y), - _6y: (&self._6y, &other._6y), - _8y: (&self._8y, &other._8y), - _10y: (&self._10y, &other._10y), - } - } - - pub(crate) fn map U>(self, mut f: F) -> ByDcaPeriod { - ByDcaPeriod { - _1w: f(self._1w), - _1m: f(self._1m), - _3m: f(self._3m), - _6m: f(self._6m), - _1y: f(self._1y), - _2y: f(self._2y), - _3y: f(self._3y), - _4y: f(self._4y), - _5y: f(self._5y), - _6y: f(self._6y), - _8y: f(self._8y), - _10y: f(self._10y), - } - } - } /// Generic wrapper for DCA CAGR data (periods >= 2 years) diff --git a/crates/brk_computer/src/market/dca/compute.rs b/crates/brk_computer/src/market/dca/compute.rs index 0a86defd9..47c22f48a 100644 --- a/crates/brk_computer/src/market/dca/compute.rs +++ b/crates/brk_computer/src/market/dca/compute.rs @@ -1,11 +1,11 @@ use brk_error::Result; use brk_types::{Bitcoin, Day1, Date, Dollars, Height, Sats, StoredF32, StoredU32}; -use vecdb::{AnyVec, EagerVec, Exit, ReadableVec, PcoVec, PcoVecValue, VecIndex}; +use vecdb::{AnyVec, EagerVec, Exit, ReadableOptionVec, ReadableVec, PcoVec, PcoVecValue, VecIndex}; use super::{ByDcaClass, ByDcaPeriod, Vecs}; use crate::{ ComputeIndexes, blocks, indexes, - internal::{ComputedFromHeightLast, LazyBinaryFromHeightLast}, + internal::{ComputedFromHeightLast, PercentageDiffDollars}, market::lookback, prices, }; @@ -42,7 +42,7 @@ impl Vecs { if same_day { (h, Sats::ZERO) } else { - let s = close.collect_one(di).map(sats_from_dca).unwrap_or(Sats::ZERO); + let s = close.collect_one_flat(di).map(sats_from_dca).unwrap_or(Sats::ZERO); (h, s) } }, @@ -89,10 +89,24 @@ impl Vecs { )?; } + // DCA by period - returns (compute from average price) + for (returns, (average_price, _)) in self + .period_returns + .iter_mut() + .zip(self.period_average_price.iter_with_days()) + { + returns.compute_binary::( + starting_indexes.height, + &prices.usd.price, + &average_price.usd.height, + exit, + )?; + } + // DCA by period - CAGR (computed from returns) for (cagr, returns, days) in self.period_cagr.zip_mut_with_period(&self.period_returns) { let years = days as f32 / 365.0; - let returns_data: Vec = returns.day1.collect(); + let returns_data: Vec = returns.day1.collect_or_default(); cagr.height.compute_transform( starting_indexes.height, h2d, @@ -142,6 +156,21 @@ impl Vecs { )?; } + // Lump sum by period - returns (compute from lookback price) + let lookback_dca2 = lookback.price_ago.as_dca_period(); + for (returns, (lookback_price, _)) in self + .period_lump_sum_returns + .iter_mut() + .zip(lookback_dca2.iter_with_days()) + { + returns.compute_binary::( + starting_indexes.height, + &prices.usd.price, + &lookback_price.usd.height, + exit, + )?; + } + // Lump sum by period - profitability compute_period_rolling( &mut self.period_lump_sum_days_in_profit, @@ -187,7 +216,7 @@ impl Vecs { } else { Sats::ZERO }; - let s = close.collect_one(di).map(sats_from_dca).unwrap_or(Sats::ZERO); + let s = close.collect_one_flat(di).map(sats_from_dca).unwrap_or(Sats::ZERO); (h, prev + s) } }, @@ -222,6 +251,21 @@ impl Vecs { )?; } + // DCA by year class - returns (compute from average price) + for (returns, average_price) in self + .class_returns + .iter_mut() + .zip(self.class_average_price.iter()) + { + + returns.compute_binary::( + starting_indexes.height, + &prices.usd.price, + &average_price.usd.height, + exit, + )?; + } + // DCA by year class - profitability compute_class_cumulative( &mut self.class_days_in_profit, @@ -252,7 +296,7 @@ fn compute_period_rolling( days_in_loss: &mut ByDcaPeriod>, min_return: &mut ByDcaPeriod>, max_return: &mut ByDcaPeriod>, - returns: &ByDcaPeriod>, + returns: &ByDcaPeriod>, blocks: &blocks::Vecs, h2d: &EagerVec>, starting_indexes: &ComputeIndexes, @@ -266,7 +310,7 @@ fn compute_period_rolling( .zip(returns.iter_with_days()) { let window_starts = blocks.count.start_vec(days as usize); - let returns_data: Vec = ret.day1.collect(); + let returns_data: Vec = ret.day1.collect_or_default(); compute_rolling( &mut dip.height, h2d, &returns_data, window_starts, starting_indexes.height, exit, @@ -307,7 +351,7 @@ fn compute_class_cumulative( days_in_loss: &mut ByDcaClass>, min_return: &mut ByDcaClass>, max_return: &mut ByDcaClass>, - returns: &ByDcaClass>, + returns: &ByDcaClass>, h2d: &EagerVec>, starting_indexes: &ComputeIndexes, exit: &Exit, @@ -323,25 +367,25 @@ fn compute_class_cumulative( .zip(start_days) { compute_cumulative( - &mut dip.height, h2d, &*ret.day1, from, starting_indexes.height, exit, + &mut dip.height, h2d, &ret.day1, from, starting_indexes.height, exit, StoredU32::ZERO, |prev, ret| if *ret > 0.0 { prev + StoredU32::ONE } else { prev }, )?; compute_cumulative( - &mut dil.height, h2d, &*ret.day1, from, starting_indexes.height, exit, + &mut dil.height, h2d, &ret.day1, from, starting_indexes.height, exit, StoredU32::ZERO, |prev, ret| if *ret < 0.0 { prev + StoredU32::ONE } else { prev }, )?; compute_cumulative( - &mut minr.height, h2d, &*ret.day1, from, starting_indexes.height, exit, + &mut minr.height, h2d, &ret.day1, from, starting_indexes.height, exit, StoredF32::from(f32::MAX), |prev, ret| if *ret < *prev { ret } else { prev }, )?; compute_cumulative( - &mut maxr.height, h2d, &*ret.day1, from, starting_indexes.height, exit, + &mut maxr.height, h2d, &ret.day1, from, starting_indexes.height, exit, StoredF32::from(f32::MIN), |prev, ret| if *ret > *prev { ret } else { prev }, )?; @@ -405,7 +449,7 @@ fn compute_rolling( fn compute_cumulative( output: &mut EagerVec>, h2d: &EagerVec>, - returns: &impl ReadableVec, + returns: &impl ReadableOptionVec, from_day1: Day1, starting_height: Height, exit: &Exit, @@ -441,7 +485,7 @@ fn compute_cumulative( } else { initial }; - let ret = returns.collect_one(di).unwrap_or_default(); + let ret = returns.collect_one_flat(di).unwrap_or_default(); (h, accumulate(prev, ret)) } }, diff --git a/crates/brk_computer/src/market/dca/import.rs b/crates/brk_computer/src/market/dca/import.rs index 3f5cab14d..a3879d81b 100644 --- a/crates/brk_computer/src/market/dca/import.rs +++ b/crates/brk_computer/src/market/dca/import.rs @@ -1,16 +1,14 @@ use brk_error::Result; use brk_types::Version; -use vecdb::{Database, ImportableVec, ReadableCloneableVec}; +use vecdb::{Database, ImportableVec}; -use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod, DCA_CLASS_NAMES, DCA_PERIOD_NAMES, Vecs}; +use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod, Vecs}; use crate::{ indexes, internal::{ - ComputedFromHeightLast, LazyBinaryFromHeightLast, PercentageDiffDollars, PriceFromHeight, + ComputedFromHeightLast, Price, ValueFromHeightLast, }, - market::lookback, - prices, }; impl Vecs { @@ -18,8 +16,6 @@ impl Vecs { db: &Database, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, - lookback: &lookback::Vecs, ) -> Result { // DCA by period - stack (KISS) let period_stack = ByDcaPeriod::try_new(|name, _days| { @@ -28,13 +24,12 @@ impl Vecs { &format!("{name}_dca_stack"), version, indexes, - prices, ) })?; // DCA by period - average price let period_average_price = ByDcaPeriod::try_new(|name, _days| { - PriceFromHeight::forced_import( + Price::forced_import( db, &format!("{name}_dca_average_price"), version, @@ -42,21 +37,14 @@ impl Vecs { ) })?; - let period_returns = - DCA_PERIOD_NAMES - .zip_ref(&period_average_price) - .map(|(name, average_price)| { - LazyBinaryFromHeightLast::from_height_and_derived_last::< - PercentageDiffDollars, - >( - &format!("{name}_dca_returns"), - version, - prices.usd.price.read_only_boxed_clone(), - average_price.height.read_only_boxed_clone(), - &prices.usd.close, - &average_price.rest, - ) - }); + let period_returns = ByDcaPeriod::try_new(|name, _days| { + ComputedFromHeightLast::forced_import( + db, + &format!("{name}_dca_returns"), + version, + indexes, + ) + })?; // DCA by period - CAGR let period_cagr = ByDcaCagr::try_new(|name, _days| { @@ -107,27 +95,18 @@ impl Vecs { &format!("{name}_lump_sum_stack"), version, indexes, - prices, ) })?; // Lump sum by period - returns - let lookback_dca = lookback.price_ago.as_dca_period(); - let period_lump_sum_returns = - DCA_PERIOD_NAMES - .zip_ref(&lookback_dca) - .map(|(name, lookback_price)| { - LazyBinaryFromHeightLast::from_height_and_derived_last::< - PercentageDiffDollars, - >( - &format!("{name}_lump_sum_returns"), - version, - prices.usd.price.read_only_boxed_clone(), - lookback_price.height.read_only_boxed_clone(), - &prices.usd.close, - &lookback_price.rest, - ) - }); + let period_lump_sum_returns = ByDcaPeriod::try_new(|name, _days| { + ComputedFromHeightLast::forced_import( + db, + &format!("{name}_lump_sum_returns"), + version, + indexes, + ) + })?; // Lump sum by period - profitability let period_lump_sum_days_in_profit = ByDcaPeriod::try_new(|name, _days| { @@ -173,30 +152,22 @@ impl Vecs { &format!("{name}_stack"), version, indexes, - prices, ) })?; // DCA by year class - average price let class_average_price = ByDcaClass::try_new(|name, _year, _day1| { - PriceFromHeight::forced_import(db, &format!("{name}_average_price"), version, indexes) + Price::forced_import(db, &format!("{name}_average_price"), version, indexes) })?; - let class_returns = - DCA_CLASS_NAMES - .zip_ref(&class_average_price) - .map(|(name, average_price)| { - LazyBinaryFromHeightLast::from_height_and_derived_last::< - PercentageDiffDollars, - >( - &format!("{name}_returns"), - version, - prices.usd.price.read_only_boxed_clone(), - average_price.height.read_only_boxed_clone(), - &prices.usd.close, - &average_price.rest, - ) - }); + let class_returns = ByDcaClass::try_new(|name, _year, _day1| { + ComputedFromHeightLast::forced_import( + db, + &format!("{name}_returns"), + version, + indexes, + ) + })?; // DCA by year class - profitability let class_days_in_profit = ByDcaClass::try_new(|name, _year, _day1| { diff --git a/crates/brk_computer/src/market/dca/vecs.rs b/crates/brk_computer/src/market/dca/vecs.rs index a66b05f04..0e32d75f5 100644 --- a/crates/brk_computer/src/market/dca/vecs.rs +++ b/crates/brk_computer/src/market/dca/vecs.rs @@ -4,7 +4,7 @@ use vecdb::{EagerVec, PcoVec, Rw, StorageMode}; use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod}; use crate::internal::{ - ComputedFromHeightLast, LazyBinaryFromHeightLast, Price, ValueFromHeightLast, + ComputedFromHeightLast, Price, ValueFromHeightLast, }; /// Dollar-cost averaging metrics by time period and year class @@ -17,7 +17,7 @@ pub struct Vecs { // DCA by period - KISS types pub period_stack: ByDcaPeriod>, pub period_average_price: ByDcaPeriod>>, - pub period_returns: ByDcaPeriod>, + pub period_returns: ByDcaPeriod>, pub period_cagr: ByDcaCagr>, // DCA by period - profitability @@ -28,7 +28,7 @@ pub struct Vecs { // Lump sum by period (for comparison with DCA) - KISS types pub period_lump_sum_stack: ByDcaPeriod>, - pub period_lump_sum_returns: ByDcaPeriod>, + pub period_lump_sum_returns: ByDcaPeriod>, // Lump sum by period - profitability pub period_lump_sum_days_in_profit: ByDcaPeriod>, @@ -39,7 +39,7 @@ pub struct Vecs { // DCA by year class - KISS types pub class_stack: ByDcaClass>, pub class_average_price: ByDcaClass>>, - pub class_returns: ByDcaClass>, + pub class_returns: ByDcaClass>, // DCA by year class - profitability pub class_days_in_profit: ByDcaClass>, diff --git a/crates/brk_computer/src/market/import.rs b/crates/brk_computer/src/market/import.rs index ca35f7dab..952297921 100644 --- a/crates/brk_computer/src/market/import.rs +++ b/crates/brk_computer/src/market/import.rs @@ -5,7 +5,7 @@ use brk_traversable::Traversable; use brk_types::Version; use vecdb::{Database, PAGE_SIZE}; -use crate::{distribution, indexes, prices, transactions}; +use crate::indexes; use super::{ AthVecs, DcaVecs, IndicatorsVecs, LookbackVecs, MovingAverageVecs, RangeVecs, ReturnsVecs, @@ -17,29 +17,23 @@ impl Vecs { parent_path: &Path, parent_version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, - distribution: &distribution::Vecs, - transactions: &transactions::Vecs, ) -> Result { let db = Database::open(&parent_path.join(super::DB_NAME))?; db.set_min_len(PAGE_SIZE * 1_000_000)?; let version = parent_version; - let ath = AthVecs::forced_import(&db, version, indexes, prices)?; + let ath = AthVecs::forced_import(&db, version, indexes)?; let lookback = LookbackVecs::forced_import(&db, version, indexes)?; - let returns = ReturnsVecs::forced_import(&db, version, indexes, prices, &lookback)?; - let volatility = VolatilityVecs::forced_import(version, &returns); + let returns = ReturnsVecs::forced_import(&db, version, indexes)?; + let volatility = VolatilityVecs::forced_import(&db, version, indexes, &returns)?; let range = RangeVecs::forced_import(&db, version, indexes)?; let moving_average = MovingAverageVecs::forced_import(&db, version, indexes)?; - let dca = DcaVecs::forced_import(&db, version, indexes, prices, &lookback)?; + let dca = DcaVecs::forced_import(&db, version, indexes)?; let indicators = IndicatorsVecs::forced_import( &db, version, indexes, - distribution, - transactions, - &moving_average, )?; let this = Self { diff --git a/crates/brk_computer/src/market/indicators/compute.rs b/crates/brk_computer/src/market/indicators/compute.rs index 14126e022..595c834f8 100644 --- a/crates/brk_computer/src/market/indicators/compute.rs +++ b/crates/brk_computer/src/market/indicators/compute.rs @@ -1,9 +1,13 @@ use brk_error::Result; -use brk_types::{Day1, StoredF32}; +use brk_types::{Day1, Dollars, StoredF32}; use vecdb::{Exit, ReadableVec}; use super::{super::range, Vecs}; -use crate::{ComputeIndexes, blocks, distribution, indexes, mining, prices}; +use crate::{ + ComputeIndexes, blocks, distribution, indexes, + internal::Ratio32, + mining, prices, transactions, +}; impl Vecs { #[allow(clippy::too_many_arguments)] @@ -16,6 +20,8 @@ impl Vecs { prices: &prices::Vecs, blocks: &blocks::Vecs, distribution: &distribution::Vecs, + transactions: &transactions::Vecs, + moving_average: &super::super::moving_average::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { @@ -32,8 +38,8 @@ impl Vecs { self.stoch_k.height.compute_transform3( starting_indexes.height, price, - &range.price_2w_min.height, - &range.price_2w_max.height, + &range.price_2w_min.usd.height, + &range.price_2w_max.usd.height, |(h, close, low, high, ..)| { let range = *high - *low; let stoch = if range == 0.0 { @@ -94,6 +100,22 @@ impl Vecs { exit, )?; + // NVT: realized_cap / tx_volume_24h + self.nvt.compute_binary::( + starting_indexes.height, + &distribution.utxo_cohorts.all.metrics.supply.total.usd.height, + &transactions.volume.sent_sum.usd.height, + exit, + )?; + + // Pi Cycle: sma_111d / sma_350d_x2 + self.pi_cycle.compute_binary::( + starting_indexes.height, + &moving_average.price_111d_sma.price.as_ref().unwrap().usd.height, + &moving_average.price_350d_sma_x2.usd.height, + exit, + )?; + Ok(()) } } diff --git a/crates/brk_computer/src/market/indicators/gini.rs b/crates/brk_computer/src/market/indicators/gini.rs index d60c99288..2d456a4fc 100644 --- a/crates/brk_computer/src/market/indicators/gini.rs +++ b/crates/brk_computer/src/market/indicators/gini.rs @@ -1,6 +1,6 @@ use brk_error::Result; use brk_types::{Day1, Sats, StoredF32, StoredU64, Version}; -use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec}; +use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableOptionVec, VecIndex, WritableVec}; use crate::{ComputeIndexes, distribution, internal::ComputedFromHeightLast}; @@ -47,11 +47,11 @@ pub(super) fn compute( // Pre-collect all daily data let supply_data: Vec> = supply_vecs .iter() - .map(|v| ReadableVec::collect(*v)) + .map(|v| v.collect_or_default()) .collect(); let count_data: Vec> = count_vecs .iter() - .map(|v| ReadableVec::collect(*v)) + .map(|v| v.collect_or_default()) .collect(); let num_days = supply_data.first().map_or(0, |v| v.len()); @@ -95,14 +95,14 @@ fn gini_from_lorenz(buckets: &[(u64, u64)]) -> f32 { return 0.0; } - let (mut cum_count, mut cum_supply, mut area) = (0u64, 0u64, 0.0f64); + let (mut cumulative_count, mut cumulative_supply, mut area) = (0u64, 0u64, 0.0f64); let (tc, ts) = (total_count as f64, total_supply as f64); for &(count, supply) in buckets { - let (p0, w0) = (cum_count as f64 / tc, cum_supply as f64 / ts); - cum_count += count; - cum_supply += supply; - let (p1, w1) = (cum_count as f64 / tc, cum_supply as f64 / ts); + let (p0, w0) = (cumulative_count as f64 / tc, cumulative_supply as f64 / ts); + cumulative_count += count; + cumulative_supply += supply; + let (p1, w1) = (cumulative_count as f64 / tc, cumulative_supply as f64 / ts); area += (p1 - p0) * (w0 + w1) / 2.0; } diff --git a/crates/brk_computer/src/market/indicators/import.rs b/crates/brk_computer/src/market/indicators/import.rs index 9da7ce156..d81fa1dc1 100644 --- a/crates/brk_computer/src/market/indicators/import.rs +++ b/crates/brk_computer/src/market/indicators/import.rs @@ -1,12 +1,11 @@ use brk_error::Result; use brk_types::Version; -use vecdb::{Database, ReadableCloneableVec, LazyVecFrom2}; +use vecdb::Database; use super::{ByIndicatorTimeframe, MacdChain, RsiChain, Vecs}; use crate::{ - distribution, indexes, - internal::{ComputedFromHeightLast, DifferenceF32, LazyBinaryFromHeightLast, Ratio32, RsiFormula}, - transactions, + indexes, + internal::ComputedFromHeightLast, }; const VERSION: Version = Version::ONE; @@ -32,12 +31,12 @@ impl RsiChain { let average_gain = import!("avg_gain"); let average_loss = import!("avg_loss"); - let rsi = LazyVecFrom2::transformed::( + let rsi = ComputedFromHeightLast::forced_import( + db, &format!("rsi_{tf}"), version, - average_gain.height.read_only_boxed_clone(), - average_loss.height.read_only_boxed_clone(), - ); + indexes, + )?; Ok(Self { gains: import!("gains"), @@ -74,12 +73,12 @@ impl MacdChain { indexes, )?; - let histogram = LazyVecFrom2::transformed::( + let histogram = ComputedFromHeightLast::forced_import( + db, &format!("macd_histogram_{tf}"), version, - line.height.read_only_boxed_clone(), - signal.height.read_only_boxed_clone(), - ); + indexes, + )?; Ok(Self { line, @@ -94,24 +93,10 @@ impl Vecs { db: &Database, version: Version, indexes: &indexes::Vecs, - distribution: &distribution::Vecs, - transactions: &transactions::Vecs, - moving_average: &super::super::moving_average::Vecs, ) -> Result { let v = version + VERSION; - let nvt = LazyBinaryFromHeightLast::from_both_lazy_binary_computed_block_last::< - Ratio32, - _, - _, - _, - _, - >( - "nvt", - v, - &distribution.utxo_cohorts.all.metrics.supply.total.usd, - &transactions.volume.sent_sum.usd, - ); + let nvt = ComputedFromHeightLast::forced_import(db, "nvt", v, indexes)?; let rsi = ByIndicatorTimeframe::try_new(|tf| RsiChain::forced_import(db, tf, v, indexes))?; let macd = ByIndicatorTimeframe::try_new(|tf| MacdChain::forced_import(db, tf, v, indexes))?; @@ -120,12 +105,7 @@ impl Vecs { let stoch_d = ComputedFromHeightLast::forced_import(db, "stoch_d", v, indexes)?; let gini = ComputedFromHeightLast::forced_import(db, "gini", v, indexes)?; - let pi_cycle = LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::( - "pi_cycle", - v, - &moving_average.price_111d_sma.price.as_ref().unwrap().usd, - &moving_average.price_350d_sma_x2.usd, - ); + let pi_cycle = ComputedFromHeightLast::forced_import(db, "pi_cycle", v, indexes)?; Ok(Self { puell_multiple: ComputedFromHeightLast::forced_import(db, "puell_multiple", v, indexes)?, diff --git a/crates/brk_computer/src/market/indicators/macd.rs b/crates/brk_computer/src/market/indicators/macd.rs index 99f9563ae..4dbe10692 100644 --- a/crates/brk_computer/src/market/indicators/macd.rs +++ b/crates/brk_computer/src/market/indicators/macd.rs @@ -41,6 +41,18 @@ pub(super) fn compute( .min(starting_indexes.height.to_usize()), )?; + chain + .histogram + .height + .validate_computed_version_or_reset(source_version)?; + chain.histogram.height.truncate_if_needed_at( + chain + .histogram + .height + .len() + .min(starting_indexes.height.to_usize()), + )?; + let start_height = chain.line.height.len(); if start_height >= total_heights { return Ok(()); @@ -58,6 +70,8 @@ pub(super) fn compute( let macd_signal = compute_ema(&macd_line, 9); + let macd_histogram: Vec = macd_line.iter().zip(macd_signal.iter()).map(|(a, b)| a - b).collect(); + // Expand to Height (start_height..total_heights).for_each(|h| { let pi = date_to_period(tf, h2d[h]); @@ -71,12 +85,18 @@ pub(super) fn compute( } else { StoredF32::NAN }); + chain.histogram.height.push(if pi < macd_histogram.len() { + StoredF32::from(macd_histogram[pi]) + } else { + StoredF32::NAN + }); }); { let _lock = exit.lock(); chain.line.height.write()?; chain.signal.height.write()?; + chain.histogram.height.write()?; } Ok(()) diff --git a/crates/brk_computer/src/market/indicators/rsi.rs b/crates/brk_computer/src/market/indicators/rsi.rs index 32af51e5c..7442e9cb9 100644 --- a/crates/brk_computer/src/market/indicators/rsi.rs +++ b/crates/brk_computer/src/market/indicators/rsi.rs @@ -26,6 +26,7 @@ pub(super) fn compute( &mut chain.losses.height, &mut chain.average_gain.height, &mut chain.average_loss.height, + &mut chain.rsi.height, &mut chain.rsi_min.height, &mut chain.rsi_max.height, &mut chain.stoch_rsi.height, @@ -100,6 +101,7 @@ pub(super) fn compute( expand!(chain.losses.height, losses); expand!(chain.average_gain.height, avg_gain); expand!(chain.average_loss.height, avg_loss); + expand!(chain.rsi.height, rsi); expand!(chain.rsi_min.height, rsi_min); expand!(chain.rsi_max.height, rsi_max); expand!(chain.stoch_rsi.height, stoch_rsi); @@ -112,6 +114,7 @@ pub(super) fn compute( chain.losses.height.write()?; chain.average_gain.height.write()?; chain.average_loss.height.write()?; + chain.rsi.height.write()?; chain.rsi_min.height.write()?; chain.rsi_max.height.write()?; chain.stoch_rsi.height.write()?; diff --git a/crates/brk_computer/src/market/indicators/timeframe.rs b/crates/brk_computer/src/market/indicators/timeframe.rs index ccd2c7a77..b234e37ac 100644 --- a/crates/brk_computer/src/market/indicators/timeframe.rs +++ b/crates/brk_computer/src/market/indicators/timeframe.rs @@ -1,43 +1,29 @@ use brk_types::{Day1, Dollars, Month1, StoredF32, Week1, Year1}; -use vecdb::{ReadableVec, VecIndex}; +use vecdb::{ReadableOptionVec, VecIndex}; use crate::{market::returns::Vecs as ReturnsVecs, prices}; -/// Returns period-level returns data pub(super) fn collect_returns(tf: &str, returns: &ReturnsVecs) -> Vec { - match tf { - "1d" => { - let data: Vec = returns.price_returns._1d.day1.collect(); - data.into_iter().map(|v| *v).collect() - } - "1w" => { - let data: Vec = returns.price_returns._1w.week1.collect(); - data.into_iter().map(|v| *v).collect() - } - "1m" => { - let data: Vec = returns.price_returns._1m.month1.collect(); - data.into_iter().map(|v| *v).collect() - } - "1y" => { - let data: Vec = returns.price_returns._1y.year1.collect(); - data.into_iter().map(|v| *v).collect() - } + let data: Vec = match tf { + "1d" => returns.price_returns._1d.day1.collect_or_default(), + "1w" => returns.price_returns._1w.week1.collect_or_default(), + "1m" => returns.price_returns._1m.month1.collect_or_default(), + "1y" => returns.price_returns._1y.year1.collect_or_default(), _ => unreachable!(), - } + }; + data.into_iter().map(|v| *v).collect() } -/// Returns period-level close prices pub(super) fn collect_closes(tf: &str, prices: &prices::Vecs) -> Vec { match tf { - "1d" => prices.usd.close.day1.collect(), - "1w" => prices.usd.close.week1.collect(), - "1m" => prices.usd.close.month1.collect(), - "1y" => prices.usd.close.year1.collect(), + "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(), _ => unreachable!(), } } -/// Maps a Day1 to a period-level index for the given timeframe #[inline] pub(super) fn date_to_period(tf: &str, di: Day1) -> usize { match tf { diff --git a/crates/brk_computer/src/market/indicators/vecs.rs b/crates/brk_computer/src/market/indicators/vecs.rs index 3a1891860..18ae0d535 100644 --- a/crates/brk_computer/src/market/indicators/vecs.rs +++ b/crates/brk_computer/src/market/indicators/vecs.rs @@ -1,8 +1,8 @@ use brk_traversable::Traversable; -use brk_types::{Dollars, Height, StoredF32}; -use vecdb::{LazyVecFrom2, Rw, StorageMode}; +use brk_types::StoredF32; +use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightLast, LazyBinaryFromHeightLast}; +use crate::internal::ComputedFromHeightLast; pub const TIMEFRAME_NAMES: [&str; 4] = ["1d", "1w", "1m", "1y"]; @@ -41,7 +41,7 @@ pub struct RsiChain { pub losses: ComputedFromHeightLast, pub average_gain: ComputedFromHeightLast, pub average_loss: ComputedFromHeightLast, - pub rsi: LazyVecFrom2, + pub rsi: ComputedFromHeightLast, pub rsi_min: ComputedFromHeightLast, pub rsi_max: ComputedFromHeightLast, pub stoch_rsi: ComputedFromHeightLast, @@ -53,20 +53,20 @@ pub struct RsiChain { pub struct MacdChain { pub line: ComputedFromHeightLast, pub signal: ComputedFromHeightLast, - pub histogram: LazyVecFrom2, + pub histogram: ComputedFromHeightLast, } #[derive(Traversable)] pub struct Vecs { pub puell_multiple: ComputedFromHeightLast, - pub nvt: LazyBinaryFromHeightLast, + pub nvt: ComputedFromHeightLast, pub rsi: ByIndicatorTimeframe>, pub stoch_k: ComputedFromHeightLast, pub stoch_d: ComputedFromHeightLast, - pub pi_cycle: LazyBinaryFromHeightLast, + pub pi_cycle: ComputedFromHeightLast, pub macd: ByIndicatorTimeframe>, diff --git a/crates/brk_computer/src/market/lookback/by_period.rs b/crates/brk_computer/src/market/lookback/by_period.rs index 270c13b34..2cc8bf4fb 100644 --- a/crates/brk_computer/src/market/lookback/by_period.rs +++ b/crates/brk_computer/src/market/lookback/by_period.rs @@ -78,6 +78,26 @@ impl ByLookbackPeriod { }) } + pub(crate) fn iter_with_days(&self) -> impl Iterator { + let d = LOOKBACK_PERIOD_DAYS; + [ + (&self._1d, d._1d), + (&self._1w, d._1w), + (&self._1m, d._1m), + (&self._3m, d._3m), + (&self._6m, d._6m), + (&self._1y, d._1y), + (&self._2y, d._2y), + (&self._3y, d._3y), + (&self._4y, d._4y), + (&self._5y, d._5y), + (&self._6y, d._6y), + (&self._8y, d._8y), + (&self._10y, d._10y), + ] + .into_iter() + } + pub(crate) fn iter_mut_with_days(&mut self) -> impl Iterator { let d = LOOKBACK_PERIOD_DAYS; [ @@ -116,39 +136,4 @@ impl ByLookbackPeriod { } } - pub(crate) fn zip_ref<'a, U>(&'a self, other: &'a ByLookbackPeriod) -> ByLookbackPeriod<(&'a T, &'a U)> { - ByLookbackPeriod { - _1d: (&self._1d, &other._1d), - _1w: (&self._1w, &other._1w), - _1m: (&self._1m, &other._1m), - _3m: (&self._3m, &other._3m), - _6m: (&self._6m, &other._6m), - _1y: (&self._1y, &other._1y), - _2y: (&self._2y, &other._2y), - _3y: (&self._3y, &other._3y), - _4y: (&self._4y, &other._4y), - _5y: (&self._5y, &other._5y), - _6y: (&self._6y, &other._6y), - _8y: (&self._8y, &other._8y), - _10y: (&self._10y, &other._10y), - } - } - - pub(crate) fn map U>(self, mut f: F) -> ByLookbackPeriod { - ByLookbackPeriod { - _1d: f(self._1d), - _1w: f(self._1w), - _1m: f(self._1m), - _3m: f(self._3m), - _6m: f(self._6m), - _1y: f(self._1y), - _2y: f(self._2y), - _3y: f(self._3y), - _4y: f(self._4y), - _5y: f(self._5y), - _6y: f(self._6y), - _8y: f(self._8y), - _10y: f(self._10y), - } - } } diff --git a/crates/brk_computer/src/market/lookback/import.rs b/crates/brk_computer/src/market/lookback/import.rs index 4bbb4b150..8b48dc302 100644 --- a/crates/brk_computer/src/market/lookback/import.rs +++ b/crates/brk_computer/src/market/lookback/import.rs @@ -3,12 +3,12 @@ use brk_types::Version; use vecdb::Database; use super::{ByLookbackPeriod, Vecs}; -use crate::{indexes, internal::PriceFromHeight}; +use crate::{indexes, internal::Price}; impl Vecs { pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result { let price_ago = ByLookbackPeriod::try_new(|name, _days| { - PriceFromHeight::forced_import(db, &format!("price_{name}_ago"), version, indexes) + Price::forced_import(db, &format!("price_{name}_ago"), version, indexes) })?; Ok(Self { price_ago }) diff --git a/crates/brk_computer/src/market/mod.rs b/crates/brk_computer/src/market/mod.rs index 7c4693560..bf1fd025e 100644 --- a/crates/brk_computer/src/market/mod.rs +++ b/crates/brk_computer/src/market/mod.rs @@ -31,7 +31,7 @@ pub struct Vecs { pub ath: AthVecs, pub lookback: LookbackVecs, pub returns: ReturnsVecs, - pub volatility: VolatilityVecs, + pub volatility: VolatilityVecs, pub range: RangeVecs, pub moving_average: MovingAverageVecs, pub dca: DcaVecs, diff --git a/crates/brk_computer/src/market/moving_average/compute.rs b/crates/brk_computer/src/market/moving_average/compute.rs index b972f4fa6..97f62133d 100644 --- a/crates/brk_computer/src/market/moving_average/compute.rs +++ b/crates/brk_computer/src/market/moving_average/compute.rs @@ -1,6 +1,6 @@ use brk_error::Result; use brk_types::Dollars; -use vecdb::{Exit, ReadableVec, VecIndex}; +use vecdb::{Exit, ReadableOptionVec, VecIndex}; use super::Vecs; use crate::{ComputeIndexes, blocks, indexes, prices}; @@ -42,7 +42,7 @@ impl Vecs { } let h2d = &indexes.height.day1; - let closes: Vec = prices.usd.close.day1.collect(); + let closes: Vec = prices.usd.close.day1.collect_or_default(); for (ema, period) in [ (&mut self.price_1w_ema, 7), diff --git a/crates/brk_computer/src/market/moving_average/import.rs b/crates/brk_computer/src/market/moving_average/import.rs index a960cb278..5bd53d9b1 100644 --- a/crates/brk_computer/src/market/moving_average/import.rs +++ b/crates/brk_computer/src/market/moving_average/import.rs @@ -5,7 +5,7 @@ use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightRatio, DollarsTimesTenths, LazyPriceFromHeight}, + internal::{ComputedFromHeightRatio, DollarsTimesTenths, Price}, }; impl Vecs { @@ -273,19 +273,19 @@ impl Vecs { )?; let price_200d_sma_source = &price_200d_sma.price.as_ref().unwrap().usd; - let price_200d_sma_x2_4 = LazyPriceFromHeight::from_computed::>( + let price_200d_sma_x2_4 = Price::from_computed::>( "price_200d_sma_x2_4", version, price_200d_sma_source, ); - let price_200d_sma_x0_8 = LazyPriceFromHeight::from_computed::>( + let price_200d_sma_x0_8 = Price::from_computed::>( "price_200d_sma_x0_8", version, price_200d_sma_source, ); let price_350d_sma_source = &price_350d_sma.price.as_ref().unwrap().usd; - let price_350d_sma_x2 = LazyPriceFromHeight::from_computed::>( + let price_350d_sma_x2 = Price::from_computed::>( "price_350d_sma_x2", version, price_350d_sma_source, diff --git a/crates/brk_computer/src/market/moving_average/vecs.rs b/crates/brk_computer/src/market/moving_average/vecs.rs index 2189cbf20..5d158ae5e 100644 --- a/crates/brk_computer/src/market/moving_average/vecs.rs +++ b/crates/brk_computer/src/market/moving_average/vecs.rs @@ -2,7 +2,7 @@ use brk_traversable::Traversable; use brk_types::Dollars; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightRatio, LazyPriceFromHeight}; +use crate::internal::{ComputedFromHeightRatio, LazyFromHeightLast, Price}; /// Simple and exponential moving average metrics #[derive(Traversable)] @@ -41,7 +41,7 @@ pub struct Vecs { pub price_200w_ema: ComputedFromHeightRatio, pub price_4y_ema: ComputedFromHeightRatio, - pub price_200d_sma_x2_4: LazyPriceFromHeight, - pub price_200d_sma_x0_8: LazyPriceFromHeight, - pub price_350d_sma_x2: LazyPriceFromHeight, + pub price_200d_sma_x2_4: Price>, + pub price_200d_sma_x0_8: Price>, + pub price_350d_sma_x2: Price>, } diff --git a/crates/brk_computer/src/market/range/compute.rs b/crates/brk_computer/src/market/range/compute.rs index a022a5746..d8a2fb51b 100644 --- a/crates/brk_computer/src/market/range/compute.rs +++ b/crates/brk_computer/src/market/range/compute.rs @@ -18,56 +18,56 @@ impl Vecs { ) -> Result<()> { let price = &prices.usd.price; - self.price_1w_min.height.compute_rolling_min_from_starts( + self.price_1w_min.usd.height.compute_rolling_min_from_starts( starting_indexes.height, &blocks.count.height_1w_ago, price, exit, )?; - self.price_1w_max.height.compute_rolling_max_from_starts( + self.price_1w_max.usd.height.compute_rolling_max_from_starts( starting_indexes.height, &blocks.count.height_1w_ago, price, exit, )?; - self.price_2w_min.height.compute_rolling_min_from_starts( + self.price_2w_min.usd.height.compute_rolling_min_from_starts( starting_indexes.height, &blocks.count.height_2w_ago, price, exit, )?; - self.price_2w_max.height.compute_rolling_max_from_starts( + self.price_2w_max.usd.height.compute_rolling_max_from_starts( starting_indexes.height, &blocks.count.height_2w_ago, price, exit, )?; - self.price_1m_min.height.compute_rolling_min_from_starts( + self.price_1m_min.usd.height.compute_rolling_min_from_starts( starting_indexes.height, &blocks.count.height_1m_ago, price, exit, )?; - self.price_1m_max.height.compute_rolling_max_from_starts( + self.price_1m_max.usd.height.compute_rolling_max_from_starts( starting_indexes.height, &blocks.count.height_1m_ago, price, exit, )?; - self.price_1y_min.height.compute_rolling_min_from_starts( + self.price_1y_min.usd.height.compute_rolling_min_from_starts( starting_indexes.height, &blocks.count.height_1y_ago, price, exit, )?; - self.price_1y_max.height.compute_rolling_max_from_starts( + self.price_1y_max.usd.height.compute_rolling_max_from_starts( starting_indexes.height, &blocks.count.height_1y_ago, price, @@ -107,8 +107,8 @@ impl Vecs { self.price_2w_choppiness_index.height.compute_transform3( starting_indexes.height, &self.price_true_range_2w_sum.height, - &self.price_2w_max.height, - &self.price_2w_min.height, + &self.price_2w_max.usd.height, + &self.price_2w_min.usd.height, |(h, tr_sum, max, min, ..)| { let range = *max - *min; let ci = if range > 0.0 { diff --git a/crates/brk_computer/src/market/range/import.rs b/crates/brk_computer/src/market/range/import.rs index 1e406ed32..169001f25 100644 --- a/crates/brk_computer/src/market/range/import.rs +++ b/crates/brk_computer/src/market/range/import.rs @@ -3,21 +3,21 @@ use brk_types::Version; use vecdb::Database; use super::Vecs; -use crate::{indexes, internal::{ComputedFromHeightLast, PriceFromHeight}}; +use crate::{indexes, internal::{ComputedFromHeightLast, Price}}; impl Vecs { pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result { let v1 = Version::ONE; Ok(Self { - price_1w_min: PriceFromHeight::forced_import(db, "price_1w_min", version + v1, indexes)?, - price_1w_max: PriceFromHeight::forced_import(db, "price_1w_max", version + v1, indexes)?, - price_2w_min: PriceFromHeight::forced_import(db, "price_2w_min", version + v1, indexes)?, - price_2w_max: PriceFromHeight::forced_import(db, "price_2w_max", version + v1, indexes)?, - price_1m_min: PriceFromHeight::forced_import(db, "price_1m_min", version + v1, indexes)?, - price_1m_max: PriceFromHeight::forced_import(db, "price_1m_max", version + v1, indexes)?, - price_1y_min: PriceFromHeight::forced_import(db, "price_1y_min", version + v1, indexes)?, - price_1y_max: PriceFromHeight::forced_import(db, "price_1y_max", version + v1, indexes)?, + price_1w_min: Price::forced_import(db, "price_1w_min", version + v1, indexes)?, + price_1w_max: Price::forced_import(db, "price_1w_max", version + v1, indexes)?, + price_2w_min: Price::forced_import(db, "price_2w_min", version + v1, indexes)?, + price_2w_max: Price::forced_import(db, "price_2w_max", version + v1, indexes)?, + price_1m_min: Price::forced_import(db, "price_1m_min", version + v1, indexes)?, + price_1m_max: Price::forced_import(db, "price_1m_max", version + v1, indexes)?, + price_1y_min: Price::forced_import(db, "price_1y_min", version + v1, indexes)?, + price_1y_max: Price::forced_import(db, "price_1y_max", version + v1, indexes)?, price_true_range: ComputedFromHeightLast::forced_import( db, "price_true_range", version + v1, indexes, )?, diff --git a/crates/brk_computer/src/market/returns/compute.rs b/crates/brk_computer/src/market/returns/compute.rs index 0e8b58865..43118200a 100644 --- a/crates/brk_computer/src/market/returns/compute.rs +++ b/crates/brk_computer/src/market/returns/compute.rs @@ -1,18 +1,34 @@ use brk_error::Result; -use brk_types::StoredF32; -use vecdb::{Exit, ReadableVec}; +use brk_types::{Dollars, StoredF32}; +use vecdb::{Exit, ReadableOptionVec}; use super::Vecs; -use crate::{ComputeIndexes, blocks, indexes}; +use crate::{ComputeIndexes, blocks, indexes, internal::PercentageDiffDollars, market::lookback, prices}; impl Vecs { pub(crate) fn compute( &mut self, indexes: &indexes::Vecs, + prices: &prices::Vecs, blocks: &blocks::Vecs, + lookback: &lookback::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { + // Compute price returns at height level + for ((returns, _), (lookback_price, _)) in self + .price_returns + .iter_mut_with_days() + .zip(lookback.price_ago.iter_with_days()) + { + returns.compute_binary::( + starting_indexes.height, + &prices.usd.price, + &lookback_price.usd.height, + exit, + )?; + } + // CAGR computed from returns (2y+ periods only) let h2d = &indexes.height.day1; let price_returns_dca = self.price_returns.as_dca_period(); @@ -28,7 +44,7 @@ impl Vecs { cached_di = Some(di); cached_val = StoredF32::from( returns.day1 - .collect_one(di) + .collect_one_flat(di) .map(|r| ((*r / 100.0 + 1.0).powf(1.0 / years) - 1.0) * 100.0) .unwrap_or(0.0) ); diff --git a/crates/brk_computer/src/market/returns/import.rs b/crates/brk_computer/src/market/returns/import.rs index f59cab8aa..97130c97d 100644 --- a/crates/brk_computer/src/market/returns/import.rs +++ b/crates/brk_computer/src/market/returns/import.rs @@ -1,17 +1,16 @@ use brk_error::Result; use brk_types::Version; -use vecdb::{Database, EagerVec, ImportableVec, ReadableCloneableVec}; +use vecdb::{Database, EagerVec, ImportableVec}; -use super::super::lookback::{self, LOOKBACK_PERIOD_NAMES}; +use super::super::lookback::ByLookbackPeriod; use super::Vecs; use crate::{ indexes, internal::{ - ComputedFromHeightLast, ComputedFromHeightStdDev, LazyBinaryFromHeightLast, - PercentageDiffDollars, StandardDeviationVecsOptions, + ComputedFromHeightLast, ComputedFromHeightStdDev, + StandardDeviationVecsOptions, }, market::dca::ByDcaCagr, - prices, }; impl Vecs { @@ -19,26 +18,17 @@ impl Vecs { db: &Database, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, - lookback: &lookback::Vecs, ) -> Result { let v1 = Version::ONE; - let price_returns = - LOOKBACK_PERIOD_NAMES - .zip_ref(&lookback.price_ago) - .map(|(name, price_ago)| { - LazyBinaryFromHeightLast::from_height_and_derived_last::< - PercentageDiffDollars, - >( - &format!("{name}_price_returns"), - version, - prices.usd.price.read_only_boxed_clone(), - price_ago.height.read_only_boxed_clone(), - &prices.usd.close, - &price_ago.rest, - ) - }); + let price_returns = ByLookbackPeriod::try_new(|name, _days| { + ComputedFromHeightLast::forced_import( + db, + &format!("{name}_price_returns"), + version, + indexes, + ) + })?; // CAGR (computed, 2y+ only) let cagr = ByDcaCagr::try_new(|name, _days| { @@ -52,7 +42,6 @@ impl Vecs { version + v1, indexes, StandardDeviationVecsOptions::default(), - None, )?; let _1d_returns_1m_sd = ComputedFromHeightStdDev::forced_import( db, @@ -61,7 +50,6 @@ impl Vecs { version + v1, indexes, StandardDeviationVecsOptions::default(), - None, )?; let _1d_returns_1y_sd = ComputedFromHeightStdDev::forced_import( db, @@ -70,7 +58,6 @@ impl Vecs { version + v1, indexes, StandardDeviationVecsOptions::default(), - None, )?; let downside_returns = EagerVec::forced_import(db, "downside_returns", version)?; @@ -81,7 +68,6 @@ impl Vecs { version + v1, indexes, StandardDeviationVecsOptions::default(), - None, )?; let downside_1m_sd = ComputedFromHeightStdDev::forced_import( db, @@ -90,7 +76,6 @@ impl Vecs { version + v1, indexes, StandardDeviationVecsOptions::default(), - None, )?; let downside_1y_sd = ComputedFromHeightStdDev::forced_import( db, @@ -99,7 +84,6 @@ impl Vecs { version + v1, indexes, StandardDeviationVecsOptions::default(), - None, )?; Ok(Self { diff --git a/crates/brk_computer/src/market/returns/vecs.rs b/crates/brk_computer/src/market/returns/vecs.rs index e041192fe..272a5ddee 100644 --- a/crates/brk_computer/src/market/returns/vecs.rs +++ b/crates/brk_computer/src/market/returns/vecs.rs @@ -1,16 +1,16 @@ use brk_traversable::Traversable; -use brk_types::{Dollars, Height, StoredF32}; +use brk_types::{Height, StoredF32}; use vecdb::{EagerVec, PcoVec, Rw, StorageMode}; use crate::{ - internal::{ComputedFromHeightLast, ComputedFromHeightStdDev, LazyBinaryFromHeightLast}, + internal::{ComputedFromHeightLast, ComputedFromHeightStdDev}, market::{dca::ByDcaCagr, lookback::ByLookbackPeriod}, }; /// Price returns, CAGR, and returns standard deviation metrics #[derive(Traversable)] pub struct Vecs { - pub price_returns: ByLookbackPeriod>, + pub price_returns: ByLookbackPeriod>, // CAGR (computed from returns, 2y+ only) pub cagr: ByDcaCagr>, diff --git a/crates/brk_computer/src/market/volatility/compute.rs b/crates/brk_computer/src/market/volatility/compute.rs new file mode 100644 index 000000000..926e804c0 --- /dev/null +++ b/crates/brk_computer/src/market/volatility/compute.rs @@ -0,0 +1,85 @@ +use brk_error::Result; +use brk_types::{Height, StoredF32}; +use vecdb::Exit; + +use super::super::returns; +use super::Vecs; + +impl Vecs { + pub(crate) fn compute( + &mut self, + returns: &returns::Vecs, + starting_indexes_height: Height, + exit: &Exit, + ) -> Result<()> { + // Sharpe ratios: returns / volatility + self.sharpe_1w.height.compute_transform2( + starting_indexes_height, + &returns.price_returns._1w.height, + &self.price_1w_volatility.height, + |(h, ret, vol, ..)| { + let ratio = if *vol == 0.0 { 0.0 } else { *ret / *vol }; + (h, StoredF32::from(ratio)) + }, + exit, + )?; + + self.sharpe_1m.height.compute_transform2( + starting_indexes_height, + &returns.price_returns._1m.height, + &self.price_1m_volatility.height, + |(h, ret, vol, ..)| { + let ratio = if *vol == 0.0 { 0.0 } else { *ret / *vol }; + (h, StoredF32::from(ratio)) + }, + exit, + )?; + + self.sharpe_1y.height.compute_transform2( + starting_indexes_height, + &returns.price_returns._1y.height, + &self.price_1y_volatility.height, + |(h, ret, vol, ..)| { + let ratio = if *vol == 0.0 { 0.0 } else { *ret / *vol }; + (h, StoredF32::from(ratio)) + }, + exit, + )?; + + // Sortino ratios: returns / downside volatility + self.sortino_1w.height.compute_transform2( + starting_indexes_height, + &returns.price_returns._1w.height, + &returns.downside_1w_sd.sd.height, + |(h, ret, vol, ..)| { + let ratio = if *vol == 0.0 { 0.0 } else { *ret / *vol }; + (h, StoredF32::from(ratio)) + }, + exit, + )?; + + self.sortino_1m.height.compute_transform2( + starting_indexes_height, + &returns.price_returns._1m.height, + &returns.downside_1m_sd.sd.height, + |(h, ret, vol, ..)| { + let ratio = if *vol == 0.0 { 0.0 } else { *ret / *vol }; + (h, StoredF32::from(ratio)) + }, + exit, + )?; + + self.sortino_1y.height.compute_transform2( + starting_indexes_height, + &returns.price_returns._1y.height, + &returns.downside_1y_sd.sd.height, + |(h, ret, vol, ..)| { + let ratio = if *vol == 0.0 { 0.0 } else { *ret / *vol }; + (h, StoredF32::from(ratio)) + }, + exit, + )?; + + Ok(()) + } +} diff --git a/crates/brk_computer/src/market/volatility/import.rs b/crates/brk_computer/src/market/volatility/import.rs index 9065c2d5b..a2554028f 100644 --- a/crates/brk_computer/src/market/volatility/import.rs +++ b/crates/brk_computer/src/market/volatility/import.rs @@ -1,14 +1,22 @@ +use brk_error::Result; use brk_types::Version; -use vecdb::{ReadableCloneableVec, LazyVecFrom2}; +use vecdb::{Database, ReadableCloneableVec}; use super::super::returns; use super::Vecs; +use crate::indexes; use crate::internal::{ - LazyFromHeightLast, RatioF32, StoredF32TimesSqrt7, StoredF32TimesSqrt30, StoredF32TimesSqrt365, + ComputedFromHeightLast, LazyFromHeightLast, StoredF32TimesSqrt7, StoredF32TimesSqrt30, + StoredF32TimesSqrt365, }; impl Vecs { - pub(crate) fn forced_import(version: Version, returns: &returns::Vecs) -> Self { + pub(crate) fn forced_import( + db: &Database, + version: Version, + indexes: &indexes::Vecs, + returns: &returns::Vecs, + ) -> Result { let v2 = Version::TWO; let price_1w_volatility = LazyFromHeightLast::from_computed::( @@ -32,50 +40,21 @@ impl Vecs { &returns._1d_returns_1y_sd.sd, ); - let sharpe_1w = LazyVecFrom2::transformed::( - "sharpe_1w", - version + v2, - returns.price_returns._1w.height.read_only_boxed_clone(), - price_1w_volatility.height.read_only_boxed_clone(), - ); + let sharpe_1w = + ComputedFromHeightLast::forced_import(db, "sharpe_1w", version + v2, indexes)?; + let sharpe_1m = + ComputedFromHeightLast::forced_import(db, "sharpe_1m", version + v2, indexes)?; + let sharpe_1y = + ComputedFromHeightLast::forced_import(db, "sharpe_1y", version + v2, indexes)?; - let sharpe_1m = LazyVecFrom2::transformed::( - "sharpe_1m", - version + v2, - returns.price_returns._1m.height.read_only_boxed_clone(), - price_1m_volatility.height.read_only_boxed_clone(), - ); + let sortino_1w = + ComputedFromHeightLast::forced_import(db, "sortino_1w", version + v2, indexes)?; + let sortino_1m = + ComputedFromHeightLast::forced_import(db, "sortino_1m", version + v2, indexes)?; + let sortino_1y = + ComputedFromHeightLast::forced_import(db, "sortino_1y", version + v2, indexes)?; - let sharpe_1y = LazyVecFrom2::transformed::( - "sharpe_1y", - version + v2, - returns.price_returns._1y.height.read_only_boxed_clone(), - price_1y_volatility.height.read_only_boxed_clone(), - ); - - // Sortino ratio = returns / downside volatility - let sortino_1w = LazyVecFrom2::transformed::( - "sortino_1w", - version + v2, - returns.price_returns._1w.height.read_only_boxed_clone(), - returns.downside_1w_sd.sd.height.read_only_boxed_clone(), - ); - - let sortino_1m = LazyVecFrom2::transformed::( - "sortino_1m", - version + v2, - returns.price_returns._1m.height.read_only_boxed_clone(), - returns.downside_1m_sd.sd.height.read_only_boxed_clone(), - ); - - let sortino_1y = LazyVecFrom2::transformed::( - "sortino_1y", - version + v2, - returns.price_returns._1y.height.read_only_boxed_clone(), - returns.downside_1y_sd.sd.height.read_only_boxed_clone(), - ); - - Self { + Ok(Self { price_1w_volatility, price_1m_volatility, price_1y_volatility, @@ -85,6 +64,6 @@ impl Vecs { sortino_1w, sortino_1m, sortino_1y, - } + }) } } diff --git a/crates/brk_computer/src/market/volatility/mod.rs b/crates/brk_computer/src/market/volatility/mod.rs index f8623047a..1136f9ebd 100644 --- a/crates/brk_computer/src/market/volatility/mod.rs +++ b/crates/brk_computer/src/market/volatility/mod.rs @@ -1,3 +1,4 @@ +mod compute; mod import; mod vecs; diff --git a/crates/brk_computer/src/market/volatility/vecs.rs b/crates/brk_computer/src/market/volatility/vecs.rs index 1808b2703..b5e5100c2 100644 --- a/crates/brk_computer/src/market/volatility/vecs.rs +++ b/crates/brk_computer/src/market/volatility/vecs.rs @@ -1,21 +1,22 @@ use brk_traversable::Traversable; -use brk_types::{Height, StoredF32}; -use vecdb::LazyVecFrom2; +use vecdb::{Rw, StorageMode}; -use crate::internal::LazyFromHeightLast; +use crate::internal::{ComputedFromHeightLast, LazyFromHeightLast}; + +use brk_types::StoredF32; /// Price volatility metrics (derived from returns standard deviation) -#[derive(Clone, Traversable)] -pub struct Vecs { +#[derive(Traversable)] +pub struct Vecs { pub price_1w_volatility: LazyFromHeightLast, pub price_1m_volatility: LazyFromHeightLast, pub price_1y_volatility: LazyFromHeightLast, - pub sharpe_1w: LazyVecFrom2, - pub sharpe_1m: LazyVecFrom2, - pub sharpe_1y: LazyVecFrom2, + pub sharpe_1w: ComputedFromHeightLast, + pub sharpe_1m: ComputedFromHeightLast, + pub sharpe_1y: ComputedFromHeightLast, - pub sortino_1w: LazyVecFrom2, - pub sortino_1m: LazyVecFrom2, - pub sortino_1y: LazyVecFrom2, + pub sortino_1w: ComputedFromHeightLast, + pub sortino_1m: ComputedFromHeightLast, + pub sortino_1y: ComputedFromHeightLast, } diff --git a/crates/brk_computer/src/mining/compute.rs b/crates/brk_computer/src/mining/compute.rs index 5f9394f4d..b5c0de1e7 100644 --- a/crates/brk_computer/src/mining/compute.rs +++ b/crates/brk_computer/src/mining/compute.rs @@ -3,15 +3,17 @@ use brk_indexer::Indexer; use vecdb::Exit; use super::Vecs; -use crate::{ComputeIndexes, blocks, indexes, transactions}; +use crate::{ComputeIndexes, blocks, indexes, prices, transactions}; impl Vecs { + #[allow(clippy::too_many_arguments)] pub(crate) fn compute( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, blocks: &blocks::Vecs, transactions: &transactions::Vecs, + prices: &prices::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { @@ -21,6 +23,7 @@ impl Vecs { indexes, &blocks.count, &transactions.fees, + prices, starting_indexes, exit, )?; diff --git a/crates/brk_computer/src/mining/import.rs b/crates/brk_computer/src/mining/import.rs index c28ea19f2..472ee425d 100644 --- a/crates/brk_computer/src/mining/import.rs +++ b/crates/brk_computer/src/mining/import.rs @@ -5,7 +5,7 @@ use brk_traversable::Traversable; use brk_types::Version; use vecdb::{Database, PAGE_SIZE}; -use crate::{indexes, prices}; +use crate::indexes; use super::{HashrateVecs, RewardsVecs, Vecs}; @@ -14,14 +14,13 @@ impl Vecs { parent_path: &Path, parent_version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { let db = Database::open(&parent_path.join(super::DB_NAME))?; db.set_min_len(PAGE_SIZE * 50_000_000)?; let version = parent_version; - let rewards = RewardsVecs::forced_import(&db, version, indexes, prices)?; + let rewards = RewardsVecs::forced_import(&db, version, indexes)?; let hashrate = HashrateVecs::forced_import(&db, version, indexes)?; let this = Self { diff --git a/crates/brk_computer/src/mining/rewards/compute.rs b/crates/brk_computer/src/mining/rewards/compute.rs index 13712b61e..015e87749 100644 --- a/crates/brk_computer/src/mining/rewards/compute.rs +++ b/crates/brk_computer/src/mining/rewards/compute.rs @@ -4,19 +4,23 @@ use brk_types::{CheckedSub, HalvingEpoch, Sats, StoredF32}; use vecdb::{Exit, ReadableVec, VecIndex}; use super::Vecs; -use crate::{ComputeIndexes, blocks, indexes, transactions}; +use crate::{ComputeIndexes, blocks, indexes, prices, transactions}; impl Vecs { + #[allow(clippy::too_many_arguments)] pub(crate) fn compute( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, count_vecs: &blocks::CountVecs, transactions_fees: &transactions::FeesVecs, + prices: &prices::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.coinbase.compute(starting_indexes, exit, |vec| { + let window_starts = count_vecs.window_starts(); + + self.coinbase.compute(starting_indexes.height, &window_starts, prices, exit, |vec| { // Cursors avoid per-height PcoVec page decompression for the // tx-indexed lookups. Coinbase txindex values are strictly // increasing, so the cursors only advance forward. @@ -48,7 +52,6 @@ impl Vecs { Ok(()) })?; - let window_starts = count_vecs.window_starts(); self.coinbase_sum.compute_rolling_sum( starting_indexes.height, &window_starts, @@ -57,7 +60,7 @@ impl Vecs { exit, )?; - let fee_sats_source = transactions_fees.fee.sum_cum.sum.inner(); + let fee_sats_source = transactions_fees.fee.sum_cumulative.sum.inner(); let fee_usd_source = &transactions_fees.fee_usd_sum; self.fee_sum.compute_rolling_sum( starting_indexes.height, @@ -67,11 +70,11 @@ impl Vecs { exit, )?; - self.subsidy.compute(starting_indexes, exit, |vec| { + self.subsidy.compute(starting_indexes.height, &window_starts, prices, exit, |vec| { vec.compute_transform2( starting_indexes.height, &self.coinbase.sats.height, - transactions_fees.fee.sum_cum.sum.inner(), + transactions_fees.fee.sum_cumulative.sum.inner(), |(height, coinbase, fees, ..)| { ( height, @@ -87,7 +90,7 @@ impl Vecs { })?; self.unclaimed_rewards - .compute(starting_indexes, exit, |vec| { + .compute(starting_indexes.height, &window_starts, prices, exit, |vec| { vec.compute_transform( starting_indexes.height, &self.subsidy.sats.height, @@ -104,8 +107,8 @@ impl Vecs { // All-time cumulative fee dominance self.fee_dominance.height.compute_percentage( starting_indexes.height, - transactions_fees.fee.sum_cum.cumulative.inner(), - self.coinbase.sats.rest.height_cumulative.inner(), + transactions_fees.fee.sum_cumulative.cumulative.inner(), + &self.coinbase.sats.cumulative.height, exit, )?; @@ -138,8 +141,8 @@ impl Vecs { // All-time cumulative subsidy dominance self.subsidy_dominance.height.compute_percentage( starting_indexes.height, - self.subsidy.sats.rest.height_cumulative.inner(), - self.coinbase.sats.rest.height_cumulative.inner(), + &self.subsidy.sats.cumulative.height, + &self.coinbase.sats.cumulative.height, exit, )?; diff --git a/crates/brk_computer/src/mining/rewards/import.rs b/crates/brk_computer/src/mining/rewards/import.rs index 56b78b81a..9c09b3128 100644 --- a/crates/brk_computer/src/mining/rewards/import.rs +++ b/crates/brk_computer/src/mining/rewards/import.rs @@ -5,8 +5,7 @@ use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightLast, StoredValueRollingWindows, ValueFromHeightFull, ValueFromHeightSumCum}, - prices, + internal::{ComputedFromHeightLast, StoredValueRollingWindows, ValueFromHeightFull, ValueFromHeightSumCumulative}, }; impl Vecs { @@ -14,19 +13,17 @@ impl Vecs { db: &Database, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { Ok(Self { coinbase_sum: StoredValueRollingWindows::forced_import(db, "coinbase_sum", version, indexes)?, fee_sum: StoredValueRollingWindows::forced_import(db, "fee_sum", version, indexes)?, - coinbase: ValueFromHeightFull::forced_import(db, "coinbase", version, indexes, prices)?, - subsidy: ValueFromHeightFull::forced_import(db, "subsidy", version, indexes, prices)?, - unclaimed_rewards: ValueFromHeightSumCum::forced_import( + coinbase: ValueFromHeightFull::forced_import(db, "coinbase", version, indexes)?, + subsidy: ValueFromHeightFull::forced_import(db, "subsidy", version, indexes)?, + unclaimed_rewards: ValueFromHeightSumCumulative::forced_import( db, "unclaimed_rewards", version, indexes, - prices, )?, fee_dominance: ComputedFromHeightLast::forced_import( db, diff --git a/crates/brk_computer/src/mining/rewards/vecs.rs b/crates/brk_computer/src/mining/rewards/vecs.rs index 1119b2aa9..2bba9788c 100644 --- a/crates/brk_computer/src/mining/rewards/vecs.rs +++ b/crates/brk_computer/src/mining/rewards/vecs.rs @@ -2,7 +2,7 @@ use brk_traversable::Traversable; use brk_types::{Dollars, StoredF32}; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightLast, StoredValueRollingWindows, ValueFromHeightFull, ValueFromHeightSumCum}; +use crate::internal::{ComputedFromHeightLast, StoredValueRollingWindows, ValueFromHeightFull, ValueFromHeightSumCumulative}; /// Coinbase/subsidy/rewards metrics #[derive(Traversable)] @@ -11,7 +11,7 @@ pub struct Vecs { pub fee_sum: StoredValueRollingWindows, pub coinbase: ValueFromHeightFull, pub subsidy: ValueFromHeightFull, - pub unclaimed_rewards: ValueFromHeightSumCum, + pub unclaimed_rewards: ValueFromHeightSumCumulative, pub fee_dominance: ComputedFromHeightLast, pub fee_dominance_24h: ComputedFromHeightLast, pub fee_dominance_7d: ComputedFromHeightLast, diff --git a/crates/brk_computer/src/outputs/count/compute.rs b/crates/brk_computer/src/outputs/count/compute.rs index 90eaca03e..33d663f38 100644 --- a/crates/brk_computer/src/outputs/count/compute.rs +++ b/crates/brk_computer/src/outputs/count/compute.rs @@ -31,14 +31,14 @@ impl Vecs { self.total_count_rolling.compute( starting_indexes.height, &window_starts, - self.total_count.sum_cum.sum.inner(), + self.total_count.sum_cumulative.sum.inner(), exit, )?; self.utxo_count.height.compute_transform3( starting_indexes.height, - &*self.total_count.sum_cum.cumulative, - &*inputs_count.height.sum_cum.cumulative, + &*self.total_count.sum_cumulative.cumulative, + &*inputs_count.height.sum_cumulative.cumulative, &scripts_count.opreturn.cumulative.height, |(h, output_count, input_count, opreturn_count, ..)| { let block_count = u64::from(h + 1_usize); diff --git a/crates/brk_computer/src/pools/mod.rs b/crates/brk_computer/src/pools/mod.rs index bd9190247..a31e3f0a2 100644 --- a/crates/brk_computer/src/pools/mod.rs +++ b/crates/brk_computer/src/pools/mod.rs @@ -7,8 +7,8 @@ use brk_traversable::Traversable; use brk_types::{Address, AddressBytes, Height, OutputType, PoolSlug, Pools, TxOutIndex, pools}; use rayon::prelude::*; use vecdb::{ - AnyStoredVec, AnyVec, BytesVec, Database, Exit, WritableVec, ImportableVec, ReadableVec, - PAGE_SIZE, Rw, StorageMode, VecIndex, Version, + AnyStoredVec, AnyVec, BytesVec, Database, Exit, ImportableVec, PAGE_SIZE, ReadableVec, Rw, + StorageMode, VecIndex, Version, WritableVec, }; mod vecs; @@ -35,10 +35,6 @@ impl Vecs { parent_path: &Path, parent_version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, - blocks: &blocks::Vecs, - mining: &mining::Vecs, - transactions: &transactions::Vecs, ) -> Result { let db = Database::open(&parent_path.join(DB_NAME))?; db.set_min_len(PAGE_SIZE * 1_000_000)?; @@ -51,17 +47,8 @@ impl Vecs { vecs: pools .iter() .map(|pool| { - vecs::Vecs::forced_import( - &db, - pool.slug, - version, - indexes, - prices, - blocks, - mining, - transactions, - ) - .map(|vecs| (pool.slug, vecs)) + vecs::Vecs::forced_import(&db, pool.slug, version, indexes) + .map(|vecs| (pool.slug, vecs)) }) .collect::>>()?, pools, @@ -78,25 +65,42 @@ impl Vecs { Ok(this) } + #[allow(clippy::too_many_arguments)] pub(crate) fn compute( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, blocks: &blocks::Vecs, + prices: &prices::Vecs, + mining: &mining::Vecs, + transactions: &transactions::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.compute_(indexer, indexes, blocks, starting_indexes, exit)?; + self.compute_( + indexer, + indexes, + blocks, + prices, + mining, + transactions, + starting_indexes, + exit, + )?; let _lock = exit.lock(); self.db.compact()?; Ok(()) } + #[allow(clippy::too_many_arguments)] fn compute_( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, blocks: &blocks::Vecs, + prices: &prices::Vecs, + mining: &mining::Vecs, + transactions: &transactions::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { @@ -107,6 +111,9 @@ impl Vecs { starting_indexes, &self.height_to_pool, blocks, + prices, + mining, + transactions, exit, ) })?; @@ -128,14 +135,11 @@ impl Vecs { indexer.vecs.transactions.first_txoutindex.reader(); let txoutindex_to_outputtype_reader = indexer.vecs.outputs.outputtype.reader(); let txoutindex_to_typeindex_reader = indexer.vecs.outputs.typeindex.reader(); - let p2pk65addressindex_to_p2pk65bytes_reader = - indexer.vecs.addresses.p2pk65bytes.reader(); - let p2pk33addressindex_to_p2pk33bytes_reader = - indexer.vecs.addresses.p2pk33bytes.reader(); + let p2pk65addressindex_to_p2pk65bytes_reader = indexer.vecs.addresses.p2pk65bytes.reader(); + let p2pk33addressindex_to_p2pk33bytes_reader = indexer.vecs.addresses.p2pk33bytes.reader(); let p2pkhaddressindex_to_p2pkhbytes_reader = indexer.vecs.addresses.p2pkhbytes.reader(); let p2shaddressindex_to_p2shbytes_reader = indexer.vecs.addresses.p2shbytes.reader(); - let p2wpkhaddressindex_to_p2wpkhbytes_reader = - indexer.vecs.addresses.p2wpkhbytes.reader(); + let p2wpkhaddressindex_to_p2wpkhbytes_reader = indexer.vecs.addresses.p2wpkhbytes.reader(); let p2wshaddressindex_to_p2wshbytes_reader = indexer.vecs.addresses.p2wshbytes.reader(); let p2traddressindex_to_p2trbytes_reader = indexer.vecs.addresses.p2trbytes.reader(); let p2aaddressindex_to_p2abytes_reader = indexer.vecs.addresses.p2abytes.reader(); diff --git a/crates/brk_computer/src/pools/vecs.rs b/crates/brk_computer/src/pools/vecs.rs index 2c1ac824b..97244a188 100644 --- a/crates/brk_computer/src/pools/vecs.rs +++ b/crates/brk_computer/src/pools/vecs.rs @@ -1,14 +1,14 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Height, PoolSlug, Sats, StoredF32, StoredU16, StoredU32}; -use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, Version}; +use brk_types::{Height, PoolSlug, StoredF32, StoredU16, StoredU32}; +use vecdb::{BinaryTransform, Database, Exit, ReadableVec, Rw, StorageMode, Version}; use crate::{ blocks, indexes::{self, ComputeIndexes}, internal::{ - ComputedFromHeightLast, ComputedFromHeightSumCum, LazyBinaryFromHeightLast, - LazyValueFromHeightSumCum, MaskSats, PercentageU32F32, + ComputedFromHeightCumulativeSum, ComputedFromHeightLast, MaskSats, PercentageU32F32, + ValueFromHeightSumCumulative, }, mining, prices, transactions, }; @@ -17,41 +17,36 @@ use crate::{ pub struct Vecs { slug: PoolSlug, - pub blocks_mined: ComputedFromHeightSumCum, + pub blocks_mined: ComputedFromHeightCumulativeSum, pub blocks_mined_24h_sum: ComputedFromHeightLast, pub blocks_mined_1w_sum: ComputedFromHeightLast, pub blocks_mined_1m_sum: ComputedFromHeightLast, pub blocks_mined_1y_sum: ComputedFromHeightLast, - pub subsidy: LazyValueFromHeightSumCum, - pub fee: LazyValueFromHeightSumCum, - pub coinbase: LazyValueFromHeightSumCum, - pub dominance: LazyBinaryFromHeightLast, + pub subsidy: ValueFromHeightSumCumulative, + pub fee: ValueFromHeightSumCumulative, + pub coinbase: ValueFromHeightSumCumulative, + pub dominance: ComputedFromHeightLast, - pub dominance_24h: LazyBinaryFromHeightLast, - pub dominance_1w: LazyBinaryFromHeightLast, - pub dominance_1m: LazyBinaryFromHeightLast, - pub dominance_1y: LazyBinaryFromHeightLast, + pub dominance_24h: ComputedFromHeightLast, + pub dominance_1w: ComputedFromHeightLast, + pub dominance_1m: ComputedFromHeightLast, + pub dominance_1y: ComputedFromHeightLast, pub blocks_since_block: ComputedFromHeightLast, pub days_since_block: ComputedFromHeightLast, } impl Vecs { - #[allow(clippy::too_many_arguments)] pub(crate) fn forced_import( db: &Database, slug: PoolSlug, parent_version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, - blocks: &blocks::Vecs, - mining: &mining::Vecs, - transactions: &transactions::Vecs, ) -> Result { let suffix = |s: &str| format!("{}_{s}", slug); let version = parent_version; let blocks_mined = - ComputedFromHeightSumCum::forced_import(db, &suffix("blocks_mined"), version, indexes)?; + ComputedFromHeightCumulativeSum::forced_import(db, &suffix("blocks_mined"), version, indexes)?; let blocks_mined_24h_sum = ComputedFromHeightLast::forced_import( db, @@ -78,67 +73,31 @@ impl Vecs { indexes, )?; - let subsidy = LazyValueFromHeightSumCum::forced_import::( - db, - &suffix("subsidy"), - version, - indexes, - blocks_mined.height.read_only_boxed_clone(), - mining.rewards.subsidy.sats.height.read_only_boxed_clone(), - prices, - )?; + let subsidy = + ValueFromHeightSumCumulative::forced_import(db, &suffix("subsidy"), version, indexes)?; - let fee = LazyValueFromHeightSumCum::forced_import::( - db, - &suffix("fee"), - version, - indexes, - blocks_mined.height.read_only_boxed_clone(), - transactions.fees.fee.boxed_sum(), - prices, - )?; + let fee = ValueFromHeightSumCumulative::forced_import(db, &suffix("fee"), version, indexes)?; - let coinbase = LazyValueFromHeightSumCum::forced_import::( - db, - &suffix("coinbase"), - version, - indexes, - blocks_mined.height.read_only_boxed_clone(), - mining.rewards.coinbase.sats.height.read_only_boxed_clone(), - prices, - )?; + let coinbase = + ValueFromHeightSumCumulative::forced_import(db, &suffix("coinbase"), version, indexes)?; + + let dominance = + ComputedFromHeightLast::forced_import(db, &suffix("dominance"), version, indexes)?; + let dominance_24h = + ComputedFromHeightLast::forced_import(db, &suffix("dominance_24h"), version, indexes)?; + let dominance_1w = + ComputedFromHeightLast::forced_import(db, &suffix("dominance_1w"), version, indexes)?; + let dominance_1m = + ComputedFromHeightLast::forced_import(db, &suffix("dominance_1m"), version, indexes)?; + let dominance_1y = + ComputedFromHeightLast::forced_import(db, &suffix("dominance_1y"), version, indexes)?; Ok(Self { - dominance: LazyBinaryFromHeightLast::from_computed_sum_cum::( - &suffix("dominance"), - version, - &blocks_mined, - &blocks.count.block_count, - ), - dominance_24h: LazyBinaryFromHeightLast::from_computed_last::( - &suffix("dominance_24h"), - version, - &blocks_mined_24h_sum, - &blocks.count.block_count_sum._24h, - ), - dominance_1w: LazyBinaryFromHeightLast::from_computed_last::( - &suffix("dominance_1w"), - version, - &blocks_mined_1w_sum, - &blocks.count.block_count_sum._7d, - ), - dominance_1m: LazyBinaryFromHeightLast::from_computed_last::( - &suffix("dominance_1m"), - version, - &blocks_mined_1m_sum, - &blocks.count.block_count_sum._30d, - ), - dominance_1y: LazyBinaryFromHeightLast::from_computed_last::( - &suffix("dominance_1y"), - version, - &blocks_mined_1y_sum, - &blocks.count.block_count_sum._1y, - ), + dominance, + dominance_24h, + dominance_1w, + dominance_1m, + dominance_1y, slug, blocks_mined, blocks_mined_24h_sum, @@ -163,14 +122,20 @@ impl Vecs { }) } + #[allow(clippy::too_many_arguments)] pub(crate) fn compute( &mut self, starting_indexes: &ComputeIndexes, height_to_pool: &impl ReadableVec, blocks: &blocks::Vecs, + prices: &prices::Vecs, + mining: &mining::Vecs, + transactions: &transactions::Vecs, exit: &Exit, ) -> Result<()> { - self.blocks_mined.compute(starting_indexes, exit, |vec| { + let window_starts = blocks.count.window_starts(); + + self.blocks_mined.compute(starting_indexes.height, &window_starts, exit, |vec| { vec.compute_transform( starting_indexes.height, height_to_pool, @@ -218,11 +183,77 @@ impl Vecs { exit, )?; - self.subsidy.compute_cumulative(starting_indexes, exit)?; + self.dominance + .compute_binary::( + starting_indexes.height, + &self.blocks_mined.cumulative.height, + &blocks.count.block_count.cumulative.height, + exit, + )?; - self.fee.compute_cumulative(starting_indexes, exit)?; + self.dominance_24h + .compute_binary::( + starting_indexes.height, + &self.blocks_mined_24h_sum.height, + &blocks.count.block_count_sum._24h.height, + exit, + )?; - self.coinbase.compute_cumulative(starting_indexes, exit)?; + self.dominance_1w + .compute_binary::( + starting_indexes.height, + &self.blocks_mined_1w_sum.height, + &blocks.count.block_count_sum._7d.height, + exit, + )?; + + self.dominance_1m + .compute_binary::( + starting_indexes.height, + &self.blocks_mined_1m_sum.height, + &blocks.count.block_count_sum._30d.height, + exit, + )?; + + self.dominance_1y + .compute_binary::( + starting_indexes.height, + &self.blocks_mined_1y_sum.height, + &blocks.count.block_count_sum._1y.height, + exit, + )?; + + self.subsidy + .compute(starting_indexes.height, &window_starts, prices, exit, |vec| { + Ok(vec.compute_transform2( + starting_indexes.height, + &self.blocks_mined.height, + &mining.rewards.subsidy.sats.height, + |(h, mask, val, ..)| (h, MaskSats::apply(mask, val)), + exit, + )?) + })?; + + self.fee.compute(starting_indexes.height, &window_starts, prices, exit, |vec| { + Ok(vec.compute_transform2( + starting_indexes.height, + &self.blocks_mined.height, + &*transactions.fees.fee.sum_cumulative.sum, + |(h, mask, val, ..)| (h, MaskSats::apply(mask, val)), + exit, + )?) + })?; + + self.coinbase + .compute(starting_indexes.height, &window_starts, prices, exit, |vec| { + Ok(vec.compute_transform2( + starting_indexes.height, + &self.blocks_mined.height, + &mining.rewards.coinbase.sats.height, + |(h, mask, val, ..)| (h, MaskSats::apply(mask, val)), + exit, + )?) + })?; { let mut prev = StoredU32::ZERO; diff --git a/crates/brk_computer/src/prices/cents/compute.rs b/crates/brk_computer/src/prices/cents/compute.rs index 04e435964..bc1fbf9da 100644 --- a/crates/brk_computer/src/prices/cents/compute.rs +++ b/crates/brk_computer/src/prices/cents/compute.rs @@ -5,7 +5,7 @@ use brk_indexer::Indexer; use brk_oracle::{Config, NUM_BINS, Oracle, START_HEIGHT, bin_to_cents, cents_to_bin}; use brk_types::{Cents, OutputType, Sats, TxIndex, TxOutIndex}; use tracing::info; -use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, StorageMode, WritableVec, VecIndex}; +use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, StorageMode, VecIndex, WritableVec}; use super::Vecs; use crate::{ComputeIndexes, indexes}; @@ -102,7 +102,11 @@ impl Vecs { /// Feed a range of blocks from the indexer into an Oracle (skipping coinbase), /// returning per-block ref_bin values. - fn feed_blocks(oracle: &mut Oracle, indexer: &Indexer, range: Range) -> Vec { + fn feed_blocks( + oracle: &mut Oracle, + indexer: &Indexer, + range: Range, + ) -> Vec { let total_txs = indexer.vecs.transactions.height.len(); let total_outputs = indexer.vecs.outputs.value.len(); @@ -183,10 +187,7 @@ impl Vecs { pub fn live_oracle(&self, indexer: &Indexer) -> Result { let config = Config::default(); let height = indexer.vecs.blocks.timestamp.len(); - let last_cents = self - .price - .collect_one_at(self.price.len() - 1) - .unwrap(); + let last_cents = self.price.collect_one_at(self.price.len() - 1).unwrap(); let seed_bin = cents_to_bin(last_cents.inner() as f64); let window_size = config.window_size; let oracle = Oracle::from_checkpoint(seed_bin, config, |o| { diff --git a/crates/brk_computer/src/scripts/compute.rs b/crates/brk_computer/src/scripts/compute.rs index a1a9a598a..22bca90a7 100644 --- a/crates/brk_computer/src/scripts/compute.rs +++ b/crates/brk_computer/src/scripts/compute.rs @@ -2,7 +2,7 @@ use brk_error::Result; use brk_indexer::Indexer; use vecdb::Exit; -use crate::{blocks, outputs, ComputeIndexes}; +use crate::{blocks, outputs, prices, ComputeIndexes}; use super::Vecs; @@ -12,6 +12,7 @@ impl Vecs { indexer: &Indexer, blocks: &blocks::Vecs, outputs: &outputs::Vecs, + prices: &prices::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { @@ -19,7 +20,7 @@ impl Vecs { .compute(indexer, &blocks.count, &outputs.count, starting_indexes, exit)?; self.value - .compute(indexer, starting_indexes, exit)?; + .compute(indexer, &blocks.count, prices, starting_indexes, exit)?; let _lock = exit.lock(); self.db.compact()?; diff --git a/crates/brk_computer/src/scripts/count/compute.rs b/crates/brk_computer/src/scripts/count/compute.rs index 71270d090..a4f8128a7 100644 --- a/crates/brk_computer/src/scripts/count/compute.rs +++ b/crates/brk_computer/src/scripts/count/compute.rs @@ -130,9 +130,9 @@ impl Vecs { self.segwit.compute(starting_indexes.height, &window_starts, exit, |v| { Ok(v.compute_transform3( starting_indexes.height, - &self.p2wpkh.last.height, - &self.p2wsh.last.height, - &self.p2tr.last.height, + &self.p2wpkh.height, + &self.p2wsh.height, + &self.p2tr.height, |(h, p2wpkh, p2wsh, p2tr, ..)| { (h, StoredU64::from(*p2wpkh + *p2wsh + *p2tr)) }, @@ -143,8 +143,8 @@ impl Vecs { // Adoption ratios: per-block ratio of script type / total outputs self.taproot_adoption.height.compute_transform2( starting_indexes.height, - &self.p2tr.last.height, - &outputs_count.total_count.sum_cum.sum.0, + &self.p2tr.height, + &outputs_count.total_count.sum_cumulative.sum.0, |(h, p2tr, total, ..)| { let ratio = if *total > 0 { StoredF32::from(*p2tr as f64 / *total as f64) @@ -158,8 +158,8 @@ impl Vecs { self.segwit_adoption.height.compute_transform2( starting_indexes.height, - &self.segwit.last.height, - &outputs_count.total_count.sum_cum.sum.0, + &self.segwit.height, + &outputs_count.total_count.sum_cumulative.sum.0, |(h, segwit, total, ..)| { let ratio = if *total > 0 { StoredF32::from(*segwit as f64 / *total as f64) diff --git a/crates/brk_computer/src/scripts/count/import.rs b/crates/brk_computer/src/scripts/count/import.rs index 7656f3cfb..30a6fbac9 100644 --- a/crates/brk_computer/src/scripts/count/import.rs +++ b/crates/brk_computer/src/scripts/count/import.rs @@ -5,7 +5,7 @@ use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeightCumSum, ComputedFromHeightLast}, + internal::{ComputedFromHeightCumulativeSum, ComputedFromHeightLast}, }; impl Vecs { @@ -14,20 +14,20 @@ impl Vecs { version: Version, indexes: &indexes::Vecs, ) -> Result { - let p2a = ComputedFromHeightCumSum::forced_import(db, "p2a_count", version, indexes)?; - let p2ms = ComputedFromHeightCumSum::forced_import(db, "p2ms_count", version, indexes)?; + let p2a = ComputedFromHeightCumulativeSum::forced_import(db, "p2a_count", version, indexes)?; + let p2ms = ComputedFromHeightCumulativeSum::forced_import(db, "p2ms_count", version, indexes)?; let p2pk33 = - ComputedFromHeightCumSum::forced_import(db, "p2pk33_count", version, indexes)?; + ComputedFromHeightCumulativeSum::forced_import(db, "p2pk33_count", version, indexes)?; let p2pk65 = - ComputedFromHeightCumSum::forced_import(db, "p2pk65_count", version, indexes)?; - let p2pkh = ComputedFromHeightCumSum::forced_import(db, "p2pkh_count", version, indexes)?; - let p2sh = ComputedFromHeightCumSum::forced_import(db, "p2sh_count", version, indexes)?; - let p2tr = ComputedFromHeightCumSum::forced_import(db, "p2tr_count", version, indexes)?; + ComputedFromHeightCumulativeSum::forced_import(db, "p2pk65_count", version, indexes)?; + let p2pkh = ComputedFromHeightCumulativeSum::forced_import(db, "p2pkh_count", version, indexes)?; + let p2sh = ComputedFromHeightCumulativeSum::forced_import(db, "p2sh_count", version, indexes)?; + let p2tr = ComputedFromHeightCumulativeSum::forced_import(db, "p2tr_count", version, indexes)?; let p2wpkh = - ComputedFromHeightCumSum::forced_import(db, "p2wpkh_count", version, indexes)?; - let p2wsh = ComputedFromHeightCumSum::forced_import(db, "p2wsh_count", version, indexes)?; + ComputedFromHeightCumulativeSum::forced_import(db, "p2wpkh_count", version, indexes)?; + let p2wsh = ComputedFromHeightCumulativeSum::forced_import(db, "p2wsh_count", version, indexes)?; let segwit = - ComputedFromHeightCumSum::forced_import(db, "segwit_count", version, indexes)?; + ComputedFromHeightCumulativeSum::forced_import(db, "segwit_count", version, indexes)?; Ok(Self { p2a, @@ -39,19 +39,19 @@ impl Vecs { p2tr, p2wpkh, p2wsh, - opreturn: ComputedFromHeightCumSum::forced_import( + opreturn: ComputedFromHeightCumulativeSum::forced_import( db, "opreturn_count", version, indexes, )?, - emptyoutput: ComputedFromHeightCumSum::forced_import( + emptyoutput: ComputedFromHeightCumulativeSum::forced_import( db, "emptyoutput_count", version, indexes, )?, - unknownoutput: ComputedFromHeightCumSum::forced_import( + unknownoutput: ComputedFromHeightCumulativeSum::forced_import( db, "unknownoutput_count", version, diff --git a/crates/brk_computer/src/scripts/count/vecs.rs b/crates/brk_computer/src/scripts/count/vecs.rs index 88a6a14e9..68c535f87 100644 --- a/crates/brk_computer/src/scripts/count/vecs.rs +++ b/crates/brk_computer/src/scripts/count/vecs.rs @@ -2,27 +2,27 @@ use brk_traversable::Traversable; use brk_types::{StoredF32, StoredU64}; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeightCumSum, ComputedFromHeightLast}; +use crate::internal::{ComputedFromHeightCumulativeSum, ComputedFromHeightLast}; #[derive(Traversable)] pub struct Vecs { // Per-type output counts - pub p2a: ComputedFromHeightCumSum, - pub p2ms: ComputedFromHeightCumSum, - pub p2pk33: ComputedFromHeightCumSum, - pub p2pk65: ComputedFromHeightCumSum, - pub p2pkh: ComputedFromHeightCumSum, - pub p2sh: ComputedFromHeightCumSum, - pub p2tr: ComputedFromHeightCumSum, - pub p2wpkh: ComputedFromHeightCumSum, - pub p2wsh: ComputedFromHeightCumSum, - pub opreturn: ComputedFromHeightCumSum, - pub emptyoutput: ComputedFromHeightCumSum, - pub unknownoutput: ComputedFromHeightCumSum, + pub p2a: ComputedFromHeightCumulativeSum, + pub p2ms: ComputedFromHeightCumulativeSum, + pub p2pk33: ComputedFromHeightCumulativeSum, + pub p2pk65: ComputedFromHeightCumulativeSum, + pub p2pkh: ComputedFromHeightCumulativeSum, + pub p2sh: ComputedFromHeightCumulativeSum, + pub p2tr: ComputedFromHeightCumulativeSum, + pub p2wpkh: ComputedFromHeightCumulativeSum, + pub p2wsh: ComputedFromHeightCumulativeSum, + pub opreturn: ComputedFromHeightCumulativeSum, + pub emptyoutput: ComputedFromHeightCumulativeSum, + pub unknownoutput: ComputedFromHeightCumulativeSum, // Aggregate counts /// SegWit output count (p2wpkh + p2wsh + p2tr) - pub segwit: ComputedFromHeightCumSum, + pub segwit: ComputedFromHeightCumulativeSum, // Adoption ratios (stored per-block, lazy period views) pub taproot_adoption: ComputedFromHeightLast, diff --git a/crates/brk_computer/src/scripts/import.rs b/crates/brk_computer/src/scripts/import.rs index 4e4196ab9..345f83d30 100644 --- a/crates/brk_computer/src/scripts/import.rs +++ b/crates/brk_computer/src/scripts/import.rs @@ -5,7 +5,7 @@ use brk_traversable::Traversable; use brk_types::Version; use vecdb::{Database, PAGE_SIZE}; -use crate::{indexes, prices}; +use crate::indexes; use super::{CountVecs, ValueVecs, Vecs}; @@ -14,7 +14,6 @@ impl Vecs { parent_path: &Path, parent_version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { let db = Database::open(&parent_path.join(super::DB_NAME))?; db.set_min_len(PAGE_SIZE * 50_000_000)?; @@ -22,7 +21,7 @@ impl Vecs { let version = parent_version; let count = CountVecs::forced_import(&db, version, indexes)?; - let value = ValueVecs::forced_import(&db, version, indexes, prices)?; + let value = ValueVecs::forced_import(&db, version, indexes)?; let this = Self { db, count, value }; diff --git a/crates/brk_computer/src/scripts/value/compute.rs b/crates/brk_computer/src/scripts/value/compute.rs index 074de81ea..d07053d5b 100644 --- a/crates/brk_computer/src/scripts/value/compute.rs +++ b/crates/brk_computer/src/scripts/value/compute.rs @@ -4,17 +4,22 @@ use brk_types::{Height, OutputType, Sats, TxOutIndex}; use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, WritableVec, VecIndex}; use super::Vecs; -use crate::ComputeIndexes; +use crate::{ComputeIndexes, blocks, prices}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, + count_vecs: &blocks::CountVecs, + prices: &prices::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { + let window_starts = count_vecs.window_starts(); + self.opreturn - .compute(starting_indexes, exit, |height_vec| { + .compute(starting_indexes.height, &window_starts, prices, exit, |height_vec| { + // Validate computed versions against dependencies let dep_version = indexer.vecs.outputs.first_txoutindex.version() + indexer.vecs.outputs.outputtype.version() diff --git a/crates/brk_computer/src/scripts/value/import.rs b/crates/brk_computer/src/scripts/value/import.rs index 80558adce..ce5729694 100644 --- a/crates/brk_computer/src/scripts/value/import.rs +++ b/crates/brk_computer/src/scripts/value/import.rs @@ -3,14 +3,13 @@ use brk_types::Version; use vecdb::Database; use super::Vecs; -use crate::{indexes, internal::ValueFromHeightFull, prices}; +use crate::{indexes, internal::ValueFromHeightFull}; impl Vecs { pub(crate) fn forced_import( db: &Database, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { Ok(Self { opreturn: ValueFromHeightFull::forced_import( @@ -18,7 +17,6 @@ impl Vecs { "opreturn_value", version, indexes, - prices, )?, }) } diff --git a/crates/brk_computer/src/supply/burned/compute.rs b/crates/brk_computer/src/supply/burned/compute.rs index a4751c277..7717e4d1f 100644 --- a/crates/brk_computer/src/supply/burned/compute.rs +++ b/crates/brk_computer/src/supply/burned/compute.rs @@ -3,19 +3,23 @@ use brk_types::{Height, Sats}; use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, WritableVec, VecIndex}; use super::Vecs; -use crate::{ComputeIndexes, mining, scripts}; +use crate::{ComputeIndexes, blocks, mining, prices, scripts}; impl Vecs { pub(crate) fn compute( &mut self, scripts: &scripts::Vecs, mining: &mining::Vecs, + count_vecs: &blocks::CountVecs, + prices: &prices::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { + let window_starts = count_vecs.window_starts(); + // 1. Compute opreturn supply - copy per-block opreturn values from scripts self.opreturn - .compute(starting_indexes, exit, |height_vec| { + .compute(starting_indexes.height, &window_starts, prices, exit, |height_vec| { // Validate computed versions against dependencies let opreturn_dep_version = scripts.value.opreturn.sats.height.version(); @@ -52,7 +56,7 @@ impl Vecs { let unclaimed_height = &mining.rewards.unclaimed_rewards.sats.height; self.unspendable - .compute(starting_indexes, exit, |height_vec| { + .compute(starting_indexes.height, &window_starts, prices, exit, |height_vec| { let unspendable_dep_version = opreturn_height.version() + unclaimed_height.version(); height_vec.validate_computed_version_or_reset(unspendable_dep_version)?; diff --git a/crates/brk_computer/src/supply/burned/import.rs b/crates/brk_computer/src/supply/burned/import.rs index dde2f9977..43e5f5f79 100644 --- a/crates/brk_computer/src/supply/burned/import.rs +++ b/crates/brk_computer/src/supply/burned/import.rs @@ -3,29 +3,26 @@ use brk_types::Version; use vecdb::Database; use super::Vecs; -use crate::{indexes, internal::ValueFromHeightSumCum, prices}; +use crate::{indexes, internal::ValueFromHeightSumCumulative}; impl Vecs { pub(crate) fn forced_import( db: &Database, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { Ok(Self { - opreturn: ValueFromHeightSumCum::forced_import( + opreturn: ValueFromHeightSumCumulative::forced_import( db, "opreturn_supply", version, indexes, - prices, )?, - unspendable: ValueFromHeightSumCum::forced_import( + unspendable: ValueFromHeightSumCumulative::forced_import( db, "unspendable_supply", version, indexes, - prices, )?, }) } diff --git a/crates/brk_computer/src/supply/burned/vecs.rs b/crates/brk_computer/src/supply/burned/vecs.rs index 88692a984..120a32d8b 100644 --- a/crates/brk_computer/src/supply/burned/vecs.rs +++ b/crates/brk_computer/src/supply/burned/vecs.rs @@ -1,11 +1,11 @@ use brk_traversable::Traversable; use vecdb::{Rw, StorageMode}; -use crate::internal::ValueFromHeightSumCum; +use crate::internal::ValueFromHeightSumCumulative; /// Burned/unspendable supply metrics #[derive(Traversable)] pub struct Vecs { - pub opreturn: ValueFromHeightSumCum, - pub unspendable: ValueFromHeightSumCum, + pub opreturn: ValueFromHeightSumCumulative, + pub unspendable: ValueFromHeightSumCumulative, } diff --git a/crates/brk_computer/src/supply/compute.rs b/crates/brk_computer/src/supply/compute.rs index 6f438ac15..989159f42 100644 --- a/crates/brk_computer/src/supply/compute.rs +++ b/crates/brk_computer/src/supply/compute.rs @@ -1,8 +1,9 @@ use brk_error::Result; +use brk_types::StoredF32; use vecdb::Exit; use super::Vecs; -use crate::{ComputeIndexes, blocks, distribution, mining, scripts, transactions}; +use crate::{ComputeIndexes, blocks, distribution, mining, prices, scripts, transactions}; impl Vecs { #[allow(clippy::too_many_arguments)] @@ -12,13 +13,14 @@ impl Vecs { blocks: &blocks::Vecs, mining: &mining::Vecs, transactions: &transactions::Vecs, + prices: &prices::Vecs, distribution: &distribution::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { // 1. Compute burned/unspendable supply self.burned - .compute(scripts, mining, starting_indexes, exit)?; + .compute(scripts, mining, &blocks.count, prices, starting_indexes, exit)?; // 2. Compute inflation rate at height level: (supply[h] - supply[1y_ago]) / supply[1y_ago] * 100 let circulating_supply = &distribution.utxo_cohorts.all.metrics.supply.total.sats; @@ -52,7 +54,14 @@ impl Vecs { exit, )?; - // Note: circulating, market_cap, cap_growth_rate_diff are lazy + // 5. Compute cap growth rate diff: market_cap_growth_rate - realized_cap_growth_rate + self.cap_growth_rate_diff.height.compute_transform2( + starting_indexes.height, + &self.market_cap_growth_rate.height, + &self.realized_cap_growth_rate.height, + |(h, a, b, ..)| (h, StoredF32::from(*a - *b)), + exit, + )?; let _lock = exit.lock(); self.db.compact()?; diff --git a/crates/brk_computer/src/supply/import.rs b/crates/brk_computer/src/supply/import.rs index b7c28eb02..3cd50bed8 100644 --- a/crates/brk_computer/src/supply/import.rs +++ b/crates/brk_computer/src/supply/import.rs @@ -9,11 +9,10 @@ use super::Vecs; use crate::{ distribution, indexes, internal::{ - ComputedFromHeightLast, DifferenceF32, DollarsIdentity, - LazyBinaryComputedFromHeightLast, LazyFromHeightLast, LazyValueFromHeightLast, - SatsIdentity, + ComputedFromHeightLast, DollarsIdentity, + LazyFromHeightLast, LazyValueFromHeightLast, + SatsIdentity, SatsToBitcoin, }, - prices, }; const VERSION: Version = Version::ONE; @@ -23,7 +22,6 @@ impl Vecs { parent: &Path, parent_version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, distribution: &distribution::Vecs, ) -> Result { let db = Database::open(&parent.join(super::DB_NAME))?; @@ -33,14 +31,14 @@ impl Vecs { let supply_metrics = &distribution.utxo_cohorts.all.metrics.supply; // Circulating supply - lazy refs to distribution - let circulating = LazyValueFromHeightLast::from_block_source::( + let circulating = LazyValueFromHeightLast::from_block_source::( "circulating_supply", &supply_metrics.total, version, ); // Burned/unspendable supply - computed from scripts - let burned = super::burned::Vecs::forced_import(&db, version, indexes, prices)?; + let burned = super::burned::Vecs::forced_import(&db, version, indexes)?; // Inflation rate let inflation = @@ -51,7 +49,7 @@ impl Vecs { super::velocity::Vecs::forced_import(&db, version, indexes)?; // Market cap - lazy identity from distribution supply in USD - let market_cap = LazyFromHeightLast::from_lazy_binary_computed::( + let market_cap = LazyFromHeightLast::from_computed::( "market_cap", version, supply_metrics.total.usd.height.read_only_boxed_clone(), @@ -72,13 +70,7 @@ impl Vecs { indexes, )?; let cap_growth_rate_diff = - LazyBinaryComputedFromHeightLast::forced_import::( - "cap_growth_rate_diff", - version, - market_cap_growth_rate.height.read_only_boxed_clone(), - realized_cap_growth_rate.height.read_only_boxed_clone(), - indexes, - ); + ComputedFromHeightLast::forced_import(&db, "cap_growth_rate_diff", version, indexes)?; let this = Self { db, diff --git a/crates/brk_computer/src/supply/vecs.rs b/crates/brk_computer/src/supply/vecs.rs index e04d73303..d1ce42640 100644 --- a/crates/brk_computer/src/supply/vecs.rs +++ b/crates/brk_computer/src/supply/vecs.rs @@ -4,8 +4,7 @@ use vecdb::{Database, Rw, StorageMode}; use super::{burned, velocity}; use crate::internal::{ - ComputedFromHeightLast, LazyBinaryComputedFromHeightLast, LazyFromHeightLast, - LazyValueFromHeightLast, + ComputedFromHeightLast, LazyFromHeightLast, LazyValueFromHeightLast, }; #[derive(Traversable)] @@ -20,5 +19,5 @@ pub struct Vecs { pub market_cap: LazyFromHeightLast, pub market_cap_growth_rate: ComputedFromHeightLast, pub realized_cap_growth_rate: ComputedFromHeightLast, - pub cap_growth_rate_diff: LazyBinaryComputedFromHeightLast, + pub cap_growth_rate_diff: ComputedFromHeightLast, } diff --git a/crates/brk_computer/src/traits/mod.rs b/crates/brk_computer/src/traits/mod.rs index c38d23cf0..0edc75cba 100644 --- a/crates/brk_computer/src/traits/mod.rs +++ b/crates/brk_computer/src/traits/mod.rs @@ -1,7 +1,8 @@ use brk_error::Result; use brk_types::StoredF32; use vecdb::{ - AnyVec, EagerVec, Exit, PcoVec, PcoVecValue, ReadableVec, VecIndex, VecValue, WritableVec, + AnyStoredVec, AnyVec, EagerVec, Exit, PcoVec, PcoVecValue, ReadableVec, VecIndex, VecValue, + WritableVec, }; mod pricing; @@ -280,21 +281,27 @@ where } } -/// Compute rolling percentiles (p10, p25, median, p75, p90) in a single pass. +/// Compute all 8 rolling distribution stats (avg, min, max, p10, p25, median, p75, p90) +/// in a single sorted-vec pass per window. /// -/// Maintains one sorted vec per window, extracting all 5 percentiles at each position. -/// Much faster than 5 separate passes since sorted vec maintenance is the bottleneck. +/// 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_percentiles_from_starts( +pub fn compute_rolling_distribution_from_starts( max_from: I, window_starts: &impl ReadableVec, values: &impl ReadableVec, + average_out: &mut EagerVec>, + min_out: &mut EagerVec>, + max_out: &mut EagerVec>, p10_out: &mut EagerVec>, p25_out: &mut EagerVec>, median_out: &mut EagerVec>, p75_out: &mut EagerVec>, p90_out: &mut EagerVec>, - _exit: &Exit, + exit: &Exit, ) -> Result<()> where I: VecIndex, @@ -303,21 +310,31 @@ where f64: From, { 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)?; + 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 5 vecs should be at the same length; use min to be safe - let skip = p10_out + // 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()) @@ -336,17 +353,19 @@ where let partial_values: Vec = values.collect_range_at(range_start, end); let mut sorted: Vec = Vec::new(); + let mut running_sum: f64 = 0.0; let mut prev_start_usize: usize = range_start; - // Reconstruct sorted state from historical data + // Reconstruct sorted state + running sum from historical data if skip > 0 { - (range_start..skip).for_each(|idx| { + 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); - }); + } } let starts_batch = window_starts.collect_range_at(skip, end); @@ -354,6 +373,7 @@ where 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); @@ -362,6 +382,7 @@ where 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)) { @@ -373,20 +394,49 @@ where let len = sorted.len(); if len == 0 { 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)?; } 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)))?; } + + 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()?; + } } + // 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()?; + Ok(()) } diff --git a/crates/brk_computer/src/transactions/compute.rs b/crates/brk_computer/src/transactions/compute.rs index 25743bc98..4c781382b 100644 --- a/crates/brk_computer/src/transactions/compute.rs +++ b/crates/brk_computer/src/transactions/compute.rs @@ -25,11 +25,11 @@ impl Vecs { // Versions depends on count self.versions - .compute(indexer, starting_indexes, exit)?; + .compute(indexer, &blocks.count, starting_indexes, exit)?; - // Size computes next + // Size computes next (uses BlockWindowStarts for 1h/24h rolling) self.size - .compute(indexer, indexes, starting_indexes, exit)?; + .compute(indexer, indexes, &blocks.count, starting_indexes, exit)?; // Fees depends on size, blocks (window starts), prices (USD conversion) self.fees.compute( @@ -48,6 +48,7 @@ impl Vecs { indexer, indexes, blocks, + prices, &self.count, &self.fees, &inputs.count, diff --git a/crates/brk_computer/src/transactions/count/import.rs b/crates/brk_computer/src/transactions/count/import.rs index c9c1aa3c1..dfef04947 100644 --- a/crates/brk_computer/src/transactions/count/import.rs +++ b/crates/brk_computer/src/transactions/count/import.rs @@ -4,7 +4,7 @@ use brk_types::{StoredBool, TxIndex, Version}; use vecdb::{Database, LazyVecFrom2, ReadableCloneableVec}; use super::Vecs; -use crate::{indexes, internal::ComputedFromHeightCumFull}; +use crate::{indexes, internal::ComputedFromHeightCumulativeFull}; impl Vecs { pub(crate) fn forced_import( @@ -24,7 +24,7 @@ impl Vecs { ); Ok(Self { - tx_count: ComputedFromHeightCumFull::forced_import( + tx_count: ComputedFromHeightCumulativeFull::forced_import( db, "tx_count", version, indexes, )?, is_coinbase: txindex_to_is_coinbase, diff --git a/crates/brk_computer/src/transactions/count/vecs.rs b/crates/brk_computer/src/transactions/count/vecs.rs index 185734e95..c20c29d87 100644 --- a/crates/brk_computer/src/transactions/count/vecs.rs +++ b/crates/brk_computer/src/transactions/count/vecs.rs @@ -2,10 +2,10 @@ use brk_traversable::Traversable; use brk_types::{Height, StoredBool, StoredU64, TxIndex}; use vecdb::{LazyVecFrom2, Rw, StorageMode}; -use crate::internal::ComputedFromHeightCumFull; +use crate::internal::ComputedFromHeightCumulativeFull; #[derive(Traversable)] pub struct Vecs { - pub tx_count: ComputedFromHeightCumFull, + pub tx_count: ComputedFromHeightCumulativeFull, pub is_coinbase: LazyVecFrom2, } diff --git a/crates/brk_computer/src/transactions/fees/compute.rs b/crates/brk_computer/src/transactions/fees/compute.rs index ea5411c0f..714cea091 100644 --- a/crates/brk_computer/src/transactions/fees/compute.rs +++ b/crates/brk_computer/src/transactions/fees/compute.rs @@ -82,7 +82,7 @@ impl Vecs { // Compute fee USD sum per block: price * Bitcoin::from(sats) self.fee_usd_sum.compute_transform2( starting_indexes.height, - self.fee.sum_cum.sum.inner(), + self.fee.sum_cumulative.sum.inner(), &prices.usd.price, |(h, sats, price, ..)| (h, price * Bitcoin::from(sats)), exit, @@ -93,7 +93,7 @@ impl Vecs { self.fee_rolling.compute( starting_indexes.height, &window_starts, - self.fee.sum_cum.sum.inner(), + self.fee.sum_cumulative.sum.inner(), exit, )?; @@ -101,7 +101,7 @@ impl Vecs { self.fee_rate_rolling.compute_distribution( starting_indexes.height, &window_starts, - &self.fee_rate.min_max_average.average.0, + &self.fee_rate.average.0, exit, )?; diff --git a/crates/brk_computer/src/transactions/import.rs b/crates/brk_computer/src/transactions/import.rs index 511fcaa38..d4a65e698 100644 --- a/crates/brk_computer/src/transactions/import.rs +++ b/crates/brk_computer/src/transactions/import.rs @@ -6,7 +6,7 @@ use brk_traversable::Traversable; use brk_types::Version; use vecdb::{Database, PAGE_SIZE}; -use crate::{indexes, prices}; +use crate::indexes; use super::{CountVecs, FeesVecs, SizeVecs, Vecs, VersionsVecs, VolumeVecs}; @@ -16,7 +16,6 @@ impl Vecs { parent_version: Version, indexer: &Indexer, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { let db = Database::open(&parent_path.join(super::DB_NAME))?; db.set_min_len(PAGE_SIZE * 50_000_000)?; @@ -24,10 +23,10 @@ impl Vecs { let version = parent_version; let count = CountVecs::forced_import(&db, version, indexer, indexes)?; - let size = SizeVecs::forced_import(&db, version, indexer, indexes)?; + let size = SizeVecs::forced_import(&db, version, indexer)?; let fees = FeesVecs::forced_import(&db, version, indexes)?; let versions = VersionsVecs::forced_import(&db, version, indexes)?; - let volume = VolumeVecs::forced_import(&db, version, indexes, prices)?; + let volume = VolumeVecs::forced_import(&db, version, indexes)?; let this = Self { db, diff --git a/crates/brk_computer/src/transactions/size/compute.rs b/crates/brk_computer/src/transactions/size/compute.rs index dafd1f190..a04e7b568 100644 --- a/crates/brk_computer/src/transactions/size/compute.rs +++ b/crates/brk_computer/src/transactions/size/compute.rs @@ -3,21 +3,24 @@ use brk_indexer::Indexer; use vecdb::Exit; use super::Vecs; -use crate::{indexes, ComputeIndexes}; +use crate::{blocks, indexes, ComputeIndexes}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, indexes: &indexes::Vecs, + count_vecs: &blocks::CountVecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { + let block_windows = count_vecs.block_window_starts(); + self.weight - .derive_from(indexer, indexes, starting_indexes, exit)?; + .derive_from(indexer, indexes, starting_indexes, &block_windows, exit)?; self.vsize - .derive_from(indexer, indexes, starting_indexes, exit)?; + .derive_from(indexer, indexes, starting_indexes, &block_windows, exit)?; Ok(()) } diff --git a/crates/brk_computer/src/transactions/size/import.rs b/crates/brk_computer/src/transactions/size/import.rs index 5473693dc..619eeb2bb 100644 --- a/crates/brk_computer/src/transactions/size/import.rs +++ b/crates/brk_computer/src/transactions/size/import.rs @@ -4,14 +4,13 @@ use brk_types::{TxIndex, VSize, Version, Weight}; use vecdb::{Database, ReadableCloneableVec, LazyVecFrom2}; use super::Vecs; -use crate::{indexes, internal::LazyFromTxDistribution}; +use crate::internal::LazyFromTxDistribution; impl Vecs { pub(crate) fn forced_import( db: &Database, version: Version, indexer: &Indexer, - indexes: &indexes::Vecs, ) -> Result { let txindex_to_weight = LazyVecFrom2::init( "tx_weight", @@ -39,14 +38,12 @@ impl Vecs { "tx_vsize", version, txindex_to_vsize, - indexes, )?, weight: LazyFromTxDistribution::forced_import( db, "tx_weight", version, txindex_to_weight, - indexes, )?, }) } diff --git a/crates/brk_computer/src/transactions/versions/compute.rs b/crates/brk_computer/src/transactions/versions/compute.rs index c5dabefb0..b072f8f55 100644 --- a/crates/brk_computer/src/transactions/versions/compute.rs +++ b/crates/brk_computer/src/transactions/versions/compute.rs @@ -4,21 +4,24 @@ use brk_types::{StoredU64, TxVersion}; use vecdb::{Exit, ReadableVec, VecIndex}; use super::Vecs; -use crate::{ComputeIndexes, internal::ComputedFromHeightSumCum}; +use crate::{ComputeIndexes, blocks, internal::ComputedFromHeightCumulativeSum}; impl Vecs { pub(crate) fn compute( &mut self, indexer: &Indexer, + count_vecs: &blocks::CountVecs, starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - let tx_vany = |tx_vany: &mut ComputedFromHeightSumCum, txversion: TxVersion| { + let window_starts = count_vecs.window_starts(); + + let tx_vany = |tx_vany: &mut ComputedFromHeightCumulativeSum, txversion: TxVersion| { let txversion_vec = &indexer.vecs.transactions.txversion; // Cursor avoids per-transaction PcoVec page decompression. // Txindex values are sequential, so the cursor only advances forward. let mut cursor = txversion_vec.cursor(); - tx_vany.compute(starting_indexes, exit, |vec| { + tx_vany.compute(starting_indexes.height, &window_starts, exit, |vec| { vec.compute_filtered_count_from_indexes( starting_indexes.height, &indexer.vecs.transactions.first_txindex, diff --git a/crates/brk_computer/src/transactions/versions/import.rs b/crates/brk_computer/src/transactions/versions/import.rs index 78ab67f32..fc9e4d01a 100644 --- a/crates/brk_computer/src/transactions/versions/import.rs +++ b/crates/brk_computer/src/transactions/versions/import.rs @@ -3,14 +3,14 @@ use brk_types::Version; use vecdb::Database; use super::Vecs; -use crate::{indexes, internal::ComputedFromHeightSumCum}; +use crate::{indexes, internal::ComputedFromHeightCumulativeSum}; impl Vecs { pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result { Ok(Self { - v1: ComputedFromHeightSumCum::forced_import(db, "tx_v1", version, indexes)?, - v2: ComputedFromHeightSumCum::forced_import(db, "tx_v2", version, indexes)?, - v3: ComputedFromHeightSumCum::forced_import(db, "tx_v3", version, indexes)?, + v1: ComputedFromHeightCumulativeSum::forced_import(db, "tx_v1", version, indexes)?, + v2: ComputedFromHeightCumulativeSum::forced_import(db, "tx_v2", version, indexes)?, + v3: ComputedFromHeightCumulativeSum::forced_import(db, "tx_v3", version, indexes)?, }) } } diff --git a/crates/brk_computer/src/transactions/versions/vecs.rs b/crates/brk_computer/src/transactions/versions/vecs.rs index d174cd73e..e9a2448a5 100644 --- a/crates/brk_computer/src/transactions/versions/vecs.rs +++ b/crates/brk_computer/src/transactions/versions/vecs.rs @@ -2,11 +2,11 @@ use brk_traversable::Traversable; use brk_types::StoredU64; use vecdb::{Rw, StorageMode}; -use crate::internal::ComputedFromHeightSumCum; +use crate::internal::ComputedFromHeightCumulativeSum; #[derive(Traversable)] pub struct Vecs { - pub v1: ComputedFromHeightSumCum, - pub v2: ComputedFromHeightSumCum, - pub v3: ComputedFromHeightSumCum, + pub v1: ComputedFromHeightCumulativeSum, + pub v2: ComputedFromHeightCumulativeSum, + pub v3: ComputedFromHeightCumulativeSum, } diff --git a/crates/brk_computer/src/transactions/volume/compute.rs b/crates/brk_computer/src/transactions/volume/compute.rs index ee86419c2..2bc794452 100644 --- a/crates/brk_computer/src/transactions/volume/compute.rs +++ b/crates/brk_computer/src/transactions/volume/compute.rs @@ -4,7 +4,7 @@ use brk_types::StoredF32; use vecdb::Exit; use super::Vecs; -use crate::{blocks, ComputeIndexes, indexes, inputs, outputs}; +use crate::{blocks, ComputeIndexes, indexes, inputs, outputs, prices}; use crate::transactions::{count, fees}; impl Vecs { @@ -14,6 +14,7 @@ impl Vecs { indexer: &Indexer, indexes: &indexes::Vecs, blocks: &blocks::Vecs, + prices: &prices::Vecs, count_vecs: &count::Vecs, fees_vecs: &fees::Vecs, inputs_count: &inputs::CountVecs, @@ -38,6 +39,12 @@ impl Vecs { exit, )?; + // Compute USD from sats × price + self.sent_sum + .compute(prices, starting_indexes.height, exit)?; + self.received_sum + .compute(prices, starting_indexes.height, exit)?; + // Annualized volume: rolling 1y sum of per-block sent volume self.annualized_volume.sats.height.compute_rolling_sum( starting_indexes.height, @@ -45,6 +52,8 @@ impl Vecs { &self.sent_sum.sats.height, exit, )?; + self.annualized_volume + .compute(prices, starting_indexes.height, exit)?; // Rolling sums for sent and received let window_starts = blocks.count.window_starts(); @@ -83,7 +92,7 @@ impl Vecs { // inputs_per_sec: per-block input count / block interval self.inputs_per_sec.height.compute_transform2( starting_indexes.height, - &inputs_count.height.sum_cum.sum.0, + &inputs_count.height.sum_cumulative.sum.0, &blocks.interval.interval.height, |(h, input_count, interval, ..)| { let interval_f64 = f64::from(*interval); @@ -100,7 +109,7 @@ impl Vecs { // outputs_per_sec: per-block output count / block interval self.outputs_per_sec.height.compute_transform2( starting_indexes.height, - &outputs_count.total_count.sum_cum.sum.0, + &outputs_count.total_count.sum_cumulative.sum.0, &blocks.interval.interval.height, |(h, output_count, interval, ..)| { let interval_f64 = f64::from(*interval); diff --git a/crates/brk_computer/src/transactions/volume/import.rs b/crates/brk_computer/src/transactions/volume/import.rs index 91bb05f8b..cba3bfe9c 100644 --- a/crates/brk_computer/src/transactions/volume/import.rs +++ b/crates/brk_computer/src/transactions/volume/import.rs @@ -6,7 +6,6 @@ use super::Vecs; use crate::{ indexes, internal::{ComputedFromHeightLast, StoredValueRollingWindows, ValueFromHeightLast}, - prices, }; impl Vecs { @@ -14,18 +13,17 @@ impl Vecs { db: &Database, version: Version, indexes: &indexes::Vecs, - prices: &prices::Vecs, ) -> Result { let v2 = Version::TWO; Ok(Self { sent_sum: ValueFromHeightLast::forced_import( - db, "sent_sum", version, indexes, prices, + db, "sent_sum", version, indexes, )?, sent_sum_rolling: StoredValueRollingWindows::forced_import( db, "sent_sum", version, indexes, )?, received_sum: ValueFromHeightLast::forced_import( - db, "received_sum", version, indexes, prices, + db, "received_sum", version, indexes, )?, received_sum_rolling: StoredValueRollingWindows::forced_import( db, "received_sum", version, indexes, @@ -35,7 +33,6 @@ impl Vecs { "annualized_volume", version, indexes, - prices, )?, tx_per_sec: ComputedFromHeightLast::forced_import( db, diff --git a/crates/brk_indexer/src/indexes.rs b/crates/brk_indexer/src/indexes.rs index 307796eff..09b8e2945 100644 --- a/crates/brk_indexer/src/indexes.rs +++ b/crates/brk_indexer/src/indexes.rs @@ -1,14 +1,20 @@ use brk_error::Result; use brk_types::{Height, Indexes}; use tracing::{debug, info}; -use vecdb::{AnyStoredVec, WritableVec, PcoVec, PcoVecValue, ReadableVec, VecIndex, VecValue}; +use vecdb::{AnyStoredVec, PcoVec, PcoVecValue, ReadableVec, VecIndex, VecValue, WritableVec}; use crate::{Stores, Vecs}; /// Extension trait for Indexes with brk_indexer-specific functionality. pub trait IndexesExt { fn checked_push(&self, vecs: &mut Vecs) -> Result<()>; - fn from_vecs_and_stores(required_height: Height, vecs: &mut Vecs, stores: &Stores) -> Option where Self: Sized; + fn from_vecs_and_stores( + required_height: Height, + vecs: &mut Vecs, + stores: &Stores, + ) -> Option + where + Self: Sized; } impl IndexesExt for Indexes { @@ -63,7 +69,11 @@ impl IndexesExt for Indexes { Ok(()) } - fn from_vecs_and_stores(required_height: Height, vecs: &mut Vecs, stores: &Stores) -> Option { + fn from_vecs_and_stores( + required_height: Height, + vecs: &mut Vecs, + stores: &Stores, + ) -> Option { debug!("Creating indexes from vecs and stores..."); // Local data height: minimum of vecs and stores @@ -159,11 +169,17 @@ impl IndexesExt for Indexes { starting_height, )?; - let txinindex = - starting_index(&vecs.inputs.first_txinindex, &vecs.inputs.outpoint, starting_height)?; + let txinindex = starting_index( + &vecs.inputs.first_txinindex, + &vecs.inputs.outpoint, + starting_height, + )?; - let txoutindex = - starting_index(&vecs.outputs.first_txoutindex, &vecs.outputs.value, starting_height)?; + let txoutindex = starting_index( + &vecs.outputs.first_txoutindex, + &vecs.outputs.value, + starting_height, + )?; let unknownoutputindex = starting_index( &vecs.scripts.first_unknownoutputindex, diff --git a/crates/brk_indexer/src/processor/mod.rs b/crates/brk_indexer/src/processor/mod.rs index f9038090f..7edfe5a41 100644 --- a/crates/brk_indexer/src/processor/mod.rs +++ b/crates/brk_indexer/src/processor/mod.rs @@ -57,8 +57,7 @@ impl BlockProcessor<'_> { let addr_hash_stores = &mut self.stores.addresstype_to_addresshash_to_addressindex; let addr_txindex_stores = &mut self.stores.addresstype_to_addressindex_and_txindex; - let addr_outpoint_stores = - &mut self.stores.addresstype_to_addressindex_and_unspentoutpoint; + let addr_outpoint_stores = &mut self.stores.addresstype_to_addressindex_and_unspentoutpoint; let txidprefix_store = &mut self.stores.txidprefix_to_txindex; let (finalize_result, metadata_result) = rayon::join( diff --git a/crates/brk_indexer/src/processor/txin.rs b/crates/brk_indexer/src/processor/txin.rs index cb986dd17..faca8acc3 100644 --- a/crates/brk_indexer/src/processor/txin.rs +++ b/crates/brk_indexer/src/processor/txin.rs @@ -135,10 +135,16 @@ impl<'a> BlockProcessor<'a> { out: &mut FxHashSet, ) { out.clear(); - out.extend(txins.iter().filter_map(|(_, input_source)| match input_source { - InputSource::SameBlock { outpoint, .. } if !outpoint.is_coinbase() => Some(*outpoint), - _ => None, - })); + out.extend( + txins + .iter() + .filter_map(|(_, input_source)| match input_source { + InputSource::SameBlock { outpoint, .. } if !outpoint.is_coinbase() => { + Some(*outpoint) + } + _ => None, + }), + ); } } @@ -165,13 +171,23 @@ pub(super) fn finalize_inputs( outpoint, } => { if outpoint.is_coinbase() { - (vin, txindex, outpoint, OutputType::Unknown, TypeIndex::COINBASE) + ( + vin, + txindex, + outpoint, + OutputType::Unknown, + TypeIndex::COINBASE, + ) } else { let info = same_block_output_info .remove(&outpoint) .ok_or(Error::Internal("Same-block output not found")) .inspect_err(|_| { - error!(?outpoint, remaining = same_block_output_info.len(), "Same-block output not found"); + error!( + ?outpoint, + remaining = same_block_output_info.len(), + "Same-block output not found" + ); })?; (vin, txindex, outpoint, info.outputtype, info.typeindex) } @@ -195,10 +211,7 @@ pub(super) fn finalize_inputs( addr_txindex_stores .get_mut_unwrap(addresstype) - .insert( - AddressIndexTxIndex::from((addressindex, txindex)), - Unit, - ); + .insert(AddressIndexTxIndex::from((addressindex, txindex)), Unit); addr_outpoint_stores .get_mut_unwrap(addresstype) diff --git a/crates/brk_indexer/src/processor/txout.rs b/crates/brk_indexer/src/processor/txout.rs index e0421752a..ded57a328 100644 --- a/crates/brk_indexer/src/processor/txout.rs +++ b/crates/brk_indexer/src/processor/txout.rs @@ -6,8 +6,8 @@ use brk_types::{ Sats, TxIndex, TxOutIndex, TypeIndex, Unit, Vout, }; use rayon::prelude::*; -use tracing::error; use rustc_hash::{FxHashMap, FxHashSet}; +use tracing::error; use vecdb::{BytesVec, WritableVec}; use super::{BlockProcessor, ProcessedOutput, SameBlockOutputInfo}; @@ -68,17 +68,21 @@ impl<'a> BlockProcessor<'a> { }); if check_collisions && let Some(typeindex) = existing_typeindex { - let prev_addressbytes = self.vecs.addresses.get_bytes_by_type( - addresstype, - typeindex, - &self.readers.addressbytes, - ) - .ok_or(Error::Internal("Missing addressbytes"))?; + let prev_addressbytes = self + .vecs + .addresses + .get_bytes_by_type(addresstype, typeindex, &self.readers.addressbytes) + .ok_or(Error::Internal("Missing addressbytes"))?; if prev_addressbytes != address_bytes { error!( - ?height, ?vout, ?block_txindex, ?addresstype, - ?prev_addressbytes, ?address_bytes, ?typeindex, + ?height, + ?vout, + ?block_txindex, + ?addresstype, + ?prev_addressbytes, + ?address_bytes, + ?typeindex, "Address hash collision" ); return Err(Error::Internal("Address hash collision")); @@ -202,10 +206,7 @@ pub(super) fn finalize_outputs( addr_txindex_stores .get_mut_unwrap(addresstype) - .insert( - AddressIndexTxIndex::from((addressindex, txindex)), - Unit, - ); + .insert(AddressIndexTxIndex::from((addressindex, txindex)), Unit); } let outpoint = OutPoint::new(txindex, vout); @@ -224,10 +225,7 @@ pub(super) fn finalize_outputs( addr_outpoint_stores .get_mut_unwrap(addresstype) - .insert( - AddressIndexOutPoint::from((addressindex, outpoint)), - Unit, - ); + .insert(AddressIndexOutPoint::from((addressindex, outpoint)), Unit); } } diff --git a/crates/brk_indexer/src/readers.rs b/crates/brk_indexer/src/readers.rs index 6fbd49879..098c95b28 100644 --- a/crates/brk_indexer/src/readers.rs +++ b/crates/brk_indexer/src/readers.rs @@ -1,8 +1,8 @@ use brk_types::{ OutputType, P2AAddressIndex, P2ABytes, P2PK33AddressIndex, P2PK33Bytes, P2PK65AddressIndex, P2PK65Bytes, P2PKHAddressIndex, P2PKHBytes, P2SHAddressIndex, P2SHBytes, P2TRAddressIndex, - P2TRBytes, P2WPKHAddressIndex, P2WPKHBytes, P2WSHAddressIndex, P2WSHBytes, TxIndex, - TxOutIndex, Txid, TypeIndex, + P2TRBytes, P2WPKHAddressIndex, P2WPKHBytes, P2WSHAddressIndex, P2WSHBytes, TxIndex, TxOutIndex, + Txid, TypeIndex, }; use vecdb::{BytesStrategy, VecReader}; @@ -25,12 +25,9 @@ pub struct AddressReaders { /// random access without recomputing `region.start() + HEADER_OFFSET` per read. pub struct Readers { pub txid: VecReader>, - pub txindex_to_first_txoutindex: - VecReader>, - pub txoutindex_to_outputtype: - VecReader>, - pub txoutindex_to_typeindex: - VecReader>, + pub txindex_to_first_txoutindex: VecReader>, + pub txoutindex_to_outputtype: VecReader>, + pub txoutindex_to_typeindex: VecReader>, pub addressbytes: AddressReaders, } diff --git a/crates/brk_indexer/src/stores.rs b/crates/brk_indexer/src/stores.rs index 297df4d49..c16fdc0dd 100644 --- a/crates/brk_indexer/src/stores.rs +++ b/crates/brk_indexer/src/stores.rs @@ -272,21 +272,24 @@ impl Stores { let start = starting_indexes.txindex.to_usize(); let end = vecs.transactions.txid.len(); let mut current_index = start; - vecs.transactions.txid.for_each_range_at(start, end, |txid| { - let txindex = TxIndex::from(current_index); - let txidprefix = TxidPrefix::from(&txid); + vecs.transactions + .txid + .for_each_range_at(start, end, |txid| { + let txindex = TxIndex::from(current_index); + let txidprefix = TxidPrefix::from(&txid); - let is_known_dup = DUPLICATE_TXID_PREFIXES - .iter() - .any(|(dup_prefix, dup_txindex)| { - txindex == *dup_txindex && txidprefix == *dup_prefix - }); + let is_known_dup = + DUPLICATE_TXID_PREFIXES + .iter() + .any(|(dup_prefix, dup_txindex)| { + txindex == *dup_txindex && txidprefix == *dup_prefix + }); - if !is_known_dup { - self.txidprefix_to_txindex.remove(txidprefix); - } - current_index += 1; - }); + if !is_known_dup { + self.txidprefix_to_txindex.remove(txidprefix); + } + current_index += 1; + }); self.txidprefix_to_txindex.clear_caches(); } @@ -302,8 +305,10 @@ impl Stores { let rollback_start = starting_indexes.txoutindex.to_usize(); let rollback_end = vecs.outputs.outputtype.len(); - let txindexes: Vec = - vecs.outputs.txindex.collect_range_at(rollback_start, rollback_end); + let txindexes: Vec = vecs + .outputs + .txindex + .collect_range_at(rollback_start, rollback_end); for (i, txoutindex) in (rollback_start..rollback_end).enumerate() { let outputtype = txoutindex_to_outputtype_reader.get(txoutindex); diff --git a/crates/brk_indexer/src/vecs/addresses.rs b/crates/brk_indexer/src/vecs/addresses.rs index f97fe5cc6..9952a7bce 100644 --- a/crates/brk_indexer/src/vecs/addresses.rs +++ b/crates/brk_indexer/src/vecs/addresses.rs @@ -8,12 +8,12 @@ use brk_types::{ }; use rayon::prelude::*; use vecdb::{ - AnyStoredVec, BytesVec, Database, WritableVec, ImportableVec, PcoVec, ReadableVec, - Rw, Stamp, StorageMode, VecIndex, + AnyStoredVec, BytesVec, Database, ImportableVec, PcoVec, ReadableVec, Rw, Stamp, StorageMode, + VecIndex, WritableVec, }; -use crate::readers::AddressReaders; use crate::parallel_import; +use crate::readers::AddressReaders; #[derive(Traversable)] pub struct AddressesVecs { @@ -212,30 +212,14 @@ impl AddressesVecs { pub fn push_bytes_if_needed(&mut self, index: TypeIndex, bytes: AddressBytes) -> Result<()> { match bytes { - AddressBytes::P2PK65(bytes) => self - .p2pk65bytes - .checked_push(index.into(), bytes)?, - AddressBytes::P2PK33(bytes) => self - .p2pk33bytes - .checked_push(index.into(), bytes)?, - AddressBytes::P2PKH(bytes) => self - .p2pkhbytes - .checked_push(index.into(), bytes)?, - AddressBytes::P2SH(bytes) => self - .p2shbytes - .checked_push(index.into(), bytes)?, - AddressBytes::P2WPKH(bytes) => self - .p2wpkhbytes - .checked_push(index.into(), bytes)?, - AddressBytes::P2WSH(bytes) => self - .p2wshbytes - .checked_push(index.into(), bytes)?, - AddressBytes::P2TR(bytes) => self - .p2trbytes - .checked_push(index.into(), bytes)?, - AddressBytes::P2A(bytes) => self - .p2abytes - .checked_push(index.into(), bytes)?, + AddressBytes::P2PK65(bytes) => self.p2pk65bytes.checked_push(index.into(), bytes)?, + AddressBytes::P2PK33(bytes) => self.p2pk33bytes.checked_push(index.into(), bytes)?, + AddressBytes::P2PKH(bytes) => self.p2pkhbytes.checked_push(index.into(), bytes)?, + AddressBytes::P2SH(bytes) => self.p2shbytes.checked_push(index.into(), bytes)?, + AddressBytes::P2WPKH(bytes) => self.p2wpkhbytes.checked_push(index.into(), bytes)?, + AddressBytes::P2WSH(bytes) => self.p2wshbytes.checked_push(index.into(), bytes)?, + AddressBytes::P2TR(bytes) => self.p2trbytes.checked_push(index.into(), bytes)?, + AddressBytes::P2A(bytes) => self.p2abytes.checked_push(index.into(), bytes)?, }; Ok(()) } @@ -271,38 +255,14 @@ impl AddressesVecs { } match address_type { - OutputType::P2PK65 => make_iter!( - self.first_p2pk65addressindex, - self.p2pk65bytes - ), - OutputType::P2PK33 => make_iter!( - self.first_p2pk33addressindex, - self.p2pk33bytes - ), - OutputType::P2PKH => make_iter!( - self.first_p2pkhaddressindex, - self.p2pkhbytes - ), - OutputType::P2SH => make_iter!( - self.first_p2shaddressindex, - self.p2shbytes - ), - OutputType::P2WPKH => make_iter!( - self.first_p2wpkhaddressindex, - self.p2wpkhbytes - ), - OutputType::P2WSH => make_iter!( - self.first_p2wshaddressindex, - self.p2wshbytes - ), - OutputType::P2TR => make_iter!( - self.first_p2traddressindex, - self.p2trbytes - ), - OutputType::P2A => make_iter!( - self.first_p2aaddressindex, - self.p2abytes - ), + OutputType::P2PK65 => make_iter!(self.first_p2pk65addressindex, self.p2pk65bytes), + OutputType::P2PK33 => make_iter!(self.first_p2pk33addressindex, self.p2pk33bytes), + OutputType::P2PKH => make_iter!(self.first_p2pkhaddressindex, self.p2pkhbytes), + OutputType::P2SH => make_iter!(self.first_p2shaddressindex, self.p2shbytes), + OutputType::P2WPKH => make_iter!(self.first_p2wpkhaddressindex, self.p2wpkhbytes), + OutputType::P2WSH => make_iter!(self.first_p2wshaddressindex, self.p2wshbytes), + OutputType::P2TR => make_iter!(self.first_p2traddressindex, self.p2trbytes), + OutputType::P2A => make_iter!(self.first_p2aaddressindex, self.p2abytes), _ => Ok(Box::new(std::iter::empty())), } } diff --git a/crates/brk_indexer/src/vecs/blocks.rs b/crates/brk_indexer/src/vecs/blocks.rs index 25f31c33c..3235bfa0c 100644 --- a/crates/brk_indexer/src/vecs/blocks.rs +++ b/crates/brk_indexer/src/vecs/blocks.rs @@ -2,7 +2,9 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{BlockHash, Height, StoredF64, StoredU64, Timestamp, Version, Weight}; use rayon::prelude::*; -use vecdb::{AnyStoredVec, BytesVec, Database, WritableVec, ImportableVec, PcoVec, Rw, Stamp, StorageMode}; +use vecdb::{ + AnyStoredVec, BytesVec, Database, ImportableVec, PcoVec, Rw, Stamp, StorageMode, WritableVec, +}; use crate::parallel_import; @@ -45,8 +47,7 @@ impl BlocksVecs { .truncate_if_needed_with_stamp(height, stamp)?; self.total_size .truncate_if_needed_with_stamp(height, stamp)?; - self.weight - .truncate_if_needed_with_stamp(height, stamp)?; + self.weight.truncate_if_needed_with_stamp(height, stamp)?; Ok(()) } diff --git a/crates/brk_indexer/src/vecs/inputs.rs b/crates/brk_indexer/src/vecs/inputs.rs index d2e298a04..a525de94a 100644 --- a/crates/brk_indexer/src/vecs/inputs.rs +++ b/crates/brk_indexer/src/vecs/inputs.rs @@ -2,7 +2,7 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, OutPoint, OutputType, TxInIndex, TxIndex, TypeIndex, Version}; use rayon::prelude::*; -use vecdb::{AnyStoredVec, Database, WritableVec, ImportableVec, PcoVec, Rw, Stamp, StorageMode}; +use vecdb::{AnyStoredVec, Database, ImportableVec, PcoVec, Rw, Stamp, StorageMode, WritableVec}; use crate::parallel_import; diff --git a/crates/brk_indexer/src/vecs/outputs.rs b/crates/brk_indexer/src/vecs/outputs.rs index a3b2c7051..57b983f4e 100644 --- a/crates/brk_indexer/src/vecs/outputs.rs +++ b/crates/brk_indexer/src/vecs/outputs.rs @@ -2,7 +2,9 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, OutputType, Sats, TxIndex, TxOutIndex, TypeIndex, Version}; use rayon::prelude::*; -use vecdb::{AnyStoredVec, BytesVec, Database, WritableVec, ImportableVec, PcoVec, Rw, Stamp, StorageMode}; +use vecdb::{ + AnyStoredVec, BytesVec, Database, ImportableVec, PcoVec, Rw, Stamp, StorageMode, WritableVec, +}; use crate::parallel_import; @@ -17,13 +19,7 @@ pub struct OutputsVecs { impl OutputsVecs { pub fn forced_import(db: &Database, version: Version) -> Result { - let ( - first_txoutindex, - value, - outputtype, - typeindex, - txindex, - ) = parallel_import! { + let (first_txoutindex, value, outputtype, typeindex, txindex) = parallel_import! { first_txoutindex = PcoVec::forced_import(db, "first_txoutindex", version), value = BytesVec::forced_import(db, "value", version), outputtype = BytesVec::forced_import(db, "outputtype", version), diff --git a/crates/brk_indexer/src/vecs/scripts.rs b/crates/brk_indexer/src/vecs/scripts.rs index a591cb07e..d08136f7e 100644 --- a/crates/brk_indexer/src/vecs/scripts.rs +++ b/crates/brk_indexer/src/vecs/scripts.rs @@ -4,7 +4,7 @@ use brk_types::{ EmptyOutputIndex, Height, OpReturnIndex, P2MSOutputIndex, TxIndex, UnknownOutputIndex, Version, }; use rayon::prelude::*; -use vecdb::{AnyStoredVec, Database, WritableVec, ImportableVec, PcoVec, Rw, Stamp, StorageMode}; +use vecdb::{AnyStoredVec, Database, ImportableVec, PcoVec, Rw, Stamp, StorageMode, WritableVec}; use crate::parallel_import; diff --git a/crates/brk_indexer/src/vecs/transactions.rs b/crates/brk_indexer/src/vecs/transactions.rs index d8fcd151e..c65ebc56c 100644 --- a/crates/brk_indexer/src/vecs/transactions.rs +++ b/crates/brk_indexer/src/vecs/transactions.rs @@ -5,7 +5,9 @@ use brk_types::{ Version, }; use rayon::prelude::*; -use vecdb::{AnyStoredVec, BytesVec, Database, ImportableVec, PcoVec, Rw, Stamp, StorageMode, WritableVec}; +use vecdb::{ + AnyStoredVec, BytesVec, Database, ImportableVec, PcoVec, Rw, Stamp, StorageMode, WritableVec, +}; use crate::parallel_import; diff --git a/crates/brk_query/src/impl/cost_basis.rs b/crates/brk_query/src/impl/cost_basis.rs index 058b71f7f..f409e048d 100644 --- a/crates/brk_query/src/impl/cost_basis.rs +++ b/crates/brk_query/src/impl/cost_basis.rs @@ -4,7 +4,7 @@ use brk_error::{Error, Result}; use brk_types::{ CostBasisBucket, CostBasisDistribution, CostBasisFormatted, CostBasisValue, Date, Day1, }; -use vecdb::ReadableVec; +use vecdb::ReadableOptionVec; use crate::Query; @@ -87,7 +87,7 @@ impl Query { .cents .close .day1 - .collect_one(day1) + .collect_one_flat(day1) .ok_or_else(|| Error::NotFound(format!("No price data for {date}")))?; Ok(distribution.format(bucket, value, spot)) } diff --git a/crates/brk_query/src/impl/mining/block_fees.rs b/crates/brk_query/src/impl/mining/block_fees.rs index 09ad2de2c..876f9c149 100644 --- a/crates/brk_query/src/impl/mining/block_fees.rs +++ b/crates/brk_query/src/impl/mining/block_fees.rs @@ -15,7 +15,7 @@ impl Query { let iter = Day1Iter::new(computer, start, current_height.to_usize()); - let cumulative = &computer.transactions.fees.fee.sum_cum.cumulative; + let cumulative = &computer.transactions.fees.fee.sum_cumulative.cumulative; let first_height = &computer.indexes.day1.first_height; Ok(iter.collect(|di, ts, h| { @@ -28,13 +28,13 @@ impl Query { return None; } - let cum_end = cumulative.collect_one_at(h_end.to_usize() - 1)?; - let cum_start = if h_start.to_usize() > 0 { + let cumulative_end = cumulative.collect_one_at(h_end.to_usize() - 1)?; + let cumulative_start = if h_start.to_usize() > 0 { cumulative.collect_one_at(h_start.to_usize() - 1).unwrap_or(Sats::ZERO) } else { Sats::ZERO }; - let daily_sum = cum_end - cum_start; + let daily_sum = cumulative_end - cumulative_start; let avg_fees = Sats::from(*daily_sum / block_count as u64); Some(BlockFeesEntry { diff --git a/crates/brk_query/src/impl/mining/block_rewards.rs b/crates/brk_query/src/impl/mining/block_rewards.rs index c46a0c556..3c785b224 100644 --- a/crates/brk_query/src/impl/mining/block_rewards.rs +++ b/crates/brk_query/src/impl/mining/block_rewards.rs @@ -1,6 +1,6 @@ use brk_error::Result; use brk_types::{BlockRewardsEntry, TimePeriod}; -use vecdb::{ReadableVec, VecIndex}; +use vecdb::{ReadableOptionVec, VecIndex}; use super::day1_iter::Day1Iter; use crate::Query; @@ -20,11 +20,14 @@ impl Query { .rewards .coinbase .sats - .day1 - .average; + .rolling + .distribution + .average + ._24h + .day1; Ok(iter.collect(|di, ts, h| { - rewards_vec.collect_one(di).map(|reward| BlockRewardsEntry { + rewards_vec.collect_one_flat(di).map(|reward| BlockRewardsEntry { avg_height: h.into(), timestamp: *ts, avg_rewards: *reward, diff --git a/crates/brk_query/src/impl/mining/block_sizes.rs b/crates/brk_query/src/impl/mining/block_sizes.rs index 8ad1eb1be..bdb012de1 100644 --- a/crates/brk_query/src/impl/mining/block_sizes.rs +++ b/crates/brk_query/src/impl/mining/block_sizes.rs @@ -1,6 +1,6 @@ use brk_error::Result; use brk_types::{BlockSizeEntry, BlockSizesWeights, BlockWeightEntry, TimePeriod}; -use vecdb::{ReadableVec, VecIndex}; +use vecdb::{ReadableOptionVec, VecIndex}; use super::day1_iter::Day1Iter; use crate::Query; @@ -36,8 +36,8 @@ impl Query { .day1; let entries: Vec<_> = iter.collect(|di, ts, h| { - let size: Option = sizes_vec.collect_one(di).map(|s| *s); - let weight: Option = weights_vec.collect_one(di).map(|w| *w); + let size: Option = sizes_vec.collect_one_flat(di).map(|s| *s); + let weight: Option = weights_vec.collect_one_flat(di).map(|w| *w); Some((u32::from(h), (*ts), size, weight)) }); diff --git a/crates/brk_query/src/impl/mining/hashrate.rs b/crates/brk_query/src/impl/mining/hashrate.rs index 70c883394..ac55deae8 100644 --- a/crates/brk_query/src/impl/mining/hashrate.rs +++ b/crates/brk_query/src/impl/mining/hashrate.rs @@ -1,6 +1,6 @@ use brk_error::Result; use brk_types::{Day1, DifficultyEntry, HashrateEntry, HashrateSummary, Height, TimePeriod}; -use vecdb::{ReadableVec, VecIndex}; +use vecdb::{ReadableOptionVec, ReadableVec, VecIndex}; use super::epochs::iter_difficulty_epochs; use crate::Query; @@ -27,8 +27,8 @@ impl Query { .hashrate .hash_rate .day1 - .collect_one(current_day1) - .unwrap() as u128; + .collect_one_flat(current_day1) + .unwrap_or_default() as u128; // Calculate start height based on time period let end = current_height.to_usize(); @@ -61,11 +61,11 @@ impl Query { while di <= end_day1.to_usize() { let day1 = Day1::from(di); if let (Some(hr), Some(timestamp)) = - (hashrate_vec.collect_one(day1), timestamp_vec.collect_one(day1)) + (hashrate_vec.collect_one_flat(day1), timestamp_vec.collect_one(day1)) { hashrates.push(HashrateEntry { timestamp, - avg_hashrate: (*hr) as u128, + avg_hashrate: *hr as u128, }); } di += step; diff --git a/crates/brk_query/src/impl/mining/pools.rs b/crates/brk_query/src/impl/mining/pools.rs index da5a91e13..caf2b4649 100644 --- a/crates/brk_query/src/impl/mining/pools.rs +++ b/crates/brk_query/src/impl/mining/pools.rs @@ -32,7 +32,7 @@ impl Query { for (pool_id, pool_vecs) in &computer.pools.vecs { let cumulative = &pool_vecs .blocks_mined - .height_cumulative; + .cumulative.height; let count_at_end: u32 = *cumulative.collect_one(current_height).unwrap_or_default(); @@ -100,7 +100,7 @@ impl Query { let cumulative = &pool_vecs .blocks_mined - .height_cumulative; + .cumulative.height; // Get total blocks (all time) let total_all: u32 = *cumulative.collect_one(current_height).unwrap_or_default(); diff --git a/crates/brk_query/src/impl/mining/reward_stats.rs b/crates/brk_query/src/impl/mining/reward_stats.rs index 2f139a8af..dba2c8e5e 100644 --- a/crates/brk_query/src/impl/mining/reward_stats.rs +++ b/crates/brk_query/src/impl/mining/reward_stats.rs @@ -13,7 +13,7 @@ impl Query { let start_block = Height::from(current_height.to_usize().saturating_sub(block_count - 1)); let coinbase_vec = &computer.mining.rewards.coinbase.sats.height; - let fee_vec = &computer.transactions.fees.fee.sum_cum.sum.0; + let fee_vec = &computer.transactions.fees.fee.sum_cumulative.sum.0; let tx_count_vec = &computer.transactions.count.tx_count.height; let start = start_block.to_usize(); diff --git a/crates/brk_traversable/src/lib.rs b/crates/brk_traversable/src/lib.rs index d874c5e65..fa9f16fc3 100644 --- a/crates/brk_traversable/src/lib.rs +++ b/crates/brk_traversable/src/lib.rs @@ -9,8 +9,8 @@ use schemars::JsonSchema; use serde::Serialize; use vecdb::{ AnyExportableVec, AnyVec, BytesVec, BytesVecValue, CompressionStrategy, EagerVec, Formattable, - LazyVecFrom1, LazyVecFrom2, LazyVecFrom3, RawStrategy, ReadOnlyCompressedVec, ReadOnlyRawVec, - StoredVec, VecIndex, VecValue, + LazyAggVec, LazyVecFrom1, LazyVecFrom2, LazyVecFrom3, RawStrategy, ReadOnlyCompressedVec, + ReadOnlyRawVec, StoredVec, VecIndex, VecValue, }; pub trait Traversable { @@ -217,6 +217,23 @@ where } } +impl Traversable for LazyAggVec +where + I: VecIndex, + O: VecValue + Formattable + Serialize + JsonSchema, + S1I: VecIndex, + S2T: VecValue, + S1T: VecValue, +{ + fn iter_any_exportable(&self) -> impl Iterator { + std::iter::once(self as &dyn AnyExportableVec) + } + + fn to_tree_node(&self) -> TreeNode { + make_leaf::(self) + } +} + impl Traversable for Box { fn to_tree_node(&self) -> TreeNode { (**self).to_tree_node() diff --git a/crates/brk_types/src/treenode.rs b/crates/brk_types/src/treenode.rs index 3f83a5660..76ad4bf1a 100644 --- a/crates/brk_types/src/treenode.rs +++ b/crates/brk_types/src/treenode.rs @@ -416,9 +416,9 @@ mod tests { assert!(sum_indexes.contains(&Index::Week1)); assert!(sum_indexes.contains(&Index::Month1)); - let cum_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); - assert!(cum_indexes.contains(&Index::Week1)); - assert!(cum_indexes.contains(&Index::Month1)); + let cumulative_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); + assert!(cumulative_indexes.contains(&Index::Week1)); + assert!(cumulative_indexes.contains(&Index::Month1)); } _ => panic!("Expected branch"), } @@ -447,9 +447,9 @@ mod tests { assert_eq!(map.len(), 2); // cumulative merged: Height + Day1 - let cum_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); - assert!(cum_indexes.contains(&Index::Height)); - assert!(cum_indexes.contains(&Index::Day1)); + let cumulative_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); + assert!(cumulative_indexes.contains(&Index::Height)); + assert!(cumulative_indexes.contains(&Index::Day1)); // sum only has Day1 let sum_indexes = get_leaf_indexes(map.get("sum").unwrap()).unwrap(); @@ -509,11 +509,11 @@ mod tests { assert!(sum_indexes.contains(&Index::DifficultyEpoch)); // cumulative: Height, Day1, Week1, DifficultyEpoch - let cum_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); - assert!(cum_indexes.contains(&Index::Height)); - assert!(cum_indexes.contains(&Index::Day1)); - assert!(cum_indexes.contains(&Index::Week1)); - assert!(cum_indexes.contains(&Index::DifficultyEpoch)); + let cumulative_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); + assert!(cumulative_indexes.contains(&Index::Height)); + assert!(cumulative_indexes.contains(&Index::Day1)); + assert!(cumulative_indexes.contains(&Index::Week1)); + assert!(cumulative_indexes.contains(&Index::DifficultyEpoch)); } _ => panic!("Expected branch"), } @@ -704,9 +704,9 @@ mod tests { assert!(sum_indexes.contains(&Index::Day1)); assert!(sum_indexes.contains(&Index::Week1)); - let cum_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); - assert!(cum_indexes.contains(&Index::Day1)); - assert!(cum_indexes.contains(&Index::Week1)); + let cumulative_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); + assert!(cumulative_indexes.contains(&Index::Day1)); + assert!(cumulative_indexes.contains(&Index::Week1)); } _ => panic!("Expected branch with sum and cumulative"), } @@ -881,13 +881,13 @@ mod tests { assert_eq!(base_indexes.len(), 1); // cumulative should include Height (from height_cumulative) - let cum_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); + let cumulative_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); assert!( - cum_indexes.contains(&Index::Height), + cumulative_indexes.contains(&Index::Height), "cumulative should include Height" ); - assert!(cum_indexes.contains(&Index::Day1)); - assert!(cum_indexes.contains(&Index::Week1)); + assert!(cumulative_indexes.contains(&Index::Day1)); + assert!(cumulative_indexes.contains(&Index::Week1)); // average, min, max, sum should have Day1 and Week1 only for key in ["average", "min", "max", "sum"] { @@ -1057,9 +1057,9 @@ mod tests { assert!(matches!(map.get("sum"), Some(TreeNode::Leaf(_)))); // cumulative: Height merged with rest's cumulative - let cum_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); - assert!(cum_indexes.contains(&Index::Height)); - assert!(cum_indexes.contains(&Index::Day1)); + let cumulative_indexes = get_leaf_indexes(map.get("cumulative").unwrap()).unwrap(); + assert!(cumulative_indexes.contains(&Index::Height)); + assert!(cumulative_indexes.contains(&Index::Day1)); } _ => panic!("Expected branch"), }