diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index d9fbfc8e2..f2e592114 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -1074,12 +1074,12 @@ pub struct CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern { pub cap: CentsDeltaToUsdPattern, pub gross_pnl: BaseCumulativeSumPattern3, pub investor: PricePattern, - pub loss: BaseCapitulationCumulativeNegativeSumToValuePattern, + pub loss: BaseCumulativeNegativeSumToPattern, pub mvrv: SeriesPattern1, pub net_pnl: BaseChangeCumulativeDeltaSumToPattern, pub peak_regret: BaseCumulativeSumToPattern, pub price: BpsCentsPercentilesRatioSatsSmaStdUsdPattern, - pub profit: BaseCumulativeDistributionSumToValuePattern, + pub profit: BaseCumulativeSumToPattern, pub profit_to_loss_ratio: _1m1w1y24hPattern, pub sell_side_risk_ratio: _1m1w1y24hPattern6, pub sopr: AdjustedRatioValuePattern, @@ -1254,18 +1254,6 @@ impl AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2 { } } -/// Pattern struct for repeated tree structure. -pub struct BaseCapitulationCumulativeNegativeSumToValuePattern { - pub base: CentsUsdPattern2, - pub capitulation_flow: SeriesPattern1, - pub cumulative: CentsUsdPattern2, - pub negative: BaseSumPattern, - pub sum: _1m1w1y24hPattern4, - pub to_rcap: BpsPercentRatioPattern4, - pub value_created: BaseCumulativeSumPattern, - pub value_destroyed: BaseCumulativeSumPattern, -} - /// Pattern struct for repeated tree structure. pub struct BpsCentsPercentilesRatioSatsSmaStdUsdPattern { pub bps: SeriesPattern1, @@ -1358,17 +1346,6 @@ impl _1m1w1y24hBpsPercentRatioPattern { } } -/// Pattern struct for repeated tree structure. -pub struct BaseCumulativeDistributionSumToValuePattern { - pub base: CentsUsdPattern2, - pub cumulative: CentsUsdPattern2, - pub distribution_flow: SeriesPattern1, - pub sum: _1m1w1y24hPattern4, - pub to_rcap: BpsPercentRatioPattern4, - pub value_created: BaseCumulativeSumPattern, - pub value_destroyed: BaseCumulativeSumPattern, -} - /// Pattern struct for repeated tree structure. pub struct CapLossMvrvNetPriceProfitSoprPattern { pub cap: CentsDeltaUsdPattern, @@ -1688,6 +1665,15 @@ impl BaseCumulativeInSumPattern { } } +/// Pattern struct for repeated tree structure. +pub struct BaseCumulativeNegativeSumToPattern { + pub base: CentsUsdPattern2, + pub cumulative: CentsUsdPattern2, + pub negative: BaseSumPattern, + pub sum: _1m1w1y24hPattern4, + pub to_rcap: BpsPercentRatioPattern4, +} + /// Pattern struct for repeated tree structure. pub struct BpsCentsRatioSatsUsdPattern { pub bps: SeriesPattern1, @@ -6332,7 +6318,7 @@ impl SeriesTree_Cohorts_Utxo_All_Activity { /// Series tree node. pub struct SeriesTree_Cohorts_Utxo_All_Realized { pub cap: CentsDeltaToUsdPattern, - pub profit: SeriesTree_Cohorts_Utxo_All_Realized_Profit, + pub profit: BaseCumulativeSumToPattern, pub loss: SeriesTree_Cohorts_Utxo_All_Realized_Loss, pub price: SeriesTree_Cohorts_Utxo_All_Realized_Price, pub mvrv: SeriesPattern1, @@ -6349,7 +6335,7 @@ impl SeriesTree_Cohorts_Utxo_All_Realized { pub fn new(client: Arc, base_path: String) -> Self { Self { cap: CentsDeltaToUsdPattern::new(client.clone(), "realized_cap".to_string()), - profit: SeriesTree_Cohorts_Utxo_All_Realized_Profit::new(client.clone(), format!("{base_path}_profit")), + profit: BaseCumulativeSumToPattern::new(client.clone(), "realized_profit".to_string()), loss: SeriesTree_Cohorts_Utxo_All_Realized_Loss::new(client.clone(), format!("{base_path}_loss")), price: SeriesTree_Cohorts_Utxo_All_Realized_Price::new(client.clone(), format!("{base_path}_price")), mvrv: SeriesPattern1::new(client.clone(), "mvrv".to_string()), @@ -6364,31 +6350,6 @@ impl SeriesTree_Cohorts_Utxo_All_Realized { } } -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_All_Realized_Profit { - pub base: CentsUsdPattern2, - pub cumulative: CentsUsdPattern2, - pub sum: _1m1w1y24hPattern4, - pub to_rcap: BpsPercentRatioPattern4, - pub value_created: BaseCumulativeSumPattern, - pub value_destroyed: BaseCumulativeSumPattern, - pub distribution_flow: SeriesPattern1, -} - -impl SeriesTree_Cohorts_Utxo_All_Realized_Profit { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - base: CentsUsdPattern2::new(client.clone(), "realized_profit".to_string()), - cumulative: CentsUsdPattern2::new(client.clone(), "realized_profit_cumulative".to_string()), - sum: _1m1w1y24hPattern4::new(client.clone(), "realized_profit_sum".to_string()), - to_rcap: BpsPercentRatioPattern4::new(client.clone(), "realized_profit_to_rcap".to_string()), - value_created: BaseCumulativeSumPattern::new(client.clone(), "profit_value_created".to_string()), - value_destroyed: BaseCumulativeSumPattern::new(client.clone(), "profit_value_destroyed".to_string()), - distribution_flow: SeriesPattern1::new(client.clone(), "distribution_flow".to_string()), - } - } -} - /// Series tree node. pub struct SeriesTree_Cohorts_Utxo_All_Realized_Loss { pub base: CentsUsdPattern2, @@ -6396,9 +6357,6 @@ pub struct SeriesTree_Cohorts_Utxo_All_Realized_Loss { pub sum: _1m1w1y24hPattern4, pub negative: BaseSumPattern, pub to_rcap: BpsPercentRatioPattern4, - pub value_created: BaseCumulativeSumPattern, - pub value_destroyed: BaseCumulativeSumPattern, - pub capitulation_flow: SeriesPattern1, } impl SeriesTree_Cohorts_Utxo_All_Realized_Loss { @@ -6409,9 +6367,6 @@ impl SeriesTree_Cohorts_Utxo_All_Realized_Loss { sum: _1m1w1y24hPattern4::new(client.clone(), "realized_loss_sum".to_string()), negative: BaseSumPattern::new(client.clone(), "neg_realized_loss".to_string()), to_rcap: BpsPercentRatioPattern4::new(client.clone(), "realized_loss_to_rcap".to_string()), - value_created: BaseCumulativeSumPattern::new(client.clone(), "loss_value_created".to_string()), - value_destroyed: BaseCumulativeSumPattern::new(client.clone(), "loss_value_destroyed".to_string()), - capitulation_flow: SeriesPattern1::new(client.clone(), "capitulation_flow".to_string()), } } } @@ -6827,7 +6782,7 @@ impl SeriesTree_Cohorts_Utxo_Sth_Activity { /// Series tree node. pub struct SeriesTree_Cohorts_Utxo_Sth_Realized { pub cap: CentsDeltaToUsdPattern, - pub profit: SeriesTree_Cohorts_Utxo_Sth_Realized_Profit, + pub profit: BaseCumulativeSumToPattern, pub loss: SeriesTree_Cohorts_Utxo_Sth_Realized_Loss, pub price: SeriesTree_Cohorts_Utxo_Sth_Realized_Price, pub mvrv: SeriesPattern1, @@ -6844,7 +6799,7 @@ impl SeriesTree_Cohorts_Utxo_Sth_Realized { pub fn new(client: Arc, base_path: String) -> Self { Self { cap: CentsDeltaToUsdPattern::new(client.clone(), "sth_realized_cap".to_string()), - profit: SeriesTree_Cohorts_Utxo_Sth_Realized_Profit::new(client.clone(), format!("{base_path}_profit")), + profit: BaseCumulativeSumToPattern::new(client.clone(), "sth_realized_profit".to_string()), loss: SeriesTree_Cohorts_Utxo_Sth_Realized_Loss::new(client.clone(), format!("{base_path}_loss")), price: SeriesTree_Cohorts_Utxo_Sth_Realized_Price::new(client.clone(), format!("{base_path}_price")), mvrv: SeriesPattern1::new(client.clone(), "sth_mvrv".to_string()), @@ -6859,31 +6814,6 @@ impl SeriesTree_Cohorts_Utxo_Sth_Realized { } } -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Sth_Realized_Profit { - pub base: CentsUsdPattern2, - pub cumulative: CentsUsdPattern2, - pub sum: _1m1w1y24hPattern4, - pub to_rcap: BpsPercentRatioPattern4, - pub value_created: BaseCumulativeSumPattern, - pub value_destroyed: BaseCumulativeSumPattern, - pub distribution_flow: SeriesPattern1, -} - -impl SeriesTree_Cohorts_Utxo_Sth_Realized_Profit { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - base: CentsUsdPattern2::new(client.clone(), "sth_realized_profit".to_string()), - cumulative: CentsUsdPattern2::new(client.clone(), "sth_realized_profit_cumulative".to_string()), - sum: _1m1w1y24hPattern4::new(client.clone(), "sth_realized_profit_sum".to_string()), - to_rcap: BpsPercentRatioPattern4::new(client.clone(), "sth_realized_profit_to_rcap".to_string()), - value_created: BaseCumulativeSumPattern::new(client.clone(), "sth_profit_value_created".to_string()), - value_destroyed: BaseCumulativeSumPattern::new(client.clone(), "sth_profit_value_destroyed".to_string()), - distribution_flow: SeriesPattern1::new(client.clone(), "sth_distribution_flow".to_string()), - } - } -} - /// Series tree node. pub struct SeriesTree_Cohorts_Utxo_Sth_Realized_Loss { pub base: CentsUsdPattern2, @@ -6891,9 +6821,6 @@ pub struct SeriesTree_Cohorts_Utxo_Sth_Realized_Loss { pub sum: _1m1w1y24hPattern4, pub negative: BaseSumPattern, pub to_rcap: BpsPercentRatioPattern4, - pub value_created: BaseCumulativeSumPattern, - pub value_destroyed: BaseCumulativeSumPattern, - pub capitulation_flow: SeriesPattern1, } impl SeriesTree_Cohorts_Utxo_Sth_Realized_Loss { @@ -6904,9 +6831,6 @@ impl SeriesTree_Cohorts_Utxo_Sth_Realized_Loss { sum: _1m1w1y24hPattern4::new(client.clone(), "sth_realized_loss_sum".to_string()), negative: BaseSumPattern::new(client.clone(), "sth_neg_realized_loss".to_string()), to_rcap: BpsPercentRatioPattern4::new(client.clone(), "sth_realized_loss_to_rcap".to_string()), - value_created: BaseCumulativeSumPattern::new(client.clone(), "sth_loss_value_created".to_string()), - value_destroyed: BaseCumulativeSumPattern::new(client.clone(), "sth_loss_value_destroyed".to_string()), - capitulation_flow: SeriesPattern1::new(client.clone(), "sth_capitulation_flow".to_string()), } } } @@ -7265,7 +7189,7 @@ impl SeriesTree_Cohorts_Utxo_Lth_Activity { /// Series tree node. pub struct SeriesTree_Cohorts_Utxo_Lth_Realized { pub cap: CentsDeltaToUsdPattern, - pub profit: SeriesTree_Cohorts_Utxo_Lth_Realized_Profit, + pub profit: BaseCumulativeSumToPattern, pub loss: SeriesTree_Cohorts_Utxo_Lth_Realized_Loss, pub price: SeriesTree_Cohorts_Utxo_Lth_Realized_Price, pub mvrv: SeriesPattern1, @@ -7282,7 +7206,7 @@ impl SeriesTree_Cohorts_Utxo_Lth_Realized { pub fn new(client: Arc, base_path: String) -> Self { Self { cap: CentsDeltaToUsdPattern::new(client.clone(), "lth_realized_cap".to_string()), - profit: SeriesTree_Cohorts_Utxo_Lth_Realized_Profit::new(client.clone(), format!("{base_path}_profit")), + profit: BaseCumulativeSumToPattern::new(client.clone(), "lth_realized_profit".to_string()), loss: SeriesTree_Cohorts_Utxo_Lth_Realized_Loss::new(client.clone(), format!("{base_path}_loss")), price: SeriesTree_Cohorts_Utxo_Lth_Realized_Price::new(client.clone(), format!("{base_path}_price")), mvrv: SeriesPattern1::new(client.clone(), "lth_mvrv".to_string()), @@ -7297,31 +7221,6 @@ impl SeriesTree_Cohorts_Utxo_Lth_Realized { } } -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Profit { - pub base: CentsUsdPattern2, - pub cumulative: CentsUsdPattern2, - pub sum: _1m1w1y24hPattern4, - pub to_rcap: BpsPercentRatioPattern4, - pub value_created: BaseCumulativeSumPattern, - pub value_destroyed: BaseCumulativeSumPattern, - pub distribution_flow: SeriesPattern1, -} - -impl SeriesTree_Cohorts_Utxo_Lth_Realized_Profit { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - base: CentsUsdPattern2::new(client.clone(), "lth_realized_profit".to_string()), - cumulative: CentsUsdPattern2::new(client.clone(), "lth_realized_profit_cumulative".to_string()), - sum: _1m1w1y24hPattern4::new(client.clone(), "lth_realized_profit_sum".to_string()), - to_rcap: BpsPercentRatioPattern4::new(client.clone(), "lth_realized_profit_to_rcap".to_string()), - value_created: BaseCumulativeSumPattern::new(client.clone(), "lth_profit_value_created".to_string()), - value_destroyed: BaseCumulativeSumPattern::new(client.clone(), "lth_profit_value_destroyed".to_string()), - distribution_flow: SeriesPattern1::new(client.clone(), "lth_distribution_flow".to_string()), - } - } -} - /// Series tree node. pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Loss { pub base: CentsUsdPattern2, @@ -7329,9 +7228,6 @@ pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Loss { pub sum: _1m1w1y24hPattern4, pub negative: BaseSumPattern, pub to_rcap: BpsPercentRatioPattern4, - pub value_created: BaseCumulativeSumPattern, - pub value_destroyed: BaseCumulativeSumPattern, - pub capitulation_flow: SeriesPattern1, } impl SeriesTree_Cohorts_Utxo_Lth_Realized_Loss { @@ -7342,9 +7238,6 @@ impl SeriesTree_Cohorts_Utxo_Lth_Realized_Loss { sum: _1m1w1y24hPattern4::new(client.clone(), "lth_realized_loss_sum".to_string()), negative: BaseSumPattern::new(client.clone(), "lth_neg_realized_loss".to_string()), to_rcap: BpsPercentRatioPattern4::new(client.clone(), "lth_realized_loss_to_rcap".to_string()), - value_created: BaseCumulativeSumPattern::new(client.clone(), "lth_loss_value_created".to_string()), - value_destroyed: BaseCumulativeSumPattern::new(client.clone(), "lth_loss_value_destroyed".to_string()), - capitulation_flow: SeriesPattern1::new(client.clone(), "lth_capitulation_flow".to_string()), } } } diff --git a/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs b/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs index 1d887ec9a..0f31d8c97 100644 --- a/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs +++ b/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs @@ -542,9 +542,9 @@ impl UTXOCohorts { .metrics .realized .minimal - .sopr - .value_created + .transfer_volume .base + .cents .height .read_only_clone(); let under_1h_value_destroyed = self @@ -552,7 +552,6 @@ impl UTXOCohorts { .under_1h .metrics .realized - .minimal .sopr .value_destroyed .base diff --git a/crates/brk_computer/src/distribution/metrics/cohort/all.rs b/crates/brk_computer/src/distribution/metrics/cohort/all.rs index 02ca730c5..b413f27d8 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/all.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/all.rs @@ -128,8 +128,8 @@ impl AllCohortMetrics { self.asopr.compute_rest_part2( starting_indexes, - &self.realized.minimal.sopr.value_created.base.height, - &self.realized.minimal.sopr.value_destroyed.base.height, + &self.realized.minimal.transfer_volume.base.cents.height, + &self.realized.core.sopr.value_destroyed.base.height, under_1h_value_created, under_1h_value_destroyed, exit, diff --git a/crates/brk_computer/src/distribution/metrics/cohort/core.rs b/crates/brk_computer/src/distribution/metrics/cohort/core.rs index 4f4c3f9e0..3f0e73d26 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/core.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/core.rs @@ -114,7 +114,7 @@ impl CoreCohortMetrics { .compute_sent_profitability(prices, starting_indexes, exit)?; self.realized - .compute_rest_part1(starting_indexes, exit)?; + .compute_rest_part1(prices, starting_indexes, exit)?; self.unrealized.compute_rest(starting_indexes, exit)?; 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 fdc677d87..17de52390 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/extended_adjusted.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/extended_adjusted.rs @@ -80,8 +80,8 @@ impl ExtendedAdjustedCohortMetrics { self.asopr.compute_rest_part2( starting_indexes, - &self.inner.realized.minimal.sopr.value_created.base.height, - &self.inner.realized.minimal.sopr.value_destroyed.base.height, + &self.inner.realized.minimal.transfer_volume.base.cents.height, + &self.inner.realized.core.sopr.value_destroyed.base.height, under_1h_value_created, under_1h_value_destroyed, exit, diff --git a/crates/brk_computer/src/distribution/metrics/cohort/minimal.rs b/crates/brk_computer/src/distribution/metrics/cohort/minimal.rs index 264a9371d..0b8ca8972 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/minimal.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/minimal.rs @@ -90,7 +90,7 @@ impl MinimalCohortMetrics { ) -> Result<()> { self.supply.compute(prices, starting_indexes.height, exit)?; self.realized - .compute_rest_part1(starting_indexes, exit)?; + .compute_rest_part1(prices, starting_indexes, exit)?; Ok(()) } diff --git a/crates/brk_computer/src/distribution/metrics/cohort/type.rs b/crates/brk_computer/src/distribution/metrics/cohort/type.rs index 309a8516b..2936b06aa 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/type.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/type.rs @@ -60,7 +60,7 @@ impl TypeCohortMetrics { ) -> Result<()> { self.supply.compute(prices, starting_indexes.height, exit)?; self.realized - .compute_rest_part1(starting_indexes, exit)?; + .compute_rest_part1(prices, starting_indexes, exit)?; Ok(()) } diff --git a/crates/brk_computer/src/distribution/metrics/config.rs b/crates/brk_computer/src/distribution/metrics/config.rs index 687ff0349..c173131af 100644 --- a/crates/brk_computer/src/distribution/metrics/config.rs +++ b/crates/brk_computer/src/distribution/metrics/config.rs @@ -8,10 +8,8 @@ use crate::{ indexes, internal::{ AmountPerBlock, AmountPerBlockCumulative, AmountPerBlockCumulativeWithSums, - CachedWindowStarts, CentsType, PerBlock, - PerBlockCumulative, PerBlockCumulativeWithSums, - FiatPerBlock, FiatPerBlockCumulativeWithSums, NumericValue, - PercentPerBlock, PercentRollingWindows, Price, + CachedWindowStarts, CentsType, FiatPerBlock, FiatPerBlockCumulativeWithSums, NumericValue, + PerBlock, PerBlockCumulativeWithSums, PercentPerBlock, PercentRollingWindows, Price, PriceWithRatioExtendedPerBlock, PriceWithRatioPerBlock, RatioPerBlock, RollingWindow24hPerBlock, RollingWindows, RollingWindowsFrom1w, }, @@ -56,11 +54,6 @@ impl ConfigImport for PerBlock { Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes) } } -impl ConfigImport for PerBlockCumulative { - fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result { - Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes) - } -} impl ConfigImport for PerBlockCumulativeWithSums where T: NumericValue + JsonSchema + Into, diff --git a/crates/brk_computer/src/distribution/metrics/mod.rs b/crates/brk_computer/src/distribution/metrics/mod.rs index e6d0aed04..b97a56605 100644 --- a/crates/brk_computer/src/distribution/metrics/mod.rs +++ b/crates/brk_computer/src/distribution/metrics/mod.rs @@ -228,7 +228,7 @@ pub trait CohortMetricsBase: .compute_sent_profitability(prices, starting_indexes, exit)?; self.realized_mut() - .compute_rest_part1(starting_indexes, exit)?; + .compute_rest_part1(prices, starting_indexes, exit)?; self.unrealized_mut() .compute_rest(prices, 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 0f021392f..3de650a59 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/adjusted.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/adjusted.rs @@ -11,7 +11,7 @@ use crate::{ #[derive(Traversable)] pub struct AdjustedSopr { pub ratio: RollingWindows, - pub value_created: PerBlockCumulativeWithSums, + pub transfer_volume: PerBlockCumulativeWithSums, pub value_destroyed: PerBlockCumulativeWithSums, } @@ -19,7 +19,7 @@ impl AdjustedSopr { pub(crate) fn forced_import(cfg: &ImportConfig) -> Result { Ok(Self { ratio: cfg.import("asopr", Version::ONE)?, - value_created: cfg.import("adj_value_created", Version::ONE)?, + transfer_volume: cfg.import("adj_value_created", Version::ONE)?, value_destroyed: cfg.import("adj_value_destroyed", Version::ONE)?, }) } @@ -28,17 +28,16 @@ impl AdjustedSopr { pub(crate) fn compute_rest_part2( &mut self, starting_indexes: &Indexes, - base_value_created: &impl ReadableVec, + base_transfer_volume: &impl ReadableVec, base_value_destroyed: &impl ReadableVec, - under_1h_value_created: &impl ReadableVec, + under_1h_transfer_volume: &impl ReadableVec, under_1h_value_destroyed: &impl ReadableVec, exit: &Exit, ) -> Result<()> { - // Compute value_created = base.value_created - under_1h.value_created - self.value_created.base.height.compute_subtract( + self.transfer_volume.base.height.compute_subtract( starting_indexes.height, - base_value_created, - under_1h_value_created, + base_transfer_volume, + under_1h_transfer_volume, exit, )?; self.value_destroyed.base.height.compute_subtract( @@ -48,23 +47,21 @@ impl AdjustedSopr { exit, )?; - // Cumulatives (rolling sums are lazy) - self.value_created + self.transfer_volume .compute_rest(starting_indexes.height, exit)?; self.value_destroyed .compute_rest(starting_indexes.height, exit)?; - // SOPR ratios from lazy rolling sums - for ((sopr, vc), vd) in self + for ((sopr, tv), vd) in self .ratio .as_mut_array() .into_iter() - .zip(self.value_created.sum.as_array()) + .zip(self.transfer_volume.sum.as_array()) .zip(self.value_destroyed.sum.as_array()) { sopr.compute_binary::( starting_indexes.height, - &vc.height, + &tv.height, &vd.height, exit, )?; diff --git a/crates/brk_computer/src/distribution/metrics/realized/core.rs b/crates/brk_computer/src/distribution/metrics/realized/core.rs index 5077a0b7a..7cc929863 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/core.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/core.rs @@ -3,14 +3,14 @@ use brk_traversable::Traversable; use brk_types::{BasisPointsSigned32, Bitcoin, Cents, CentsSigned, Dollars, Height, Indexes, StoredF64, Version}; use derive_more::{Deref, DerefMut}; use vecdb::{ - AnyStoredVec, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, + AnyStoredVec, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, WritableVec, }; use crate::{ distribution::state::{CohortState, CostBasisOps, RealizedOps}, internal::{ FiatPerBlockCumulativeWithSumsAndDeltas, LazyPerBlock, NegCentsUnsignedToDollars, - RatioCents64, RollingWindow24hPerBlock, Windows, + PerBlockCumulativeWithSums, RatioCents64, RollingWindow24hPerBlock, Windows, }, prices, }; @@ -28,6 +28,7 @@ pub struct NegRealizedLoss { #[derive(Traversable)] pub struct RealizedSoprCore { + pub value_destroyed: PerBlockCumulativeWithSums, pub ratio: RollingWindow24hPerBlock, } @@ -80,11 +81,20 @@ impl RealizedCore { cfg.cached_starts, )?; + let value_destroyed = PerBlockCumulativeWithSums::forced_import( + cfg.db, + &cfg.name("value_destroyed"), + cfg.version + v1, + cfg.indexes, + cfg.cached_starts, + )?; + Ok(Self { minimal, neg_loss, net_pnl, sopr: RealizedSoprCore { + value_destroyed, ratio: cfg.import("sopr", v1)?, }, }) @@ -97,10 +107,13 @@ impl RealizedCore { #[inline(always)] pub(crate) fn push_state(&mut self, state: &CohortState) { self.minimal.push_state(state); + self.sopr.value_destroyed.base.height.push(state.realized.value_destroyed()); } pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> { - self.minimal.collect_vecs_mut() + let mut vecs = self.minimal.collect_vecs_mut(); + vecs.push(&mut self.sopr.value_destroyed.base.height); + vecs } pub(crate) fn compute_from_stateful( @@ -113,16 +126,22 @@ impl RealizedCore { self.minimal .compute_from_stateful(starting_indexes, &minimal_refs, exit)?; + sum_others!(self, starting_indexes, others, exit; sopr.value_destroyed.base.height); Ok(()) } pub(crate) fn compute_rest_part1( &mut self, + prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit, ) -> Result<()> { self.minimal - .compute_rest_part1(starting_indexes, exit)?; + .compute_rest_part1(prices, starting_indexes, exit)?; + + self.sopr + .value_destroyed + .compute_rest(starting_indexes.height, exit)?; self.net_pnl.base.cents.height.compute_transform2( starting_indexes.height, @@ -158,8 +177,8 @@ impl RealizedCore { ._24h .compute_binary::( starting_indexes.height, - &self.minimal.sopr.value_created.sum._24h.height, - &self.minimal.sopr.value_destroyed.sum._24h.height, + &self.minimal.transfer_volume.sum._24h.cents.height, + &self.sopr.value_destroyed.sum._24h.height, exit, )?; diff --git a/crates/brk_computer/src/distribution/metrics/realized/full.rs b/crates/brk_computer/src/distribution/metrics/realized/full.rs index b897b5e03..e8ebf770e 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/full.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/full.rs @@ -6,7 +6,7 @@ use brk_types::{ }; use derive_more::{Deref, DerefMut}; use vecdb::{ - AnyStoredVec, AnyVec, BytesVec, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, + AnyStoredVec, AnyVec, BytesVec, Exit, ReadableVec, Rw, StorageMode, WritableVec, }; @@ -14,9 +14,8 @@ use crate::{ blocks, distribution::state::{WithCapital, CohortState, CostBasisData, RealizedState}, internal::{ - CentsUnsignedToDollars, - PerBlockCumulativeWithSums, FiatPerBlockCumulativeWithSums, - LazyPerBlock, PercentPerBlock, PercentRollingWindows, + FiatPerBlockCumulativeWithSums, + PercentPerBlock, PercentRollingWindows, PriceWithRatioExtendedPerBlock, RatioCents64, RatioCentsBp32, RatioCentsSignedCentsBps32, RatioCentsSignedDollarsBps32, RatioDollarsBp32, RatioPerBlockPercentiles, RatioPerBlockStdDevBands, RatioSma, RollingWindows, @@ -30,19 +29,13 @@ use crate::distribution::metrics::ImportConfig; use super::RealizedCore; #[derive(Traversable)] -pub struct RealizedProfit { +pub struct RealizedProfitFull { pub to_rcap: PercentPerBlock, - pub value_created: PerBlockCumulativeWithSums, - pub value_destroyed: PerBlockCumulativeWithSums, - pub distribution_flow: LazyPerBlock, } #[derive(Traversable)] -pub struct RealizedLoss { +pub struct RealizedLossFull { pub to_rcap: PercentPerBlock, - pub value_created: PerBlockCumulativeWithSums, - pub value_destroyed: PerBlockCumulativeWithSums, - pub capitulation_flow: LazyPerBlock, } #[derive(Traversable)] @@ -81,8 +74,8 @@ pub struct RealizedFull { #[traversable(flatten)] pub core: RealizedCore, - pub profit: RealizedProfit, - pub loss: RealizedLoss, + pub profit: RealizedProfitFull, + pub loss: RealizedLossFull, pub gross_pnl: FiatPerBlockCumulativeWithSums, pub sell_side_risk_ratio: PercentRollingWindows, pub net_pnl: RealizedNetPnl, @@ -112,36 +105,11 @@ impl RealizedFull { let core = RealizedCore::forced_import(cfg)?; - // Profit - let profit_value_destroyed: PerBlockCumulativeWithSums = - cfg.import("profit_value_destroyed", v1)?; - let profit_flow = LazyPerBlock::from_computed::( - &cfg.name("distribution_flow"), - cfg.version, - profit_value_destroyed.base.height.read_only_boxed_clone(), - &profit_value_destroyed.base, - ); - let profit = RealizedProfit { + let profit = RealizedProfitFull { to_rcap: cfg.import("realized_profit_to_rcap", Version::new(2))?, - value_created: cfg.import("profit_value_created", v1)?, - value_destroyed: profit_value_destroyed, - distribution_flow: profit_flow, }; - - // Loss - let loss_value_destroyed: PerBlockCumulativeWithSums = - cfg.import("loss_value_destroyed", v1)?; - let capitulation_flow = LazyPerBlock::from_computed::( - &cfg.name("capitulation_flow"), - cfg.version, - loss_value_destroyed.base.height.read_only_boxed_clone(), - &loss_value_destroyed.base, - ); - let loss = RealizedLoss { + let loss = RealizedLossFull { to_rcap: cfg.import("realized_loss_to_rcap", Version::new(2))?, - value_created: cfg.import("loss_value_created", v1)?, - value_destroyed: loss_value_destroyed, - capitulation_flow, }; // Gross PnL @@ -216,15 +184,7 @@ impl RealizedFull { } pub(crate) fn min_stateful_len(&self) -> usize { - self.profit - .value_created - .base - .height - .len() - .min(self.profit.value_destroyed.base.height.len()) - .min(self.loss.value_created.base.height.len()) - .min(self.loss.value_destroyed.base.height.len()) - .min(self.investor.price.cents.height.len()) + self.investor.price.cents.height.len() .min(self.cap_raw.len()) .min(self.investor.cap_raw.len()) .min(self.peak_regret.value.base.cents.height.len()) @@ -236,26 +196,6 @@ impl RealizedFull { state: &CohortState>, ) { self.core.push_state(state); - self.profit - .value_created - .base - .height - .push(state.realized.profit_value_created()); - self.profit - .value_destroyed - .base - .height - .push(state.realized.profit_value_destroyed()); - self.loss - .value_created - .base - .height - .push(state.realized.loss_value_created()); - self.loss - .value_destroyed - .base - .height - .push(state.realized.loss_value_destroyed()); self.investor .price .cents @@ -276,10 +216,6 @@ impl RealizedFull { pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> { let mut vecs = self.core.collect_vecs_mut(); - vecs.push(&mut self.profit.value_created.base.height as &mut dyn AnyStoredVec); - vecs.push(&mut self.profit.value_destroyed.base.height); - vecs.push(&mut self.loss.value_created.base.height); - vecs.push(&mut self.loss.value_destroyed.base.height); vecs.push(&mut self.investor.price.cents.height); vecs.push(&mut self.cap_raw as &mut dyn AnyStoredVec); vecs.push(&mut self.investor.cap_raw as &mut dyn AnyStoredVec); @@ -304,26 +240,6 @@ impl RealizedFull { &mut self, accum: &RealizedFullAccum, ) { - self.profit - .value_created - .base - .height - .push(accum.profit_value_created()); - self.profit - .value_destroyed - .base - .height - .push(accum.profit_value_destroyed()); - self.loss - .value_created - .base - .height - .push(accum.loss_value_created()); - self.loss - .value_destroyed - .base - .height - .push(accum.loss_value_destroyed()); self.cap_raw .push(accum.cap_raw); self.investor @@ -354,11 +270,12 @@ impl RealizedFull { pub(crate) fn compute_rest_part1( &mut self, + prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit, ) -> Result<()> { self.core - .compute_rest_part1(starting_indexes, exit)?; + .compute_rest_part1(prices, starting_indexes, exit)?; self.peak_regret .value @@ -388,12 +305,12 @@ impl RealizedFull { .ratio_extended .as_mut_array() .into_iter() - .zip(self.core.minimal.sopr.value_created.sum.as_array()[1..].iter()) - .zip(self.core.minimal.sopr.value_destroyed.sum.as_array()[1..].iter()) + .zip(self.core.minimal.transfer_volume.sum.0.as_array()[1..].iter()) + .zip(self.core.sopr.value_destroyed.sum.as_array()[1..].iter()) { sopr.compute_binary::( starting_indexes.height, - &vc.height, + &vc.cents.height, &vd.height, exit, )?; @@ -425,20 +342,6 @@ impl RealizedFull { exit, )?; - // Profit/loss value created/destroyed cumulatives (rolling sums are lazy) - self.profit - .value_created - .compute_rest(starting_indexes.height, exit)?; - self.profit - .value_destroyed - .compute_rest(starting_indexes.height, exit)?; - self.loss - .value_created - .compute_rest(starting_indexes.height, exit)?; - self.loss - .value_destroyed - .compute_rest(starting_indexes.height, exit)?; - // Gross PnL self.gross_pnl.base.cents.height.compute_add( starting_indexes.height, @@ -554,10 +457,6 @@ impl RealizedFull { #[derive(Default)] pub struct RealizedFullAccum { - profit_value_created: CentsSats, - profit_value_destroyed: CentsSats, - loss_value_created: CentsSats, - loss_value_destroyed: CentsSats, pub(crate) cap_raw: CentsSats, pub(crate) investor_cap_raw: CentsSquaredSats, peak_regret: CentsSats, @@ -565,31 +464,11 @@ pub struct RealizedFullAccum { impl RealizedFullAccum { pub(crate) fn add(&mut self, state: &RealizedState) { - self.profit_value_created += CentsSats::new(state.profit_value_created_raw()); - self.profit_value_destroyed += CentsSats::new(state.profit_value_destroyed_raw()); - self.loss_value_created += CentsSats::new(state.loss_value_created_raw()); - self.loss_value_destroyed += CentsSats::new(state.loss_value_destroyed_raw()); self.cap_raw += state.cap_raw(); self.investor_cap_raw += state.investor_cap_raw(); self.peak_regret += CentsSats::new(state.peak_regret_raw()); } - pub(crate) fn profit_value_created(&self) -> Cents { - self.profit_value_created.to_cents() - } - - pub(crate) fn profit_value_destroyed(&self) -> Cents { - self.profit_value_destroyed.to_cents() - } - - pub(crate) fn loss_value_created(&self) -> Cents { - self.loss_value_created.to_cents() - } - - pub(crate) fn loss_value_destroyed(&self) -> Cents { - self.loss_value_destroyed.to_cents() - } - pub(crate) fn peak_regret(&self) -> Cents { self.peak_regret.to_cents() } diff --git a/crates/brk_computer/src/distribution/metrics/realized/minimal.rs b/crates/brk_computer/src/distribution/metrics/realized/minimal.rs index c752de780..1b35404f6 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/minimal.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/minimal.rs @@ -11,7 +11,7 @@ use vecdb::{ use crate::{ distribution::state::{CohortState, CostBasisOps, RealizedOps}, internal::{ - PerBlockCumulativeWithSums, FiatPerBlockCumulativeWithSums, + AmountPerBlockCumulativeWithSums, FiatPerBlockCumulativeWithSums, FiatPerBlockWithDeltas, Identity, LazyPerBlock, PriceWithRatioPerBlock, }, prices, @@ -19,12 +19,6 @@ use crate::{ use crate::distribution::metrics::ImportConfig; -#[derive(Traversable)] -pub struct RealizedSoprMinimal { - pub value_created: PerBlockCumulativeWithSums, - pub value_destroyed: PerBlockCumulativeWithSums, -} - #[derive(Traversable)] pub struct RealizedMinimal { pub cap: FiatPerBlockWithDeltas, @@ -33,7 +27,7 @@ pub struct RealizedMinimal { pub price: PriceWithRatioPerBlock, pub mvrv: LazyPerBlock, - pub sopr: RealizedSoprMinimal, + pub transfer_volume: AmountPerBlockCumulativeWithSums, } impl RealizedMinimal { @@ -56,16 +50,21 @@ impl RealizedMinimal { &price.ratio, ); + let transfer_volume = AmountPerBlockCumulativeWithSums::forced_import( + cfg.db, + &cfg.name("transfer_volume"), + cfg.version, + cfg.indexes, + cfg.cached_starts, + )?; + Ok(Self { cap, profit: cfg.import("realized_profit", v1)?, loss: cfg.import("realized_loss", v1)?, price, mvrv, - sopr: RealizedSoprMinimal { - value_created: cfg.import("value_created", v1)?, - value_destroyed: cfg.import("value_destroyed", v1)?, - }, + transfer_volume, }) } @@ -76,8 +75,7 @@ impl RealizedMinimal { .len() .min(self.profit.base.cents.height.len()) .min(self.loss.base.cents.height.len()) - .min(self.sopr.value_created.base.height.len()) - .min(self.sopr.value_destroyed.base.height.len()) + .min(self.transfer_volume.base.sats.height.len()) } #[inline(always)] @@ -85,16 +83,7 @@ impl RealizedMinimal { self.cap.cents.height.push(state.realized.cap()); self.profit.base.cents.height.push(state.realized.profit()); self.loss.base.cents.height.push(state.realized.loss()); - self.sopr - .value_created - .base - .height - .push(state.realized.value_created()); - self.sopr - .value_destroyed - .base - .height - .push(state.realized.value_destroyed()); + self.transfer_volume.base.sats.height.push(state.sent); } pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> { @@ -102,8 +91,7 @@ impl RealizedMinimal { &mut self.cap.cents.height as &mut dyn AnyStoredVec, &mut self.profit.base.cents.height, &mut self.loss.base.cents.height, - &mut self.sopr.value_created.base.height, - &mut self.sopr.value_destroyed.base.height, + &mut self.transfer_volume.base.sats.height, ] } @@ -116,24 +104,20 @@ impl RealizedMinimal { sum_others!(self, starting_indexes, others, exit; cap.cents.height); sum_others!(self, starting_indexes, others, exit; profit.base.cents.height); sum_others!(self, starting_indexes, others, exit; loss.base.cents.height); - sum_others!(self, starting_indexes, others, exit; sopr.value_created.base.height); - sum_others!(self, starting_indexes, others, exit; sopr.value_destroyed.base.height); + sum_others!(self, starting_indexes, others, exit; transfer_volume.base.sats.height); Ok(()) } pub(crate) fn compute_rest_part1( &mut self, + prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit, ) -> Result<()> { self.profit.compute_rest(starting_indexes.height, exit)?; self.loss.compute_rest(starting_indexes.height, exit)?; - self.sopr - .value_created - .compute_rest(starting_indexes.height, exit)?; - self.sopr - .value_destroyed - .compute_rest(starting_indexes.height, exit)?; + self.transfer_volume + .compute_rest(starting_indexes.height, prices, exit)?; Ok(()) } diff --git a/crates/brk_computer/src/distribution/metrics/realized/mod.rs b/crates/brk_computer/src/distribution/metrics/realized/mod.rs index 70e39fe88..b272c5e40 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/mod.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/mod.rs @@ -12,14 +12,14 @@ use brk_error::Result; use brk_types::Indexes; use vecdb::Exit; -use crate::distribution::state::{WithCapital, CohortState, CostBasisData, RealizedState}; +use crate::{distribution::state::{WithCapital, CohortState, CostBasisData, RealizedState}, prices}; pub trait RealizedLike: Send + Sync { fn as_core(&self) -> &RealizedCore; fn as_core_mut(&mut self) -> &mut RealizedCore; fn min_stateful_len(&self) -> usize; fn push_state(&mut self, state: &CohortState>); - fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()>; + fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()>; fn compute_from_stateful( &mut self, starting_indexes: &Indexes, @@ -36,8 +36,8 @@ impl RealizedLike for RealizedCore { fn push_state(&mut self, state: &CohortState>) { self.push_state(state) } - fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> { - self.compute_rest_part1(starting_indexes, exit) + fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> { + self.compute_rest_part1(prices, starting_indexes, exit) } fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedCore], exit: &Exit) -> Result<()> { self.compute_from_stateful(starting_indexes, others, exit) @@ -52,8 +52,8 @@ impl RealizedLike for RealizedFull { fn push_state(&mut self, state: &CohortState>) { self.push_state(state) } - fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> { - self.compute_rest_part1(starting_indexes, exit) + fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> { + self.compute_rest_part1(prices, starting_indexes, exit) } fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedCore], exit: &Exit) -> Result<()> { self.compute_from_stateful(starting_indexes, others, exit) diff --git a/crates/brk_computer/src/distribution/state/cohort/base.rs b/crates/brk_computer/src/distribution/state/cohort/base.rs index ec2d79158..8f1f004e4 100644 --- a/crates/brk_computer/src/distribution/state/cohort/base.rs +++ b/crates/brk_computer/src/distribution/state/cohort/base.rs @@ -96,8 +96,8 @@ impl CohortState { } pub(crate) fn reset_single_iteration_values(&mut self) { + self.sent = Sats::ZERO; if R::TRACK_ACTIVITY { - self.sent = Sats::ZERO; self.satdays_destroyed = Sats::ZERO; } self.realized.reset_single_iteration_values(); @@ -196,8 +196,8 @@ impl CohortState { pre: &SendPrecomputed, ) { self.supply -= supply; + self.sent += pre.sats; if R::TRACK_ACTIVITY { - self.sent += pre.sats; self.satdays_destroyed += pre.age.satdays_destroyed(pre.sats); } @@ -241,8 +241,8 @@ impl CohortState { self.supply -= supply; if supply.value > Sats::ZERO { + self.sent += supply.value; if R::TRACK_ACTIVITY { - self.sent += supply.value; self.satdays_destroyed += age.satdays_destroyed(supply.value); } diff --git a/crates/brk_computer/src/distribution/state/cost_basis/realized.rs b/crates/brk_computer/src/distribution/state/cost_basis/realized.rs index e80e9919e..e47e08fdc 100644 --- a/crates/brk_computer/src/distribution/state/cost_basis/realized.rs +++ b/crates/brk_computer/src/distribution/state/cost_basis/realized.rs @@ -8,9 +8,6 @@ pub trait RealizedOps: Default + Clone + Send + Sync + 'static { fn cap(&self) -> Cents; fn profit(&self) -> Cents; fn loss(&self) -> Cents; - fn value_created(&self) -> Cents { - Cents::ZERO - } fn value_destroyed(&self) -> Cents { Cents::ZERO } @@ -46,7 +43,6 @@ pub struct MinimalRealizedState { cap_raw: u128, profit_raw: u128, loss_raw: u128, - value_created_raw: u128, value_destroyed_raw: u128, } @@ -76,13 +72,6 @@ impl RealizedOps for MinimalRealizedState { } #[inline] - fn value_created(&self) -> Cents { - if self.value_created_raw == 0 { - return Cents::ZERO; - } - Cents::new((self.value_created_raw / Sats::ONE_BTC_U128) as u64) - } - #[inline] fn value_destroyed(&self) -> Cents { if self.value_destroyed_raw == 0 { @@ -103,7 +92,6 @@ impl RealizedOps for MinimalRealizedState { fn reset_single_iteration_values(&mut self) { self.profit_raw = 0; self.loss_raw = 0; - self.value_created_raw = 0; self.value_destroyed_raw = 0; } @@ -145,7 +133,6 @@ impl RealizedOps for MinimalRealizedState { Ordering::Equal => {} } self.cap_raw -= prev_ps.as_u128(); - self.value_created_raw += current_ps.as_u128(); self.value_destroyed_raw += prev_ps.as_u128(); } } @@ -177,11 +164,6 @@ impl RealizedOps for CoreRealizedState { self.minimal.loss() } - #[inline] - fn value_created(&self) -> Cents { - self.minimal.value_created() - } - #[inline] fn value_destroyed(&self) -> Cents { self.minimal.value_destroyed() @@ -263,14 +245,6 @@ pub struct RealizedState { core: CoreRealizedState, /// Raw investor cap: Σ(price² × sats) investor_cap_raw: CentsSquaredSats, - /// sell_price × sats for profit cases - profit_value_created_raw: u128, - /// cost_basis × sats for profit cases - profit_value_destroyed_raw: u128, - /// sell_price × sats for loss cases - loss_value_created_raw: u128, - /// cost_basis × sats for loss cases (= capitulation_flow) - loss_value_destroyed_raw: u128, /// Raw realized peak regret: Σ((peak - sell_price) × sats) peak_regret_raw: u128, } @@ -293,22 +267,9 @@ impl RealizedOps for RealizedState { self.core.loss() } - #[inline] - fn value_created(&self) -> Cents { - let raw = self.profit_value_created_raw + self.loss_value_created_raw; - if raw == 0 { - return Cents::ZERO; - } - Cents::new((raw / Sats::ONE_BTC_U128) as u64) - } - #[inline] fn value_destroyed(&self) -> Cents { - let raw = self.profit_value_destroyed_raw + self.loss_value_destroyed_raw; - if raw == 0 { - return Cents::ZERO; - } - Cents::new((raw / Sats::ONE_BTC_U128) as u64) + self.core.value_destroyed() } #[inline] @@ -334,10 +295,6 @@ impl RealizedOps for RealizedState { #[inline] fn reset_single_iteration_values(&mut self) { self.core.reset_single_iteration_values(); - self.profit_value_created_raw = 0; - self.profit_value_destroyed_raw = 0; - self.loss_value_created_raw = 0; - self.loss_value_destroyed_raw = 0; self.peak_regret_raw = 0; } @@ -370,24 +327,9 @@ impl RealizedOps for RealizedState { ath_ps: CentsSats, prev_investor_cap: CentsSquaredSats, ) { - // Delegate cap/profit/loss + value_created/destroyed + sent tracking to core self.core .send(sats, current_ps, prev_ps, ath_ps, prev_investor_cap); - // Per-component value flow tracking - let current = current_ps.as_u128(); - let prev = prev_ps.as_u128(); - match current_ps.cmp(&prev_ps) { - Ordering::Greater | Ordering::Equal => { - self.profit_value_created_raw += current; - self.profit_value_destroyed_raw += prev; - } - Ordering::Less => { - self.loss_value_created_raw += current; - self.loss_value_destroyed_raw += prev; - } - } - self.peak_regret_raw += (ath_ps - current_ps).as_u128(); self.investor_cap_raw -= prev_investor_cap; } @@ -417,42 +359,6 @@ impl RealizedState { self.investor_cap_raw } - /// Get profit value created as CentsUnsigned (sell_price × sats for profit cases). - #[inline] - pub(crate) fn profit_value_created(&self) -> Cents { - if self.profit_value_created_raw == 0 { - return Cents::ZERO; - } - Cents::new((self.profit_value_created_raw / Sats::ONE_BTC_U128) as u64) - } - - /// Get profit value destroyed as CentsUnsigned (cost_basis × sats for profit cases). - #[inline] - pub(crate) fn profit_value_destroyed(&self) -> Cents { - if self.profit_value_destroyed_raw == 0 { - return Cents::ZERO; - } - Cents::new((self.profit_value_destroyed_raw / Sats::ONE_BTC_U128) as u64) - } - - /// Get loss value created as CentsUnsigned (sell_price × sats for loss cases). - #[inline] - pub(crate) fn loss_value_created(&self) -> Cents { - if self.loss_value_created_raw == 0 { - return Cents::ZERO; - } - Cents::new((self.loss_value_created_raw / Sats::ONE_BTC_U128) as u64) - } - - /// Get loss value destroyed as CentsUnsigned (cost_basis × sats for loss cases). - #[inline] - pub(crate) fn loss_value_destroyed(&self) -> Cents { - if self.loss_value_destroyed_raw == 0 { - return Cents::ZERO; - } - Cents::new((self.loss_value_destroyed_raw / Sats::ONE_BTC_U128) as u64) - } - /// Get realized peak regret as CentsUnsigned. #[inline] pub(crate) fn peak_regret(&self) -> Cents { @@ -462,30 +368,6 @@ impl RealizedState { Cents::new((self.peak_regret_raw / Sats::ONE_BTC_U128) as u64) } - /// Raw profit value created for lossless aggregation. - #[inline] - pub(crate) fn profit_value_created_raw(&self) -> u128 { - self.profit_value_created_raw - } - - /// Raw profit value destroyed for lossless aggregation. - #[inline] - pub(crate) fn profit_value_destroyed_raw(&self) -> u128 { - self.profit_value_destroyed_raw - } - - /// Raw loss value created for lossless aggregation. - #[inline] - pub(crate) fn loss_value_created_raw(&self) -> u128 { - self.loss_value_created_raw - } - - /// Raw loss value destroyed for lossless aggregation. - #[inline] - pub(crate) fn loss_value_destroyed_raw(&self) -> u128 { - self.loss_value_destroyed_raw - } - /// Raw peak regret for lossless aggregation. #[inline] pub(crate) fn peak_regret_raw(&self) -> u128 { diff --git a/crates/brk_computer/src/internal/per_block/computed/cumulative.rs b/crates/brk_computer/src/internal/per_block/computed/cumulative.rs deleted file mode 100644 index df43696bf..000000000 --- a/crates/brk_computer/src/internal/per_block/computed/cumulative.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! PerBlockCumulative - base PerBlock + cumulative PerBlock. -//! -//! Like PerBlockCumulativeWithSums but without RollingWindows. -//! Used for distribution metrics where rolling is optional per cohort. - -use brk_error::Result; -use brk_traversable::Traversable; -use brk_types::{Height, Version}; -use schemars::JsonSchema; -use vecdb::{Database, Exit, Rw, StorageMode}; - -use crate::{ - indexes, - internal::{PerBlock, NumericValue}, -}; - -#[derive(Traversable)] -pub struct PerBlockCumulative -where - T: NumericValue + JsonSchema, -{ - pub base: PerBlock, - pub cumulative: PerBlock, -} - -impl PerBlockCumulative -where - T: NumericValue + JsonSchema, -{ - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let base = PerBlock::forced_import(db, name, version, indexes)?; - let cumulative = - PerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?; - - Ok(Self { base, cumulative }) - } - - /// Compute cumulative from already-filled base vec. - pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> - where - T: Default, - { - self.cumulative - .height - .compute_cumulative(max_from, &self.base.height, exit)?; - Ok(()) - } -} diff --git a/crates/brk_computer/src/internal/per_block/computed/mod.rs b/crates/brk_computer/src/internal/per_block/computed/mod.rs index bfed10b39..b6e362127 100644 --- a/crates/brk_computer/src/internal/per_block/computed/mod.rs +++ b/crates/brk_computer/src/internal/per_block/computed/mod.rs @@ -1,6 +1,5 @@ mod aggregated; mod base; -mod cumulative; mod cumulative_sum; mod distribution; mod full; @@ -13,7 +12,6 @@ mod with_deltas; pub use aggregated::*; pub use base::*; -pub use cumulative::*; pub use cumulative_sum::*; pub use distribution::*; pub use full::*; diff --git a/crates/brk_computer/src/internal/transform/bps.rs b/crates/brk_computer/src/internal/transform/bps.rs index 1f6740fa9..eb7c236c8 100644 --- a/crates/brk_computer/src/internal/transform/bps.rs +++ b/crates/brk_computer/src/internal/transform/bps.rs @@ -44,7 +44,7 @@ pub struct Bp16ToPercent; impl UnaryTransform for Bp16ToPercent { #[inline(always)] fn apply(bp: BasisPoints16) -> StoredF32 { - StoredF32::from(bp.inner() as f32 / 100.0) + StoredF32::from(bp.to_f32() * 100.0) } } @@ -53,7 +53,7 @@ pub struct Bp32ToPercent; impl UnaryTransform for Bp32ToPercent { #[inline(always)] fn apply(bp: BasisPoints32) -> StoredF32 { - StoredF32::from(bp.inner() as f32 / 100.0) + StoredF32::from(bp.to_f32() * 100.0) } } @@ -62,7 +62,7 @@ pub struct Bps16ToPercent; impl UnaryTransform for Bps16ToPercent { #[inline(always)] fn apply(bp: BasisPointsSigned16) -> StoredF32 { - StoredF32::from(bp.inner() as f32 / 100.0) + StoredF32::from(bp.to_f32() * 100.0) } } @@ -71,6 +71,6 @@ pub struct Bps32ToPercent; impl UnaryTransform for Bps32ToPercent { #[inline(always)] fn apply(bp: BasisPointsSigned32) -> StoredF32 { - StoredF32::from(bp.inner() as f32 / 100.0) + StoredF32::from(bp.to_f32() * 100.0) } } diff --git a/crates/brk_computer/src/supply/compute.rs b/crates/brk_computer/src/supply/compute.rs index 2afc00abb..56a1394c7 100644 --- a/crates/brk_computer/src/supply/compute.rs +++ b/crates/brk_computer/src/supply/compute.rs @@ -1,7 +1,10 @@ use brk_error::Result; -use brk_types::Indexes; +use brk_types::{Indexes, Sats}; use vecdb::Exit; +/// Initial block subsidy (50 BTC) in sats, as f64 for floating-point comparisons. +const INITIAL_SUBSIDY: f64 = Sats::ONE_BTC_U64 as f64 * 50.0; + use super::Vecs; use crate::{blocks, distribution, mining, prices, scripts, transactions}; @@ -28,15 +31,24 @@ impl Vecs { )?; // 2. Compute inflation rate: (supply[h] / supply[1y_ago]) - 1 + // Skip when lookback supply <= first block (50 BTC = 5B sats), + // i.e. the lookback points to block 0 or 1 in the genesis era. let circulating_supply = &distribution.utxo_cohorts.all.metrics.supply.total.sats; self.inflation_rate .bps .height - .compute_rolling_ratio_change( + .compute_rolling_from_window_starts( starting_indexes.height, &blocks.lookback._1y, &circulating_supply.height, exit, + |current, previous| { + if previous.is_nan() || previous <= INITIAL_SUBSIDY { + f64::NAN + } else { + current / previous - 1.0 + } + }, )?; // 3. Compute velocity at height level diff --git a/crates/brk_computer/src/supply/import.rs b/crates/brk_computer/src/supply/import.rs index 98bc17768..de25201a1 100644 --- a/crates/brk_computer/src/supply/import.rs +++ b/crates/brk_computer/src/supply/import.rs @@ -37,8 +37,12 @@ impl Vecs { let burned = burned::Vecs::forced_import(&db, version, indexes, cached_starts)?; // Inflation rate - let inflation_rate = - PercentPerBlock::forced_import(&db, "inflation_rate", version, indexes)?; + let inflation_rate = PercentPerBlock::forced_import( + &db, + "inflation_rate", + version + Version::ONE, + indexes, + )?; // Velocity let velocity = super::velocity::Vecs::forced_import(&db, version, indexes)?; diff --git a/crates/brk_types/src/basis_points_16.rs b/crates/brk_types/src/basis_points_16.rs index 158db615a..d8f626585 100644 --- a/crates/brk_types/src/basis_points_16.rs +++ b/crates/brk_types/src/basis_points_16.rs @@ -3,13 +3,14 @@ use std::ops::{Add, AddAssign, Div, Sub}; use derive_more::Deref; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use vecdb::{CheckedSub, Formattable, Pco}; +use vecdb::{unlikely, CheckedSub, Formattable, Pco}; use super::StoredF32; /// Unsigned basis points stored as u16. /// 1 bp = 0.0001. Range: 0–6.5535. /// Use for bounded 0–1 ratios (dominance, adoption, liveliness, etc.). +/// `u16::MAX` is reserved as a NaN sentinel. #[derive( Debug, Deref, @@ -31,9 +32,12 @@ pub struct BasisPoints16(u16); impl BasisPoints16 { pub const ZERO: Self = Self(0); pub const ONE: Self = Self(10000); + /// NaN sentinel — uses u16::MAX which is outside the practical range. + pub const NAN: Self = Self(u16::MAX); #[inline] pub const fn new(value: u16) -> Self { + debug_assert!(value != u16::MAX, "u16::MAX is reserved as NaN sentinel"); Self(value) } @@ -42,10 +46,19 @@ impl BasisPoints16 { self.0 } - /// Convert to f32: divide by 10000. + #[inline] + pub fn is_nan(self) -> bool { + self.0 == u16::MAX + } + + /// Convert to f32: divide by 10000. Returns NaN for sentinel value. #[inline] pub fn to_f32(self) -> f32 { - self.0 as f32 / 10000.0 + if unlikely(self.0 == u16::MAX) { + f32::NAN + } else { + self.0 as f32 / 10000.0 + } } } @@ -53,7 +66,7 @@ impl From for BasisPoints16 { #[inline] fn from(value: usize) -> Self { debug_assert!( - value <= u16::MAX as usize, + value < u16::MAX as usize, "usize out of BasisPoints16 range: {value}" ); Self(value as u16) @@ -63,6 +76,7 @@ impl From for BasisPoints16 { impl From for BasisPoints16 { #[inline] fn from(value: u16) -> Self { + debug_assert!(value != u16::MAX, "u16::MAX is reserved as NaN sentinel"); Self(value) } } @@ -76,28 +90,28 @@ impl From for u16 { /// Convert from f32: multiply by 10000 and round. /// Input is in ratio form (e.g., 0.4523 for 45.23%). +/// NaN/Inf → NaN sentinel. impl From for BasisPoints16 { #[inline] fn from(value: f32) -> Self { - let scaled = (value * 10000.0).round(); - debug_assert!( - scaled >= 0.0 && scaled <= u16::MAX as f32, - "f32 out of BasisPoints16 range: {value}" - ); + if unlikely(!value.is_finite()) { + return Self::NAN; + } + let scaled = (value * 10000.0).round().clamp(0.0, u16::MAX as f32 - 1.0); Self(scaled as u16) } } /// Convert from f64: multiply by 10000 and round. /// Input is in ratio form (e.g., 0.4523 for 45.23%). +/// NaN/Inf → NaN sentinel. impl From for BasisPoints16 { #[inline] fn from(value: f64) -> Self { - let scaled = (value * 10000.0).round(); - debug_assert!( - scaled >= 0.0 && scaled <= u16::MAX as f64, - "f64 out of BasisPoints16 range: {value}" - ); + if unlikely(!value.is_finite()) { + return Self::NAN; + } + let scaled = (value * 10000.0).round().clamp(0.0, u16::MAX as f64 - 1.0); Self(scaled as u16) } } @@ -105,7 +119,11 @@ impl From for BasisPoints16 { impl From for f64 { #[inline] fn from(value: BasisPoints16) -> Self { - value.0 as f64 / 10000.0 + if unlikely(value.0 == u16::MAX) { + f64::NAN + } else { + value.0 as f64 / 10000.0 + } } } @@ -120,7 +138,11 @@ impl Add for BasisPoints16 { type Output = Self; #[inline] fn add(self, rhs: Self) -> Self::Output { - Self(self.0 + rhs.0) + if unlikely(self.0 == u16::MAX || rhs.0 == u16::MAX) { + Self::NAN + } else { + Self(self.0 + rhs.0) + } } } @@ -128,14 +150,18 @@ impl Sub for BasisPoints16 { type Output = Self; #[inline] fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) + if unlikely(self.0 == u16::MAX || rhs.0 == u16::MAX) { + Self::NAN + } else { + Self(self.0 - rhs.0) + } } } impl AddAssign for BasisPoints16 { #[inline] fn add_assign(&mut self, rhs: Self) { - self.0 += rhs.0; + *self = *self + rhs; } } @@ -143,14 +169,22 @@ impl Div for BasisPoints16 { type Output = Self; #[inline] fn div(self, rhs: usize) -> Self::Output { - debug_assert!(rhs <= u16::MAX as usize, "divisor out of u16 range: {rhs}"); - Self(self.0 / rhs as u16) + if unlikely(self.0 == u16::MAX) { + Self::NAN + } else { + debug_assert!(rhs <= u16::MAX as usize, "divisor out of u16 range: {rhs}"); + Self(self.0 / rhs as u16) + } } } impl CheckedSub for BasisPoints16 { fn checked_sub(self, rhs: Self) -> Option { - self.0.checked_sub(rhs.0).map(Self) + if unlikely(self.0 == u16::MAX || rhs.0 == u16::MAX) { + Some(Self::NAN) + } else { + self.0.checked_sub(rhs.0).map(Self) + } } } @@ -168,4 +202,13 @@ impl Formattable for BasisPoints16 { let mut b = itoa::Buffer::new(); buf.extend_from_slice(b.format(self.0).as_bytes()); } + + #[inline(always)] + fn fmt_json(&self, buf: &mut Vec) { + if unlikely(self.0 == u16::MAX) { + buf.extend_from_slice(b"null"); + } else { + self.write_to(buf); + } + } } diff --git a/crates/brk_types/src/basis_points_signed_16.rs b/crates/brk_types/src/basis_points_signed_16.rs index 532ce8ae0..c5d153288 100644 --- a/crates/brk_types/src/basis_points_signed_16.rs +++ b/crates/brk_types/src/basis_points_signed_16.rs @@ -3,13 +3,14 @@ use std::ops::{Add, AddAssign, Div, Sub, SubAssign}; use derive_more::Deref; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use vecdb::{CheckedSub, Formattable, Pco}; +use vecdb::{unlikely, CheckedSub, Formattable, Pco}; use super::StoredF32; /// Signed basis points stored as i16. /// 1 bp = 0.0001. Range: -3.2767 to +3.2767. /// Use for signed bounded ratios (NUPL, net PnL ratios, etc.). +/// `i16::MIN` is reserved as a NaN sentinel. #[derive( Debug, Deref, @@ -30,9 +31,12 @@ pub struct BasisPointsSigned16(i16); impl BasisPointsSigned16 { pub const ZERO: Self = Self(0); + /// NaN sentinel — uses i16::MIN which is outside the documented range. + pub const NAN: Self = Self(i16::MIN); #[inline] pub const fn new(value: i16) -> Self { + debug_assert!(value != i16::MIN, "i16::MIN is reserved as NaN sentinel"); Self(value) } @@ -42,14 +46,23 @@ impl BasisPointsSigned16 { } #[inline] - pub fn is_negative(self) -> bool { - self.0 < 0 + pub fn is_nan(self) -> bool { + self.0 == i16::MIN } - /// Convert to f32: divide by 10000. + #[inline] + pub fn is_negative(self) -> bool { + self.0 < 0 && self.0 != i16::MIN + } + + /// Convert to f32: divide by 10000. Returns NaN for sentinel value. #[inline] pub fn to_f32(self) -> f32 { - self.0 as f32 / 10000.0 + if unlikely(self.0 == i16::MIN) { + f32::NAN + } else { + self.0 as f32 / 10000.0 + } } } @@ -67,6 +80,7 @@ impl From for BasisPointsSigned16 { impl From for BasisPointsSigned16 { #[inline] fn from(value: i16) -> Self { + debug_assert!(value != i16::MIN, "i16::MIN is reserved as NaN sentinel"); Self(value) } } @@ -80,21 +94,28 @@ impl From for i16 { /// Convert from float: multiply by 10000 and round. /// Input is in ratio form (e.g., -0.4523 for -45.23%). +/// NaN/Inf → NaN sentinel. impl From for BasisPointsSigned16 { #[inline] fn from(value: f64) -> Self { - debug_assert!( - value >= i16::MIN as f64 / 10000.0 && value <= i16::MAX as f64 / 10000.0, - "f64 out of BasisPointsSigned16 range: {value}" - ); - Self((value * 10000.0).round() as i16) + if unlikely(!value.is_finite()) { + return Self::NAN; + } + let scaled = (value * 10000.0) + .round() + .clamp(i16::MIN as f64 + 1.0, i16::MAX as f64); + Self(scaled as i16) } } impl From for f64 { #[inline] fn from(value: BasisPointsSigned16) -> Self { - value.0 as f64 / 10000.0 + if unlikely(value.0 == i16::MIN) { + f64::NAN + } else { + value.0 as f64 / 10000.0 + } } } @@ -109,14 +130,18 @@ impl Add for BasisPointsSigned16 { type Output = Self; #[inline] fn add(self, rhs: Self) -> Self::Output { - Self(self.0 + rhs.0) + if unlikely(self.0 == i16::MIN || rhs.0 == i16::MIN) { + Self::NAN + } else { + Self(self.0 + rhs.0) + } } } impl AddAssign for BasisPointsSigned16 { #[inline] fn add_assign(&mut self, rhs: Self) { - self.0 += rhs.0; + *self = *self + rhs; } } @@ -124,14 +149,18 @@ impl Sub for BasisPointsSigned16 { type Output = Self; #[inline] fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) + if unlikely(self.0 == i16::MIN || rhs.0 == i16::MIN) { + Self::NAN + } else { + Self(self.0 - rhs.0) + } } } impl SubAssign for BasisPointsSigned16 { #[inline] fn sub_assign(&mut self, rhs: Self) { - self.0 -= rhs.0; + *self = *self - rhs; } } @@ -139,14 +168,22 @@ impl Div for BasisPointsSigned16 { type Output = Self; #[inline] fn div(self, rhs: usize) -> Self::Output { - debug_assert!(rhs <= i16::MAX as usize, "divisor out of i16 range: {rhs}"); - Self(self.0 / rhs as i16) + if unlikely(self.0 == i16::MIN) { + Self::NAN + } else { + debug_assert!(rhs <= i16::MAX as usize, "divisor out of i16 range: {rhs}"); + Self(self.0 / rhs as i16) + } } } impl CheckedSub for BasisPointsSigned16 { fn checked_sub(self, rhs: Self) -> Option { - self.0.checked_sub(rhs.0).map(Self) + if unlikely(self.0 == i16::MIN || rhs.0 == i16::MIN) { + Some(Self::NAN) + } else { + self.0.checked_sub(rhs.0).map(Self) + } } } @@ -164,4 +201,13 @@ impl Formattable for BasisPointsSigned16 { let mut b = itoa::Buffer::new(); buf.extend_from_slice(b.format(self.0).as_bytes()); } + + #[inline(always)] + fn fmt_json(&self, buf: &mut Vec) { + if unlikely(self.0 == i16::MIN) { + buf.extend_from_slice(b"null"); + } else { + self.write_to(buf); + } + } } diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index 71a1a5fed..a4d3efa79 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -70,6 +70,7 @@ * Unsigned basis points stored as u16. * 1 bp = 0.0001. Range: 0–6.5535. * Use for bounded 0–1 ratios (dominance, adoption, liveliness, etc.). + * `u16::MAX` is reserved as a NaN sentinel. * * @typedef {number} BasisPoints16 */ @@ -85,6 +86,7 @@ * Signed basis points stored as i16. * 1 bp = 0.0001. Range: -3.2767 to +3.2767. * Use for signed bounded ratios (NUPL, net PnL ratios, etc.). + * `i16::MIN` is reserved as a NaN sentinel. * * @typedef {number} BasisPointsSigned16 */ @@ -1786,12 +1788,12 @@ function create_10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, acc) { * @property {CentsDeltaToUsdPattern} cap * @property {BaseCumulativeSumPattern3} grossPnl * @property {PricePattern} investor - * @property {BaseCapitulationCumulativeNegativeSumToValuePattern} loss + * @property {BaseCumulativeNegativeSumToPattern} loss * @property {SeriesPattern1} mvrv * @property {BaseChangeCumulativeDeltaSumToPattern} netPnl * @property {BaseCumulativeSumToPattern} peakRegret * @property {BpsCentsPercentilesRatioSatsSmaStdUsdPattern} price - * @property {BaseCumulativeDistributionSumToValuePattern} profit + * @property {BaseCumulativeSumToPattern} profit * @property {_1m1w1y24hPattern} profitToLossRatio * @property {_1m1w1y24hPattern6} sellSideRiskRatio * @property {AdjustedRatioValuePattern} sopr @@ -1983,18 +1985,6 @@ function createAverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2(client, acc) { }; } -/** - * @typedef {Object} BaseCapitulationCumulativeNegativeSumToValuePattern - * @property {CentsUsdPattern2} base - * @property {SeriesPattern1} capitulationFlow - * @property {CentsUsdPattern2} cumulative - * @property {BaseSumPattern} negative - * @property {_1m1w1y24hPattern4} sum - * @property {BpsPercentRatioPattern4} toRcap - * @property {BaseCumulativeSumPattern} valueCreated - * @property {BaseCumulativeSumPattern} valueDestroyed - */ - /** * @typedef {Object} BpsCentsPercentilesRatioSatsSmaStdUsdPattern * @property {SeriesPattern1} bps @@ -2098,17 +2088,6 @@ function create_1m1w1y24hBpsPercentRatioPattern(client, acc) { }; } -/** - * @typedef {Object} BaseCumulativeDistributionSumToValuePattern - * @property {CentsUsdPattern2} base - * @property {CentsUsdPattern2} cumulative - * @property {SeriesPattern1} distributionFlow - * @property {_1m1w1y24hPattern4} sum - * @property {BpsPercentRatioPattern4} toRcap - * @property {BaseCumulativeSumPattern} valueCreated - * @property {BaseCumulativeSumPattern} valueDestroyed - */ - /** * @typedef {Object} CapLossMvrvNetPriceProfitSoprPattern * @property {CentsDeltaUsdPattern} cap @@ -2467,6 +2446,15 @@ function createBaseCumulativeInSumPattern(client, acc) { }; } +/** + * @typedef {Object} BaseCumulativeNegativeSumToPattern + * @property {CentsUsdPattern2} base + * @property {CentsUsdPattern2} cumulative + * @property {BaseSumPattern} negative + * @property {_1m1w1y24hPattern4} sum + * @property {BpsPercentRatioPattern4} toRcap + */ + /** * @typedef {Object} BpsCentsRatioSatsUsdPattern * @property {SeriesPattern1} bps @@ -5404,7 +5392,7 @@ function createUnspentPattern(client, acc) { /** * @typedef {Object} SeriesTree_Cohorts_Utxo_All_Realized * @property {CentsDeltaToUsdPattern} cap - * @property {SeriesTree_Cohorts_Utxo_All_Realized_Profit} profit + * @property {BaseCumulativeSumToPattern} profit * @property {SeriesTree_Cohorts_Utxo_All_Realized_Loss} loss * @property {SeriesTree_Cohorts_Utxo_All_Realized_Price} price * @property {SeriesPattern1} mvrv @@ -5417,17 +5405,6 @@ function createUnspentPattern(client, acc) { * @property {_1m1w1y24hPattern} profitToLossRatio */ -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_All_Realized_Profit - * @property {CentsUsdPattern2} base - * @property {CentsUsdPattern2} cumulative - * @property {_1m1w1y24hPattern4} sum - * @property {BpsPercentRatioPattern4} toRcap - * @property {BaseCumulativeSumPattern} valueCreated - * @property {BaseCumulativeSumPattern} valueDestroyed - * @property {SeriesPattern1} distributionFlow - */ - /** * @typedef {Object} SeriesTree_Cohorts_Utxo_All_Realized_Loss * @property {CentsUsdPattern2} base @@ -5435,9 +5412,6 @@ function createUnspentPattern(client, acc) { * @property {_1m1w1y24hPattern4} sum * @property {BaseSumPattern} negative * @property {BpsPercentRatioPattern4} toRcap - * @property {BaseCumulativeSumPattern} valueCreated - * @property {BaseCumulativeSumPattern} valueDestroyed - * @property {SeriesPattern1} capitulationFlow */ /** @@ -5623,7 +5597,7 @@ function createUnspentPattern(client, acc) { /** * @typedef {Object} SeriesTree_Cohorts_Utxo_Sth_Realized * @property {CentsDeltaToUsdPattern} cap - * @property {SeriesTree_Cohorts_Utxo_Sth_Realized_Profit} profit + * @property {BaseCumulativeSumToPattern} profit * @property {SeriesTree_Cohorts_Utxo_Sth_Realized_Loss} loss * @property {SeriesTree_Cohorts_Utxo_Sth_Realized_Price} price * @property {SeriesPattern1} mvrv @@ -5636,17 +5610,6 @@ function createUnspentPattern(client, acc) { * @property {_1m1w1y24hPattern} profitToLossRatio */ -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Sth_Realized_Profit - * @property {CentsUsdPattern2} base - * @property {CentsUsdPattern2} cumulative - * @property {_1m1w1y24hPattern4} sum - * @property {BpsPercentRatioPattern4} toRcap - * @property {BaseCumulativeSumPattern} valueCreated - * @property {BaseCumulativeSumPattern} valueDestroyed - * @property {SeriesPattern1} distributionFlow - */ - /** * @typedef {Object} SeriesTree_Cohorts_Utxo_Sth_Realized_Loss * @property {CentsUsdPattern2} base @@ -5654,9 +5617,6 @@ function createUnspentPattern(client, acc) { * @property {_1m1w1y24hPattern4} sum * @property {BaseSumPattern} negative * @property {BpsPercentRatioPattern4} toRcap - * @property {BaseCumulativeSumPattern} valueCreated - * @property {BaseCumulativeSumPattern} valueDestroyed - * @property {SeriesPattern1} capitulationFlow */ /** @@ -5818,7 +5778,7 @@ function createUnspentPattern(client, acc) { /** * @typedef {Object} SeriesTree_Cohorts_Utxo_Lth_Realized * @property {CentsDeltaToUsdPattern} cap - * @property {SeriesTree_Cohorts_Utxo_Lth_Realized_Profit} profit + * @property {BaseCumulativeSumToPattern} profit * @property {SeriesTree_Cohorts_Utxo_Lth_Realized_Loss} loss * @property {SeriesTree_Cohorts_Utxo_Lth_Realized_Price} price * @property {SeriesPattern1} mvrv @@ -5831,17 +5791,6 @@ function createUnspentPattern(client, acc) { * @property {_1m1w1y24hPattern} profitToLossRatio */ -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Lth_Realized_Profit - * @property {CentsUsdPattern2} base - * @property {CentsUsdPattern2} cumulative - * @property {_1m1w1y24hPattern4} sum - * @property {BpsPercentRatioPattern4} toRcap - * @property {BaseCumulativeSumPattern} valueCreated - * @property {BaseCumulativeSumPattern} valueDestroyed - * @property {SeriesPattern1} distributionFlow - */ - /** * @typedef {Object} SeriesTree_Cohorts_Utxo_Lth_Realized_Loss * @property {CentsUsdPattern2} base @@ -5849,9 +5798,6 @@ function createUnspentPattern(client, acc) { * @property {_1m1w1y24hPattern4} sum * @property {BaseSumPattern} negative * @property {BpsPercentRatioPattern4} toRcap - * @property {BaseCumulativeSumPattern} valueCreated - * @property {BaseCumulativeSumPattern} valueDestroyed - * @property {SeriesPattern1} capitulationFlow */ /** @@ -8521,24 +8467,13 @@ class BrkClient extends BrkClientBase { }, realized: { cap: createCentsDeltaToUsdPattern(this, 'realized_cap'), - profit: { - base: createCentsUsdPattern2(this, 'realized_profit'), - cumulative: createCentsUsdPattern2(this, 'realized_profit_cumulative'), - sum: create_1m1w1y24hPattern4(this, 'realized_profit_sum'), - toRcap: createBpsPercentRatioPattern4(this, 'realized_profit_to_rcap'), - valueCreated: createBaseCumulativeSumPattern(this, 'profit_value_created'), - valueDestroyed: createBaseCumulativeSumPattern(this, 'profit_value_destroyed'), - distributionFlow: createSeriesPattern1(this, 'distribution_flow'), - }, + profit: createBaseCumulativeSumToPattern(this, 'realized_profit'), loss: { base: createCentsUsdPattern2(this, 'realized_loss'), cumulative: createCentsUsdPattern2(this, 'realized_loss_cumulative'), sum: create_1m1w1y24hPattern4(this, 'realized_loss_sum'), negative: createBaseSumPattern(this, 'neg_realized_loss'), toRcap: createBpsPercentRatioPattern4(this, 'realized_loss_to_rcap'), - valueCreated: createBaseCumulativeSumPattern(this, 'loss_value_created'), - valueDestroyed: createBaseCumulativeSumPattern(this, 'loss_value_destroyed'), - capitulationFlow: createSeriesPattern1(this, 'capitulation_flow'), }, price: { usd: createSeriesPattern1(this, 'realized_price'), @@ -8684,24 +8619,13 @@ class BrkClient extends BrkClientBase { }, realized: { cap: createCentsDeltaToUsdPattern(this, 'sth_realized_cap'), - profit: { - base: createCentsUsdPattern2(this, 'sth_realized_profit'), - cumulative: createCentsUsdPattern2(this, 'sth_realized_profit_cumulative'), - sum: create_1m1w1y24hPattern4(this, 'sth_realized_profit_sum'), - toRcap: createBpsPercentRatioPattern4(this, 'sth_realized_profit_to_rcap'), - valueCreated: createBaseCumulativeSumPattern(this, 'sth_profit_value_created'), - valueDestroyed: createBaseCumulativeSumPattern(this, 'sth_profit_value_destroyed'), - distributionFlow: createSeriesPattern1(this, 'sth_distribution_flow'), - }, + profit: createBaseCumulativeSumToPattern(this, 'sth_realized_profit'), loss: { base: createCentsUsdPattern2(this, 'sth_realized_loss'), cumulative: createCentsUsdPattern2(this, 'sth_realized_loss_cumulative'), sum: create_1m1w1y24hPattern4(this, 'sth_realized_loss_sum'), negative: createBaseSumPattern(this, 'sth_neg_realized_loss'), toRcap: createBpsPercentRatioPattern4(this, 'sth_realized_loss_to_rcap'), - valueCreated: createBaseCumulativeSumPattern(this, 'sth_loss_value_created'), - valueDestroyed: createBaseCumulativeSumPattern(this, 'sth_loss_value_destroyed'), - capitulationFlow: createSeriesPattern1(this, 'sth_capitulation_flow'), }, price: { usd: createSeriesPattern1(this, 'sth_realized_price'), @@ -8832,24 +8756,13 @@ class BrkClient extends BrkClientBase { }, realized: { cap: createCentsDeltaToUsdPattern(this, 'lth_realized_cap'), - profit: { - base: createCentsUsdPattern2(this, 'lth_realized_profit'), - cumulative: createCentsUsdPattern2(this, 'lth_realized_profit_cumulative'), - sum: create_1m1w1y24hPattern4(this, 'lth_realized_profit_sum'), - toRcap: createBpsPercentRatioPattern4(this, 'lth_realized_profit_to_rcap'), - valueCreated: createBaseCumulativeSumPattern(this, 'lth_profit_value_created'), - valueDestroyed: createBaseCumulativeSumPattern(this, 'lth_profit_value_destroyed'), - distributionFlow: createSeriesPattern1(this, 'lth_distribution_flow'), - }, + profit: createBaseCumulativeSumToPattern(this, 'lth_realized_profit'), loss: { base: createCentsUsdPattern2(this, 'lth_realized_loss'), cumulative: createCentsUsdPattern2(this, 'lth_realized_loss_cumulative'), sum: create_1m1w1y24hPattern4(this, 'lth_realized_loss_sum'), negative: createBaseSumPattern(this, 'lth_neg_realized_loss'), toRcap: createBpsPercentRatioPattern4(this, 'lth_realized_loss_to_rcap'), - valueCreated: createBaseCumulativeSumPattern(this, 'lth_loss_value_created'), - valueDestroyed: createBaseCumulativeSumPattern(this, 'lth_loss_value_destroyed'), - capitulationFlow: createSeriesPattern1(this, 'lth_capitulation_flow'), }, price: { usd: createSeriesPattern1(this, 'lth_realized_price'), diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index d69efdfc5..2656e3fae 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -30,6 +30,7 @@ AnyAddrIndex = TypeIndex # Unsigned basis points stored as u16. # 1 bp = 0.0001. Range: 0–6.5535. # Use for bounded 0–1 ratios (dominance, adoption, liveliness, etc.). +# `u16::MAX` is reserved as a NaN sentinel. BasisPoints16 = int # Unsigned basis points stored as u32. # 1 bp = 0.0001. Range: 0–429,496.7295. @@ -39,6 +40,7 @@ BasisPoints32 = int # Signed basis points stored as i16. # 1 bp = 0.0001. Range: -3.2767 to +3.2767. # Use for signed bounded ratios (NUPL, net PnL ratios, etc.). +# `i16::MIN` is reserved as a NaN sentinel. BasisPointsSigned16 = int # Signed basis points stored as i32. # 1 bp = 0.0001. Range: -214,748.3647 to +214,748.3647. @@ -2304,10 +2306,6 @@ class AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2: self.pct75: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct75')) self.pct90: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct90')) -class BaseCapitulationCumulativeNegativeSumToValuePattern: - """Pattern struct for repeated tree structure.""" - pass - class BpsCentsPercentilesRatioSatsSmaStdUsdPattern: """Pattern struct for repeated tree structure.""" pass @@ -2352,10 +2350,6 @@ class _1m1w1y24hBpsPercentRatioPattern: self.percent: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc) self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'ratio')) -class BaseCumulativeDistributionSumToValuePattern: - """Pattern struct for repeated tree structure.""" - pass - class CapLossMvrvNetPriceProfitSoprPattern: """Pattern struct for repeated tree structure.""" @@ -2514,6 +2508,10 @@ class BaseCumulativeInSumPattern: self.in_profit: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, _m(acc, 'in_profit')) self.sum: _1m1w1y24hPattern5 = _1m1w1y24hPattern5(client, _m(acc, 'sum')) +class BaseCumulativeNegativeSumToPattern: + """Pattern struct for repeated tree structure.""" + pass + class BpsCentsRatioSatsUsdPattern: """Pattern struct for repeated tree structure.""" @@ -4705,18 +4703,6 @@ class SeriesTree_Cohorts_Utxo_All_Activity: self.coinyears_destroyed: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'coinyears_destroyed') self.dormancy: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'dormancy') -class SeriesTree_Cohorts_Utxo_All_Realized_Profit: - """Series tree node.""" - - def __init__(self, client: BrkClientBase, base_path: str = ''): - self.base: CentsUsdPattern2 = CentsUsdPattern2(client, 'realized_profit') - self.cumulative: CentsUsdPattern2 = CentsUsdPattern2(client, 'realized_profit_cumulative') - self.sum: _1m1w1y24hPattern4 = _1m1w1y24hPattern4(client, 'realized_profit_sum') - self.to_rcap: BpsPercentRatioPattern4 = BpsPercentRatioPattern4(client, 'realized_profit_to_rcap') - self.value_created: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'profit_value_created') - self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'profit_value_destroyed') - self.distribution_flow: SeriesPattern1[Dollars] = SeriesPattern1(client, 'distribution_flow') - class SeriesTree_Cohorts_Utxo_All_Realized_Loss: """Series tree node.""" @@ -4726,9 +4712,6 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Loss: self.sum: _1m1w1y24hPattern4 = _1m1w1y24hPattern4(client, 'realized_loss_sum') self.negative: BaseSumPattern = BaseSumPattern(client, 'neg_realized_loss') self.to_rcap: BpsPercentRatioPattern4 = BpsPercentRatioPattern4(client, 'realized_loss_to_rcap') - self.value_created: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'loss_value_created') - self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'loss_value_destroyed') - self.capitulation_flow: SeriesPattern1[Dollars] = SeriesPattern1(client, 'capitulation_flow') class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All: """Series tree node.""" @@ -4854,7 +4837,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized: def __init__(self, client: BrkClientBase, base_path: str = ''): self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, 'realized_cap') - self.profit: SeriesTree_Cohorts_Utxo_All_Realized_Profit = SeriesTree_Cohorts_Utxo_All_Realized_Profit(client) + self.profit: BaseCumulativeSumToPattern = BaseCumulativeSumToPattern(client, 'realized_profit') self.loss: SeriesTree_Cohorts_Utxo_All_Realized_Loss = SeriesTree_Cohorts_Utxo_All_Realized_Loss(client) self.price: SeriesTree_Cohorts_Utxo_All_Realized_Price = SeriesTree_Cohorts_Utxo_All_Realized_Price(client) self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'mvrv') @@ -4943,18 +4926,6 @@ class SeriesTree_Cohorts_Utxo_Sth_Activity: self.coinyears_destroyed: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'sth_coinyears_destroyed') self.dormancy: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_dormancy') -class SeriesTree_Cohorts_Utxo_Sth_Realized_Profit: - """Series tree node.""" - - def __init__(self, client: BrkClientBase, base_path: str = ''): - self.base: CentsUsdPattern2 = CentsUsdPattern2(client, 'sth_realized_profit') - self.cumulative: CentsUsdPattern2 = CentsUsdPattern2(client, 'sth_realized_profit_cumulative') - self.sum: _1m1w1y24hPattern4 = _1m1w1y24hPattern4(client, 'sth_realized_profit_sum') - self.to_rcap: BpsPercentRatioPattern4 = BpsPercentRatioPattern4(client, 'sth_realized_profit_to_rcap') - self.value_created: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'sth_profit_value_created') - self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'sth_profit_value_destroyed') - self.distribution_flow: SeriesPattern1[Dollars] = SeriesPattern1(client, 'sth_distribution_flow') - class SeriesTree_Cohorts_Utxo_Sth_Realized_Loss: """Series tree node.""" @@ -4964,9 +4935,6 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized_Loss: self.sum: _1m1w1y24hPattern4 = _1m1w1y24hPattern4(client, 'sth_realized_loss_sum') self.negative: BaseSumPattern = BaseSumPattern(client, 'sth_neg_realized_loss') self.to_rcap: BpsPercentRatioPattern4 = BpsPercentRatioPattern4(client, 'sth_realized_loss_to_rcap') - self.value_created: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'sth_loss_value_created') - self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'sth_loss_value_destroyed') - self.capitulation_flow: SeriesPattern1[Dollars] = SeriesPattern1(client, 'sth_capitulation_flow') class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All: """Series tree node.""" @@ -5092,7 +5060,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized: def __init__(self, client: BrkClientBase, base_path: str = ''): self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, 'sth_realized_cap') - self.profit: SeriesTree_Cohorts_Utxo_Sth_Realized_Profit = SeriesTree_Cohorts_Utxo_Sth_Realized_Profit(client) + self.profit: BaseCumulativeSumToPattern = BaseCumulativeSumToPattern(client, 'sth_realized_profit') self.loss: SeriesTree_Cohorts_Utxo_Sth_Realized_Loss = SeriesTree_Cohorts_Utxo_Sth_Realized_Loss(client) self.price: SeriesTree_Cohorts_Utxo_Sth_Realized_Price = SeriesTree_Cohorts_Utxo_Sth_Realized_Price(client) self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_mvrv') @@ -5154,18 +5122,6 @@ class SeriesTree_Cohorts_Utxo_Lth_Activity: self.coinyears_destroyed: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'lth_coinyears_destroyed') self.dormancy: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_dormancy') -class SeriesTree_Cohorts_Utxo_Lth_Realized_Profit: - """Series tree node.""" - - def __init__(self, client: BrkClientBase, base_path: str = ''): - self.base: CentsUsdPattern2 = CentsUsdPattern2(client, 'lth_realized_profit') - self.cumulative: CentsUsdPattern2 = CentsUsdPattern2(client, 'lth_realized_profit_cumulative') - self.sum: _1m1w1y24hPattern4 = _1m1w1y24hPattern4(client, 'lth_realized_profit_sum') - self.to_rcap: BpsPercentRatioPattern4 = BpsPercentRatioPattern4(client, 'lth_realized_profit_to_rcap') - self.value_created: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'lth_profit_value_created') - self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'lth_profit_value_destroyed') - self.distribution_flow: SeriesPattern1[Dollars] = SeriesPattern1(client, 'lth_distribution_flow') - class SeriesTree_Cohorts_Utxo_Lth_Realized_Loss: """Series tree node.""" @@ -5175,9 +5131,6 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized_Loss: self.sum: _1m1w1y24hPattern4 = _1m1w1y24hPattern4(client, 'lth_realized_loss_sum') self.negative: BaseSumPattern = BaseSumPattern(client, 'lth_neg_realized_loss') self.to_rcap: BpsPercentRatioPattern4 = BpsPercentRatioPattern4(client, 'lth_realized_loss_to_rcap') - self.value_created: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'lth_loss_value_created') - self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'lth_loss_value_destroyed') - self.capitulation_flow: SeriesPattern1[Dollars] = SeriesPattern1(client, 'lth_capitulation_flow') class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All: """Series tree node.""" @@ -5294,7 +5247,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized: def __init__(self, client: BrkClientBase, base_path: str = ''): self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, 'lth_realized_cap') - self.profit: SeriesTree_Cohorts_Utxo_Lth_Realized_Profit = SeriesTree_Cohorts_Utxo_Lth_Realized_Profit(client) + self.profit: BaseCumulativeSumToPattern = BaseCumulativeSumToPattern(client, 'lth_realized_profit') self.loss: SeriesTree_Cohorts_Utxo_Lth_Realized_Loss = SeriesTree_Cohorts_Utxo_Lth_Realized_Loss(client) self.price: SeriesTree_Cohorts_Utxo_Lth_Realized_Price = SeriesTree_Cohorts_Utxo_Lth_Realized_Price(client) self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_mvrv') diff --git a/website/scripts/options/distribution/activity.js b/website/scripts/options/distribution/activity.js index f06771fee..db6ddcc90 100644 --- a/website/scripts/options/distribution/activity.js +++ b/website/scripts/options/distribution/activity.js @@ -9,8 +9,9 @@ */ import { Unit } from "../../utils/units.js"; -import { line, baseline, dotsBaseline, percentRatio, percentRatioDots } from "../series.js"; +import { line, baseline, dotsBaseline, percentRatio, chartsFromCount, ROLLING_WINDOWS } from "../series.js"; import { + satsBtcUsdFullTree, mapCohortsWithAll, flatMapCohortsWithAll, } from "../shared.js"; @@ -30,49 +31,21 @@ function volumeAndCoinsTree(activity, color, title) { return [ { name: "Volume", - tree: [ - { - name: "Per Block", - title: title("Sent Volume"), - bottom: [ - line({ series: activity.transferVolume.base.sats, name: "Sum", color, unit: Unit.sats }), - line({ series: activity.transferVolume.sum._24h.sats, name: "24h", color: colors.time._24h, unit: Unit.sats, defaultActive: false }), - line({ series: activity.transferVolume.sum._1w.sats, name: "1w", color: colors.time._1w, unit: Unit.sats, defaultActive: false }), - line({ series: activity.transferVolume.sum._1m.sats, name: "1m", color: colors.time._1m, unit: Unit.sats, defaultActive: false }), - line({ series: activity.transferVolume.sum._1y.sats, name: "1y", color: colors.time._1y, unit: Unit.sats, defaultActive: false }), - ], - }, - { - name: "Cumulative", - title: title("Sent Volume (Total)"), - bottom: [ - line({ series: activity.transferVolume.cumulative.sats, name: "All-time", color, unit: Unit.sats }), - ], - }, - ], + tree: satsBtcUsdFullTree({ + pattern: activity.transferVolume, + name: "Volume", + title: title("Sent Volume"), + color, + }), }, { name: "Coindays Destroyed", - tree: [ - { - name: "Per Block", - title: title("Coindays Destroyed"), - bottom: [ - line({ series: activity.coindaysDestroyed.base, name: "Base", color, unit: Unit.coindays }), - line({ series: activity.coindaysDestroyed.sum._24h, name: "24h", color: colors.time._24h, unit: Unit.coindays, defaultActive: false }), - line({ series: activity.coindaysDestroyed.sum._1w, name: "1w", color: colors.time._1w, unit: Unit.coindays, defaultActive: false }), - line({ series: activity.coindaysDestroyed.sum._1m, name: "1m", color: colors.time._1m, unit: Unit.coindays, defaultActive: false }), - line({ series: activity.coindaysDestroyed.sum._1y, name: "1y", color: colors.time._1y, unit: Unit.coindays, defaultActive: false }), - ], - }, - { - name: "Cumulative", - title: title("Cumulative Coindays Destroyed"), - bottom: [ - line({ series: activity.coindaysDestroyed.cumulative, name: "All-time", color, unit: Unit.coindays }), - ], - }, - ], + tree: chartsFromCount({ + pattern: activity.coindaysDestroyed, + title: title("Coindays Destroyed"), + unit: Unit.coindays, + color, + }), }, ]; } @@ -87,95 +60,27 @@ function sentProfitLossTree(sent, title) { return [ { name: "Sent In Profit", - tree: [ - { - name: "USD", - title: title("Sent Volume In Profit"), - bottom: [ - line({ series: sent.inProfit.base.usd, name: "Base", color: colors.profit, unit: Unit.usd }), - line({ series: sent.inProfit.sum._24h.usd, name: "24h", color: colors.time._24h, unit: Unit.usd, defaultActive: false }), - line({ series: sent.inProfit.sum._1w.usd, name: "1w", color: colors.time._1w, unit: Unit.usd, defaultActive: false }), - line({ series: sent.inProfit.sum._1m.usd, name: "1m", color: colors.time._1m, unit: Unit.usd, defaultActive: false }), - line({ series: sent.inProfit.sum._1y.usd, name: "1y", color: colors.time._1y, unit: Unit.usd, defaultActive: false }), - ], - }, - { - name: "BTC", - title: title("Sent Volume In Profit (BTC)"), - bottom: [ - line({ series: sent.inProfit.base.btc, name: "Base", color: colors.profit, unit: Unit.btc }), - line({ series: sent.inProfit.sum._24h.btc, name: "24h", color: colors.time._24h, unit: Unit.btc, defaultActive: false }), - line({ series: sent.inProfit.sum._1w.btc, name: "1w", color: colors.time._1w, unit: Unit.btc, defaultActive: false }), - line({ series: sent.inProfit.sum._1m.btc, name: "1m", color: colors.time._1m, unit: Unit.btc, defaultActive: false }), - line({ series: sent.inProfit.sum._1y.btc, name: "1y", color: colors.time._1y, unit: Unit.btc, defaultActive: false }), - ], - }, - { - name: "Sats", - title: title("Sent Volume In Profit (Sats)"), - bottom: [ - line({ series: sent.inProfit.base.sats, name: "Base", color: colors.profit, unit: Unit.sats }), - line({ series: sent.inProfit.sum._24h.sats, name: "24h", color: colors.time._24h, unit: Unit.sats, defaultActive: false }), - line({ series: sent.inProfit.sum._1w.sats, name: "1w", color: colors.time._1w, unit: Unit.sats, defaultActive: false }), - line({ series: sent.inProfit.sum._1m.sats, name: "1m", color: colors.time._1m, unit: Unit.sats, defaultActive: false }), - line({ series: sent.inProfit.sum._1y.sats, name: "1y", color: colors.time._1y, unit: Unit.sats, defaultActive: false }), - ], - }, - { name: "Cumulative", title: title("Cumulative Sent In Profit"), bottom: [ - line({ series: sent.inProfit.cumulative.usd, name: "USD", color: colors.profit, unit: Unit.usd }), - line({ series: sent.inProfit.cumulative.btc, name: "BTC", color: colors.profit, unit: Unit.btc, defaultActive: false }), - line({ series: sent.inProfit.cumulative.sats, name: "Sats", color: colors.profit, unit: Unit.sats, defaultActive: false }), - ]}, - ], + tree: satsBtcUsdFullTree({ + pattern: sent.inProfit, + name: "In Profit", + title: title("Sent Volume In Profit"), + color: colors.profit, + }), }, { name: "Sent In Loss", - tree: [ - { - name: "USD", - title: title("Sent Volume In Loss"), - bottom: [ - line({ series: sent.inLoss.base.usd, name: "Base", color: colors.loss, unit: Unit.usd }), - line({ series: sent.inLoss.sum._24h.usd, name: "24h", color: colors.time._24h, unit: Unit.usd, defaultActive: false }), - line({ series: sent.inLoss.sum._1w.usd, name: "1w", color: colors.time._1w, unit: Unit.usd, defaultActive: false }), - line({ series: sent.inLoss.sum._1m.usd, name: "1m", color: colors.time._1m, unit: Unit.usd, defaultActive: false }), - line({ series: sent.inLoss.sum._1y.usd, name: "1y", color: colors.time._1y, unit: Unit.usd, defaultActive: false }), - ], - }, - { - name: "BTC", - title: title("Sent Volume In Loss (BTC)"), - bottom: [ - line({ series: sent.inLoss.base.btc, name: "Base", color: colors.loss, unit: Unit.btc }), - line({ series: sent.inLoss.sum._24h.btc, name: "24h", color: colors.time._24h, unit: Unit.btc, defaultActive: false }), - line({ series: sent.inLoss.sum._1w.btc, name: "1w", color: colors.time._1w, unit: Unit.btc, defaultActive: false }), - line({ series: sent.inLoss.sum._1m.btc, name: "1m", color: colors.time._1m, unit: Unit.btc, defaultActive: false }), - line({ series: sent.inLoss.sum._1y.btc, name: "1y", color: colors.time._1y, unit: Unit.btc, defaultActive: false }), - ], - }, - { - name: "Sats", - title: title("Sent Volume In Loss (Sats)"), - bottom: [ - line({ series: sent.inLoss.base.sats, name: "Base", color: colors.loss, unit: Unit.sats }), - line({ series: sent.inLoss.sum._24h.sats, name: "24h", color: colors.time._24h, unit: Unit.sats, defaultActive: false }), - line({ series: sent.inLoss.sum._1w.sats, name: "1w", color: colors.time._1w, unit: Unit.sats, defaultActive: false }), - line({ series: sent.inLoss.sum._1m.sats, name: "1m", color: colors.time._1m, unit: Unit.sats, defaultActive: false }), - line({ series: sent.inLoss.sum._1y.sats, name: "1y", color: colors.time._1y, unit: Unit.sats, defaultActive: false }), - ], - }, - { name: "Cumulative", title: title("Cumulative Sent In Loss"), bottom: [ - line({ series: sent.inLoss.cumulative.usd, name: "USD", color: colors.loss, unit: Unit.usd }), - line({ series: sent.inLoss.cumulative.btc, name: "BTC", color: colors.loss, unit: Unit.btc, defaultActive: false }), - line({ series: sent.inLoss.cumulative.sats, name: "Sats", color: colors.loss, unit: Unit.sats, defaultActive: false }), - ]}, - ], + tree: satsBtcUsdFullTree({ + pattern: sent.inLoss, + name: "In Loss", + title: title("Sent Volume In Loss"), + color: colors.loss, + }), }, ]; } /** - * Volume and coins tree with coinyears, dormancy, and sent in profit/loss (All/STH/LTH) + * Volume and coins tree with dormancy, and sent in profit/loss (All/STH/LTH) * @param {FullActivityPattern} activity * @param {Color} color * @param {(name: string) => string} title @@ -185,11 +90,6 @@ function fullVolumeTree(activity, color, title) { return [ ...volumeAndCoinsTree(activity, color, title), ...sentProfitLossTree(activity.transferVolume, title), - { - name: "Coinyears Destroyed", - title: title("Coinyears Destroyed"), - bottom: [line({ series: activity.coinyearsDestroyed, name: "CYD", color, unit: Unit.years })], - }, { name: "Dormancy", title: title("Dormancy"), @@ -212,34 +112,16 @@ function singleRollingSoprTree(ratio, title, prefix = "") { return [ { name: "Compare", - title: title(`Rolling ${prefix}SOPR`), - bottom: [ - baseline({ series: ratio._24h, name: "24h", color: colors.time._24h, unit: Unit.ratio, base: 1 }), - baseline({ series: ratio._1w, name: "7d", color: colors.time._1w, unit: Unit.ratio, base: 1 }), - baseline({ series: ratio._1m, name: "30d", color: colors.time._1m, unit: Unit.ratio, base: 1 }), - baseline({ series: ratio._1y, name: "1y", color: colors.time._1y, unit: Unit.ratio, base: 1 }), - ], - }, - { - name: "24h", - title: title(`${prefix}SOPR (24h)`), - bottom: [dotsBaseline({ series: ratio._24h, name: "24h", unit: Unit.ratio, base: 1 })], - }, - { - name: "7d", - title: title(`${prefix}SOPR (7d)`), - bottom: [baseline({ series: ratio._1w, name: "SOPR", unit: Unit.ratio, base: 1 })], - }, - { - name: "30d", - title: title(`${prefix}SOPR (30d)`), - bottom: [baseline({ series: ratio._1m, name: "SOPR", unit: Unit.ratio, base: 1 })], - }, - { - name: "1y", - title: title(`${prefix}SOPR (1y)`), - bottom: [baseline({ series: ratio._1y, name: "SOPR", unit: Unit.ratio, base: 1 })], + title: title(`${prefix}SOPR`), + bottom: ROLLING_WINDOWS.map((w) => + baseline({ series: ratio[w.key], name: w.name, color: w.color, unit: Unit.ratio, base: 1 }), + ), }, + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`${prefix}SOPR (${w.title})`), + bottom: [baseline({ series: ratio[w.key], name: "SOPR", unit: Unit.ratio, base: 1 })], + })), ]; } @@ -256,173 +138,19 @@ function singleSellSideRiskTree(sellSideRisk, title) { return [ { name: "Compare", - title: title("Rolling Sell Side Risk"), - bottom: [ - ...percentRatioDots({ pattern: sellSideRisk._24h, name: "24h", color: colors.time._24h }), - ...percentRatio({ pattern: sellSideRisk._1w, name: "7d", color: colors.time._1w }), - ...percentRatio({ pattern: sellSideRisk._1m, name: "30d", color: colors.time._1m }), - ...percentRatio({ pattern: sellSideRisk._1y, name: "1y", color: colors.time._1y }), - ], - }, - { - name: "24h", - title: title("Sell Side Risk (24h)"), - bottom: percentRatioDots({ pattern: sellSideRisk._24h, name: "Raw", color: colors.bitcoin }), - }, - { - name: "7d", - title: title("Sell Side Risk (7d)"), - bottom: percentRatio({ pattern: sellSideRisk._1w, name: "Risk" }), - }, - { - name: "30d", - title: title("Sell Side Risk (30d)"), - bottom: percentRatio({ pattern: sellSideRisk._1m, name: "Risk" }), - }, - { - name: "1y", - title: title("Sell Side Risk (1y)"), - bottom: percentRatio({ pattern: sellSideRisk._1y, name: "Risk" }), + title: title("Sell Side Risk"), + bottom: ROLLING_WINDOWS.flatMap((w) => + percentRatio({ pattern: sellSideRisk[w.key], name: w.name, color: w.color }), + ), }, + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Sell Side Risk (${w.title})`), + bottom: percentRatio({ pattern: sellSideRisk[w.key], name: "Risk" }), + })), ]; } -// ============================================================================ -// Shared Value Helpers -// ============================================================================ - -/** - * @param {CountPattern} valueCreated - * @param {CountPattern} valueDestroyed - * @param {(name: string) => string} title - * @param {string} [prefix] - * @returns {PartialOptionsTree} - */ -function singleRollingValueTree(valueCreated, valueDestroyed, title, prefix = "") { - return [ - { - name: "Compare", - tree: [ - { - name: "Created", - title: title(`Rolling ${prefix}Value Created`), - bottom: [ - line({ series: valueCreated.sum._24h, name: "24h", color: colors.time._24h, unit: Unit.usd }), - line({ series: valueCreated.sum._1w, name: "7d", color: colors.time._1w, unit: Unit.usd }), - line({ series: valueCreated.sum._1m, name: "30d", color: colors.time._1m, unit: Unit.usd }), - line({ series: valueCreated.sum._1y, name: "1y", color: colors.time._1y, unit: Unit.usd }), - ], - }, - { - name: "Destroyed", - title: title(`Rolling ${prefix}Value Destroyed`), - bottom: [ - line({ series: valueDestroyed.sum._24h, name: "24h", color: colors.time._24h, unit: Unit.usd }), - line({ series: valueDestroyed.sum._1w, name: "7d", color: colors.time._1w, unit: Unit.usd }), - line({ series: valueDestroyed.sum._1m, name: "30d", color: colors.time._1m, unit: Unit.usd }), - line({ series: valueDestroyed.sum._1y, name: "1y", color: colors.time._1y, unit: Unit.usd }), - ], - }, - ], - }, - { - name: "24h", - title: title(`${prefix}Value Created & Destroyed (24h)`), - bottom: [ - line({ series: valueCreated.sum._24h, name: "Created", color: colors.usd, unit: Unit.usd }), - line({ series: valueDestroyed.sum._24h, name: "Destroyed", color: colors.loss, unit: Unit.usd }), - ], - }, - { - name: "7d", - title: title(`${prefix}Value Created & Destroyed (7d)`), - bottom: [ - line({ series: valueCreated.sum._1w, name: "Created", color: colors.usd, unit: Unit.usd }), - line({ series: valueDestroyed.sum._1w, name: "Destroyed", color: colors.loss, unit: Unit.usd }), - ], - }, - { - name: "30d", - title: title(`${prefix}Value Created & Destroyed (30d)`), - bottom: [ - line({ series: valueCreated.sum._1m, name: "Created", color: colors.usd, unit: Unit.usd }), - line({ series: valueDestroyed.sum._1m, name: "Destroyed", color: colors.loss, unit: Unit.usd }), - ], - }, - { - name: "1y", - title: title(`${prefix}Value Created & Destroyed (1y)`), - bottom: [ - line({ series: valueCreated.sum._1y, name: "Created", color: colors.usd, unit: Unit.usd }), - line({ series: valueDestroyed.sum._1y, name: "Destroyed", color: colors.loss, unit: Unit.usd }), - ], - }, - { - name: "Cumulative", - title: title(`${prefix}Value Created & Destroyed (Total)`), - bottom: [ - line({ series: valueCreated.cumulative, name: "Created", color: colors.usd, unit: Unit.usd }), - line({ series: valueDestroyed.cumulative, name: "Destroyed", color: colors.loss, unit: Unit.usd }), - ], - }, - ]; -} - -/** - * Value section: created & destroyed + rolling - * @param {Object} args - * @param {CountPattern} args.valueCreated - * @param {CountPattern} args.valueDestroyed - * @param {AnyFetchedSeriesBlueprint[]} [args.extraValueSeries] - * @param {PartialOptionsTree} args.rollingTree - * @param {(name: string) => string} args.title - * @returns {PartialOptionsGroup} - */ -function fullValueSection({ valueCreated, valueDestroyed, extraValueSeries = [], rollingTree, title }) { - return { - name: "Value", - tree: [ - { - name: "Created & Destroyed", - title: title("Value Created & Destroyed"), - bottom: [ - line({ series: valueCreated.base, name: "Created", color: colors.usd, unit: Unit.usd }), - line({ series: valueDestroyed.base, name: "Destroyed", color: colors.loss, unit: Unit.usd }), - ...extraValueSeries, - ], - }, - { name: "Rolling", tree: rollingTree }, - ], - }; -} - -/** - * Simple value section (created & destroyed + rolling) - * @param {CountPattern} valueCreated - * @param {CountPattern} valueDestroyed - * @param {(name: string) => string} title - * @returns {PartialOptionsGroup} - */ -function simpleValueSection(valueCreated, valueDestroyed, title) { - return { - name: "Value", - tree: [ - { - name: "Created & Destroyed", - title: title("Value Created & Destroyed"), - bottom: [ - line({ series: valueCreated.base, name: "Created", color: colors.usd, unit: Unit.usd }), - line({ series: valueDestroyed.base, name: "Destroyed", color: colors.loss, unit: Unit.usd }), - ], - }, - { - name: "Rolling", - tree: singleRollingValueTree(valueCreated, valueDestroyed, title), - }, - ], - }; -} - // ============================================================================ // Single Cohort Activity Sections // ============================================================================ @@ -444,10 +172,7 @@ export function createActivitySectionWithAdjusted({ cohort, title }) { { name: "SOPR", tree: [ - { - name: "Normal", - tree: singleRollingSoprTree(sopr.ratio, title), - }, + ...singleRollingSoprTree(sopr.ratio, title), { name: "Adjusted", tree: singleRollingSoprTree(sopr.adjusted.ratio, title, "Adjusted "), @@ -455,25 +180,6 @@ export function createActivitySectionWithAdjusted({ cohort, title }) { ], }, { name: "Sell Side Risk", tree: singleSellSideRiskTree(r.sellSideRiskRatio, title) }, - fullValueSection({ - valueCreated: sopr.valueCreated, - valueDestroyed: sopr.valueDestroyed, - extraValueSeries: [ - line({ series: sopr.adjusted.valueCreated.base, name: "Adjusted Created", color: colors.adjustedCreated, unit: Unit.usd, defaultActive: false }), - line({ series: sopr.adjusted.valueDestroyed.base, name: "Adjusted Destroyed", color: colors.adjustedDestroyed, unit: Unit.usd, defaultActive: false }), - ], - rollingTree: [ - { - name: "Normal", - tree: singleRollingValueTree(sopr.valueCreated, sopr.valueDestroyed, title), - }, - { - name: "Adjusted", - tree: singleRollingValueTree(sopr.adjusted.valueCreated, sopr.adjusted.valueDestroyed, title, "Adjusted "), - }, - ], - title, - }), ], }; } @@ -497,12 +203,6 @@ export function createActivitySection({ cohort, title }) { tree: singleRollingSoprTree(sopr.ratio, title), }, { name: "Sell Side Risk", tree: singleSellSideRiskTree(r.sellSideRiskRatio, title) }, - fullValueSection({ - valueCreated: sopr.valueCreated, - valueDestroyed: sopr.valueDestroyed, - rollingTree: singleRollingValueTree(sopr.valueCreated, sopr.valueDestroyed, title), - title, - }), ], }; } @@ -526,24 +226,42 @@ export function createActivitySectionWithActivity({ cohort, title }) { title: title("SOPR (24h)"), bottom: [dotsBaseline({ series: sopr.ratio._24h, name: "SOPR", unit: Unit.ratio, base: 1 })], }, - simpleValueSection(sopr.valueCreated, sopr.valueDestroyed, title), ], }; } + /** - * Minimal activity section for cohorts without activity field (value only) + * Minimal activity section: volume only * @param {{ cohort: CohortBasicWithMarketCap | CohortBasicWithoutMarketCap | CohortWithoutRelative | CohortAddr | AddrCohortObject, title: (name: string) => string }} args * @returns {PartialOptionsGroup} */ export function createActivitySectionMinimal({ cohort, title }) { - const sopr = cohort.tree.realized.sopr; - return { name: "Activity", - tree: [ - simpleValueSection(sopr.valueCreated, sopr.valueDestroyed, title), - ], + tree: chartsFromCount({ + pattern: cohort.tree.realized.sopr.valueCreated, + title: title("Volume"), + unit: Unit.usd, + }), + }; +} + +/** + * Grouped minimal activity: volume + * @param {{ list: readonly (UtxoCohortObject | CohortWithoutRelative | CohortAddr | AddrCohortObject)[], all: CohortAll, title: (name: string) => string }} args + * @returns {PartialOptionsGroup} + */ +export function createGroupedActivitySectionMinimal({ list, all, title }) { + return { + name: "Activity", + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Volume (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.realized.sopr.valueCreated.sum[w.key], name, color, unit: Unit.usd }), + ), + })), }; } @@ -556,83 +274,19 @@ export function createActivitySectionMinimal({ cohort, title }) { * @template {{ color: Color, name: string }} A * @param {readonly T[]} list * @param {A} all - * @param {(item: T | A) => AnySeriesPattern} getRaw - * @param {(item: T | A) => AnySeriesPattern} get7d - * @param {(item: T | A) => AnySeriesPattern} get30d + * @param {(item: T | A) => { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} getRatio * @param {(name: string) => string} title * @param {string} [prefix] * @returns {PartialOptionsTree} */ -function groupedSoprCharts(list, all, getRaw, get7d, get30d, title, prefix = "") { - return [ - { - name: "Raw", - title: title(`${prefix}SOPR`), - bottom: mapCohortsWithAll(list, all, (item) => - baseline({ series: getRaw(item), name: item.name, color: item.color, unit: Unit.ratio, base: 1 }), - ), - }, - { - name: "7d", - title: title(`${prefix}SOPR (7d)`), - bottom: mapCohortsWithAll(list, all, (item) => - baseline({ series: get7d(item), name: item.name, color: item.color, unit: Unit.ratio, base: 1 }), - ), - }, - { - name: "30d", - title: title(`${prefix}SOPR (30d)`), - bottom: mapCohortsWithAll(list, all, (item) => - baseline({ series: get30d(item), name: item.name, color: item.color, unit: Unit.ratio, base: 1 }), - ), - }, - ]; -} - -/** - * @template {{ color: Color, name: string }} T - * @template {{ color: Color, name: string }} A - * @param {readonly T[]} list - * @param {A} all - * @param {(item: T | A) => AnySeriesPattern} get24h - * @param {(item: T | A) => AnySeriesPattern} get7d - * @param {(item: T | A) => AnySeriesPattern} get30d - * @param {(item: T | A) => AnySeriesPattern} get1y - * @param {(name: string) => string} title - * @param {string} [prefix] - * @returns {PartialOptionsTree} - */ -function groupedRollingSoprCharts(list, all, get24h, get7d, get30d, get1y, title, prefix = "") { - return [ - { - name: "24h", - title: title(`${prefix}SOPR (24h)`), - bottom: mapCohortsWithAll(list, all, (c) => - baseline({ series: get24h(c), name: c.name, color: c.color, unit: Unit.ratio, base: 1 }), - ), - }, - { - name: "7d", - title: title(`${prefix}SOPR (7d)`), - bottom: mapCohortsWithAll(list, all, (c) => - baseline({ series: get7d(c), name: c.name, color: c.color, unit: Unit.ratio, base: 1 }), - ), - }, - { - name: "30d", - title: title(`${prefix}SOPR (30d)`), - bottom: mapCohortsWithAll(list, all, (c) => - baseline({ series: get30d(c), name: c.name, color: c.color, unit: Unit.ratio, base: 1 }), - ), - }, - { - name: "1y", - title: title(`${prefix}SOPR (1y)`), - bottom: mapCohortsWithAll(list, all, (c) => - baseline({ series: get1y(c), name: c.name, color: c.color, unit: Unit.ratio, base: 1 }), - ), - }, - ]; +function groupedSoprCharts(list, all, getRatio, title, prefix = "") { + return ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`${prefix}SOPR (${w.title})`), + bottom: mapCohortsWithAll(list, all, (c) => + baseline({ series: getRatio(c)[w.key], name: c.name, color: c.color, unit: Unit.ratio, base: 1 }), + ), + })); } // ============================================================================ @@ -649,43 +303,7 @@ function groupedRollingSoprCharts(list, all, get24h, get7d, get30d, get1y, title * @param {string} [prefix] * @returns {PartialOptionsTree} */ -function groupedRollingValueCharts(list, all, windows, title, prefix = "") { - return [ - { - name: "Created", - tree: windows.map((w) => ({ - name: w.name, - title: title(`${prefix}Value Created (${w.title})`), - bottom: mapCohortsWithAll(list, all, (item) => - line({ series: w.getCreated(item), name: item.name, color: item.color, unit: Unit.usd }), - ), - })), - }, - { - name: "Destroyed", - tree: windows.map((w) => ({ - name: w.name, - title: title(`${prefix}Value Destroyed (${w.title})`), - bottom: mapCohortsWithAll(list, all, (item) => - line({ series: w.getDestroyed(item), name: item.name, color: item.color, unit: Unit.usd }), - ), - })), - }, - ]; -} -/** - * @param {readonly (CohortFull | CohortLongTerm | CohortAll)[]} list - * @param {CohortAll} all - */ -function valueWindows(list, all) { - return [ - { name: "24h", title: "Daily", getCreated: (/** @type {typeof list[number] | typeof all} */ c) => c.tree.realized.sopr.valueCreated.sum._24h, getDestroyed: (/** @type {typeof list[number] | typeof all} */ c) => c.tree.realized.sopr.valueDestroyed.sum._24h }, - { name: "7d", title: "Weekly", getCreated: (/** @type {typeof list[number] | typeof all} */ c) => c.tree.realized.sopr.valueCreated.sum._1w, getDestroyed: (/** @type {typeof list[number] | typeof all} */ c) => c.tree.realized.sopr.valueDestroyed.sum._1w }, - { name: "30d", title: "Monthly", getCreated: (/** @type {typeof list[number] | typeof all} */ c) => c.tree.realized.sopr.valueCreated.sum._1m, getDestroyed: (/** @type {typeof list[number] | typeof all} */ c) => c.tree.realized.sopr.valueDestroyed.sum._1m }, - { name: "1y", title: "Yearly", getCreated: (/** @type {typeof list[number] | typeof all} */ c) => c.tree.realized.sopr.valueCreated.sum._1y, getDestroyed: (/** @type {typeof list[number] | typeof all} */ c) => c.tree.realized.sopr.valueDestroyed.sum._1y }, - ]; -} // ============================================================================ // Grouped Activity Sections @@ -709,94 +327,22 @@ export function createGroupedActivitySectionWithAdjusted({ list, all, title }) { { name: "SOPR", tree: [ - { - name: "Normal", - tree: [ - ...groupedSoprCharts( - list, all, - (c) => c.tree.realized.sopr.ratio._24h, - (c) => c.tree.realized.sopr.ratio._1w, - (c) => c.tree.realized.sopr.ratio._1m, - title, - ), - { - name: "Rolling", - tree: groupedRollingSoprCharts( - list, all, - (c) => c.tree.realized.sopr.ratio._24h, - (c) => c.tree.realized.sopr.ratio._1w, - (c) => c.tree.realized.sopr.ratio._1m, - (c) => c.tree.realized.sopr.ratio._1y, - title, - ), - }, - ], - }, + ...groupedSoprCharts(list, all, (c) => c.tree.realized.sopr.ratio, title), { name: "Adjusted", - tree: [ - ...groupedSoprCharts( - list, all, - (c) => c.tree.realized.sopr.adjusted.ratio._24h, - (c) => c.tree.realized.sopr.adjusted.ratio._1w, - (c) => c.tree.realized.sopr.adjusted.ratio._1m, - title, - "Adjusted ", - ), - { - name: "Rolling", - tree: groupedRollingSoprCharts( - list, all, - (c) => c.tree.realized.sopr.adjusted.ratio._24h, - (c) => c.tree.realized.sopr.adjusted.ratio._1w, - (c) => c.tree.realized.sopr.adjusted.ratio._1m, - (c) => c.tree.realized.sopr.adjusted.ratio._1y, - title, - "Adjusted ", - ), - }, - ], + tree: groupedSoprCharts(list, all, (c) => c.tree.realized.sopr.adjusted.ratio, title, "Adjusted "), }, ], }, { name: "Sell Side Risk", - tree: [ - { name: "24h", title: title("Sell Side Risk (24h)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sellSideRiskRatio._24h.ratio, name, color, unit: Unit.ratio })) }, - { name: "7d", title: title("Sell Side Risk (7d)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sellSideRiskRatio._1w.ratio, name, color, unit: Unit.ratio })) }, - { name: "30d", title: title("Sell Side Risk (30d)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sellSideRiskRatio._1m.ratio, name, color, unit: Unit.ratio })) }, - { name: "1y", title: title("Sell Side Risk (1y)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sellSideRiskRatio._1y.ratio, name, color, unit: Unit.ratio })) }, - ], - }, - { - name: "Value", - tree: [ - { name: "Created", title: title("Value Created"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sopr.valueCreated.base, name, color, unit: Unit.usd })) }, - { name: "Destroyed", title: title("Value Destroyed"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sopr.valueDestroyed.base, name, color, unit: Unit.usd })) }, - { - name: "Rolling", - tree: [ - { - name: "Normal", - tree: groupedRollingValueCharts(list, all, valueWindows(list, all), title), - }, - { - name: "Adjusted", - tree: groupedRollingValueCharts( - list, all, - [ - { name: "24h", title: "Daily", getCreated: (c) => c.tree.realized.sopr.adjusted.valueCreated.sum._24h, getDestroyed: (c) => c.tree.realized.sopr.adjusted.valueDestroyed.sum._24h }, - { name: "7d", title: "Weekly", getCreated: (c) => c.tree.realized.sopr.adjusted.valueCreated.sum._1w, getDestroyed: (c) => c.tree.realized.sopr.adjusted.valueDestroyed.sum._1w }, - { name: "30d", title: "Monthly", getCreated: (c) => c.tree.realized.sopr.adjusted.valueCreated.sum._1m, getDestroyed: (c) => c.tree.realized.sopr.adjusted.valueDestroyed.sum._1m }, - { name: "1y", title: "Yearly", getCreated: (c) => c.tree.realized.sopr.adjusted.valueCreated.sum._1y, getDestroyed: (c) => c.tree.realized.sopr.adjusted.valueDestroyed.sum._1y }, - ], - title, - "Adjusted ", - ), - }, - ], - }, - ], + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Sell Side Risk (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.realized.sellSideRiskRatio[w.key].ratio, name, color, unit: Unit.ratio }), + ), + })), }, { name: "Coindays Destroyed", @@ -828,45 +374,18 @@ export function createGroupedActivitySection({ list, all, title }) { { name: "SOPR", tree: [ - ...groupedSoprCharts( - list, all, - (c) => c.tree.realized.sopr.ratio._24h, - (c) => c.tree.realized.sopr.ratio._1w, - (c) => c.tree.realized.sopr.ratio._1m, - title, - ), - { - name: "Rolling", - tree: groupedRollingSoprCharts( - list, all, - (c) => c.tree.realized.sopr.ratio._24h, - (c) => c.tree.realized.sopr.ratio._1w, - (c) => c.tree.realized.sopr.ratio._1m, - (c) => c.tree.realized.sopr.ratio._1y, - title, - ), - }, + ...groupedSoprCharts(list, all, (c) => c.tree.realized.sopr.ratio, title), ], }, { name: "Sell Side Risk", - tree: [ - { name: "24h", title: title("Sell Side Risk (24h)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sellSideRiskRatio._24h.ratio, name, color, unit: Unit.ratio })) }, - { name: "7d", title: title("Sell Side Risk (7d)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sellSideRiskRatio._1w.ratio, name, color, unit: Unit.ratio })) }, - { name: "30d", title: title("Sell Side Risk (30d)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sellSideRiskRatio._1m.ratio, name, color, unit: Unit.ratio })) }, - { name: "1y", title: title("Sell Side Risk (1y)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sellSideRiskRatio._1y.ratio, name, color, unit: Unit.ratio })) }, - ], - }, - { - name: "Value", - tree: [ - { name: "Created", title: title("Value Created"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sopr.valueCreated.base, name, color, unit: Unit.usd })) }, - { name: "Destroyed", title: title("Value Destroyed"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sopr.valueDestroyed.base, name, color, unit: Unit.usd })) }, - { - name: "Rolling", - tree: groupedRollingValueCharts(list, all, valueWindows(list, all), title), - }, - ], + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Sell Side Risk (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.realized.sellSideRiskRatio[w.key].ratio, name, color, unit: Unit.ratio }), + ), + })), }, { name: "Coindays Destroyed", @@ -902,13 +421,6 @@ export function createGroupedActivitySectionWithActivity({ list, all, title }) { baseline({ series: tree.realized.sopr.ratio._24h, name, color, unit: Unit.ratio, base: 1 }), ), }, - { - name: "Value", - tree: [ - { name: "Created", title: title("Value Created"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sopr.valueCreated.base, name, color, unit: Unit.usd })) }, - { name: "Destroyed", title: title("Value Destroyed"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sopr.valueDestroyed.base, name, color, unit: Unit.usd })) }, - ], - }, { name: "Coindays Destroyed", title: title("Coindays Destroyed"), @@ -920,17 +432,3 @@ export function createGroupedActivitySectionWithActivity({ list, all, title }) { }; } -/** - * Grouped minimal activity (value only, no activity field) - * @param {{ list: readonly (UtxoCohortObject | CohortWithoutRelative | CohortAddr | AddrCohortObject)[], all: CohortAll, title: (name: string) => string }} args - * @returns {PartialOptionsGroup} - */ -export function createGroupedActivitySectionMinimal({ list, all, title }) { - return { - name: "Activity", - tree: [ - { name: "Value Created", title: title("Value Created"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sopr.valueCreated.base, name, color, unit: Unit.usd })) }, - { name: "Value Destroyed", title: title("Value Destroyed"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.sopr.valueDestroyed.base, name, color, unit: Unit.usd })) }, - ], - }; -} diff --git a/website/scripts/options/distribution/index.js b/website/scripts/options/distribution/index.js index f98942df9..8478a026a 100644 --- a/website/scripts/options/distribution/index.js +++ b/website/scripts/options/distribution/index.js @@ -66,10 +66,10 @@ import { createActivitySection, createActivitySectionWithAdjusted, createActivitySectionWithActivity, - createActivitySectionMinimal, createGroupedActivitySection, createGroupedActivitySectionWithAdjusted, createGroupedActivitySectionWithActivity, + createActivitySectionMinimal, createGroupedActivitySectionMinimal, } from "./activity.js"; diff --git a/website/scripts/options/distribution/profitability.js b/website/scripts/options/distribution/profitability.js index dcef26229..a16104475 100644 --- a/website/scripts/options/distribution/profitability.js +++ b/website/scripts/options/distribution/profitability.js @@ -369,7 +369,7 @@ function realizedNetFolder({ netPnl, title, toRcap, extraChange = [] }) { * @param {{ sum: Record, negative: { sum: Record } }} args.loss * @param {{ sum: Record }} args.netPnl * @param {{ sum: Record }} [args.grossPnl] - * @param {{ sum: Record }} [args.peakRegret] + * @param {{ sum: Record }} [args.peakRegret] * @param {(name: string) => string} args.title * @returns {PartialOptionsGroup} */ diff --git a/website/scripts/types.js b/website/scripts/types.js index ccc05eaa3..36de5bf81 100644 --- a/website/scripts/types.js +++ b/website/scripts/types.js @@ -60,14 +60,10 @@ * @typedef {Brk.BpsCentsPercentilesRatioSatsUsdPattern} PriceRatioPercentilesPattern * AnyRatioPattern: full ratio pattern with percentiles, SMAs, and std dev bands * @typedef {Brk.BpsCentsPercentilesRatioSatsSmaStdUsdPattern} AnyRatioPattern - * ValuePattern: patterns with base + cumulative (no rolling) - * @typedef {Brk.BaseCumulativeSumPattern | Brk.BaseCumulativeToPattern} ValuePattern * FullValuePattern: base + cumulative + rolling windows (flattened) * @typedef {Brk.BaseCumulativeSumPattern4} FullValuePattern * RollingWindowSlot: a single rolling window with stats (average, pct10, pct25, median, pct75, pct90, max, min) per unit * @typedef {Brk.AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern} RollingWindowSlot - * AnyValuePatternType: union of all value pattern types - * @typedef {Brk.BaseCumulativeSumPattern4 | Brk.BaseCumulativeSumPattern | Brk.BaseCumulativeToPattern} AnyValuePatternType * @typedef {Brk.AnySeriesPattern} AnySeriesPattern * @typedef {Brk.CentsSatsUsdPattern} ActivePricePattern * @typedef {Brk.AnySeriesEndpoint} AnySeriesEndpoint @@ -101,11 +97,6 @@ * Full activity pattern (coindays, coinyears, dormancy, transfer volume) * @typedef {Brk.CoindaysCoinyearsDormancyTransferPattern} FullActivityPattern * - * Profit distribution detail (base + cumulative + distribution flow + rel + sum + value) - * @typedef {Brk.BaseCumulativeDistributionSumToValuePattern} ProfitDetailPattern - * - * Loss detail with capitulation (base + capitulation + cumulative + negative + rel + sum + value) - * @typedef {Brk.BaseCapitulationCumulativeNegativeSumToValuePattern} LossDetailPattern * * BPS + ratio pattern (for NUPL and similar) * @typedef {Brk.BpsRatioPattern} NuplPattern