diff --git a/crates/brk_computer/src/blocks/count/compute.rs b/crates/brk_computer/src/blocks/count/compute.rs index 39be456eb..26b0d990b 100644 --- a/crates/brk_computer/src/blocks/count/compute.rs +++ b/crates/brk_computer/src/blocks/count/compute.rs @@ -162,8 +162,8 @@ impl Vecs { // Compute rolling window block counts (both block_count's own rolling + separate block_count_sum) let ws = WindowStarts { _24h: &self.height_24h_ago, - _7d: &self.height_1w_ago, - _30d: &self.height_1m_ago, + _1w: &self.height_1w_ago, + _1m: &self.height_1m_ago, _1y: &self.height_1y_ago, }; self.block_count.sum.compute_rolling_sum( diff --git a/crates/brk_computer/src/blocks/count/vecs.rs b/crates/brk_computer/src/blocks/count/vecs.rs index 170497e7a..bda68acd6 100644 --- a/crates/brk_computer/src/blocks/count/vecs.rs +++ b/crates/brk_computer/src/blocks/count/vecs.rs @@ -59,12 +59,12 @@ pub struct Vecs { } impl Vecs { - /// Get the standard 4 rolling window start heights (24h, 7d, 30d, 1y). + /// Get the standard 4 rolling window start heights (24h, 1w, 1m, 1y). pub fn window_starts(&self) -> WindowStarts<'_> { WindowStarts { _24h: &self.height_24h_ago, - _7d: &self.height_1w_ago, - _30d: &self.height_1m_ago, + _1w: &self.height_1w_ago, + _1m: &self.height_1m_ago, _1y: &self.height_1y_ago, } } diff --git a/crates/brk_computer/src/blocks/difficulty/compute.rs b/crates/brk_computer/src/blocks/difficulty/compute.rs index e9118efa6..9c8bc5a23 100644 --- a/crates/brk_computer/src/blocks/difficulty/compute.rs +++ b/crates/brk_computer/src/blocks/difficulty/compute.rs @@ -26,8 +26,8 @@ impl Vecs { exit, )?; - // Compute difficulty adjustment percentage - self.adjustment.height.compute_percentage_change( + // Compute difficulty adjustment ratio: (current - previous) / previous + self.adjustment.bps.height.compute_ratio_change( starting_indexes.height, &indexer.vecs.blocks.difficulty, 1, diff --git a/crates/brk_computer/src/blocks/difficulty/import.rs b/crates/brk_computer/src/blocks/difficulty/import.rs index 3adad5daa..cbfa9fa42 100644 --- a/crates/brk_computer/src/blocks/difficulty/import.rs +++ b/crates/brk_computer/src/blocks/difficulty/import.rs @@ -6,7 +6,7 @@ use vecdb::{Database, ReadableCloneableVec}; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeight, ComputedHeightDerived}, + internal::{Bps32ToFloat, Bps32ToPercent, ComputedFromHeight, ComputedHeightDerived, PercentFromHeight}, }; impl Vecs { @@ -26,7 +26,7 @@ impl Vecs { indexes, ), as_hash: ComputedFromHeight::forced_import(db, "difficulty_as_hash", version, indexes)?, - adjustment: ComputedFromHeight::forced_import(db, "difficulty_adjustment", version, indexes)?, + adjustment: PercentFromHeight::forced_import::(db, "difficulty_adjustment", version, indexes)?, epoch: ComputedFromHeight::forced_import(db, "difficulty_epoch", version, indexes)?, blocks_before_next_adjustment: ComputedFromHeight::forced_import( db, diff --git a/crates/brk_computer/src/blocks/difficulty/vecs.rs b/crates/brk_computer/src/blocks/difficulty/vecs.rs index 55a1971cc..4e202c00f 100644 --- a/crates/brk_computer/src/blocks/difficulty/vecs.rs +++ b/crates/brk_computer/src/blocks/difficulty/vecs.rs @@ -1,15 +1,15 @@ use brk_traversable::Traversable; -use brk_types::{DifficultyEpoch, StoredF32, StoredF64, StoredU32}; +use brk_types::{BasisPointsSigned32, DifficultyEpoch, StoredF32, StoredF64, StoredU32}; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeight, ComputedHeightDerived}; +use crate::internal::{ComputedFromHeight, ComputedHeightDerived, PercentFromHeight}; /// Difficulty metrics: raw difficulty, derived stats, adjustment, and countdown #[derive(Traversable)] pub struct Vecs { pub raw: ComputedHeightDerived, pub as_hash: ComputedFromHeight, - pub adjustment: ComputedFromHeight, + pub adjustment: PercentFromHeight, pub epoch: ComputedFromHeight, pub blocks_before_next_adjustment: ComputedFromHeight, pub days_before_next_adjustment: ComputedFromHeight, diff --git a/crates/brk_computer/src/cointime/adjusted/compute.rs b/crates/brk_computer/src/cointime/adjusted/compute.rs index 2b059bb93..284f855ba 100644 --- a/crates/brk_computer/src/cointime/adjusted/compute.rs +++ b/crates/brk_computer/src/cointime/adjusted/compute.rs @@ -1,5 +1,5 @@ use brk_error::Result; -use brk_types::{StoredF32, StoredF64}; +use brk_types::{BasisPointsSigned32, StoredF64}; use vecdb::Exit; use super::super::activity; @@ -14,15 +14,15 @@ impl Vecs { activity: &activity::Vecs, exit: &Exit, ) -> Result<()> { - self.cointime_adj_inflation_rate.height.compute_transform2( + self.cointime_adj_inflation_rate.bps.height.compute_transform2( starting_indexes.height, &activity.activity_to_vaultedness_ratio.height, - &supply.inflation.height, - |(h, ratio, inflation, ..)| (h, StoredF32::from((*ratio) * f64::from(*inflation))), + &supply.inflation_rate.bps.height, + |(h, ratio, inflation, ..)| (h, BasisPointsSigned32::from((*ratio) * f64::from(inflation))), exit, )?; - self.cointime_adj_tx_btc_velocity + self.cointime_adj_tx_velocity_btc .height .compute_transform2( starting_indexes.height, @@ -32,7 +32,7 @@ impl Vecs { exit, )?; - self.cointime_adj_tx_usd_velocity + self.cointime_adj_tx_velocity_usd .height .compute_transform2( starting_indexes.height, diff --git a/crates/brk_computer/src/cointime/adjusted/import.rs b/crates/brk_computer/src/cointime/adjusted/import.rs index 6443eae49..f524eeaed 100644 --- a/crates/brk_computer/src/cointime/adjusted/import.rs +++ b/crates/brk_computer/src/cointime/adjusted/import.rs @@ -3,26 +3,29 @@ use brk_types::Version; use vecdb::Database; use super::Vecs; -use crate::{indexes, internal::ComputedFromHeight}; +use crate::{ + indexes, + internal::{Bps32ToFloat, Bps32ToPercent, ComputedFromHeight, PercentFromHeight}, +}; impl Vecs { pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result { Ok(Self { - cointime_adj_inflation_rate: ComputedFromHeight::forced_import( + cointime_adj_inflation_rate: PercentFromHeight::forced_import::( db, "cointime_adj_inflation_rate", version, indexes, )?, - cointime_adj_tx_btc_velocity: ComputedFromHeight::forced_import( + cointime_adj_tx_velocity_btc: ComputedFromHeight::forced_import( db, - "cointime_adj_tx_btc_velocity", + "cointime_adj_tx_velocity_btc", version, indexes, )?, - cointime_adj_tx_usd_velocity: ComputedFromHeight::forced_import( + cointime_adj_tx_velocity_usd: ComputedFromHeight::forced_import( db, - "cointime_adj_tx_usd_velocity", + "cointime_adj_tx_velocity_usd", version, indexes, )?, diff --git a/crates/brk_computer/src/cointime/adjusted/vecs.rs b/crates/brk_computer/src/cointime/adjusted/vecs.rs index 1e61e898f..d55178777 100644 --- a/crates/brk_computer/src/cointime/adjusted/vecs.rs +++ b/crates/brk_computer/src/cointime/adjusted/vecs.rs @@ -1,12 +1,12 @@ use brk_traversable::Traversable; -use brk_types::{StoredF32, StoredF64}; +use brk_types::{BasisPointsSigned32, StoredF64}; use vecdb::{Rw, StorageMode}; -use crate::internal::ComputedFromHeight; +use crate::internal::{ComputedFromHeight, PercentFromHeight}; #[derive(Traversable)] pub struct Vecs { - pub cointime_adj_inflation_rate: ComputedFromHeight, - pub cointime_adj_tx_btc_velocity: ComputedFromHeight, - pub cointime_adj_tx_usd_velocity: ComputedFromHeight, + pub cointime_adj_inflation_rate: PercentFromHeight, + pub cointime_adj_tx_velocity_btc: ComputedFromHeight, + pub cointime_adj_tx_velocity_usd: ComputedFromHeight, } diff --git a/crates/brk_computer/src/cointime/reserve_risk/compute.rs b/crates/brk_computer/src/cointime/reserve_risk/compute.rs index 6d8b404c9..e1a9b24aa 100644 --- a/crates/brk_computer/src/cointime/reserve_risk/compute.rs +++ b/crates/brk_computer/src/cointime/reserve_risk/compute.rs @@ -14,7 +14,7 @@ impl Vecs { value: &value::Vecs, exit: &Exit, ) -> Result<()> { - self.vocdd_365d_median.compute_rolling_median_from_starts( + self.vocdd_median_1y.compute_rolling_median_from_starts( starting_indexes.height, &blocks.count.height_1y_ago, &value.vocdd.height, @@ -24,7 +24,7 @@ impl Vecs { self.hodl_bank.compute_cumulative_transformed_binary( starting_indexes.height, &prices.price.usd.height, - &self.vocdd_365d_median, + &self.vocdd_median_1y, |price, median| StoredF64::from(f64::from(price) - f64::from(median)), exit, )?; diff --git a/crates/brk_computer/src/cointime/reserve_risk/import.rs b/crates/brk_computer/src/cointime/reserve_risk/import.rs index 55dcc9161..1da7f71d2 100644 --- a/crates/brk_computer/src/cointime/reserve_risk/import.rs +++ b/crates/brk_computer/src/cointime/reserve_risk/import.rs @@ -13,7 +13,7 @@ impl Vecs { ) -> Result { let v1 = version + Version::ONE; Ok(Self { - vocdd_365d_median: EagerVec::forced_import(db, "vocdd_365d_median", v1)?, + vocdd_median_1y: EagerVec::forced_import(db, "vocdd_median_1y", v1)?, hodl_bank: EagerVec::forced_import(db, "hodl_bank", v1)?, reserve_risk: ComputedFromHeight::forced_import(db, "reserve_risk", v1, indexes)?, }) diff --git a/crates/brk_computer/src/cointime/reserve_risk/vecs.rs b/crates/brk_computer/src/cointime/reserve_risk/vecs.rs index 520841739..e81e8e6f0 100644 --- a/crates/brk_computer/src/cointime/reserve_risk/vecs.rs +++ b/crates/brk_computer/src/cointime/reserve_risk/vecs.rs @@ -6,7 +6,7 @@ use crate::internal::ComputedFromHeight; #[derive(Traversable)] pub struct Vecs { - pub vocdd_365d_median: M::Stored>>, + pub vocdd_median_1y: M::Stored>>, pub hodl_bank: M::Stored>>, pub reserve_risk: ComputedFromHeight, } diff --git a/crates/brk_computer/src/distribution/address/address_count.rs b/crates/brk_computer/src/distribution/address/address_count.rs index e7f05d7a8..d7286cadc 100644 --- a/crates/brk_computer/src/distribution/address/address_count.rs +++ b/crates/brk_computer/src/distribution/address/address_count.rs @@ -11,12 +11,12 @@ use vecdb::{ use crate::{ComputeIndexes, blocks, indexes, internal::ComputedFromHeight}; -/// Address count with 30d change metric for a single type. +/// Address count with 1m change metric for a single type. #[derive(Traversable)] pub struct AddrCountVecs { #[traversable(flatten)] pub count: ComputedFromHeight, - pub _30d_change: ComputedFromHeight, + pub change_1m: ComputedFromHeight, } impl AddrCountVecs { @@ -28,9 +28,9 @@ impl AddrCountVecs { ) -> Result { Ok(Self { count: ComputedFromHeight::forced_import(db, name, version, indexes)?, - _30d_change: ComputedFromHeight::forced_import( + change_1m: ComputedFromHeight::forced_import( db, - &format!("{name}_30d_change"), + &format!("{name}_change_1m"), version, indexes, )?, @@ -43,7 +43,7 @@ impl AddrCountVecs { starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self._30d_change.height.compute_rolling_change( + self.change_1m.height.compute_rolling_change( starting_indexes.height, &blocks.count.height_1m_ago, &self.count.height, @@ -85,7 +85,7 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount { } } -/// Address count per address type, with height + derived indexes + 30d change. +/// Address count per address type, with height + derived indexes + 1m change. #[derive(Deref, DerefMut, Traversable)] pub struct AddressTypeToAddrCountVecs(ByAddressType>); @@ -290,7 +290,7 @@ impl AddrCountsVecs { .height .compute_sum_of_others(starting_indexes.height, &sources, exit)?; - self.all._30d_change.height.compute_rolling_change( + self.all.change_1m.height.compute_rolling_change( starting_indexes.height, &blocks.count.height_1m_ago, &self.all.count.height, diff --git a/crates/brk_computer/src/distribution/cohorts/address/groups.rs b/crates/brk_computer/src/distribution/cohorts/address/groups.rs index 85340ea6e..fcd1b67ea 100644 --- a/crates/brk_computer/src/distribution/cohorts/address/groups.rs +++ b/crates/brk_computer/src/distribution/cohorts/address/groups.rs @@ -99,9 +99,9 @@ impl AddressCohorts { starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - // 1. Compute addr_count_30d_change using rolling window + // 1. Compute addr_count_change_1m using rolling window self.par_iter_mut().try_for_each(|v| { - v.addr_count_30d_change.height.compute_rolling_change( + v.addr_count_change_1m.height.compute_rolling_change( starting_indexes.height, &blocks.count.height_1m_ago, &v.addr_count.height, diff --git a/crates/brk_computer/src/distribution/cohorts/address/vecs.rs b/crates/brk_computer/src/distribution/cohorts/address/vecs.rs index 54ebf880b..646c8a2ed 100644 --- a/crates/brk_computer/src/distribution/cohorts/address/vecs.rs +++ b/crates/brk_computer/src/distribution/cohorts/address/vecs.rs @@ -36,7 +36,7 @@ pub struct AddressCohortVecs { pub metrics: BasicCohortMetrics, pub addr_count: ComputedFromHeight, - pub addr_count_30d_change: ComputedFromHeight, + pub addr_count_change_1m: ComputedFromHeight, } impl AddressCohortVecs { @@ -73,9 +73,9 @@ impl AddressCohortVecs { version + VERSION, indexes, )?, - addr_count_30d_change: ComputedFromHeight::forced_import( + addr_count_change_1m: ComputedFromHeight::forced_import( db, - &cfg.name("addr_count_30d_change"), + &cfg.name("addr_count_change_1m"), version + VERSION, indexes, )?, diff --git a/crates/brk_computer/src/distribution/metrics/activity.rs b/crates/brk_computer/src/distribution/metrics/activity.rs index ec7116c0c..d9131a812 100644 --- a/crates/brk_computer/src/distribution/metrics/activity.rs +++ b/crates/brk_computer/src/distribution/metrics/activity.rs @@ -6,7 +6,7 @@ use vecdb::{AnyStoredVec, AnyVec, EagerVec, Exit, ImportableVec, PcoVec, Rw, Sto use crate::{ ComputeIndexes, blocks, - internal::{ComputedFromHeightCumulativeSum, ValueFromHeightCumulative, ValueFromHeight}, + internal::{ComputedFromHeightCumulativeSum, RollingEmas2w, ValueFromHeightCumulative}, }; use super::ImportConfig; @@ -18,7 +18,7 @@ pub struct ActivityMetrics { pub sent: ValueFromHeightCumulative, /// 14-day EMA of sent supply (sats, btc, usd) - pub sent_14d_ema: ValueFromHeight, + pub sent_ema: RollingEmas2w, /// Satoshi-blocks destroyed (supply * blocks_old when spent) pub satblocks_destroyed: M::Stored>>, @@ -44,9 +44,9 @@ impl ActivityMetrics { cfg.indexes, )?, - sent_14d_ema: ValueFromHeight::forced_import( + sent_ema: RollingEmas2w::forced_import( cfg.db, - &cfg.name("sent_14d_ema"), + &cfg.name("sent"), cfg.version, cfg.indexes, )?, @@ -166,7 +166,7 @@ impl ActivityMetrics { let window_starts = blocks.count.window_starts(); // 14-day EMA of sent (sats and dollars) - self.sent_14d_ema.compute_ema( + self.sent_ema.compute( starting_indexes.height, &blocks.count.height_2w_ago, &self.sent.base.sats.height, diff --git a/crates/brk_computer/src/distribution/metrics/cost_basis/base.rs b/crates/brk_computer/src/distribution/metrics/cost_basis/base.rs index 13a58a07d..dfeee4417 100644 --- a/crates/brk_computer/src/distribution/metrics/cost_basis/base.rs +++ b/crates/brk_computer/src/distribution/metrics/cost_basis/base.rs @@ -26,13 +26,13 @@ impl CostBasisBase { Ok(Self { min: Price::forced_import( cfg.db, - &cfg.name("min_cost_basis"), + &cfg.name("cost_basis_min"), cfg.version, cfg.indexes, )?, max: Price::forced_import( cfg.db, - &cfg.name("max_cost_basis"), + &cfg.name("cost_basis_max"), cfg.version, cfg.indexes, )?, diff --git a/crates/brk_computer/src/distribution/metrics/outputs.rs b/crates/brk_computer/src/distribution/metrics/outputs.rs index d8b8b76af..7ae4f9dc5 100644 --- a/crates/brk_computer/src/distribution/metrics/outputs.rs +++ b/crates/brk_computer/src/distribution/metrics/outputs.rs @@ -12,7 +12,7 @@ use super::ImportConfig; #[derive(Traversable)] pub struct OutputsMetrics { pub utxo_count: ComputedFromHeight, - pub utxo_count_30d_change: ComputedFromHeight, + pub utxo_count_change_1m: ComputedFromHeight, } impl OutputsMetrics { @@ -25,9 +25,9 @@ impl OutputsMetrics { cfg.version, cfg.indexes, )?, - utxo_count_30d_change: ComputedFromHeight::forced_import( + utxo_count_change_1m: ComputedFromHeight::forced_import( cfg.db, - &cfg.name("utxo_count_30d_change"), + &cfg.name("utxo_count_change_1m"), cfg.version, cfg.indexes, )?, @@ -77,7 +77,7 @@ impl OutputsMetrics { starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.utxo_count_30d_change.height.compute_rolling_change( + self.utxo_count_change_1m.height.compute_rolling_change( starting_indexes.height, &blocks.count.height_1m_ago, &self.utxo_count.height, diff --git a/crates/brk_computer/src/distribution/metrics/realized/adjusted.rs b/crates/brk_computer/src/distribution/metrics/realized/adjusted.rs index 127a1b571..cc6f54d60 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/adjusted.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/adjusted.rs @@ -5,7 +5,7 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode}; use crate::{ ComputeIndexes, blocks, - internal::{ComputedFromHeight, RatioCents64, RollingEmas7d30d, RollingWindows}, + internal::{ComputedFromHeight, RatioCents64, RollingEmas1w1m, RollingWindows}, }; use crate::distribution::metrics::ImportConfig; @@ -23,7 +23,7 @@ pub struct RealizedAdjusted { // === Adjusted SOPR (rolling window ratios) === pub adjusted_sopr: RollingWindows, - pub adjusted_sopr_ema: RollingEmas7d30d, + pub adjusted_sopr_ema: RollingEmas1w1m, } impl RealizedAdjusted { @@ -52,7 +52,7 @@ impl RealizedAdjusted { let adjusted_sopr = RollingWindows::forced_import( cfg.db, &cfg.name("adjusted_sopr"), cfg.version + v1, cfg.indexes, )?; - let adjusted_sopr_ema = RollingEmas7d30d::forced_import( + let adjusted_sopr_ema = RollingEmas1w1m::forced_import( cfg.db, &cfg.name("adjusted_sopr_24h"), cfg.version + v1, cfg.indexes, )?; diff --git a/crates/brk_computer/src/distribution/metrics/realized/base.rs b/crates/brk_computer/src/distribution/metrics/realized/base.rs index 925e3cdda..623fdcf0e 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/base.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/base.rs @@ -1,6 +1,7 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{ + BasisPoints16, BasisPointsSigned16, Bitcoin, Cents, CentsSats, CentsSigned, CentsSquaredSats, Dollars, Height, Sats, StoredF32, StoredF64, Version, }; use vecdb::{ @@ -12,10 +13,13 @@ use crate::{ ComputeIndexes, blocks, distribution::state::RealizedState, internal::{ + Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent, CentsPlus, CentsUnsignedToDollars, ComputedFromHeightCumulative, ComputedFromHeight, - ComputedFromHeightRatio, NegCentsUnsignedToDollars, ValueFromHeightCumulative, LazyFromHeight, - PercentageCentsF32, PercentageCentsSignedCentsF32, PercentageCentsSignedDollarsF32, Price, RatioCents64, - RollingEmas7d30d, RollingWindows, Identity, ValueFromHeight, + ComputedFromHeightRatio, FiatFromHeight, NegCentsUnsignedToDollars, PercentFromHeight, + PercentRollingEmas1w1m, PercentRollingWindows, ValueFromHeightCumulative, LazyFromHeight, + Price, + RatioCentsBp16, RatioCentsSignedCentsBps16, RatioCentsSignedDollarsBps16, RatioCents64, + RollingEmas1w1m, RollingEmas2w, RollingWindows, Identity, }, prices, }; @@ -29,12 +33,12 @@ pub struct RealizedBase { pub realized_cap_cents: ComputedFromHeight, pub realized_cap: LazyFromHeight, pub realized_price: Price>, - pub realized_price_extra: ComputedFromHeightRatio, - pub realized_cap_30d_delta: ComputedFromHeight, + pub realized_price_ratio: ComputedFromHeightRatio, + pub realized_cap_change_1m: ComputedFromHeight, // === Investor Price === pub investor_price: Price>, - pub investor_price_extra: ComputedFromHeightRatio, + pub investor_price_ratio: ComputedFromHeightRatio, // === Floor/Ceiling Price Bands === pub lower_price_band: Price>, @@ -49,21 +53,18 @@ pub struct RealizedBase { // === Realized Profit/Loss === pub realized_profit: ComputedFromHeightCumulative, - pub realized_profit_7d_ema: ComputedFromHeight, + pub realized_profit_ema_1w: ComputedFromHeight, pub realized_loss: ComputedFromHeightCumulative, - pub realized_loss_7d_ema: ComputedFromHeight, + pub realized_loss_ema_1w: ComputedFromHeight, pub neg_realized_loss: LazyFromHeight, pub net_realized_pnl: ComputedFromHeightCumulative, - pub net_realized_pnl_7d_ema: ComputedFromHeight, - pub realized_value: ComputedFromHeight, + pub net_realized_pnl_ema_1w: ComputedFromHeight, + pub gross_pnl: FiatFromHeight, // === Realized vs Realized Cap Ratios === - pub realized_profit_rel_to_realized_cap: ComputedFromHeight, - pub realized_loss_rel_to_realized_cap: ComputedFromHeight, - pub net_realized_pnl_rel_to_realized_cap: ComputedFromHeight, - - // === Total Realized PnL === - pub total_realized_pnl: LazyFromHeight, + pub realized_profit_rel_to_realized_cap: PercentFromHeight, + pub realized_loss_rel_to_realized_cap: PercentFromHeight, + pub net_realized_pnl_rel_to_realized_cap: PercentFromHeight, // === Value Created/Destroyed Splits (stored) === pub profit_value_created: ComputedFromHeight, @@ -85,29 +86,29 @@ pub struct RealizedBase { // === SOPR (rolling window ratios) === pub sopr: RollingWindows, - pub sopr_ema: RollingEmas7d30d, + pub sopr_24h_ema: RollingEmas1w1m, // === Sell Side Risk === - pub realized_value_sum: RollingWindows, - pub sell_side_risk_ratio: RollingWindows, - pub sell_side_risk_ratio_ema: RollingEmas7d30d, + pub gross_pnl_sum: RollingWindows, + pub sell_side_risk_ratio: PercentRollingWindows, + pub sell_side_risk_ratio_24h_ema: PercentRollingEmas1w1m, // === Net Realized PnL Deltas === - pub net_realized_pnl_cumulative_30d_delta: ComputedFromHeight, - pub net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap: - ComputedFromHeight, - pub net_realized_pnl_cumulative_30d_delta_rel_to_market_cap: - ComputedFromHeight, + pub net_pnl_change_1m: ComputedFromHeight, + pub net_pnl_change_1m_rel_to_realized_cap: + PercentFromHeight, + pub net_pnl_change_1m_rel_to_market_cap: + PercentFromHeight, // === Peak Regret === pub peak_regret: ComputedFromHeightCumulative, - pub peak_regret_rel_to_realized_cap: ComputedFromHeight, + pub peak_regret_rel_to_realized_cap: PercentFromHeight, // === Sent in Profit/Loss === pub sent_in_profit: ValueFromHeightCumulative, - pub sent_in_profit_14d_ema: ValueFromHeight, + pub sent_in_profit_ema: RollingEmas2w, pub sent_in_loss: ValueFromHeightCumulative, - pub sent_in_loss_14d_ema: ValueFromHeight, + pub sent_in_loss_ema: RollingEmas2w, } impl RealizedBase { @@ -138,9 +139,9 @@ impl RealizedBase { cfg.indexes, )?; - let realized_profit_7d_ema = ComputedFromHeight::forced_import( + let realized_profit_ema_1w = ComputedFromHeight::forced_import( cfg.db, - &cfg.name("realized_profit_7d_ema"), + &cfg.name("realized_profit_ema_1w"), cfg.version, cfg.indexes, )?; @@ -152,9 +153,9 @@ impl RealizedBase { cfg.indexes, )?; - let realized_loss_7d_ema = ComputedFromHeight::forced_import( + let realized_loss_ema_1w = ComputedFromHeight::forced_import( cfg.db, - &cfg.name("realized_loss_7d_ema"), + &cfg.name("realized_loss_ema_1w"), cfg.version, cfg.indexes, )?; @@ -173,9 +174,9 @@ impl RealizedBase { cfg.indexes, )?; - let net_realized_pnl_7d_ema = ComputedFromHeight::forced_import( + let net_realized_pnl_ema_1w = ComputedFromHeight::forced_import( cfg.db, - &cfg.name("net_realized_pnl_7d_ema"), + &cfg.name("net_realized_pnl_ema_1w"), cfg.version, cfg.indexes, )?; @@ -187,40 +188,36 @@ impl RealizedBase { cfg.indexes, )?; - let realized_value = ComputedFromHeight::forced_import( + let gross_pnl = FiatFromHeight::forced_import( cfg.db, - &cfg.name("realized_value"), + &cfg.name("gross_pnl"), cfg.version, cfg.indexes, )?; - let total_realized_pnl = LazyFromHeight::from_computed::( - &cfg.name("total_realized_pnl"), - cfg.version + v1, - realized_value.height.read_only_boxed_clone(), - &realized_value, - ); + let realized_profit_rel_to_realized_cap = + PercentFromHeight::forced_import::( + cfg.db, + &cfg.name("realized_profit_rel_to_realized_cap"), + cfg.version + v1, + cfg.indexes, + )?; - let realized_profit_rel_to_realized_cap = ComputedFromHeight::forced_import( - cfg.db, - &cfg.name("realized_profit_rel_to_realized_cap"), - cfg.version + v1, - cfg.indexes, - )?; + let realized_loss_rel_to_realized_cap = + PercentFromHeight::forced_import::( + cfg.db, + &cfg.name("realized_loss_rel_to_realized_cap"), + cfg.version + v1, + cfg.indexes, + )?; - let realized_loss_rel_to_realized_cap = ComputedFromHeight::forced_import( - cfg.db, - &cfg.name("realized_loss_rel_to_realized_cap"), - cfg.version + v1, - cfg.indexes, - )?; - - let net_realized_pnl_rel_to_realized_cap = ComputedFromHeight::forced_import( - cfg.db, - &cfg.name("net_realized_pnl_rel_to_realized_cap"), - cfg.version + v1, - cfg.indexes, - )?; + let net_realized_pnl_rel_to_realized_cap = + PercentFromHeight::forced_import::( + cfg.db, + &cfg.name("net_realized_pnl_rel_to_realized_cap"), + cfg.version + v1, + cfg.indexes, + )?; let realized_price = Price::forced_import( cfg.db, @@ -236,7 +233,7 @@ impl RealizedBase { cfg.indexes, )?; - let investor_price_extra = ComputedFromHeightRatio::forced_import( + let investor_price_ratio = ComputedFromHeightRatio::forced_import( cfg.db, &cfg.name("investor_price"), cfg.version, @@ -312,7 +309,7 @@ impl RealizedBase { &profit_value_destroyed, ); - let realized_price_extra = ComputedFromHeightRatio::forced_import( + let realized_price_ratio = ComputedFromHeightRatio::forced_import( cfg.db, &cfg.name("realized_price"), cfg.version + v1, @@ -322,8 +319,8 @@ impl RealizedBase { let mvrv = LazyFromHeight::from_computed::>( &cfg.name("mvrv"), cfg.version, - realized_price_extra.ratio.height.read_only_boxed_clone(), - &realized_price_extra.ratio, + realized_price_ratio.ratio.height.read_only_boxed_clone(), + &realized_price_ratio.ratio, ); // === Rolling windows === @@ -333,61 +330,61 @@ impl RealizedBase { let value_destroyed_sum = RollingWindows::forced_import( cfg.db, &cfg.name("value_destroyed"), cfg.version + v1, cfg.indexes, )?; - let realized_value_sum = RollingWindows::forced_import( - cfg.db, &cfg.name("realized_value"), cfg.version + v1, cfg.indexes, + let gross_pnl_sum = RollingWindows::forced_import( + cfg.db, &cfg.name("gross_pnl_sum"), cfg.version + v1, cfg.indexes, )?; let sopr = RollingWindows::forced_import( cfg.db, &cfg.name("sopr"), cfg.version + v1, cfg.indexes, )?; - let sell_side_risk_ratio = RollingWindows::forced_import( + let sell_side_risk_ratio = PercentRollingWindows::forced_import::( cfg.db, &cfg.name("sell_side_risk_ratio"), cfg.version + v1, cfg.indexes, )?; // === EMA imports === - let sopr_ema = RollingEmas7d30d::forced_import( + let sopr_24h_ema = RollingEmas1w1m::forced_import( cfg.db, &cfg.name("sopr_24h"), cfg.version + v1, cfg.indexes, )?; - let sell_side_risk_ratio_ema = RollingEmas7d30d::forced_import( + let sell_side_risk_ratio_24h_ema = PercentRollingEmas1w1m::forced_import::( cfg.db, &cfg.name("sell_side_risk_ratio_24h"), cfg.version + v1, cfg.indexes, )?; - let peak_regret_rel_to_realized_cap = ComputedFromHeight::forced_import( - cfg.db, - &cfg.name("peak_regret_rel_to_realized_cap"), - cfg.version + v1, - cfg.indexes, - )?; + let peak_regret_rel_to_realized_cap = + PercentFromHeight::forced_import::( + cfg.db, + &cfg.name("realized_peak_regret_rel_to_realized_cap"), + cfg.version + v1, + cfg.indexes, + )?; Ok(Self { realized_cap_cents, realized_cap, realized_price, - realized_price_extra, - realized_cap_30d_delta: ComputedFromHeight::forced_import( + realized_price_ratio, + realized_cap_change_1m: ComputedFromHeight::forced_import( cfg.db, - &cfg.name("realized_cap_30d_delta"), + &cfg.name("realized_cap_change_1m"), cfg.version, cfg.indexes, )?, investor_price, - investor_price_extra, + investor_price_ratio, lower_price_band, upper_price_band, cap_raw, investor_cap_raw, mvrv, realized_profit, - realized_profit_7d_ema, + realized_profit_ema_1w, realized_loss, - realized_loss_7d_ema, + realized_loss_ema_1w, neg_realized_loss, net_realized_pnl, - net_realized_pnl_7d_ema, - realized_value, + net_realized_pnl_ema_1w, + gross_pnl, realized_profit_rel_to_realized_cap, realized_loss_rel_to_realized_cap, net_realized_pnl_rel_to_realized_cap, - total_realized_pnl, profit_value_created, profit_value_destroyed, loss_value_created, @@ -399,27 +396,27 @@ impl RealizedBase { value_created_sum, value_destroyed_sum, sopr, - sopr_ema, - realized_value_sum, + sopr_24h_ema, + gross_pnl_sum, sell_side_risk_ratio, - sell_side_risk_ratio_ema, - net_realized_pnl_cumulative_30d_delta: ComputedFromHeight::forced_import( + sell_side_risk_ratio_24h_ema, + net_pnl_change_1m: ComputedFromHeight::forced_import( cfg.db, - &cfg.name("net_realized_pnl_cumulative_30d_delta"), + &cfg.name("net_pnl_change_1m"), cfg.version + v3, cfg.indexes, )?, - net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap: - ComputedFromHeight::forced_import( + net_pnl_change_1m_rel_to_realized_cap: + PercentFromHeight::forced_import::( cfg.db, - &cfg.name("net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap"), + &cfg.name("net_pnl_change_1m_rel_to_realized_cap"), cfg.version + v3, cfg.indexes, )?, - net_realized_pnl_cumulative_30d_delta_rel_to_market_cap: - ComputedFromHeight::forced_import( + net_pnl_change_1m_rel_to_market_cap: + PercentFromHeight::forced_import::( cfg.db, - &cfg.name("net_realized_pnl_cumulative_30d_delta_rel_to_market_cap"), + &cfg.name("net_pnl_change_1m_rel_to_market_cap"), cfg.version + v3, cfg.indexes, )?, @@ -431,9 +428,9 @@ impl RealizedBase { cfg.version, cfg.indexes, )?, - sent_in_profit_14d_ema: ValueFromHeight::forced_import( + sent_in_profit_ema: RollingEmas2w::forced_import( cfg.db, - &cfg.name("sent_in_profit_14d_ema"), + &cfg.name("sent_in_profit"), cfg.version, cfg.indexes, )?, @@ -443,9 +440,9 @@ impl RealizedBase { cfg.version, cfg.indexes, )?, - sent_in_loss_14d_ema: ValueFromHeight::forced_import( + sent_in_loss_ema: RollingEmas2w::forced_import( cfg.db, - &cfg.name("sent_in_loss_14d_ema"), + &cfg.name("sent_in_loss"), cfg.version, cfg.indexes, )?, @@ -710,7 +707,7 @@ impl RealizedBase { Ok(()) })?; - self.realized_value.height.compute_add( + self.gross_pnl.cents.height.compute_add( starting_indexes.height, &self.realized_profit.height, &self.realized_loss.height, @@ -750,14 +747,14 @@ impl RealizedBase { exit, )?; - self.realized_price_extra.compute_ratio( + self.realized_price_ratio.compute_ratio( starting_indexes, &prices.price.cents.height, &self.realized_price.cents.height, exit, )?; - self.investor_price_extra.compute_ratio( + self.investor_price_ratio.compute_ratio( starting_indexes, &prices.price.cents.height, &self.investor_price.cents.height, @@ -796,7 +793,7 @@ impl RealizedBase { exit, )?; - self.realized_cap_30d_delta.height.compute_rolling_change( + self.realized_cap_change_1m.height.compute_rolling_change( starting_indexes.height, &blocks.count.height_1m_ago, &self.realized_cap_cents.height, @@ -827,8 +824,8 @@ impl RealizedBase { self.value_destroyed_sum.compute_rolling_sum( starting_indexes.height, &window_starts, &self.value_destroyed.height, exit, )?; - self.realized_value_sum.compute_rolling_sum( - starting_indexes.height, &window_starts, &self.realized_value.height, exit, + self.gross_pnl_sum.compute_rolling_sum( + starting_indexes.height, &window_starts, &self.gross_pnl.cents.height, exit, )?; // Compute SOPR from rolling sums @@ -843,27 +840,27 @@ impl RealizedBase { // Compute sell-side risk ratios for (ssrr, rv) in self.sell_side_risk_ratio.as_mut_array().into_iter() - .zip(self.realized_value_sum.as_array()) + .zip(self.gross_pnl_sum.as_array()) { - ssrr.compute_binary::( + ssrr.compute_binary::( starting_indexes.height, &rv.height, &self.realized_cap_cents.height, exit, )?; } // 7d EMAs - self.realized_profit_7d_ema.height.compute_rolling_ema( + self.realized_profit_ema_1w.height.compute_rolling_ema( starting_indexes.height, &blocks.count.height_1w_ago, &self.realized_profit.height, exit, )?; - self.realized_loss_7d_ema.height.compute_rolling_ema( + self.realized_loss_ema_1w.height.compute_rolling_ema( starting_indexes.height, &blocks.count.height_1w_ago, &self.realized_loss.height, exit, )?; - self.net_realized_pnl_7d_ema + self.net_realized_pnl_ema_1w .height .compute_rolling_ema( starting_indexes.height, @@ -873,14 +870,14 @@ impl RealizedBase { )?; // 14-day EMA of sent in profit/loss - self.sent_in_profit_14d_ema.compute_ema( + self.sent_in_profit_ema.compute( starting_indexes.height, &blocks.count.height_2w_ago, &self.sent_in_profit.base.sats.height, &self.sent_in_profit.base.cents.height, exit, )?; - self.sent_in_loss_14d_ema.compute_ema( + self.sent_in_loss_ema.compute( starting_indexes.height, &blocks.count.height_2w_ago, &self.sent_in_loss.base.sats.height, @@ -889,7 +886,7 @@ impl RealizedBase { )?; // SOPR EMAs (based on 24h window) - self.sopr_ema.compute_from_24h( + self.sopr_24h_ema.compute_from_24h( starting_indexes.height, &blocks.count.height_1w_ago, &blocks.count.height_1m_ago, @@ -898,38 +895,38 @@ impl RealizedBase { )?; // Sell side risk EMAs (based on 24h window) - self.sell_side_risk_ratio_ema.compute_from_24h( + self.sell_side_risk_ratio_24h_ema.compute_from_24h( starting_indexes.height, &blocks.count.height_1w_ago, &blocks.count.height_1m_ago, - &self.sell_side_risk_ratio._24h.height, + &self.sell_side_risk_ratio._24h.bps.height, exit, )?; // Realized profit/loss/net relative to realized cap self.realized_profit_rel_to_realized_cap - .compute_binary::( + .compute_binary::( starting_indexes.height, &self.realized_profit.height, &self.realized_cap_cents.height, exit, )?; self.realized_loss_rel_to_realized_cap - .compute_binary::( + .compute_binary::( starting_indexes.height, &self.realized_loss.height, &self.realized_cap_cents.height, exit, )?; self.net_realized_pnl_rel_to_realized_cap - .compute_binary::( + .compute_binary::( starting_indexes.height, &self.net_realized_pnl.height, &self.realized_cap_cents.height, exit, )?; self.peak_regret_rel_to_realized_cap - .compute_binary::( + .compute_binary::( starting_indexes.height, &self.peak_regret.height, &self.realized_cap_cents.height, @@ -937,7 +934,7 @@ impl RealizedBase { )?; // Net realized PnL cumulative 30d delta - self.net_realized_pnl_cumulative_30d_delta + self.net_pnl_change_1m .height .compute_rolling_change( starting_indexes.height, @@ -946,18 +943,18 @@ impl RealizedBase { exit, )?; - self.net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap - .compute_binary::( + self.net_pnl_change_1m_rel_to_realized_cap + .compute_binary::( starting_indexes.height, - &self.net_realized_pnl_cumulative_30d_delta.height, + &self.net_pnl_change_1m.height, &self.realized_cap_cents.height, exit, )?; - self.net_realized_pnl_cumulative_30d_delta_rel_to_market_cap - .compute_binary::( + self.net_pnl_change_1m_rel_to_market_cap + .compute_binary::( starting_indexes.height, - &self.net_realized_pnl_cumulative_30d_delta.height, + &self.net_pnl_change_1m.height, height_to_market_cap, exit, )?; diff --git a/crates/brk_computer/src/distribution/metrics/realized/extended.rs b/crates/brk_computer/src/distribution/metrics/realized/extended.rs index c42f3f308..27459af65 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/extended.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/extended.rs @@ -1,11 +1,14 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Cents, Dollars, Height, StoredF32, StoredF64, Version}; +use brk_types::{BasisPoints16, Cents, Dollars, Height, StoredF64, Version}; use vecdb::{Exit, ReadableVec, Rw, StorageMode}; use crate::{ ComputeIndexes, blocks, - internal::{ComputedFromHeight, ComputedFromHeightRatioExtension, RatioCents64, RollingWindows}, + internal::{ + Bp16ToFloat, Bp16ToPercent, ComputedFromHeightRatioExtension, PercentFromHeight, + RatioCents64, RatioDollarsBp16, RollingWindows, + }, }; use crate::distribution::metrics::ImportConfig; @@ -15,7 +18,7 @@ use super::RealizedBase; /// Extended realized metrics (only for extended cohorts: all, sth, lth, age_range). #[derive(Traversable)] pub struct RealizedExtended { - pub realized_cap_rel_to_own_market_cap: ComputedFromHeight, + pub realized_cap_rel_to_own_market_cap: PercentFromHeight, // === Realized Profit/Loss Rolling Sums === pub realized_profit_sum: RollingWindows, @@ -34,7 +37,7 @@ impl RealizedExtended { let v1 = Version::ONE; Ok(RealizedExtended { - realized_cap_rel_to_own_market_cap: ComputedFromHeight::forced_import( + realized_cap_rel_to_own_market_cap: PercentFromHeight::forced_import::( cfg.db, &cfg.name("realized_cap_rel_to_own_market_cap"), cfg.version, @@ -84,8 +87,7 @@ impl RealizedExtended { // Realized cap relative to own market cap self.realized_cap_rel_to_own_market_cap - .height - .compute_percentage( + .compute_binary::( starting_indexes.height, &base.realized_cap.height, height_to_market_cap, @@ -107,7 +109,7 @@ impl RealizedExtended { blocks, starting_indexes, exit, - &base.realized_price_extra.ratio.height, + &base.realized_price_ratio.ratio.height, )?; self.realized_price_ratio_ext.compute_cents_bands( starting_indexes, @@ -119,7 +121,7 @@ impl RealizedExtended { blocks, starting_indexes, exit, - &base.investor_price_extra.ratio.height, + &base.investor_price_ratio.ratio.height, )?; self.investor_price_ratio_ext.compute_cents_bands( starting_indexes, diff --git a/crates/brk_computer/src/distribution/metrics/relative/base.rs b/crates/brk_computer/src/distribution/metrics/relative/base.rs index 938dfe28a..388c21a5e 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/base.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/base.rs @@ -1,11 +1,11 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Dollars, Height, Sats, StoredF32, StoredF64, Version}; +use brk_types::{BasisPoints16, BasisPointsSigned16, Dollars, Height, Sats, StoredF32, Version}; use vecdb::{Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode}; use crate::internal::{ - ComputedFromHeight, Identity, LazyFromHeight, - NegPercentageDollarsF32, PercentageDollarsF32, PercentageSatsF64, + Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent, LazyFromHeight, + NegRatioDollarsBps16, PercentFromHeight, RatioDollarsBp16, RatioDollarsBps16, RatioSatsBp16, }; use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase}; @@ -16,19 +16,19 @@ use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase}; #[derive(Traversable)] pub struct RelativeBase { // === Supply in Profit/Loss Relative to Own Supply === - pub supply_in_profit_rel_to_own_supply: ComputedFromHeight, - pub supply_in_loss_rel_to_own_supply: ComputedFromHeight, + pub supply_in_profit_rel_to_own_supply: PercentFromHeight, + pub supply_in_loss_rel_to_own_supply: PercentFromHeight, // === Unrealized vs Market Cap === - pub unrealized_profit_rel_to_market_cap: ComputedFromHeight, - pub unrealized_loss_rel_to_market_cap: ComputedFromHeight, - pub neg_unrealized_loss_rel_to_market_cap: ComputedFromHeight, - pub net_unrealized_pnl_rel_to_market_cap: ComputedFromHeight, - pub nupl: LazyFromHeight, + pub unrealized_profit_rel_to_market_cap: PercentFromHeight, + pub unrealized_loss_rel_to_market_cap: PercentFromHeight, + pub neg_unrealized_loss_rel_to_market_cap: PercentFromHeight, + pub net_unrealized_pnl_rel_to_market_cap: PercentFromHeight, + pub nupl: LazyFromHeight, // === Invested Capital in Profit/Loss as % of Realized Cap === - pub invested_capital_in_profit_pct: ComputedFromHeight, - pub invested_capital_in_loss_pct: ComputedFromHeight, + pub invested_capital_in_profit_rel_to_realized_cap: PercentFromHeight, + pub invested_capital_in_loss_rel_to_realized_cap: PercentFromHeight, } impl RelativeBase { @@ -36,41 +36,49 @@ impl RelativeBase { let v1 = Version::ONE; let v2 = Version::new(2); - let net_unrealized_pnl_rel_to_market_cap = ComputedFromHeight::forced_import( - cfg.db, &cfg.name("net_unrealized_pnl_rel_to_market_cap"), cfg.version + v2, cfg.indexes, - )?; + let net_unrealized_pnl_rel_to_market_cap = + PercentFromHeight::forced_import::( + cfg.db, &cfg.name("net_unrealized_pnl_rel_to_market_cap"), cfg.version + v2, cfg.indexes, + )?; - let nupl = LazyFromHeight::from_computed::>( + let nupl = LazyFromHeight::from_computed::( &cfg.name("nupl"), cfg.version + v2, - net_unrealized_pnl_rel_to_market_cap.height.read_only_boxed_clone(), - &net_unrealized_pnl_rel_to_market_cap, + net_unrealized_pnl_rel_to_market_cap.bps.height.read_only_boxed_clone(), + &net_unrealized_pnl_rel_to_market_cap.bps, ); Ok(Self { - supply_in_profit_rel_to_own_supply: ComputedFromHeight::forced_import( - cfg.db, &cfg.name("supply_in_profit_rel_to_own_supply"), cfg.version + v1, cfg.indexes, - )?, - supply_in_loss_rel_to_own_supply: ComputedFromHeight::forced_import( - cfg.db, &cfg.name("supply_in_loss_rel_to_own_supply"), cfg.version + v1, cfg.indexes, - )?, - unrealized_profit_rel_to_market_cap: ComputedFromHeight::forced_import( - cfg.db, &cfg.name("unrealized_profit_rel_to_market_cap"), cfg.version + v2, cfg.indexes, - )?, - unrealized_loss_rel_to_market_cap: ComputedFromHeight::forced_import( - cfg.db, &cfg.name("unrealized_loss_rel_to_market_cap"), cfg.version + v2, cfg.indexes, - )?, - neg_unrealized_loss_rel_to_market_cap: ComputedFromHeight::forced_import( - cfg.db, &cfg.name("neg_unrealized_loss_rel_to_market_cap"), cfg.version + v2, cfg.indexes, - )?, + supply_in_profit_rel_to_own_supply: + PercentFromHeight::forced_import::( + cfg.db, &cfg.name("supply_in_profit_rel_to_own_supply"), cfg.version + v1, cfg.indexes, + )?, + supply_in_loss_rel_to_own_supply: + PercentFromHeight::forced_import::( + cfg.db, &cfg.name("supply_in_loss_rel_to_own_supply"), cfg.version + v1, cfg.indexes, + )?, + unrealized_profit_rel_to_market_cap: + PercentFromHeight::forced_import::( + cfg.db, &cfg.name("unrealized_profit_rel_to_market_cap"), cfg.version + v2, cfg.indexes, + )?, + unrealized_loss_rel_to_market_cap: + PercentFromHeight::forced_import::( + cfg.db, &cfg.name("unrealized_loss_rel_to_market_cap"), cfg.version + v2, cfg.indexes, + )?, + neg_unrealized_loss_rel_to_market_cap: + PercentFromHeight::forced_import::( + cfg.db, &cfg.name("neg_unrealized_loss_rel_to_market_cap"), cfg.version + v2, cfg.indexes, + )?, net_unrealized_pnl_rel_to_market_cap, nupl, - invested_capital_in_profit_pct: ComputedFromHeight::forced_import( - cfg.db, &cfg.name("invested_capital_in_profit_pct"), cfg.version, cfg.indexes, - )?, - invested_capital_in_loss_pct: ComputedFromHeight::forced_import( - cfg.db, &cfg.name("invested_capital_in_loss_pct"), cfg.version, cfg.indexes, - )?, + invested_capital_in_profit_rel_to_realized_cap: + PercentFromHeight::forced_import::( + cfg.db, &cfg.name("invested_capital_in_profit_rel_to_realized_cap"), cfg.version, cfg.indexes, + )?, + invested_capital_in_loss_rel_to_realized_cap: + PercentFromHeight::forced_import::( + cfg.db, &cfg.name("invested_capital_in_loss_rel_to_realized_cap"), cfg.version, cfg.indexes, + )?, }) } @@ -84,35 +92,35 @@ impl RelativeBase { exit: &Exit, ) -> Result<()> { self.supply_in_profit_rel_to_own_supply - .compute_binary::( + .compute_binary::( max_from, &unrealized.supply_in_profit.sats.height, supply_total_sats, exit, )?; self.supply_in_loss_rel_to_own_supply - .compute_binary::( + .compute_binary::( max_from, &unrealized.supply_in_loss.sats.height, supply_total_sats, exit, )?; self.unrealized_profit_rel_to_market_cap - .compute_binary::( + .compute_binary::( max_from, &unrealized.unrealized_profit.usd.height, market_cap, exit, )?; self.unrealized_loss_rel_to_market_cap - .compute_binary::( + .compute_binary::( max_from, &unrealized.unrealized_loss.usd.height, market_cap, exit, )?; self.neg_unrealized_loss_rel_to_market_cap - .compute_binary::( + .compute_binary::( max_from, &unrealized.unrealized_loss.usd.height, market_cap, exit, )?; self.net_unrealized_pnl_rel_to_market_cap - .compute_binary::( + .compute_binary::( max_from, &unrealized.net_unrealized_pnl.usd.height, market_cap, exit, )?; - self.invested_capital_in_profit_pct - .compute_binary::( + self.invested_capital_in_profit_rel_to_realized_cap + .compute_binary::( max_from, &unrealized.invested_capital_in_profit.usd.height, &realized.realized_cap.height, exit, )?; - self.invested_capital_in_loss_pct - .compute_binary::( + self.invested_capital_in_loss_rel_to_realized_cap + .compute_binary::( max_from, &unrealized.invested_capital_in_loss.usd.height, &realized.realized_cap.height, exit, )?; Ok(()) diff --git a/crates/brk_computer/src/distribution/metrics/relative/extended_own_market_cap.rs b/crates/brk_computer/src/distribution/metrics/relative/extended_own_market_cap.rs index e3dd68d09..09561c03f 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/extended_own_market_cap.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/extended_own_market_cap.rs @@ -1,10 +1,11 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Dollars, Height, StoredF32}; +use brk_types::{BasisPoints16, BasisPointsSigned16, Dollars, Height}; use vecdb::{Exit, ReadableVec, Rw, StorageMode}; use crate::internal::{ - ComputedFromHeight, NegPercentageDollarsF32, PercentageDollarsF32, + Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent, + NegRatioDollarsBps16, PercentFromHeight, RatioDollarsBp16, RatioDollarsBps16, }; use crate::distribution::metrics::{ImportConfig, UnrealizedBase}; @@ -13,13 +14,13 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedBase}; #[derive(Traversable)] pub struct RelativeExtendedOwnMarketCap { pub unrealized_profit_rel_to_own_market_cap: - ComputedFromHeight, + PercentFromHeight, pub unrealized_loss_rel_to_own_market_cap: - ComputedFromHeight, + PercentFromHeight, pub neg_unrealized_loss_rel_to_own_market_cap: - ComputedFromHeight, + PercentFromHeight, pub net_unrealized_pnl_rel_to_own_market_cap: - ComputedFromHeight, + PercentFromHeight, } impl RelativeExtendedOwnMarketCap { @@ -30,28 +31,28 @@ impl RelativeExtendedOwnMarketCap { Ok(Self { unrealized_profit_rel_to_own_market_cap: - ComputedFromHeight::forced_import( + PercentFromHeight::forced_import::( cfg.db, &cfg.name("unrealized_profit_rel_to_own_market_cap"), cfg.version + v2, cfg.indexes, )?, unrealized_loss_rel_to_own_market_cap: - ComputedFromHeight::forced_import( + PercentFromHeight::forced_import::( cfg.db, &cfg.name("unrealized_loss_rel_to_own_market_cap"), cfg.version + v2, cfg.indexes, )?, neg_unrealized_loss_rel_to_own_market_cap: - ComputedFromHeight::forced_import( + PercentFromHeight::forced_import::( cfg.db, &cfg.name("neg_unrealized_loss_rel_to_own_market_cap"), cfg.version + v2, cfg.indexes, )?, net_unrealized_pnl_rel_to_own_market_cap: - ComputedFromHeight::forced_import( + PercentFromHeight::forced_import::( cfg.db, &cfg.name("net_unrealized_pnl_rel_to_own_market_cap"), cfg.version + v2, @@ -68,19 +69,19 @@ impl RelativeExtendedOwnMarketCap { exit: &Exit, ) -> Result<()> { self.unrealized_profit_rel_to_own_market_cap - .compute_binary::( + .compute_binary::( max_from, &unrealized.unrealized_profit.usd.height, own_market_cap, exit, )?; self.unrealized_loss_rel_to_own_market_cap - .compute_binary::( + .compute_binary::( max_from, &unrealized.unrealized_loss.usd.height, own_market_cap, exit, )?; self.neg_unrealized_loss_rel_to_own_market_cap - .compute_binary::( + .compute_binary::( max_from, &unrealized.unrealized_loss.usd.height, own_market_cap, exit, )?; self.net_unrealized_pnl_rel_to_own_market_cap - .compute_binary::( + .compute_binary::( max_from, &unrealized.net_unrealized_pnl.usd.height, own_market_cap, exit, )?; Ok(()) diff --git a/crates/brk_computer/src/distribution/metrics/relative/extended_own_pnl.rs b/crates/brk_computer/src/distribution/metrics/relative/extended_own_pnl.rs index f96ec3806..704e6f959 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/extended_own_pnl.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/extended_own_pnl.rs @@ -1,10 +1,11 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Dollars, Height, StoredF32}; +use brk_types::{BasisPoints16, BasisPointsSigned16, Dollars, Height}; use vecdb::{Exit, Rw, StorageMode}; use crate::internal::{ - ComputedFromHeight, NegPercentageDollarsF32, PercentageDollarsF32, + Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent, + NegRatioDollarsBps16, PercentFromHeight, RatioDollarsBp16, RatioDollarsBps16, }; use crate::distribution::metrics::{ImportConfig, UnrealizedBase}; @@ -12,14 +13,14 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedBase}; /// Extended relative metrics for own total unrealized PnL (extended only). #[derive(Traversable)] pub struct RelativeExtendedOwnPnl { - pub unrealized_profit_rel_to_own_total_unrealized_pnl: - ComputedFromHeight, - pub unrealized_loss_rel_to_own_total_unrealized_pnl: - ComputedFromHeight, - pub neg_unrealized_loss_rel_to_own_total_unrealized_pnl: - ComputedFromHeight, - pub net_unrealized_pnl_rel_to_own_total_unrealized_pnl: - ComputedFromHeight, + pub unrealized_profit_rel_to_own_gross_pnl: + PercentFromHeight, + pub unrealized_loss_rel_to_own_gross_pnl: + PercentFromHeight, + pub neg_unrealized_loss_rel_to_own_gross_pnl: + PercentFromHeight, + pub net_unrealized_pnl_rel_to_own_gross_pnl: + PercentFromHeight, } impl RelativeExtendedOwnPnl { @@ -30,31 +31,31 @@ impl RelativeExtendedOwnPnl { let v2 = brk_types::Version::new(2); Ok(Self { - unrealized_profit_rel_to_own_total_unrealized_pnl: - ComputedFromHeight::forced_import( + unrealized_profit_rel_to_own_gross_pnl: + PercentFromHeight::forced_import::( cfg.db, - &cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"), + &cfg.name("unrealized_profit_rel_to_own_gross_pnl"), cfg.version + v1, cfg.indexes, )?, - unrealized_loss_rel_to_own_total_unrealized_pnl: - ComputedFromHeight::forced_import( + unrealized_loss_rel_to_own_gross_pnl: + PercentFromHeight::forced_import::( cfg.db, - &cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"), + &cfg.name("unrealized_loss_rel_to_own_gross_pnl"), cfg.version + v1, cfg.indexes, )?, - neg_unrealized_loss_rel_to_own_total_unrealized_pnl: - ComputedFromHeight::forced_import( + neg_unrealized_loss_rel_to_own_gross_pnl: + PercentFromHeight::forced_import::( cfg.db, - &cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"), + &cfg.name("neg_unrealized_loss_rel_to_own_gross_pnl"), cfg.version + v1, cfg.indexes, )?, - net_unrealized_pnl_rel_to_own_total_unrealized_pnl: - ComputedFromHeight::forced_import( + net_unrealized_pnl_rel_to_own_gross_pnl: + PercentFromHeight::forced_import::( cfg.db, - &cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"), + &cfg.name("net_unrealized_pnl_rel_to_own_gross_pnl"), cfg.version + v2, cfg.indexes, )?, @@ -67,21 +68,21 @@ impl RelativeExtendedOwnPnl { unrealized: &UnrealizedBase, exit: &Exit, ) -> Result<()> { - self.unrealized_profit_rel_to_own_total_unrealized_pnl - .compute_binary::( - max_from, &unrealized.unrealized_profit.usd.height, &unrealized.total_unrealized_pnl.usd.height, exit, + self.unrealized_profit_rel_to_own_gross_pnl + .compute_binary::( + max_from, &unrealized.unrealized_profit.usd.height, &unrealized.gross_pnl.usd.height, exit, )?; - self.unrealized_loss_rel_to_own_total_unrealized_pnl - .compute_binary::( - max_from, &unrealized.unrealized_loss.usd.height, &unrealized.total_unrealized_pnl.usd.height, exit, + self.unrealized_loss_rel_to_own_gross_pnl + .compute_binary::( + max_from, &unrealized.unrealized_loss.usd.height, &unrealized.gross_pnl.usd.height, exit, )?; - self.neg_unrealized_loss_rel_to_own_total_unrealized_pnl - .compute_binary::( - max_from, &unrealized.unrealized_loss.usd.height, &unrealized.total_unrealized_pnl.usd.height, exit, + self.neg_unrealized_loss_rel_to_own_gross_pnl + .compute_binary::( + max_from, &unrealized.unrealized_loss.usd.height, &unrealized.gross_pnl.usd.height, exit, )?; - self.net_unrealized_pnl_rel_to_own_total_unrealized_pnl - .compute_binary::( - max_from, &unrealized.net_unrealized_pnl.usd.height, &unrealized.total_unrealized_pnl.usd.height, exit, + self.net_unrealized_pnl_rel_to_own_gross_pnl + .compute_binary::( + max_from, &unrealized.net_unrealized_pnl.usd.height, &unrealized.gross_pnl.usd.height, exit, )?; Ok(()) } diff --git a/crates/brk_computer/src/distribution/metrics/relative/to_all.rs b/crates/brk_computer/src/distribution/metrics/relative/to_all.rs index edb9d83f3..1d50a6ecb 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/to_all.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/to_all.rs @@ -1,9 +1,9 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Height, Sats, StoredF64}; +use brk_types::{BasisPoints16, Height, Sats}; use vecdb::{Exit, ReadableVec, Rw, StorageMode}; -use crate::internal::{ComputedFromHeight, PercentageSatsF64}; +use crate::internal::{Bp16ToFloat, Bp16ToPercent, PercentFromHeight, RatioSatsBp16}; use crate::distribution::metrics::{ImportConfig, UnrealizedBase}; @@ -11,11 +11,11 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedBase}; #[derive(Traversable)] pub struct RelativeToAll { pub supply_rel_to_circulating_supply: - ComputedFromHeight, + PercentFromHeight, pub supply_in_profit_rel_to_circulating_supply: - ComputedFromHeight, + PercentFromHeight, pub supply_in_loss_rel_to_circulating_supply: - ComputedFromHeight, + PercentFromHeight, } impl RelativeToAll { @@ -24,21 +24,21 @@ impl RelativeToAll { ) -> Result { Ok(Self { supply_rel_to_circulating_supply: - ComputedFromHeight::forced_import( + PercentFromHeight::forced_import::( cfg.db, &cfg.name("supply_rel_to_circulating_supply"), cfg.version + brk_types::Version::ONE, cfg.indexes, )?, supply_in_profit_rel_to_circulating_supply: - ComputedFromHeight::forced_import( + PercentFromHeight::forced_import::( cfg.db, &cfg.name("supply_in_profit_rel_to_circulating_supply"), cfg.version + brk_types::Version::ONE, cfg.indexes, )?, supply_in_loss_rel_to_circulating_supply: - ComputedFromHeight::forced_import( + PercentFromHeight::forced_import::( cfg.db, &cfg.name("supply_in_loss_rel_to_circulating_supply"), cfg.version + brk_types::Version::ONE, @@ -56,15 +56,15 @@ impl RelativeToAll { exit: &Exit, ) -> Result<()> { self.supply_rel_to_circulating_supply - .compute_binary::( + .compute_binary::( max_from, supply_total_sats, all_supply_sats, exit, )?; self.supply_in_profit_rel_to_circulating_supply - .compute_binary::( + .compute_binary::( max_from, &unrealized.supply_in_profit.sats.height, all_supply_sats, exit, )?; self.supply_in_loss_rel_to_circulating_supply - .compute_binary::( + .compute_binary::( max_from, &unrealized.supply_in_loss.sats.height, all_supply_sats, exit, )?; Ok(()) diff --git a/crates/brk_computer/src/distribution/metrics/supply.rs b/crates/brk_computer/src/distribution/metrics/supply.rs index 53c0c7e2d..997ec4d30 100644 --- a/crates/brk_computer/src/distribution/metrics/supply.rs +++ b/crates/brk_computer/src/distribution/metrics/supply.rs @@ -18,8 +18,8 @@ use super::ImportConfig; pub struct SupplyMetrics { pub total: ValueFromHeight, pub halved: LazyValueFromHeight, - /// 30-day change in supply (net position change) - sats, btc, usd - pub _30d_change: ValueFromHeightChange, + /// 1-month change in supply (net position change) - sats, btc, usd + pub change_1m: ValueFromHeightChange, } impl SupplyMetrics { @@ -39,9 +39,9 @@ impl SupplyMetrics { HalveDollars, >(&cfg.name("supply_halved"), &supply, cfg.version); - let _30d_change = ValueFromHeightChange::forced_import( + let change_1m = ValueFromHeightChange::forced_import( cfg.db, - &cfg.name("_30d_change"), + &cfg.name("supply_change_1m"), cfg.version, cfg.indexes, )?; @@ -49,7 +49,7 @@ impl SupplyMetrics { Ok(Self { total: supply, halved: supply_halved, - _30d_change, + change_1m, }) } @@ -114,7 +114,7 @@ impl SupplyMetrics { starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self._30d_change.compute_rolling( + self.change_1m.compute_rolling( starting_indexes.height, &blocks.count.height_1m_ago, &self.total.sats.height, diff --git a/crates/brk_computer/src/distribution/metrics/unrealized/base.rs b/crates/brk_computer/src/distribution/metrics/unrealized/base.rs index fa773e6de..2d37d13d1 100644 --- a/crates/brk_computer/src/distribution/metrics/unrealized/base.rs +++ b/crates/brk_computer/src/distribution/metrics/unrealized/base.rs @@ -51,7 +51,7 @@ pub struct UnrealizedBase { // === Net and Total === pub net_unrealized_pnl: FiatFromHeight, - pub total_unrealized_pnl: FiatFromHeight, + pub gross_pnl: FiatFromHeight, } impl UnrealizedBase { @@ -148,9 +148,9 @@ impl UnrealizedBase { cfg.version, cfg.indexes, )?; - let total_unrealized_pnl = FiatFromHeight::forced_import( + let gross_pnl = FiatFromHeight::forced_import( cfg.db, - &cfg.name("total_unrealized_pnl"), + &cfg.name("gross_pnl"), cfg.version, cfg.indexes, )?; @@ -171,7 +171,7 @@ impl UnrealizedBase { net_sentiment, neg_unrealized_loss, net_unrealized_pnl, - total_unrealized_pnl, + gross_pnl, }) } @@ -444,7 +444,7 @@ impl UnrealizedBase { &self.unrealized_loss.cents.height, exit, )?; - self.total_unrealized_pnl.cents.height.compute_add( + self.gross_pnl.cents.height.compute_add( starting_indexes.height, &self.unrealized_profit.cents.height, &self.unrealized_loss.cents.height, diff --git a/crates/brk_computer/src/internal/emas/mod.rs b/crates/brk_computer/src/internal/emas/mod.rs index 99a5634e2..0d2f00275 100644 --- a/crates/brk_computer/src/internal/emas/mod.rs +++ b/crates/brk_computer/src/internal/emas/mod.rs @@ -1,5 +1,5 @@ -mod _14d; -mod _7d_30d; +mod _1w_1m; +mod _2w; -pub use _14d::*; -pub use _7d_30d::*; +pub use _1w_1m::*; +pub use _2w::*; diff --git a/crates/brk_computer/src/internal/from_height/bps.rs b/crates/brk_computer/src/internal/from_height/float32.rs similarity index 54% rename from crates/brk_computer/src/internal/from_height/bps.rs rename to crates/brk_computer/src/internal/from_height/float32.rs index 58797a6b1..8ed205764 100644 --- a/crates/brk_computer/src/internal/from_height/bps.rs +++ b/crates/brk_computer/src/internal/from_height/float32.rs @@ -1,20 +1,20 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{StoredF32, Version}; +use brk_types::{Height, StoredF32, Version}; use schemars::JsonSchema; -use vecdb::{Database, ReadableCloneableVec, Rw, StorageMode, UnaryTransform}; +use vecdb::{BinaryTransform, Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, UnaryTransform, VecValue}; use crate::indexes; use super::{ComputedFromHeight, LazyFromHeight}; use crate::internal::NumericValue; -/// Basis-point storage with lazy float view. +/// Basis-point storage with lazy ratio float view (÷10000). /// /// Stores integer basis points on disk (Pco-compressed), -/// exposes a lazy StoredF32 view (bps / 100). +/// exposes a lazy StoredF32 ratio (e.g., 25000 bps → 2.5). #[derive(Traversable)] -pub struct BpsFromHeight +pub struct Float32FromHeight where B: NumericValue + JsonSchema, { @@ -22,7 +22,7 @@ where pub float: LazyFromHeight, } -impl BpsFromHeight +impl Float32FromHeight where B: NumericValue + JsonSchema, { @@ -43,4 +43,19 @@ where Ok(Self { bps, float }) } + + pub(crate) fn compute_binary( + &mut self, + max_from: Height, + source1: &impl ReadableVec, + source2: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + S1T: VecValue, + S2T: VecValue, + F: BinaryTransform, + { + self.bps.compute_binary::(max_from, source1, source2, exit) + } } diff --git a/crates/brk_computer/src/internal/from_height/mod.rs b/crates/brk_computer/src/internal/from_height/mod.rs index c303c1efa..55e4efd5e 100644 --- a/crates/brk_computer/src/internal/from_height/mod.rs +++ b/crates/brk_computer/src/internal/from_height/mod.rs @@ -1,14 +1,15 @@ mod aggregated; mod base; -mod bps; mod by_unit; mod constant; mod cumulative; mod cumulative_sum; mod distribution; mod fiat; +mod float32; mod full; mod lazy_base; +mod percent; mod percentiles; mod price; mod ratio; @@ -17,15 +18,16 @@ mod value; pub use aggregated::*; pub use base::*; -pub use bps::*; pub use by_unit::*; pub use constant::*; pub use cumulative::*; pub use cumulative_sum::*; pub use distribution::*; pub use fiat::*; +pub use float32::*; pub use full::*; pub use lazy_base::*; +pub use percent::*; pub use percentiles::*; pub use price::*; pub use ratio::*; diff --git a/crates/brk_computer/src/internal/from_height/percent.rs b/crates/brk_computer/src/internal/from_height/percent.rs new file mode 100644 index 000000000..33828c851 --- /dev/null +++ b/crates/brk_computer/src/internal/from_height/percent.rs @@ -0,0 +1,96 @@ +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{Height, StoredF32, Version}; +use schemars::JsonSchema; +use vecdb::{BinaryTransform, Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, UnaryTransform, VecValue}; + +use crate::{ + indexes, + internal::NumericValue, + traits::ComputeDrawdown, +}; + +use super::{ComputedFromHeight, LazyFromHeight}; + +/// Basis-point storage with both ratio and percentage float views. +/// +/// Stores integer basis points on disk (Pco-compressed), +/// exposes two lazy StoredF32 views: +/// - `ratio`: bps ÷ 10000 (e.g., 4523 bps → 0.4523) +/// - `percent`: bps ÷ 100 (e.g., 4523 bps → 45.23%) +/// +/// Use for dominance, adoption, RSI, and other percentage-valued metrics. +#[derive(Traversable)] +pub struct PercentFromHeight +where + B: NumericValue + JsonSchema, +{ + pub bps: ComputedFromHeight, + pub ratio: LazyFromHeight, + pub percent: LazyFromHeight, +} + +impl PercentFromHeight +where + B: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result + where + RatioTransform: UnaryTransform, + PercentTransform: UnaryTransform, + { + let bps = ComputedFromHeight::forced_import(db, name, version, indexes)?; + + let ratio = LazyFromHeight::from_computed::( + &format!("{name}_ratio"), + version, + bps.height.read_only_boxed_clone(), + &bps, + ); + + let percent = LazyFromHeight::from_computed::( + &format!("{name}_percent"), + version, + bps.height.read_only_boxed_clone(), + &bps, + ); + + Ok(Self { bps, ratio, percent }) + } + + pub(crate) fn compute_binary( + &mut self, + max_from: Height, + source1: &impl ReadableVec, + source2: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + S1T: VecValue, + S2T: VecValue, + F: BinaryTransform, + { + self.bps.compute_binary::(max_from, source1, source2, exit) + } + + pub(crate) fn compute_drawdown( + &mut self, + max_from: Height, + current: &impl ReadableVec, + ath: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + C: VecValue, + A: VecValue, + f64: From + From, + vecdb::EagerVec>: ComputeDrawdown, + { + self.bps.height.compute_drawdown(max_from, current, ath, exit) + } +} diff --git a/crates/brk_computer/src/internal/from_height/ratio/extension.rs b/crates/brk_computer/src/internal/from_height/ratio/extension.rs index 094d3127a..7ccbdd86f 100644 --- a/crates/brk_computer/src/internal/from_height/ratio/extension.rs +++ b/crates/brk_computer/src/internal/from_height/ratio/extension.rs @@ -12,8 +12,8 @@ use super::super::ComputedFromHeight; #[derive(Traversable)] pub struct ComputedFromHeightRatioExtension { - pub ratio_1w_sma: ComputedFromHeight, - pub ratio_1m_sma: ComputedFromHeight, + pub ratio_sma_1w: ComputedFromHeight, + pub ratio_sma_1m: ComputedFromHeight, pub ratio_pct99: ComputedFromHeight, pub ratio_pct98: ComputedFromHeight, pub ratio_pct95: ComputedFromHeight, @@ -28,9 +28,9 @@ pub struct ComputedFromHeightRatioExtension { pub ratio_pct1_price: Price>, pub ratio_sd: ComputedFromHeightStdDevExtended, - pub ratio_4y_sd: ComputedFromHeightStdDevExtended, - pub ratio_2y_sd: ComputedFromHeightStdDevExtended, - pub ratio_1y_sd: ComputedFromHeightStdDevExtended, + pub ratio_sd_4y: ComputedFromHeightStdDevExtended, + pub ratio_sd_2y: ComputedFromHeightStdDevExtended, + pub ratio_sd_1y: ComputedFromHeightStdDevExtended, #[traversable(skip)] tdigest: TDigest, @@ -59,10 +59,11 @@ impl ComputedFromHeightRatioExtension { } macro_rules! import_sd { - ($suffix:expr, $days:expr) => { + ($suffix:expr, $period:expr, $days:expr) => { ComputedFromHeightStdDevExtended::forced_import( db, &format!("{name}_{}", $suffix), + $period, $days, v, indexes, @@ -77,12 +78,12 @@ impl ComputedFromHeightRatioExtension { } Ok(Self { - ratio_1w_sma: import!("ratio_1w_sma"), - ratio_1m_sma: import!("ratio_1m_sma"), - ratio_sd: import_sd!("ratio", usize::MAX), - ratio_1y_sd: import_sd!("ratio_1y", 365), - ratio_2y_sd: import_sd!("ratio_2y", 2 * 365), - ratio_4y_sd: import_sd!("ratio_4y", 4 * 365), + ratio_sma_1w: import!("ratio_sma_1w"), + ratio_sma_1m: import!("ratio_sma_1m"), + ratio_sd: import_sd!("ratio", "", usize::MAX), + ratio_sd_1y: import_sd!("ratio", "1y", 365), + ratio_sd_2y: import_sd!("ratio", "2y", 2 * 365), + ratio_sd_4y: import_sd!("ratio", "4y", 4 * 365), ratio_pct99: import!("ratio_pct99"), ratio_pct98: import!("ratio_pct98"), ratio_pct95: import!("ratio_pct95"), @@ -108,14 +109,14 @@ impl ComputedFromHeightRatioExtension { ratio_source: &impl ReadableVec, ) -> Result<()> { // SMA using lookback vecs - self.ratio_1w_sma.height.compute_rolling_average( + self.ratio_sma_1w.height.compute_rolling_average( starting_indexes.height, &blocks.count.height_1w_ago, ratio_source, exit, )?; - self.ratio_1m_sma.height.compute_rolling_average( + self.ratio_sma_1m.height.compute_rolling_average( starting_indexes.height, &blocks.count.height_1m_ago, ratio_source, @@ -183,11 +184,11 @@ impl ComputedFromHeightRatioExtension { // Compute stddev at height level self.ratio_sd .compute_all(blocks, starting_indexes, exit, ratio_source)?; - self.ratio_4y_sd + self.ratio_sd_4y .compute_all(blocks, starting_indexes, exit, ratio_source)?; - self.ratio_2y_sd + self.ratio_sd_2y .compute_all(blocks, starting_indexes, exit, ratio_source)?; - self.ratio_1y_sd + self.ratio_sd_1y .compute_all(blocks, starting_indexes, exit, ratio_source)?; Ok(()) @@ -225,11 +226,11 @@ impl ComputedFromHeightRatioExtension { // Stddev cents bands self.ratio_sd .compute_cents_bands(starting_indexes, metric_price, exit)?; - self.ratio_4y_sd + self.ratio_sd_4y .compute_cents_bands(starting_indexes, metric_price, exit)?; - self.ratio_2y_sd + self.ratio_sd_2y .compute_cents_bands(starting_indexes, metric_price, exit)?; - self.ratio_1y_sd + self.ratio_sd_1y .compute_cents_bands(starting_indexes, metric_price, exit)?; Ok(()) diff --git a/crates/brk_computer/src/internal/from_height/stddev/extended.rs b/crates/brk_computer/src/internal/from_height/stddev/extended.rs index 789ff85ad..2444e012d 100644 --- a/crates/brk_computer/src/internal/from_height/stddev/extended.rs +++ b/crates/brk_computer/src/internal/from_height/stddev/extended.rs @@ -51,17 +51,19 @@ impl ComputedFromHeightStdDevExtended { pub(crate) fn forced_import( db: &Database, name: &str, + period: &str, days: usize, parent_version: Version, indexes: &indexes::Vecs, ) -> Result { let version = parent_version + Version::TWO; + let p = super::period_suffix(period); macro_rules! import { ($suffix:expr) => { ComputedFromHeight::forced_import( db, - &format!("{name}_{}", $suffix), + &format!("{name}_{}{p}", $suffix), version, indexes, )? @@ -70,12 +72,12 @@ impl ComputedFromHeightStdDevExtended { macro_rules! import_price { ($suffix:expr) => { - Price::forced_import(db, &format!("{name}_{}", $suffix), version, indexes)? + Price::forced_import(db, &format!("{name}_{}{p}", $suffix), version, indexes)? }; } Ok(Self { - base: ComputedFromHeightStdDev::forced_import(db, name, days, parent_version, indexes)?, + base: ComputedFromHeightStdDev::forced_import(db, name, period, days, parent_version, indexes)?, zscore: import!("zscore"), p0_5sd: import!("p0_5sd"), p1sd: import!("p1sd"), diff --git a/crates/brk_computer/src/internal/from_height/stddev/mod.rs b/crates/brk_computer/src/internal/from_height/stddev/mod.rs index 96a1483ee..bc1d2d178 100644 --- a/crates/brk_computer/src/internal/from_height/stddev/mod.rs +++ b/crates/brk_computer/src/internal/from_height/stddev/mod.rs @@ -11,6 +11,14 @@ use crate::{ComputeIndexes, blocks, indexes}; use crate::internal::ComputedFromHeight; +fn period_suffix(period: &str) -> String { + if period.is_empty() { + String::new() + } else { + format!("_{period}") + } +} + #[derive(Traversable)] pub struct ComputedFromHeightStdDev { days: usize, @@ -22,21 +30,23 @@ impl ComputedFromHeightStdDev { pub(crate) fn forced_import( db: &Database, name: &str, + period: &str, days: usize, parent_version: Version, indexes: &indexes::Vecs, ) -> Result { let version = parent_version + Version::TWO; + let p = period_suffix(period); let sma = ComputedFromHeight::forced_import( db, - &format!("{name}_sma"), + &format!("{name}_sma{p}"), version, indexes, )?; let sd = ComputedFromHeight::forced_import( db, - &format!("{name}_sd"), + &format!("{name}_sd{p}"), version, indexes, )?; diff --git a/crates/brk_computer/src/internal/rolling/distribution.rs b/crates/brk_computer/src/internal/rolling/distribution.rs index 2537bcc37..c7713dedd 100644 --- a/crates/brk_computer/src/internal/rolling/distribution.rs +++ b/crates/brk_computer/src/internal/rolling/distribution.rs @@ -74,18 +74,18 @@ where &mut self.0.pct75._24h.height, &mut self.0.pct90._24h.height, exit, )?; compute_rolling_distribution_from_starts( - max_from, windows._7d, source, - &mut self.0.average._7d.height, &mut self.0.min._7d.height, - &mut self.0.max._7d.height, &mut self.0.pct10._7d.height, - &mut self.0.pct25._7d.height, &mut self.0.median._7d.height, - &mut self.0.pct75._7d.height, &mut self.0.pct90._7d.height, exit, + max_from, windows._1w, source, + &mut self.0.average._1w.height, &mut self.0.min._1w.height, + &mut self.0.max._1w.height, &mut self.0.pct10._1w.height, + &mut self.0.pct25._1w.height, &mut self.0.median._1w.height, + &mut self.0.pct75._1w.height, &mut self.0.pct90._1w.height, exit, )?; compute_rolling_distribution_from_starts( - max_from, windows._30d, source, - &mut self.0.average._30d.height, &mut self.0.min._30d.height, - &mut self.0.max._30d.height, &mut self.0.pct10._30d.height, - &mut self.0.pct25._30d.height, &mut self.0.median._30d.height, - &mut self.0.pct75._30d.height, &mut self.0.pct90._30d.height, exit, + max_from, windows._1m, source, + &mut self.0.average._1m.height, &mut self.0.min._1m.height, + &mut self.0.max._1m.height, &mut self.0.pct10._1m.height, + &mut self.0.pct25._1m.height, &mut self.0.median._1m.height, + &mut self.0.pct75._1m.height, &mut self.0.pct90._1m.height, exit, )?; compute_rolling_distribution_from_starts( max_from, windows._1y, source, diff --git a/crates/brk_computer/src/internal/rolling/emas/mod.rs b/crates/brk_computer/src/internal/rolling/emas/mod.rs index 99a5634e2..a01b8d4d8 100644 --- a/crates/brk_computer/src/internal/rolling/emas/mod.rs +++ b/crates/brk_computer/src/internal/rolling/emas/mod.rs @@ -1,5 +1,7 @@ -mod _14d; -mod _7d_30d; +mod _1w_1m; +mod _2w; +mod percent_1w_1m; -pub use _14d::*; -pub use _7d_30d::*; +pub use _1w_1m::*; +pub use _2w::*; +pub use percent_1w_1m::*; diff --git a/crates/brk_computer/src/internal/rolling/emas/percent_1w_1m.rs b/crates/brk_computer/src/internal/rolling/emas/percent_1w_1m.rs new file mode 100644 index 000000000..a5687b2c2 --- /dev/null +++ b/crates/brk_computer/src/internal/rolling/emas/percent_1w_1m.rs @@ -0,0 +1,70 @@ +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{Height, StoredF32, Version}; +use derive_more::{Deref, DerefMut}; +use schemars::JsonSchema; +use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode, UnaryTransform}; + +use crate::{ + indexes, + internal::{Emas1w1m, NumericValue, PercentFromHeight}, +}; + +const VERSION: Version = Version::ZERO; + +/// 2 EMA vecs (1w, 1m) sourced from 24h rolling window, +/// each storing basis points with lazy ratio and percent float views. +#[derive(Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct PercentRollingEmas1w1m(pub Emas1w1m>) +where + B: NumericValue + JsonSchema; + +impl PercentRollingEmas1w1m +where + B: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result + where + RatioTransform: UnaryTransform, + PercentTransform: UnaryTransform, + { + let v = version + VERSION; + Ok(Self(Emas1w1m::try_from_fn(|suffix| { + PercentFromHeight::forced_import::( + db, + &format!("{name}_{suffix}"), + v, + indexes, + ) + })?)) + } + + pub(crate) fn compute_from_24h( + &mut self, + max_from: Height, + height_1w_ago: &impl ReadableVec, + height_1m_ago: &impl ReadableVec, + source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + f64: From, + B: From + Default, + { + self._1w + .bps + .height + .compute_rolling_ema(max_from, height_1w_ago, source, exit)?; + self._1m + .bps + .height + .compute_rolling_ema(max_from, height_1m_ago, source, exit)?; + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/rolling/mod.rs b/crates/brk_computer/src/internal/rolling/mod.rs index f90f60fc3..3050a0a0e 100644 --- a/crates/brk_computer/src/internal/rolling/mod.rs +++ b/crates/brk_computer/src/internal/rolling/mod.rs @@ -1,11 +1,13 @@ mod distribution; mod emas; mod full; +mod percent_windows; mod value_windows; mod windows; pub use distribution::*; pub use emas::*; pub use full::*; +pub use percent_windows::*; pub use value_windows::*; pub use windows::*; diff --git a/crates/brk_computer/src/internal/rolling/percent_windows.rs b/crates/brk_computer/src/internal/rolling/percent_windows.rs new file mode 100644 index 000000000..3950f1b44 --- /dev/null +++ b/crates/brk_computer/src/internal/rolling/percent_windows.rs @@ -0,0 +1,47 @@ +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{StoredF32, Version}; +use derive_more::{Deref, DerefMut}; +use schemars::JsonSchema; +use vecdb::{Database, Rw, StorageMode, UnaryTransform}; + +use crate::{ + indexes, + internal::{NumericValue, PercentFromHeight, Windows}, +}; + +const VERSION: Version = Version::ZERO; + +/// 4 rolling window vecs (24h, 1w, 1m, 1y), each storing basis points +/// with lazy ratio and percent float views. +#[derive(Deref, DerefMut, Traversable)] +#[traversable(transparent)] +pub struct PercentRollingWindows(pub Windows>) +where + B: NumericValue + JsonSchema; + +impl PercentRollingWindows +where + B: NumericValue + JsonSchema, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result + where + RatioTransform: UnaryTransform, + PercentTransform: UnaryTransform, + { + let v = version + VERSION; + Ok(Self(Windows::try_from_fn(|suffix| { + PercentFromHeight::forced_import::( + db, + &format!("{name}_{suffix}"), + v, + indexes, + ) + })?)) + } +} diff --git a/crates/brk_computer/src/internal/rolling/windows.rs b/crates/brk_computer/src/internal/rolling/windows.rs index a16fd76c1..110732399 100644 --- a/crates/brk_computer/src/internal/rolling/windows.rs +++ b/crates/brk_computer/src/internal/rolling/windows.rs @@ -1,6 +1,6 @@ //! RollingWindows - newtype on Windows with ComputedFromHeight per window duration. //! -//! Each of the 4 windows (24h, 7d, 30d, 1y) contains a height-level stored vec +//! Each of the 4 windows (24h, 1w, 1m, 1y) contains a height-level stored vec //! plus all 17 LazyAggVec index views. use std::ops::SubAssign; @@ -18,10 +18,10 @@ use crate::{ internal::{ComputedFromHeight, ComputedVecValue, NumericValue, Windows}, }; -/// Rolling window start heights — the 4 height-ago vecs (24h, 7d, 30d, 1y). +/// Rolling window start heights — the 4 height-ago vecs (24h, 1w, 1m, 1y). pub type WindowStarts<'a> = Windows<&'a EagerVec>>; -/// 4 rolling window vecs (24h, 7d, 30d, 1y), each with height data + all 17 index views. +/// 4 rolling window vecs (24h, 1w, 1m, 1y), each with height data + all 17 index views. #[derive(Deref, DerefMut, Traversable)] #[traversable(transparent)] pub struct RollingWindows(pub Windows>) diff --git a/crates/brk_computer/src/internal/transform/bp16_to_percent.rs b/crates/brk_computer/src/internal/transform/bp16_to_percent.rs new file mode 100644 index 000000000..4fc3644af --- /dev/null +++ b/crates/brk_computer/src/internal/transform/bp16_to_percent.rs @@ -0,0 +1,11 @@ +use brk_types::{BasisPoints16, StoredF32}; +use vecdb::UnaryTransform; + +pub struct Bp16ToPercent; + +impl UnaryTransform for Bp16ToPercent { + #[inline(always)] + fn apply(bp: BasisPoints16) -> StoredF32 { + StoredF32::from(bp.inner() as f32 / 100.0) + } +} diff --git a/crates/brk_computer/src/internal/transform/bp32_to_percent.rs b/crates/brk_computer/src/internal/transform/bp32_to_percent.rs new file mode 100644 index 000000000..d8d02e06f --- /dev/null +++ b/crates/brk_computer/src/internal/transform/bp32_to_percent.rs @@ -0,0 +1,11 @@ +use brk_types::{BasisPoints32, StoredF32}; +use vecdb::UnaryTransform; + +pub struct Bp32ToPercent; + +impl UnaryTransform for Bp32ToPercent { + #[inline(always)] + fn apply(bp: BasisPoints32) -> StoredF32 { + StoredF32::from(bp.inner() as f32 / 100.0) + } +} diff --git a/crates/brk_computer/src/internal/transform/bps16_to_percent.rs b/crates/brk_computer/src/internal/transform/bps16_to_percent.rs new file mode 100644 index 000000000..800e7ab23 --- /dev/null +++ b/crates/brk_computer/src/internal/transform/bps16_to_percent.rs @@ -0,0 +1,11 @@ +use brk_types::{BasisPointsSigned16, StoredF32}; +use vecdb::UnaryTransform; + +pub struct Bps16ToPercent; + +impl UnaryTransform for Bps16ToPercent { + #[inline(always)] + fn apply(bp: BasisPointsSigned16) -> StoredF32 { + StoredF32::from(bp.inner() as f32 / 100.0) + } +} diff --git a/crates/brk_computer/src/internal/transform/bps32_to_percent.rs b/crates/brk_computer/src/internal/transform/bps32_to_percent.rs new file mode 100644 index 000000000..63ed256c9 --- /dev/null +++ b/crates/brk_computer/src/internal/transform/bps32_to_percent.rs @@ -0,0 +1,11 @@ +use brk_types::{BasisPointsSigned32, StoredF32}; +use vecdb::UnaryTransform; + +pub struct Bps32ToPercent; + +impl UnaryTransform for Bps32ToPercent { + #[inline(always)] + fn apply(bp: BasisPointsSigned32) -> StoredF32 { + StoredF32::from(bp.inner() as f32 / 100.0) + } +} diff --git a/crates/brk_computer/src/internal/transform/mod.rs b/crates/brk_computer/src/internal/transform/mod.rs index b52c6e49f..c8bddf1b9 100644 --- a/crates/brk_computer/src/internal/transform/mod.rs +++ b/crates/brk_computer/src/internal/transform/mod.rs @@ -1,7 +1,11 @@ mod bp16_to_float; +mod bp16_to_percent; mod bp32_to_float; +mod bp32_to_percent; mod bps16_to_float; +mod bps16_to_percent; mod bps32_to_float; +mod bps32_to_percent; mod block_count_target; mod cents_halve; mod identity; @@ -16,19 +20,15 @@ mod dollars_to_sats_fract; mod neg_cents_to_dollars; mod ohlc_cents_to_dollars; mod ohlc_cents_to_sats; -mod percentage_cents_f32; -mod percentage_cents_signed_dollars_f32; -mod percentage_cents_signed_f32; mod percentage_diff_close_cents; mod percentage_diff_close_dollars; -mod percentage_dollars_f32; -mod percentage_dollars_f32_neg; -mod percentage_sats_f64; -mod percentage_u32_f32; mod price_times_ratio_cents; mod ratio32; mod ratio_cents64; mod per_sec; +mod ratio_bp16; +mod ratio_bps16; +mod ratio_bps32; mod ratio_u64_f32; mod return_f32_tenths; mod return_i8; @@ -43,9 +43,13 @@ mod days_to_years; mod volatility; pub use bp16_to_float::*; +pub use bp16_to_percent::*; pub use bp32_to_float::*; +pub use bp32_to_percent::*; pub use bps16_to_float::*; +pub use bps16_to_percent::*; pub use bps32_to_float::*; +pub use bps32_to_percent::*; pub use block_count_target::*; pub use cents_halve::*; pub use identity::*; @@ -58,22 +62,17 @@ pub use cents_to_sats::*; pub use neg_cents_to_dollars::*; pub use ohlc_cents_to_dollars::*; pub use ohlc_cents_to_sats::*; -pub use percentage_cents_f32::*; -pub use percentage_cents_signed_dollars_f32::*; -pub use percentage_cents_signed_f32::*; - pub use dollar_halve::*; pub use dollars_to_sats_fract::*; pub use percentage_diff_close_cents::*; pub use percentage_diff_close_dollars::*; -pub use percentage_dollars_f32::*; -pub use percentage_dollars_f32_neg::*; -pub use percentage_sats_f64::*; -pub use percentage_u32_f32::*; pub use price_times_ratio_cents::*; pub use ratio32::*; pub use ratio_cents64::*; pub use per_sec::*; +pub use ratio_bp16::*; +pub use ratio_bps16::*; +pub use ratio_bps32::*; pub use ratio_u64_f32::*; pub use return_f32_tenths::*; pub use return_i8::*; diff --git a/crates/brk_computer/src/internal/transform/percentage_cents_f32.rs b/crates/brk_computer/src/internal/transform/percentage_cents_f32.rs deleted file mode 100644 index 35cafdcc4..000000000 --- a/crates/brk_computer/src/internal/transform/percentage_cents_f32.rs +++ /dev/null @@ -1,16 +0,0 @@ -use brk_types::{Cents, StoredF32}; -use vecdb::BinaryTransform; - -/// (Cents, Cents) -> StoredF32 percentage (a/b × 100) -pub struct PercentageCentsF32; - -impl BinaryTransform for PercentageCentsF32 { - #[inline(always)] - fn apply(numerator: Cents, denominator: Cents) -> StoredF32 { - if denominator == Cents::ZERO { - StoredF32::default() - } else { - StoredF32::from(numerator.inner() as f64 / denominator.inner() as f64 * 100.0) - } - } -} diff --git a/crates/brk_computer/src/internal/transform/percentage_cents_signed_dollars_f32.rs b/crates/brk_computer/src/internal/transform/percentage_cents_signed_dollars_f32.rs deleted file mode 100644 index d7c90c2df..000000000 --- a/crates/brk_computer/src/internal/transform/percentage_cents_signed_dollars_f32.rs +++ /dev/null @@ -1,17 +0,0 @@ -use brk_types::{CentsSigned, Dollars, StoredF32}; -use vecdb::BinaryTransform; - -/// (CentsSigned, Dollars) -> StoredF32 percentage (a/b × 100) -/// For cross-type percentage when numerator is CentsSigned and denominator is Dollars. -pub struct PercentageCentsSignedDollarsF32; - -impl BinaryTransform for PercentageCentsSignedDollarsF32 { - #[inline(always)] - fn apply(numerator: CentsSigned, denominator: Dollars) -> StoredF32 { - if denominator == Dollars::ZERO { - StoredF32::default() - } else { - StoredF32::from(numerator.inner() as f64 / *denominator * 100.0) - } - } -} diff --git a/crates/brk_computer/src/internal/transform/percentage_cents_signed_f32.rs b/crates/brk_computer/src/internal/transform/percentage_cents_signed_f32.rs deleted file mode 100644 index 8de911909..000000000 --- a/crates/brk_computer/src/internal/transform/percentage_cents_signed_f32.rs +++ /dev/null @@ -1,17 +0,0 @@ -use brk_types::{Cents, CentsSigned, StoredF32}; -use vecdb::BinaryTransform; - -/// (CentsSigned, Cents) -> StoredF32 percentage (a/b × 100) -/// For cross-type percentage when numerator is signed. -pub struct PercentageCentsSignedCentsF32; - -impl BinaryTransform for PercentageCentsSignedCentsF32 { - #[inline(always)] - fn apply(numerator: CentsSigned, denominator: Cents) -> StoredF32 { - if denominator == Cents::ZERO { - StoredF32::default() - } else { - StoredF32::from(numerator.inner() as f64 / denominator.inner() as f64 * 100.0) - } - } -} diff --git a/crates/brk_computer/src/internal/transform/percentage_dollars_f32.rs b/crates/brk_computer/src/internal/transform/percentage_dollars_f32.rs deleted file mode 100644 index 9f61b7175..000000000 --- a/crates/brk_computer/src/internal/transform/percentage_dollars_f32.rs +++ /dev/null @@ -1,14 +0,0 @@ -use brk_types::{Dollars, StoredF32}; -use vecdb::BinaryTransform; - -/// (Dollars, Dollars) -> StoredF32 percentage (a/b × 100) -/// Used for unrealized/realized ratio calculations -pub struct PercentageDollarsF32; - -impl BinaryTransform for PercentageDollarsF32 { - #[inline(always)] - fn apply(numerator: Dollars, denominator: Dollars) -> StoredF32 { - // Dollars / Dollars returns StoredF64, so dereference and multiply - StoredF32::from(*(numerator / denominator) * 100.0) - } -} diff --git a/crates/brk_computer/src/internal/transform/percentage_dollars_f32_neg.rs b/crates/brk_computer/src/internal/transform/percentage_dollars_f32_neg.rs deleted file mode 100644 index 26e8654cf..000000000 --- a/crates/brk_computer/src/internal/transform/percentage_dollars_f32_neg.rs +++ /dev/null @@ -1,14 +0,0 @@ -use brk_types::{Dollars, StoredF32}; -use vecdb::BinaryTransform; - -/// (Dollars, Dollars) -> StoredF32 negated percentage (-(a/b × 100)) -/// Used for negated loss ratio calculations, avoiding lazy-from-lazy chains. -pub struct NegPercentageDollarsF32; - -impl BinaryTransform for NegPercentageDollarsF32 { - #[inline(always)] - fn apply(numerator: Dollars, denominator: Dollars) -> StoredF32 { - // Dollars / Dollars returns StoredF64, so dereference and multiply - StoredF32::from(-(*(numerator / denominator) * 100.0)) - } -} diff --git a/crates/brk_computer/src/internal/transform/percentage_sats_f64.rs b/crates/brk_computer/src/internal/transform/percentage_sats_f64.rs deleted file mode 100644 index f02b7ceb0..000000000 --- a/crates/brk_computer/src/internal/transform/percentage_sats_f64.rs +++ /dev/null @@ -1,13 +0,0 @@ -use brk_types::{Sats, StoredF64}; -use vecdb::BinaryTransform; - -/// (Sats, Sats) -> StoredF64 percentage (a/b × 100) -/// Used for supply ratio calculations (equivalent to Bitcoin/Bitcoin since 1e8 cancels) -pub struct PercentageSatsF64; - -impl BinaryTransform for PercentageSatsF64 { - #[inline(always)] - fn apply(numerator: Sats, denominator: Sats) -> StoredF64 { - StoredF64::from((*numerator as f64 / *denominator as f64) * 100.0) - } -} diff --git a/crates/brk_computer/src/internal/transform/percentage_u32_f32.rs b/crates/brk_computer/src/internal/transform/percentage_u32_f32.rs deleted file mode 100644 index eb26fbd8c..000000000 --- a/crates/brk_computer/src/internal/transform/percentage_u32_f32.rs +++ /dev/null @@ -1,13 +0,0 @@ -use brk_types::{StoredF32, StoredU32}; -use vecdb::BinaryTransform; - -/// (StoredU32, StoredU32) -> StoredF32 percentage (a/b × 100) -/// Used for pool dominance calculations (pool_blocks / total_blocks × 100) -pub struct PercentageU32F32; - -impl BinaryTransform for PercentageU32F32 { - #[inline(always)] - fn apply(numerator: StoredU32, denominator: StoredU32) -> StoredF32 { - StoredF32::from((*numerator as f64 / *denominator as f64) * 100.0) - } -} diff --git a/crates/brk_computer/src/internal/transform/ratio_bp16.rs b/crates/brk_computer/src/internal/transform/ratio_bp16.rs new file mode 100644 index 000000000..8bd87162f --- /dev/null +++ b/crates/brk_computer/src/internal/transform/ratio_bp16.rs @@ -0,0 +1,73 @@ +use brk_types::{BasisPoints16, Cents, Dollars, Sats, StoredU32, StoredU64}; +use vecdb::BinaryTransform; + +/// (StoredU64, StoredU64) -> BasisPoints16 ratio (a/b × 10000) +pub struct RatioU64Bp16; + +impl BinaryTransform for RatioU64Bp16 { + #[inline(always)] + fn apply(numerator: StoredU64, denominator: StoredU64) -> BasisPoints16 { + if *denominator > 0 { + BasisPoints16::from(*numerator as f64 / *denominator as f64) + } else { + BasisPoints16::ZERO + } + } +} + +/// (Sats, Sats) -> BasisPoints16 ratio (a/b × 10000) +pub struct RatioSatsBp16; + +impl BinaryTransform for RatioSatsBp16 { + #[inline(always)] + fn apply(numerator: Sats, denominator: Sats) -> BasisPoints16 { + if *denominator > 0 { + BasisPoints16::from(*numerator as f64 / *denominator as f64) + } else { + BasisPoints16::ZERO + } + } +} + +/// (Cents, Cents) -> BasisPoints16 ratio (a/b × 10000) +pub struct RatioCentsBp16; + +impl BinaryTransform for RatioCentsBp16 { + #[inline(always)] + fn apply(numerator: Cents, denominator: Cents) -> BasisPoints16 { + if denominator == Cents::ZERO { + BasisPoints16::ZERO + } else { + BasisPoints16::from(numerator.inner() as f64 / denominator.inner() as f64) + } + } +} + +/// (StoredU32, StoredU32) -> BasisPoints16 ratio (a/b × 10000) +pub struct RatioU32Bp16; + +impl BinaryTransform for RatioU32Bp16 { + #[inline(always)] + fn apply(numerator: StoredU32, denominator: StoredU32) -> BasisPoints16 { + if *denominator > 0 { + BasisPoints16::from(*numerator as f64 / *denominator as f64) + } else { + BasisPoints16::ZERO + } + } +} + +/// (Dollars, Dollars) -> BasisPoints16 ratio (a/b × 10000) +pub struct RatioDollarsBp16; + +impl BinaryTransform for RatioDollarsBp16 { + #[inline(always)] + fn apply(numerator: Dollars, denominator: Dollars) -> BasisPoints16 { + let ratio = *(numerator / denominator); + if ratio.is_finite() { + BasisPoints16::from(ratio) + } else { + BasisPoints16::ZERO + } + } +} diff --git a/crates/brk_computer/src/internal/transform/ratio_bps16.rs b/crates/brk_computer/src/internal/transform/ratio_bps16.rs new file mode 100644 index 000000000..cb1715c5e --- /dev/null +++ b/crates/brk_computer/src/internal/transform/ratio_bps16.rs @@ -0,0 +1,62 @@ +use brk_types::{BasisPointsSigned16, Cents, CentsSigned, Dollars}; +use vecdb::BinaryTransform; + +/// (Dollars, Dollars) -> BasisPointsSigned16 ratio (a/b × 10000) +pub struct RatioDollarsBps16; + +impl BinaryTransform for RatioDollarsBps16 { + #[inline(always)] + fn apply(numerator: Dollars, denominator: Dollars) -> BasisPointsSigned16 { + let ratio = *(numerator / denominator); + if ratio.is_finite() { + BasisPointsSigned16::from(ratio) + } else { + BasisPointsSigned16::ZERO + } + } +} + +/// (Dollars, Dollars) -> BasisPointsSigned16 negated ratio (-(a/b) × 10000) +pub struct NegRatioDollarsBps16; + +impl BinaryTransform for NegRatioDollarsBps16 { + #[inline(always)] + fn apply(numerator: Dollars, denominator: Dollars) -> BasisPointsSigned16 { + let ratio = *(numerator / denominator); + if ratio.is_finite() { + BasisPointsSigned16::from(-ratio) + } else { + BasisPointsSigned16::ZERO + } + } +} + +/// (CentsSigned, Cents) -> BasisPointsSigned16 ratio (a/b × 10000) +pub struct RatioCentsSignedCentsBps16; + +impl BinaryTransform for RatioCentsSignedCentsBps16 { + #[inline(always)] + fn apply(numerator: CentsSigned, denominator: Cents) -> BasisPointsSigned16 { + if denominator == Cents::ZERO { + BasisPointsSigned16::ZERO + } else { + BasisPointsSigned16::from(numerator.inner() as f64 / denominator.inner() as f64) + } + } +} + +/// (CentsSigned, Dollars) -> BasisPointsSigned16 ratio (a/b × 10000) +pub struct RatioCentsSignedDollarsBps16; + +impl BinaryTransform for RatioCentsSignedDollarsBps16 { + #[inline(always)] + fn apply(numerator: CentsSigned, denominator: Dollars) -> BasisPointsSigned16 { + let d: f64 = denominator.into(); + if d > 0.0 { + // Convert cents to dollars first, then compute ratio + BasisPointsSigned16::from(numerator.inner() as f64 / 100.0 / d) + } else { + BasisPointsSigned16::ZERO + } + } +} diff --git a/crates/brk_computer/src/internal/transform/ratio_bps32.rs b/crates/brk_computer/src/internal/transform/ratio_bps32.rs new file mode 100644 index 000000000..660fafab8 --- /dev/null +++ b/crates/brk_computer/src/internal/transform/ratio_bps32.rs @@ -0,0 +1,32 @@ +use brk_types::{BasisPointsSigned32, Cents, Dollars}; +use vecdb::BinaryTransform; + +/// (Dollars, Dollars) -> BasisPointsSigned32 ratio diff ((a/b - 1) × 10000) +pub struct RatioDiffDollarsBps32; + +impl BinaryTransform for RatioDiffDollarsBps32 { + #[inline(always)] + fn apply(close: Dollars, base: Dollars) -> BasisPointsSigned32 { + let base_f64: f64 = base.into(); + if base_f64 == 0.0 { + BasisPointsSigned32::ZERO + } else { + BasisPointsSigned32::from(f64::from(close) / base_f64 - 1.0) + } + } +} + +/// (Cents, Cents) -> BasisPointsSigned32 ratio diff ((a/b - 1) × 10000) +pub struct RatioDiffCentsBps32; + +impl BinaryTransform for RatioDiffCentsBps32 { + #[inline(always)] + fn apply(close: Cents, base: Cents) -> BasisPointsSigned32 { + let base_f64 = f64::from(base); + if base_f64 == 0.0 { + BasisPointsSigned32::ZERO + } else { + BasisPointsSigned32::from(f64::from(close) / base_f64 - 1.0) + } + } +} diff --git a/crates/brk_computer/src/internal/windows.rs b/crates/brk_computer/src/internal/windows.rs index 7d7ebd546..875adf04e 100644 --- a/crates/brk_computer/src/internal/windows.rs +++ b/crates/brk_computer/src/internal/windows.rs @@ -1,6 +1,6 @@ //! Base generic struct with 4 type parameters — one per rolling window duration. //! -//! Foundation for all rolling window types (24h, 7d, 30d, 1y). +//! Foundation for all rolling window types (24h, 1w, 1m, 1y). use brk_traversable::Traversable; @@ -8,33 +8,33 @@ use brk_traversable::Traversable; pub struct Windows { #[traversable(rename = "24h")] pub _24h: A, - #[traversable(rename = "7d")] - pub _7d: B, - #[traversable(rename = "30d")] - pub _30d: C, + #[traversable(rename = "1w")] + pub _1w: B, + #[traversable(rename = "1m")] + pub _1m: C, #[traversable(rename = "1y")] pub _1y: D, } impl Windows { - pub const SUFFIXES: [&'static str; 4] = ["24h", "7d", "30d", "1y"]; + pub const SUFFIXES: [&'static str; 4] = ["24h", "1w", "1m", "1y"]; pub fn try_from_fn( mut f: impl FnMut(&str) -> std::result::Result, ) -> std::result::Result { Ok(Self { _24h: f(Self::SUFFIXES[0])?, - _7d: f(Self::SUFFIXES[1])?, - _30d: f(Self::SUFFIXES[2])?, + _1w: f(Self::SUFFIXES[1])?, + _1m: f(Self::SUFFIXES[2])?, _1y: f(Self::SUFFIXES[3])?, }) } pub fn as_array(&self) -> [&A; 4] { - [&self._24h, &self._7d, &self._30d, &self._1y] + [&self._24h, &self._1w, &self._1m, &self._1y] } pub fn as_mut_array(&mut self) -> [&mut A; 4] { - [&mut self._24h, &mut self._7d, &mut self._30d, &mut self._1y] + [&mut self._24h, &mut self._1w, &mut self._1m, &mut self._1y] } } diff --git a/crates/brk_computer/src/market/ath/compute.rs b/crates/brk_computer/src/market/ath/compute.rs index e228e548f..50863f770 100644 --- a/crates/brk_computer/src/market/ath/compute.rs +++ b/crates/brk_computer/src/market/ath/compute.rs @@ -47,7 +47,7 @@ impl Vecs { )?; let mut prev = None; - self.max_days_between_price_aths.height.compute_transform( + self.max_days_between_price_ath.height.compute_transform( starting_indexes.height, &self.days_since_price_ath.height, |(i, days, slf)| { @@ -66,7 +66,7 @@ impl Vecs { exit, )?; - self.price_drawdown.height.compute_drawdown( + self.price_drawdown.compute_drawdown( starting_indexes.height, &prices.price.cents.height, &self.price_ath.cents.height, diff --git a/crates/brk_computer/src/market/ath/import.rs b/crates/brk_computer/src/market/ath/import.rs index b24c0de86..25326d8d6 100644 --- a/crates/brk_computer/src/market/ath/import.rs +++ b/crates/brk_computer/src/market/ath/import.rs @@ -5,7 +5,7 @@ use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::{ComputedFromHeight, DaysToYears, LazyHeightDerived, Price}, + internal::{Bps16ToFloat, Bps16ToPercent, ComputedFromHeight, DaysToYears, LazyHeightDerived, PercentFromHeight, Price}, }; const VERSION: Version = Version::ONE; @@ -20,14 +20,14 @@ impl Vecs { let price_ath = Price::forced_import(db, "price_ath", v, indexes)?; - let max_days_between_price_aths = - ComputedFromHeight::forced_import(db, "max_days_between_price_aths", v, indexes)?; + let max_days_between_price_ath = + ComputedFromHeight::forced_import(db, "max_days_between_price_ath", v, indexes)?; - let max_years_between_price_aths = + let max_years_between_price_ath = LazyHeightDerived::from_computed::( - "max_years_between_price_aths", + "max_years_between_price_ath", v, - &max_days_between_price_aths, + &max_days_between_price_ath, ); let days_since_price_ath = @@ -40,15 +40,15 @@ impl Vecs { ); let price_drawdown = - ComputedFromHeight::forced_import(db, "price_drawdown", v, indexes)?; + PercentFromHeight::forced_import::(db, "price_drawdown", v, indexes)?; Ok(Self { price_ath, price_drawdown, days_since_price_ath, years_since_price_ath, - max_days_between_price_aths, - max_years_between_price_aths, + max_days_between_price_ath, + max_years_between_price_ath, }) } } diff --git a/crates/brk_computer/src/market/ath/vecs.rs b/crates/brk_computer/src/market/ath/vecs.rs index a54d10561..603ed27a7 100644 --- a/crates/brk_computer/src/market/ath/vecs.rs +++ b/crates/brk_computer/src/market/ath/vecs.rs @@ -1,15 +1,15 @@ use brk_traversable::Traversable; -use brk_types::{Cents, StoredF32}; +use brk_types::{BasisPointsSigned16, Cents, StoredF32}; use vecdb::{Rw, StorageMode}; -use crate::internal::{ComputedFromHeight, LazyHeightDerived, Price}; +use crate::internal::{ComputedFromHeight, LazyHeightDerived, PercentFromHeight, Price}; #[derive(Traversable)] pub struct Vecs { pub price_ath: Price>, - pub price_drawdown: ComputedFromHeight, + pub price_drawdown: PercentFromHeight, pub days_since_price_ath: ComputedFromHeight, pub years_since_price_ath: LazyHeightDerived, - pub max_days_between_price_aths: ComputedFromHeight, - pub max_years_between_price_aths: LazyHeightDerived, + pub max_days_between_price_ath: ComputedFromHeight, + pub max_years_between_price_ath: LazyHeightDerived, } diff --git a/crates/brk_computer/src/market/dca/by_class.rs b/crates/brk_computer/src/market/dca/by_class.rs index 537728d9d..c7a2a668b 100644 --- a/crates/brk_computer/src/market/dca/by_class.rs +++ b/crates/brk_computer/src/market/dca/by_class.rs @@ -3,51 +3,51 @@ use brk_types::{Date, Day1}; /// DCA class years pub const DCA_CLASS_YEARS: ByDcaClass = ByDcaClass { - _2015: 2015, - _2016: 2016, - _2017: 2017, - _2018: 2018, - _2019: 2019, - _2020: 2020, - _2021: 2021, - _2022: 2022, - _2023: 2023, - _2024: 2024, - _2025: 2025, - _2026: 2026, + from_2015: 2015, + from_2016: 2016, + from_2017: 2017, + from_2018: 2018, + from_2019: 2019, + from_2020: 2020, + from_2021: 2021, + from_2022: 2022, + from_2023: 2023, + from_2024: 2024, + from_2025: 2025, + from_2026: 2026, }; /// DCA class names pub const DCA_CLASS_NAMES: ByDcaClass<&'static str> = ByDcaClass { - _2015: "dca_class_2015", - _2016: "dca_class_2016", - _2017: "dca_class_2017", - _2018: "dca_class_2018", - _2019: "dca_class_2019", - _2020: "dca_class_2020", - _2021: "dca_class_2021", - _2022: "dca_class_2022", - _2023: "dca_class_2023", - _2024: "dca_class_2024", - _2025: "dca_class_2025", - _2026: "dca_class_2026", + from_2015: "from_2015", + from_2016: "from_2016", + from_2017: "from_2017", + from_2018: "from_2018", + from_2019: "from_2019", + from_2020: "from_2020", + from_2021: "from_2021", + from_2022: "from_2022", + from_2023: "from_2023", + from_2024: "from_2024", + from_2025: "from_2025", + from_2026: "from_2026", }; /// Generic wrapper for DCA year class data #[derive(Clone, Default, Traversable)] pub struct ByDcaClass { - pub _2015: T, - pub _2016: T, - pub _2017: T, - pub _2018: T, - pub _2019: T, - pub _2020: T, - pub _2021: T, - pub _2022: T, - pub _2023: T, - pub _2024: T, - pub _2025: T, - pub _2026: T, + pub from_2015: T, + pub from_2016: T, + pub from_2017: T, + pub from_2018: T, + pub from_2019: T, + pub from_2020: T, + pub from_2021: T, + pub from_2022: T, + pub from_2023: T, + pub from_2024: T, + pub from_2025: T, + pub from_2026: T, } impl ByDcaClass { @@ -58,18 +58,18 @@ impl ByDcaClass { let n = DCA_CLASS_NAMES; let y = DCA_CLASS_YEARS; Ok(Self { - _2015: create(n._2015, y._2015, Self::day1(y._2015))?, - _2016: create(n._2016, y._2016, Self::day1(y._2016))?, - _2017: create(n._2017, y._2017, Self::day1(y._2017))?, - _2018: create(n._2018, y._2018, Self::day1(y._2018))?, - _2019: create(n._2019, y._2019, Self::day1(y._2019))?, - _2020: create(n._2020, y._2020, Self::day1(y._2020))?, - _2021: create(n._2021, y._2021, Self::day1(y._2021))?, - _2022: create(n._2022, y._2022, Self::day1(y._2022))?, - _2023: create(n._2023, y._2023, Self::day1(y._2023))?, - _2024: create(n._2024, y._2024, Self::day1(y._2024))?, - _2025: create(n._2025, y._2025, Self::day1(y._2025))?, - _2026: create(n._2026, y._2026, Self::day1(y._2026))?, + from_2015: create(n.from_2015, y.from_2015, Self::day1(y.from_2015))?, + from_2016: create(n.from_2016, y.from_2016, Self::day1(y.from_2016))?, + from_2017: create(n.from_2017, y.from_2017, Self::day1(y.from_2017))?, + from_2018: create(n.from_2018, y.from_2018, Self::day1(y.from_2018))?, + from_2019: create(n.from_2019, y.from_2019, Self::day1(y.from_2019))?, + from_2020: create(n.from_2020, y.from_2020, Self::day1(y.from_2020))?, + from_2021: create(n.from_2021, y.from_2021, Self::day1(y.from_2021))?, + from_2022: create(n.from_2022, y.from_2022, Self::day1(y.from_2022))?, + from_2023: create(n.from_2023, y.from_2023, Self::day1(y.from_2023))?, + from_2024: create(n.from_2024, y.from_2024, Self::day1(y.from_2024))?, + from_2025: create(n.from_2025, y.from_2025, Self::day1(y.from_2025))?, + from_2026: create(n.from_2026, y.from_2026, Self::day1(y.from_2026))?, }) } @@ -79,36 +79,36 @@ impl ByDcaClass { pub(crate) fn iter(&self) -> impl Iterator { [ - &self._2015, - &self._2016, - &self._2017, - &self._2018, - &self._2019, - &self._2020, - &self._2021, - &self._2022, - &self._2023, - &self._2024, - &self._2025, - &self._2026, + &self.from_2015, + &self.from_2016, + &self.from_2017, + &self.from_2018, + &self.from_2019, + &self.from_2020, + &self.from_2021, + &self.from_2022, + &self.from_2023, + &self.from_2024, + &self.from_2025, + &self.from_2026, ] .into_iter() } pub(crate) fn iter_mut(&mut self) -> impl Iterator { [ - &mut self._2015, - &mut self._2016, - &mut self._2017, - &mut self._2018, - &mut self._2019, - &mut self._2020, - &mut self._2021, - &mut self._2022, - &mut self._2023, - &mut self._2024, - &mut self._2025, - &mut self._2026, + &mut self.from_2015, + &mut self.from_2016, + &mut self.from_2017, + &mut self.from_2018, + &mut self.from_2019, + &mut self.from_2020, + &mut self.from_2021, + &mut self.from_2022, + &mut self.from_2023, + &mut self.from_2024, + &mut self.from_2025, + &mut self.from_2026, ] .into_iter() } @@ -116,19 +116,18 @@ impl ByDcaClass { pub(crate) fn start_days() -> [Day1; 12] { let y = DCA_CLASS_YEARS; [ - Self::day1(y._2015), - Self::day1(y._2016), - Self::day1(y._2017), - Self::day1(y._2018), - Self::day1(y._2019), - Self::day1(y._2020), - Self::day1(y._2021), - Self::day1(y._2022), - Self::day1(y._2023), - Self::day1(y._2024), - Self::day1(y._2025), - Self::day1(y._2026), + Self::day1(y.from_2015), + Self::day1(y.from_2016), + Self::day1(y.from_2017), + Self::day1(y.from_2018), + Self::day1(y.from_2019), + Self::day1(y.from_2020), + Self::day1(y.from_2021), + Self::day1(y.from_2022), + Self::day1(y.from_2023), + Self::day1(y.from_2024), + Self::day1(y.from_2025), + Self::day1(y.from_2026), ] } - } diff --git a/crates/brk_computer/src/market/dca/compute.rs b/crates/brk_computer/src/market/dca/compute.rs index e34b65d36..134c4f847 100644 --- a/crates/brk_computer/src/market/dca/compute.rs +++ b/crates/brk_computer/src/market/dca/compute.rs @@ -1,10 +1,10 @@ use brk_error::Result; -use brk_types::{Bitcoin, Cents, Date, Day1, Dollars, Sats, StoredF32}; +use brk_types::{BasisPointsSigned32, Bitcoin, Cents, Date, Day1, Dollars, Sats}; use vecdb::{AnyVec, Exit, ReadableOptionVec, ReadableVec, VecIndex}; use super::Vecs; use crate::{ - ComputeIndexes, blocks, indexes, internal::PercentageDiffCents, market::lookback, prices, + ComputeIndexes, blocks, indexes, internal::RatioDiffCentsBps32, market::lookback, prices, }; const DCA_AMOUNT: Dollars = Dollars::mint(100.0); @@ -65,7 +65,7 @@ impl Vecs { // DCA by period - average price (derived from stack) let sh = starting_indexes.height.to_usize(); for (average_price, stack, days) in self - .period_average_price + .period_cost_basis .zip_mut_with_days(&self.period_stack) { let days = days as usize; @@ -93,11 +93,11 @@ impl Vecs { // DCA by period - returns (compute from average price) for (returns, (average_price, _)) in self - .period_returns + .period_return .iter_mut() - .zip(self.period_average_price.iter_with_days()) + .zip(self.period_cost_basis.iter_with_days()) { - returns.compute_binary::( + returns.compute_binary::( starting_indexes.height, &prices.price.cents.height, &average_price.cents.height, @@ -106,21 +106,22 @@ impl Vecs { } // DCA by period - CAGR (computed from returns at height level) - for (cagr, returns, days) in self.period_cagr.zip_mut_with_period(&self.period_returns) { - let years = days as f32 / 365.0; - cagr.height.compute_transform( + for (cagr, returns, days) in self.period_cagr.zip_mut_with_period(&self.period_return) { + let years = days as f64 / 365.0; + cagr.bps.height.compute_transform( starting_indexes.height, - &returns.height, + &returns.bps.height, |(h, r, ..)| { - let v = ((*r / 100.0 + 1.0).powf(1.0 / years) - 1.0) * 100.0; - (h, StoredF32::from(v)) + let ratio = f64::from(r); + let v = (ratio + 1.0).powf(1.0 / years) - 1.0; + (h, BasisPointsSigned32::from(v)) }, exit, )?; } // Lump sum by period - stack - let lookback_dca = lookback.price_ago.as_dca_period(); + let lookback_dca = lookback.price_lookback.as_dca_period(); for (stack, lookback_price, days) in self.period_lump_sum_stack.zip_mut_with_days(&lookback_dca) { @@ -146,13 +147,13 @@ impl Vecs { } // Lump sum by period - returns (compute from lookback price) - let lookback_dca2 = lookback.price_ago.as_dca_period(); + let lookback_dca2 = lookback.price_lookback.as_dca_period(); for (returns, (lookback_price, _)) in self - .period_lump_sum_returns + .period_lump_sum_return .iter_mut() .zip(lookback_dca2.iter_with_days()) { - returns.compute_binary::( + returns.compute_binary::( starting_indexes.height, &prices.price.cents.height, &lookback_price.cents.height, @@ -214,7 +215,7 @@ impl Vecs { // DCA by year class - average price (derived from stack) let start_days = super::ByDcaClass::<()>::start_days(); for ((average_price, stack), from) in self - .class_average_price + .class_cost_basis .iter_mut() .zip(self.class_stack.iter()) .zip(start_days) @@ -243,11 +244,11 @@ impl Vecs { // DCA by year class - returns (compute from average price) for (returns, average_price) in self - .class_returns + .class_return .iter_mut() - .zip(self.class_average_price.iter()) + .zip(self.class_cost_basis.iter()) { - returns.compute_binary::( + returns.compute_binary::( starting_indexes.height, &prices.price.cents.height, &average_price.cents.height, diff --git a/crates/brk_computer/src/market/dca/import.rs b/crates/brk_computer/src/market/dca/import.rs index 2fc87a0d4..8e148758a 100644 --- a/crates/brk_computer/src/market/dca/import.rs +++ b/crates/brk_computer/src/market/dca/import.rs @@ -5,7 +5,7 @@ use vecdb::{Database, ImportableVec}; use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod, Vecs}; use crate::{ indexes, - internal::{ComputedFromHeight, Price, ValueFromHeight}, + internal::{Bps32ToFloat, Bps32ToPercent, PercentFromHeight, Price, ValueFromHeight}, }; impl Vecs { @@ -15,57 +15,72 @@ impl Vecs { indexes: &indexes::Vecs, ) -> Result { let period_stack = ByDcaPeriod::try_new(|name, _days| { - ValueFromHeight::forced_import(db, &format!("{name}_dca_stack"), version, indexes) + ValueFromHeight::forced_import(db, &format!("dca_stack_{name}"), version, indexes) })?; - let period_average_price = ByDcaPeriod::try_new(|name, _days| { - Price::forced_import(db, &format!("{name}_dca_average_price"), version, indexes) + let period_cost_basis = ByDcaPeriod::try_new(|name, _days| { + Price::forced_import(db, &format!("dca_cost_basis_{name}"), version, indexes) })?; - let period_returns = ByDcaPeriod::try_new(|name, _days| { - ComputedFromHeight::forced_import(db, &format!("{name}_dca_returns"), version, indexes) + let period_return = ByDcaPeriod::try_new(|name, _days| { + PercentFromHeight::forced_import::( + db, + &format!("dca_return_{name}"), + version, + indexes, + ) })?; let period_cagr = ByDcaCagr::try_new(|name, _days| { - ComputedFromHeight::forced_import(db, &format!("{name}_dca_cagr"), version, indexes) + PercentFromHeight::forced_import::( + db, + &format!("dca_cagr_{name}"), + version, + indexes, + ) })?; let period_lump_sum_stack = ByDcaPeriod::try_new(|name, _days| { - ValueFromHeight::forced_import(db, &format!("{name}_lump_sum_stack"), version, indexes) + ValueFromHeight::forced_import(db, &format!("lump_sum_stack_{name}"), version, indexes) })?; - let period_lump_sum_returns = ByDcaPeriod::try_new(|name, _days| { - ComputedFromHeight::forced_import( + let period_lump_sum_return = ByDcaPeriod::try_new(|name, _days| { + PercentFromHeight::forced_import::( db, - &format!("{name}_lump_sum_returns"), + &format!("lump_sum_return_{name}"), version, indexes, ) })?; let class_stack = ByDcaClass::try_new(|name, _year, _day1| { - ValueFromHeight::forced_import(db, &format!("{name}_stack"), version, indexes) + ValueFromHeight::forced_import(db, &format!("dca_stack_{name}"), version, indexes) })?; - let class_average_price = ByDcaClass::try_new(|name, _year, _day1| { - Price::forced_import(db, &format!("{name}_average_price"), version, indexes) + let class_cost_basis = ByDcaClass::try_new(|name, _year, _day1| { + Price::forced_import(db, &format!("dca_cost_basis_{name}"), version, indexes) })?; - let class_returns = ByDcaClass::try_new(|name, _year, _day1| { - ComputedFromHeight::forced_import(db, &format!("{name}_returns"), version, indexes) + let class_return = ByDcaClass::try_new(|name, _year, _day1| { + PercentFromHeight::forced_import::( + db, + &format!("dca_return_{name}"), + version, + indexes, + ) })?; Ok(Self { dca_sats_per_day: ImportableVec::forced_import(db, "dca_sats_per_day", version)?, period_stack, - period_average_price, - period_returns, + period_cost_basis, + period_return, period_cagr, period_lump_sum_stack, - period_lump_sum_returns, + period_lump_sum_return, class_stack, - class_average_price, - class_returns, + class_cost_basis, + class_return, }) } } diff --git a/crates/brk_computer/src/market/dca/vecs.rs b/crates/brk_computer/src/market/dca/vecs.rs index caa4bb561..c07d9d960 100644 --- a/crates/brk_computer/src/market/dca/vecs.rs +++ b/crates/brk_computer/src/market/dca/vecs.rs @@ -1,9 +1,9 @@ use brk_traversable::Traversable; -use brk_types::{Cents, Height, Sats, StoredF32}; +use brk_types::{BasisPointsSigned32, Cents, Height, Sats}; use vecdb::{EagerVec, PcoVec, Rw, StorageMode}; use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod}; -use crate::internal::{ComputedFromHeight, Price, ValueFromHeight}; +use crate::internal::{ComputedFromHeight, PercentFromHeight, Price, ValueFromHeight}; /// Dollar-cost averaging metrics by time period and year class #[derive(Traversable)] @@ -14,16 +14,16 @@ pub struct Vecs { // DCA by period pub period_stack: ByDcaPeriod>, - pub period_average_price: ByDcaPeriod>>, - pub period_returns: ByDcaPeriod>, - pub period_cagr: ByDcaCagr>, + pub period_cost_basis: ByDcaPeriod>>, + pub period_return: ByDcaPeriod>, + pub period_cagr: ByDcaCagr>, // Lump sum by period (for comparison with DCA) pub period_lump_sum_stack: ByDcaPeriod>, - pub period_lump_sum_returns: ByDcaPeriod>, + pub period_lump_sum_return: ByDcaPeriod>, // DCA by year class pub class_stack: ByDcaClass>, - pub class_average_price: ByDcaClass>>, - pub class_returns: ByDcaClass>, + pub class_cost_basis: ByDcaClass>>, + pub class_return: ByDcaClass>, } diff --git a/crates/brk_computer/src/market/indicators/compute.rs b/crates/brk_computer/src/market/indicators/compute.rs index fddb7f7ac..3d1772681 100644 --- a/crates/brk_computer/src/market/indicators/compute.rs +++ b/crates/brk_computer/src/market/indicators/compute.rs @@ -5,13 +5,13 @@ use vecdb::Exit; use super::{super::range, Vecs}; use crate::{ ComputeIndexes, blocks, distribution, - internal::Ratio32, + internal::{Ratio32, Windows}, mining, prices, transactions, }; fn tf_multiplier(tf: &str) -> usize { match tf { - "1d" => 1, + "24h" => 1, "1w" => 7, "1m" => 30, "1y" => 365, @@ -37,7 +37,7 @@ impl Vecs { self.puell_multiple.height.compute_divide( starting_indexes.height, &rewards.subsidy.base.usd.height, - &rewards.subsidy_usd_1y_sma.usd.height, + &rewards.subsidy_sma_1y.usd.height, exit, )?; @@ -47,8 +47,8 @@ impl Vecs { self.stoch_k.height.compute_transform3( starting_indexes.height, price, - &range.price_2w_min.usd.height, - &range.price_2w_max.usd.height, + &range.price_min_2w.usd.height, + &range.price_max_2w.usd.height, |(h, close, low, high, ..)| { let range = *high - *low; let stoch = if range == 0.0 { @@ -70,13 +70,15 @@ impl Vecs { } // RSI per timeframe - for (tf, rsi_chain) in self.rsi.iter_mut() { + for (tf, rsi_chain) in Windows::<()>::SUFFIXES.into_iter() + .zip(self.rsi.as_mut_array()) + { let m = tf_multiplier(tf); let returns_source = match tf { - "1d" => &returns.price_returns._24h.height, - "1w" => &returns.price_returns._1w.height, - "1m" => &returns.price_returns._1m.height, - "1y" => &returns.price_returns._1y.height, + "24h" => &returns.price_return._24h.height, + "1w" => &returns.price_return._1w.height, + "1m" => &returns.price_return._1m.height, + "1y" => &returns.price_return._1y.height, _ => unreachable!(), }; super::rsi::compute( @@ -91,7 +93,9 @@ impl Vecs { } // MACD per timeframe - for (tf, macd_chain) in self.macd.iter_mut() { + for (tf, macd_chain) in Windows::<()>::SUFFIXES.into_iter() + .zip(self.macd.as_mut_array()) + { let m = tf_multiplier(tf); super::macd::compute( macd_chain, @@ -124,8 +128,8 @@ impl Vecs { // Pi Cycle: sma_111d / sma_350d_x2 self.pi_cycle.compute_binary::( starting_indexes.height, - &moving_average.price_111d_sma.price.usd.height, - &moving_average.price_350d_sma_x2.usd.height, + &moving_average.price_sma_111d.price.usd.height, + &moving_average.price_sma_350d_x2.usd.height, exit, )?; diff --git a/crates/brk_computer/src/market/indicators/gini.rs b/crates/brk_computer/src/market/indicators/gini.rs index 221cdfc1d..c5b662d63 100644 --- a/crates/brk_computer/src/market/indicators/gini.rs +++ b/crates/brk_computer/src/market/indicators/gini.rs @@ -1,11 +1,11 @@ use brk_error::Result; -use brk_types::{Sats, StoredF32, StoredU64, Version}; +use brk_types::{BasisPoints16, Sats, StoredU64, Version}; use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec}; -use crate::{ComputeIndexes, distribution, internal::ComputedFromHeight}; +use crate::{ComputeIndexes, distribution, internal::PercentFromHeight}; pub(super) fn compute( - gini: &mut ComputedFromHeight, + gini: &mut PercentFromHeight, distribution: &distribution::Vecs, starting_indexes: &ComputeIndexes, exit: &Exit, @@ -32,10 +32,10 @@ pub(super) fn compute( .iter() .fold(Version::ZERO, |acc, v| acc + v.version()); - gini.height + gini.bps.height .validate_computed_version_or_reset(source_version)?; - gini.height - .truncate_if_needed_at(gini.height.len().min(starting_indexes.height.to_usize()))?; + gini.bps.height + .truncate_if_needed_at(gini.bps.height.len().min(starting_indexes.height.to_usize()))?; let total_heights = supply_vecs .iter() @@ -44,7 +44,7 @@ pub(super) fn compute( .unwrap_or(0) .min(count_vecs.iter().map(|v| v.len()).min().unwrap_or(0)); - let start_height = gini.height.len(); + let start_height = gini.bps.height.len(); if start_height >= total_heights { return Ok(()); } @@ -68,24 +68,24 @@ pub(super) fn compute( let count: u64 = count_data[c][offset].into(); buckets.push((count, supply)); } - gini.height - .push(StoredF32::from(gini_from_lorenz(&buckets))); + gini.bps.height + .push(gini_from_lorenz(&buckets)); } { let _lock = exit.lock(); - gini.height.write()?; + gini.bps.height.write()?; } Ok(()) } -fn gini_from_lorenz(buckets: &[(u64, u64)]) -> f32 { +fn gini_from_lorenz(buckets: &[(u64, u64)]) -> BasisPoints16 { let total_count: u64 = buckets.iter().map(|(c, _)| c).sum(); let total_supply: u64 = buckets.iter().map(|(_, s)| s).sum(); if total_count == 0 || total_supply == 0 { - return f32::NAN; + return BasisPoints16::ZERO; } let (mut cumulative_count, mut cumulative_supply, mut area) = (0u64, 0u64, 0.0f64); @@ -99,5 +99,5 @@ fn gini_from_lorenz(buckets: &[(u64, u64)]) -> f32 { area += (p1 - p0) * (w0 + w1) / 2.0; } - (1.0 - 2.0 * area) as f32 + BasisPoints16::from(1.0 - 2.0 * area) } diff --git a/crates/brk_computer/src/market/indicators/import.rs b/crates/brk_computer/src/market/indicators/import.rs index 13209c46c..98225c869 100644 --- a/crates/brk_computer/src/market/indicators/import.rs +++ b/crates/brk_computer/src/market/indicators/import.rs @@ -2,10 +2,10 @@ use brk_error::Result; use brk_types::Version; use vecdb::Database; -use super::{ByIndicatorTimeframe, MacdChain, RsiChain, Vecs}; +use super::{MacdChain, RsiChain, Vecs}; use crate::{ indexes, - internal::ComputedFromHeight, + internal::{Bp16ToFloat, Bp16ToPercent, ComputedFromHeight, PercentFromHeight, Windows}, }; const VERSION: Version = Version::ONE; @@ -28,8 +28,8 @@ impl RsiChain { }; } - let average_gain = import!("avg_gain"); - let average_loss = import!("avg_loss"); + let average_gain = import!("average_gain"); + let average_loss = import!("average_loss"); let rsi = ComputedFromHeight::forced_import( db, @@ -44,11 +44,11 @@ impl RsiChain { average_gain, average_loss, rsi, - rsi_min: import!("rsi_min"), - rsi_max: import!("rsi_max"), - stoch_rsi: import!("stoch_rsi"), - stoch_rsi_k: import!("stoch_rsi_k"), - stoch_rsi_d: import!("stoch_rsi_d"), + rsi_min: import!("min"), + rsi_max: import!("max"), + stoch_rsi: import!("stoch"), + stoch_rsi_k: import!("stoch_k"), + stoch_rsi_d: import!("stoch_d"), }) } } @@ -110,12 +110,12 @@ impl Vecs { let nvt = ComputedFromHeight::forced_import(db, "nvt", v, indexes)?; - let rsi = ByIndicatorTimeframe::try_new(|tf| RsiChain::forced_import(db, tf, v, indexes))?; - let macd = ByIndicatorTimeframe::try_new(|tf| MacdChain::forced_import(db, tf, v, indexes))?; + let rsi = Windows::try_from_fn(|tf| RsiChain::forced_import(db, tf, v, indexes))?; + let macd = Windows::try_from_fn(|tf| MacdChain::forced_import(db, tf, v, indexes))?; let stoch_k = ComputedFromHeight::forced_import(db, "stoch_k", v, indexes)?; let stoch_d = ComputedFromHeight::forced_import(db, "stoch_d", v, indexes)?; - let gini = ComputedFromHeight::forced_import(db, "gini", v, indexes)?; + let gini = PercentFromHeight::forced_import::(db, "gini", v, indexes)?; let pi_cycle = ComputedFromHeight::forced_import(db, "pi_cycle", v, indexes)?; diff --git a/crates/brk_computer/src/market/indicators/mod.rs b/crates/brk_computer/src/market/indicators/mod.rs index 982f5726a..4b8b68f48 100644 --- a/crates/brk_computer/src/market/indicators/mod.rs +++ b/crates/brk_computer/src/market/indicators/mod.rs @@ -5,4 +5,4 @@ mod macd; mod rsi; mod vecs; -pub use vecs::{ByIndicatorTimeframe, MacdChain, RsiChain, Vecs}; +pub use vecs::{MacdChain, RsiChain, Vecs}; diff --git a/crates/brk_computer/src/market/indicators/vecs.rs b/crates/brk_computer/src/market/indicators/vecs.rs index 0e32445b4..0666b3afe 100644 --- a/crates/brk_computer/src/market/indicators/vecs.rs +++ b/crates/brk_computer/src/market/indicators/vecs.rs @@ -1,39 +1,8 @@ use brk_traversable::Traversable; -use brk_types::StoredF32; +use brk_types::{BasisPoints16, StoredF32}; use vecdb::{Rw, StorageMode}; -use crate::internal::ComputedFromHeight; - -pub const TIMEFRAME_NAMES: [&str; 4] = ["1d", "1w", "1m", "1y"]; - -#[derive(Clone, Traversable)] -pub struct ByIndicatorTimeframe { - pub _1d: T, - pub _1w: T, - pub _1m: T, - pub _1y: T, -} - -impl ByIndicatorTimeframe { - pub fn try_new(mut create: impl FnMut(&str) -> Result) -> Result { - Ok(Self { - _1d: create(TIMEFRAME_NAMES[0])?, - _1w: create(TIMEFRAME_NAMES[1])?, - _1m: create(TIMEFRAME_NAMES[2])?, - _1y: create(TIMEFRAME_NAMES[3])?, - }) - } - - pub fn iter_mut(&mut self) -> impl Iterator { - [ - (TIMEFRAME_NAMES[0], &mut self._1d), - (TIMEFRAME_NAMES[1], &mut self._1w), - (TIMEFRAME_NAMES[2], &mut self._1m), - (TIMEFRAME_NAMES[3], &mut self._1y), - ] - .into_iter() - } -} +use crate::internal::{ComputedFromHeight, PercentFromHeight, Windows}; #[derive(Traversable)] pub struct RsiChain { @@ -63,14 +32,14 @@ pub struct Vecs { pub puell_multiple: ComputedFromHeight, pub nvt: ComputedFromHeight, - pub rsi: ByIndicatorTimeframe>, + pub rsi: Windows>, pub stoch_k: ComputedFromHeight, pub stoch_d: ComputedFromHeight, pub pi_cycle: ComputedFromHeight, - pub macd: ByIndicatorTimeframe>, + pub macd: Windows>, - pub gini: ComputedFromHeight, + pub gini: PercentFromHeight, } diff --git a/crates/brk_computer/src/market/lookback/compute.rs b/crates/brk_computer/src/market/lookback/compute.rs index 76943d566..ec1baa422 100644 --- a/crates/brk_computer/src/market/lookback/compute.rs +++ b/crates/brk_computer/src/market/lookback/compute.rs @@ -14,9 +14,9 @@ impl Vecs { ) -> Result<()> { let price = &prices.price.cents.height; - for (price_ago, days) in self.price_ago.iter_mut_with_days() { + for (price_lookback, days) in self.price_lookback.iter_mut_with_days() { let window_starts = blocks.count.start_vec(days as usize); - price_ago.cents.height.compute_lookback( + price_lookback.cents.height.compute_lookback( starting_indexes.height, window_starts, price, diff --git a/crates/brk_computer/src/market/lookback/import.rs b/crates/brk_computer/src/market/lookback/import.rs index 8b48dc302..989bfa8c8 100644 --- a/crates/brk_computer/src/market/lookback/import.rs +++ b/crates/brk_computer/src/market/lookback/import.rs @@ -7,10 +7,10 @@ use crate::{indexes, internal::Price}; impl Vecs { pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result { - let price_ago = ByLookbackPeriod::try_new(|name, _days| { - Price::forced_import(db, &format!("price_{name}_ago"), version, indexes) + let price_lookback = ByLookbackPeriod::try_new(|name, _days| { + Price::forced_import(db, &format!("price_lookback_{name}"), version, indexes) })?; - Ok(Self { price_ago }) + Ok(Self { price_lookback }) } } diff --git a/crates/brk_computer/src/market/lookback/vecs.rs b/crates/brk_computer/src/market/lookback/vecs.rs index b90794df3..404100bf0 100644 --- a/crates/brk_computer/src/market/lookback/vecs.rs +++ b/crates/brk_computer/src/market/lookback/vecs.rs @@ -9,5 +9,5 @@ use crate::internal::{ComputedFromHeight, Price}; #[derive(Traversable)] pub struct Vecs { #[traversable(flatten)] - pub price_ago: ByLookbackPeriod>>, + pub price_lookback: ByLookbackPeriod>>, } diff --git a/crates/brk_computer/src/market/moving_average/compute.rs b/crates/brk_computer/src/market/moving_average/compute.rs index 7b717dea5..e52afe487 100644 --- a/crates/brk_computer/src/market/moving_average/compute.rs +++ b/crates/brk_computer/src/market/moving_average/compute.rs @@ -15,22 +15,22 @@ impl Vecs { let close = &prices.price.cents.height; for (sma, period) in [ - (&mut self.price_1w_sma, 7), - (&mut self.price_8d_sma, 8), - (&mut self.price_13d_sma, 13), - (&mut self.price_21d_sma, 21), - (&mut self.price_1m_sma, 30), - (&mut self.price_34d_sma, 34), - (&mut self.price_55d_sma, 55), - (&mut self.price_89d_sma, 89), - (&mut self.price_111d_sma, 111), - (&mut self.price_144d_sma, 144), - (&mut self.price_200d_sma, 200), - (&mut self.price_350d_sma, 350), - (&mut self.price_1y_sma, 365), - (&mut self.price_2y_sma, 2 * 365), - (&mut self.price_200w_sma, 200 * 7), - (&mut self.price_4y_sma, 4 * 365), + (&mut self.price_sma_1w, 7), + (&mut self.price_sma_8d, 8), + (&mut self.price_sma_13d, 13), + (&mut self.price_sma_21d, 21), + (&mut self.price_sma_1m, 30), + (&mut self.price_sma_34d, 34), + (&mut self.price_sma_55d, 55), + (&mut self.price_sma_89d, 89), + (&mut self.price_sma_111d, 111), + (&mut self.price_sma_144d, 144), + (&mut self.price_sma_200d, 200), + (&mut self.price_sma_350d, 350), + (&mut self.price_sma_1y, 365), + (&mut self.price_sma_2y, 2 * 365), + (&mut self.price_sma_200w, 200 * 7), + (&mut self.price_sma_4y, 4 * 365), ] { let window_starts = blocks.count.start_vec(period); sma.compute_all(blocks, prices, starting_indexes, exit, |v| { @@ -40,22 +40,22 @@ impl Vecs { } for (ema, period) in [ - (&mut self.price_1w_ema, 7), - (&mut self.price_8d_ema, 8), - (&mut self.price_12d_ema, 12), - (&mut self.price_13d_ema, 13), - (&mut self.price_21d_ema, 21), - (&mut self.price_26d_ema, 26), - (&mut self.price_1m_ema, 30), - (&mut self.price_34d_ema, 34), - (&mut self.price_55d_ema, 55), - (&mut self.price_89d_ema, 89), - (&mut self.price_144d_ema, 144), - (&mut self.price_200d_ema, 200), - (&mut self.price_1y_ema, 365), - (&mut self.price_2y_ema, 2 * 365), - (&mut self.price_200w_ema, 200 * 7), - (&mut self.price_4y_ema, 4 * 365), + (&mut self.price_ema_1w, 7), + (&mut self.price_ema_8d, 8), + (&mut self.price_ema_12d, 12), + (&mut self.price_ema_13d, 13), + (&mut self.price_ema_21d, 21), + (&mut self.price_ema_26d, 26), + (&mut self.price_ema_1m, 30), + (&mut self.price_ema_34d, 34), + (&mut self.price_ema_55d, 55), + (&mut self.price_ema_89d, 89), + (&mut self.price_ema_144d, 144), + (&mut self.price_ema_200d, 200), + (&mut self.price_ema_1y, 365), + (&mut self.price_ema_2y, 2 * 365), + (&mut self.price_ema_200w, 200 * 7), + (&mut self.price_ema_4y, 4 * 365), ] { let window_starts = blocks.count.start_vec(period); ema.compute_all(blocks, prices, starting_indexes, exit, |v| { diff --git a/crates/brk_computer/src/market/moving_average/import.rs b/crates/brk_computer/src/market/moving_average/import.rs index 0a1d4e03d..7186f9589 100644 --- a/crates/brk_computer/src/market/moving_average/import.rs +++ b/crates/brk_computer/src/market/moving_average/import.rs @@ -25,66 +25,66 @@ impl Vecs { }; } - let price_200d_sma = import!("price_200d_sma"); - let price_350d_sma = import!("price_350d_sma"); + let price_sma_200d = import!("price_sma_200d"); + let price_sma_350d = import!("price_sma_350d"); - let price_200d_sma_source = &price_200d_sma.price.cents; - let price_200d_sma_x2_4 = Price::from_cents_source::>( - "price_200d_sma_x2_4", + let price_sma_200d_source = &price_sma_200d.price.cents; + let price_sma_200d_x2_4 = Price::from_cents_source::>( + "price_sma_200d_x2_4", version, - price_200d_sma_source, + price_sma_200d_source, ); - let price_200d_sma_x0_8 = Price::from_cents_source::>( - "price_200d_sma_x0_8", + let price_sma_200d_x0_8 = Price::from_cents_source::>( + "price_sma_200d_x0_8", version, - price_200d_sma_source, + price_sma_200d_source, ); - let price_350d_sma_source = &price_350d_sma.price.cents; - let price_350d_sma_x2 = Price::from_cents_source::>( - "price_350d_sma_x2", + let price_sma_350d_source = &price_sma_350d.price.cents; + let price_sma_350d_x2 = Price::from_cents_source::>( + "price_sma_350d_x2", version, - price_350d_sma_source, + price_sma_350d_source, ); Ok(Self { - price_1w_sma: import!("price_1w_sma"), - price_8d_sma: import!("price_8d_sma"), - price_13d_sma: import!("price_13d_sma"), - price_21d_sma: import!("price_21d_sma"), - price_1m_sma: import!("price_1m_sma"), - price_34d_sma: import!("price_34d_sma"), - price_55d_sma: import!("price_55d_sma"), - price_89d_sma: import!("price_89d_sma"), - price_111d_sma: import!("price_111d_sma"), - price_144d_sma: import!("price_144d_sma"), - price_200d_sma, - price_350d_sma, - price_1y_sma: import!("price_1y_sma"), - price_2y_sma: import!("price_2y_sma"), - price_200w_sma: import!("price_200w_sma"), - price_4y_sma: import!("price_4y_sma"), + price_sma_1w: import!("price_sma_1w"), + price_sma_8d: import!("price_sma_8d"), + price_sma_13d: import!("price_sma_13d"), + price_sma_21d: import!("price_sma_21d"), + price_sma_1m: import!("price_sma_1m"), + price_sma_34d: import!("price_sma_34d"), + price_sma_55d: import!("price_sma_55d"), + price_sma_89d: import!("price_sma_89d"), + price_sma_111d: import!("price_sma_111d"), + price_sma_144d: import!("price_sma_144d"), + price_sma_200d, + price_sma_350d, + price_sma_1y: import!("price_sma_1y"), + price_sma_2y: import!("price_sma_2y"), + price_sma_200w: import!("price_sma_200w"), + price_sma_4y: import!("price_sma_4y"), - price_1w_ema: import!("price_1w_ema"), - price_8d_ema: import!("price_8d_ema"), - price_12d_ema: import!("price_12d_ema"), - price_13d_ema: import!("price_13d_ema"), - price_21d_ema: import!("price_21d_ema"), - price_26d_ema: import!("price_26d_ema"), - price_1m_ema: import!("price_1m_ema"), - price_34d_ema: import!("price_34d_ema"), - price_55d_ema: import!("price_55d_ema"), - price_89d_ema: import!("price_89d_ema"), - price_144d_ema: import!("price_144d_ema"), - price_200d_ema: import!("price_200d_ema"), - price_1y_ema: import!("price_1y_ema"), - price_2y_ema: import!("price_2y_ema"), - price_200w_ema: import!("price_200w_ema"), - price_4y_ema: import!("price_4y_ema"), + price_ema_1w: import!("price_ema_1w"), + price_ema_8d: import!("price_ema_8d"), + price_ema_12d: import!("price_ema_12d"), + price_ema_13d: import!("price_ema_13d"), + price_ema_21d: import!("price_ema_21d"), + price_ema_26d: import!("price_ema_26d"), + price_ema_1m: import!("price_ema_1m"), + price_ema_34d: import!("price_ema_34d"), + price_ema_55d: import!("price_ema_55d"), + price_ema_89d: import!("price_ema_89d"), + price_ema_144d: import!("price_ema_144d"), + price_ema_200d: import!("price_ema_200d"), + price_ema_1y: import!("price_ema_1y"), + price_ema_2y: import!("price_ema_2y"), + price_ema_200w: import!("price_ema_200w"), + price_ema_4y: import!("price_ema_4y"), - price_200d_sma_x2_4, - price_200d_sma_x0_8, - price_350d_sma_x2, + price_sma_200d_x2_4, + price_sma_200d_x0_8, + price_sma_350d_x2, }) } } diff --git a/crates/brk_computer/src/market/moving_average/vecs.rs b/crates/brk_computer/src/market/moving_average/vecs.rs index 46e35a0ac..7e0349e86 100644 --- a/crates/brk_computer/src/market/moving_average/vecs.rs +++ b/crates/brk_computer/src/market/moving_average/vecs.rs @@ -7,41 +7,41 @@ use crate::internal::{ComputedFromHeightPriceWithRatioExtended, LazyFromHeight, /// Simple and exponential moving average metrics #[derive(Traversable)] pub struct Vecs { - pub price_1w_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_8d_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_13d_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_21d_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_1m_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_34d_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_55d_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_89d_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_111d_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_144d_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_200d_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_350d_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_1y_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_2y_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_200w_sma: ComputedFromHeightPriceWithRatioExtended, - pub price_4y_sma: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_1w: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_8d: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_13d: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_21d: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_1m: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_34d: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_55d: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_89d: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_111d: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_144d: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_200d: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_350d: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_1y: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_2y: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_200w: ComputedFromHeightPriceWithRatioExtended, + pub price_sma_4y: ComputedFromHeightPriceWithRatioExtended, - pub price_1w_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_8d_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_12d_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_13d_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_21d_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_26d_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_1m_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_34d_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_55d_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_89d_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_144d_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_200d_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_1y_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_2y_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_200w_ema: ComputedFromHeightPriceWithRatioExtended, - pub price_4y_ema: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_1w: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_8d: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_12d: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_13d: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_21d: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_26d: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_1m: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_34d: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_55d: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_89d: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_144d: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_200d: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_1y: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_2y: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_200w: ComputedFromHeightPriceWithRatioExtended, + pub price_ema_4y: ComputedFromHeightPriceWithRatioExtended, - pub price_200d_sma_x2_4: Price>, - pub price_200d_sma_x0_8: Price>, - pub price_350d_sma_x2: Price>, + pub price_sma_200d_x2_4: Price>, + pub price_sma_200d_x0_8: Price>, + pub price_sma_350d_x2: Price>, } diff --git a/crates/brk_computer/src/market/range/compute.rs b/crates/brk_computer/src/market/range/compute.rs index cbbb6063c..3f35083e5 100644 --- a/crates/brk_computer/src/market/range/compute.rs +++ b/crates/brk_computer/src/market/range/compute.rs @@ -16,10 +16,10 @@ impl Vecs { let price = &prices.price.cents.height; for (min_vec, max_vec, starts) in [ - (&mut self.price_1w_min.cents.height, &mut self.price_1w_max.cents.height, &blocks.count.height_1w_ago), - (&mut self.price_2w_min.cents.height, &mut self.price_2w_max.cents.height, &blocks.count.height_2w_ago), - (&mut self.price_1m_min.cents.height, &mut self.price_1m_max.cents.height, &blocks.count.height_1m_ago), - (&mut self.price_1y_min.cents.height, &mut self.price_1y_max.cents.height, &blocks.count.height_1y_ago), + (&mut self.price_min_1w.cents.height, &mut self.price_max_1w.cents.height, &blocks.count.height_1w_ago), + (&mut self.price_min_2w.cents.height, &mut self.price_max_2w.cents.height, &blocks.count.height_2w_ago), + (&mut self.price_min_1m.cents.height, &mut self.price_max_1m.cents.height, &blocks.count.height_1m_ago), + (&mut self.price_min_1y.cents.height, &mut self.price_max_1y.cents.height, &blocks.count.height_1y_ago), ] { min_vec.compute_rolling_min_from_starts(starting_indexes.height, starts, price, exit)?; max_vec.compute_rolling_max_from_starts(starting_indexes.height, starts, price, exit)?; @@ -47,18 +47,18 @@ impl Vecs { )?; // 2w rolling sum of true range - self.price_true_range_2w_sum.height.compute_rolling_sum( + self.price_true_range_sum_2w.height.compute_rolling_sum( starting_indexes.height, &blocks.count.height_2w_ago, &self.price_true_range.height, exit, )?; - self.price_2w_choppiness_index.height.compute_transform4( + self.price_choppiness_index_2w.height.compute_transform4( starting_indexes.height, - &self.price_true_range_2w_sum.height, - &self.price_2w_max.cents.height, - &self.price_2w_min.cents.height, + &self.price_true_range_sum_2w.height, + &self.price_max_2w.cents.height, + &self.price_min_2w.cents.height, &blocks.count.height_2w_ago, |(h, tr_sum, max, min, window_start, ..)| { let range = f64::from(max) - f64::from(min); diff --git a/crates/brk_computer/src/market/range/import.rs b/crates/brk_computer/src/market/range/import.rs index ebae058e8..2cf0564ba 100644 --- a/crates/brk_computer/src/market/range/import.rs +++ b/crates/brk_computer/src/market/range/import.rs @@ -10,22 +10,22 @@ impl Vecs { let v1 = Version::ONE; Ok(Self { - price_1w_min: Price::forced_import(db, "price_1w_min", version + v1, indexes)?, - price_1w_max: Price::forced_import(db, "price_1w_max", version + v1, indexes)?, - price_2w_min: Price::forced_import(db, "price_2w_min", version + v1, indexes)?, - price_2w_max: Price::forced_import(db, "price_2w_max", version + v1, indexes)?, - price_1m_min: Price::forced_import(db, "price_1m_min", version + v1, indexes)?, - price_1m_max: Price::forced_import(db, "price_1m_max", version + v1, indexes)?, - price_1y_min: Price::forced_import(db, "price_1y_min", version + v1, indexes)?, - price_1y_max: Price::forced_import(db, "price_1y_max", version + v1, indexes)?, + price_min_1w: Price::forced_import(db, "price_min_1w", version + v1, indexes)?, + price_max_1w: Price::forced_import(db, "price_max_1w", version + v1, indexes)?, + price_min_2w: Price::forced_import(db, "price_min_2w", version + v1, indexes)?, + price_max_2w: Price::forced_import(db, "price_max_2w", version + v1, indexes)?, + price_min_1m: Price::forced_import(db, "price_min_1m", version + v1, indexes)?, + price_max_1m: Price::forced_import(db, "price_max_1m", version + v1, indexes)?, + price_min_1y: Price::forced_import(db, "price_min_1y", version + v1, indexes)?, + price_max_1y: Price::forced_import(db, "price_max_1y", version + v1, indexes)?, price_true_range: ComputedFromHeight::forced_import( db, "price_true_range", version + v1, indexes, )?, - price_true_range_2w_sum: ComputedFromHeight::forced_import( - db, "price_true_range_2w_sum", version + v1, indexes, + price_true_range_sum_2w: ComputedFromHeight::forced_import( + db, "price_true_range_sum_2w", version + v1, indexes, )?, - price_2w_choppiness_index: ComputedFromHeight::forced_import( - db, "price_2w_choppiness_index", version + v1, indexes, + price_choppiness_index_2w: ComputedFromHeight::forced_import( + db, "price_choppiness_index_2w", version + v1, indexes, )?, }) } diff --git a/crates/brk_computer/src/market/range/vecs.rs b/crates/brk_computer/src/market/range/vecs.rs index bcaf330dd..aa5907a32 100644 --- a/crates/brk_computer/src/market/range/vecs.rs +++ b/crates/brk_computer/src/market/range/vecs.rs @@ -7,15 +7,15 @@ use crate::internal::{ComputedFromHeight, Price}; /// Price range and choppiness metrics #[derive(Traversable)] pub struct Vecs { - pub price_1w_min: Price>, - pub price_1w_max: Price>, - pub price_2w_min: Price>, - pub price_2w_max: Price>, - pub price_1m_min: Price>, - pub price_1m_max: Price>, - pub price_1y_min: Price>, - pub price_1y_max: Price>, + pub price_min_1w: Price>, + pub price_max_1w: Price>, + pub price_min_2w: Price>, + pub price_max_2w: Price>, + pub price_min_1m: Price>, + pub price_max_1m: Price>, + pub price_min_1y: Price>, + pub price_max_1y: Price>, pub price_true_range: ComputedFromHeight, - pub price_true_range_2w_sum: ComputedFromHeight, - pub price_2w_choppiness_index: ComputedFromHeight, + pub price_true_range_sum_2w: ComputedFromHeight, + pub price_choppiness_index_2w: ComputedFromHeight, } diff --git a/crates/brk_computer/src/market/returns/compute.rs b/crates/brk_computer/src/market/returns/compute.rs index 71038cf6e..94ee28504 100644 --- a/crates/brk_computer/src/market/returns/compute.rs +++ b/crates/brk_computer/src/market/returns/compute.rs @@ -1,9 +1,9 @@ use brk_error::Result; -use brk_types::{Dollars, StoredF32}; +use brk_types::{BasisPointsSigned32, Dollars, StoredF32}; use vecdb::Exit; use super::Vecs; -use crate::{ComputeIndexes, blocks, internal::PercentageDiffDollars, market::lookback, prices}; +use crate::{ComputeIndexes, blocks, internal::RatioDiffDollarsBps32, market::lookback, prices}; impl Vecs { pub(crate) fn compute( @@ -16,11 +16,11 @@ impl Vecs { ) -> Result<()> { // Compute price returns at height level for ((returns, _), (lookback_price, _)) in self - .price_returns + .price_return .iter_mut_with_days() - .zip(lookback.price_ago.iter_with_days()) + .zip(lookback.price_lookback.iter_with_days()) { - returns.compute_binary::( + returns.compute_binary::( starting_indexes.height, &prices.price.usd.height, &lookback_price.usd.height, @@ -29,44 +29,48 @@ impl Vecs { } // CAGR computed from returns at height level (2y+ periods only) - let price_returns_dca = self.price_returns.as_dca_period(); - for (cagr, returns, days) in self.cagr.zip_mut_with_period(&price_returns_dca) { - let years = days as f32 / 365.0; - cagr.height.compute_transform( + let price_return_dca = self.price_return.as_dca_period(); + for (cagr, returns, days) in self.price_cagr.zip_mut_with_period(&price_return_dca) { + let years = days as f64 / 365.0; + cagr.bps.height.compute_transform( starting_indexes.height, - &returns.height, + &returns.bps.height, |(h, r, ..)| { - let v = ((*r / 100.0 + 1.0).powf(1.0 / years) - 1.0) * 100.0; - (h, StoredF32::from(v)) + let ratio = f64::from(r); + let v = (ratio + 1.0).powf(1.0 / years) - 1.0; + (h, BasisPointsSigned32::from(v)) }, exit, )?; } - let _24h_price_returns_height = &self.price_returns._24h.height; + let _24h_price_return_height = &self.price_return._24h.bps.height; - self._1d_returns_1w_sd - .compute_all(blocks, starting_indexes, exit, _24h_price_returns_height)?; - self._1d_returns_1m_sd - .compute_all(blocks, starting_indexes, exit, _24h_price_returns_height)?; - self._1d_returns_1y_sd - .compute_all(blocks, starting_indexes, exit, _24h_price_returns_height)?; + self.price_return_24h_sd_1w + .compute_all(blocks, starting_indexes, exit, _24h_price_return_height)?; + self.price_return_24h_sd_1m + .compute_all(blocks, starting_indexes, exit, _24h_price_return_height)?; + self.price_return_24h_sd_1y + .compute_all(blocks, starting_indexes, exit, _24h_price_return_height)?; // Downside returns: min(return, 0) - self.downside_returns.compute_transform( + self.price_downside_24h.compute_transform( starting_indexes.height, - _24h_price_returns_height, - |(i, ret, ..)| (i, StoredF32::from((*ret).min(0.0))), + _24h_price_return_height, + |(i, ret, ..)| { + let v = f64::from(ret).min(0.0); + (i, StoredF32::from(v as f32)) + }, exit, )?; // Downside deviation (SD of downside returns) - self.downside_1w_sd - .compute_all(blocks, starting_indexes, exit, &self.downside_returns)?; - self.downside_1m_sd - .compute_all(blocks, starting_indexes, exit, &self.downside_returns)?; - self.downside_1y_sd - .compute_all(blocks, starting_indexes, exit, &self.downside_returns)?; + self.price_downside_24h_sd_1w + .compute_all(blocks, starting_indexes, exit, &self.price_downside_24h)?; + self.price_downside_24h_sd_1m + .compute_all(blocks, starting_indexes, exit, &self.price_downside_24h)?; + self.price_downside_24h_sd_1y + .compute_all(blocks, starting_indexes, exit, &self.price_downside_24h)?; Ok(()) } diff --git a/crates/brk_computer/src/market/returns/import.rs b/crates/brk_computer/src/market/returns/import.rs index 2363a2dfb..8ba4cee71 100644 --- a/crates/brk_computer/src/market/returns/import.rs +++ b/crates/brk_computer/src/market/returns/import.rs @@ -6,8 +6,7 @@ use super::super::lookback::ByLookbackPeriod; use super::Vecs; use crate::{ indexes, - internal::ComputedFromHeight, - internal::ComputedFromHeightStdDev, + internal::{Bps32ToFloat, Bps32ToPercent, ComputedFromHeightStdDev, PercentFromHeight}, market::dca::ByDcaCagr, }; @@ -19,75 +18,86 @@ impl Vecs { ) -> Result { let v1 = Version::ONE; - let price_returns = ByLookbackPeriod::try_new(|name, _days| { - ComputedFromHeight::forced_import( + let price_return = ByLookbackPeriod::try_new(|name, _days| { + PercentFromHeight::forced_import::( db, - &format!("{name}_price_returns"), + &format!("price_return_{name}"), version, indexes, ) })?; // CAGR (computed, 2y+ only) - let cagr = ByDcaCagr::try_new(|name, _days| { - ComputedFromHeight::forced_import(db, &format!("{name}_cagr"), version, indexes) + let price_cagr = ByDcaCagr::try_new(|name, _days| { + PercentFromHeight::forced_import::( + db, + &format!("price_cagr_{name}"), + version, + indexes, + ) })?; - let _1d_returns_1w_sd = ComputedFromHeightStdDev::forced_import( + let price_return_24h_sd_1w = ComputedFromHeightStdDev::forced_import( db, - "1d_returns_1w_sd", + "price_return_24h", + "1w", 7, version + v1, indexes, )?; - let _1d_returns_1m_sd = ComputedFromHeightStdDev::forced_import( + let price_return_24h_sd_1m = ComputedFromHeightStdDev::forced_import( db, - "1d_returns_1m_sd", + "price_return_24h", + "1m", 30, version + v1, indexes, )?; - let _1d_returns_1y_sd = ComputedFromHeightStdDev::forced_import( + let price_return_24h_sd_1y = ComputedFromHeightStdDev::forced_import( db, - "1d_returns_1y_sd", + "price_return_24h", + "1y", 365, version + v1, indexes, )?; - let downside_returns = EagerVec::forced_import(db, "downside_returns", version)?; - let downside_1w_sd = ComputedFromHeightStdDev::forced_import( + let price_downside_24h = EagerVec::forced_import(db, "price_downside_24h", version)?; + let price_downside_24h_sd_1w = ComputedFromHeightStdDev::forced_import( db, - "downside_1w_sd", + "price_downside_24h", + "1w", 7, version + v1, indexes, )?; - let downside_1m_sd = ComputedFromHeightStdDev::forced_import( + let price_downside_24h_sd_1m = ComputedFromHeightStdDev::forced_import( db, - "downside_1m_sd", + "price_downside_24h", + "1m", 30, version + v1, indexes, )?; - let downside_1y_sd = ComputedFromHeightStdDev::forced_import( + let price_downside_24h_sd_1y = ComputedFromHeightStdDev::forced_import( db, - "downside_1y_sd", + "price_downside_24h", + "1y", 365, version + v1, indexes, )?; Ok(Self { - price_returns, - cagr, - _1d_returns_1w_sd, - _1d_returns_1m_sd, - _1d_returns_1y_sd, - downside_returns, - downside_1w_sd, - downside_1m_sd, - downside_1y_sd, + price_return, + price_cagr, + price_return_24h_sd_1w, + price_return_24h_sd_1m, + price_return_24h_sd_1y, + price_downside_24h, + price_downside_24h_sd_1w, + price_downside_24h_sd_1m, + price_downside_24h_sd_1y, }) } } diff --git a/crates/brk_computer/src/market/returns/vecs.rs b/crates/brk_computer/src/market/returns/vecs.rs index 9ba9a11de..412ed31fc 100644 --- a/crates/brk_computer/src/market/returns/vecs.rs +++ b/crates/brk_computer/src/market/returns/vecs.rs @@ -1,28 +1,28 @@ use brk_traversable::Traversable; -use brk_types::{Height, StoredF32}; +use brk_types::{BasisPointsSigned32, Height, StoredF32}; use vecdb::{EagerVec, PcoVec, Rw, StorageMode}; use crate::{ - internal::{ComputedFromHeight, ComputedFromHeightStdDev}, + internal::{ComputedFromHeight, ComputedFromHeightStdDev, PercentFromHeight}, market::{dca::ByDcaCagr, lookback::ByLookbackPeriod}, }; /// Price returns, CAGR, and returns standard deviation metrics #[derive(Traversable)] pub struct Vecs { - pub price_returns: ByLookbackPeriod>, + pub price_return: ByLookbackPeriod>, // CAGR (computed from returns, 2y+ only) - pub cagr: ByDcaCagr>, + pub price_cagr: ByDcaCagr>, - // Returns standard deviation (computed from 1d returns) - pub _1d_returns_1w_sd: ComputedFromHeightStdDev, - pub _1d_returns_1m_sd: ComputedFromHeightStdDev, - pub _1d_returns_1y_sd: ComputedFromHeightStdDev, + // Returns standard deviation (computed from 24h returns) + pub price_return_24h_sd_1w: ComputedFromHeightStdDev, + pub price_return_24h_sd_1m: ComputedFromHeightStdDev, + pub price_return_24h_sd_1y: ComputedFromHeightStdDev, // Downside returns and deviation (for Sortino ratio) - pub downside_returns: M::Stored>>, - pub downside_1w_sd: ComputedFromHeightStdDev, - pub downside_1m_sd: ComputedFromHeightStdDev, - pub downside_1y_sd: ComputedFromHeightStdDev, + pub price_downside_24h: M::Stored>>, + pub price_downside_24h_sd_1w: ComputedFromHeightStdDev, + pub price_downside_24h_sd_1m: ComputedFromHeightStdDev, + pub price_downside_24h_sd_1y: ComputedFromHeightStdDev, } diff --git a/crates/brk_computer/src/market/volatility/compute.rs b/crates/brk_computer/src/market/volatility/compute.rs index 3124b5fa9..de08e6cb3 100644 --- a/crates/brk_computer/src/market/volatility/compute.rs +++ b/crates/brk_computer/src/market/volatility/compute.rs @@ -14,17 +14,17 @@ impl Vecs { ) -> Result<()> { // Sharpe ratios: returns / volatility for (out, ret, vol) in [ - (&mut self.sharpe_1w, &returns.price_returns._1w.height, &self.price_1w_volatility.height), - (&mut self.sharpe_1m, &returns.price_returns._1m.height, &self.price_1m_volatility.height), - (&mut self.sharpe_1y, &returns.price_returns._1y.height, &self.price_1y_volatility.height), + (&mut self.price_sharpe_1w, &returns.price_return._1w.height, &self.price_volatility_1w.height), + (&mut self.price_sharpe_1m, &returns.price_return._1m.height, &self.price_volatility_1m.height), + (&mut self.price_sharpe_1y, &returns.price_return._1y.height, &self.price_volatility_1y.height), ] { compute_ratio(&mut out.height, starting_indexes_height, ret, vol, exit)?; } // Sortino ratios: returns / downside volatility - compute_ratio(&mut self.sortino_1w.height, starting_indexes_height, &returns.price_returns._1w.height, &returns.downside_1w_sd.sd.height, exit)?; - compute_ratio(&mut self.sortino_1m.height, starting_indexes_height, &returns.price_returns._1m.height, &returns.downside_1m_sd.sd.height, exit)?; - compute_ratio(&mut self.sortino_1y.height, starting_indexes_height, &returns.price_returns._1y.height, &returns.downside_1y_sd.sd.height, exit)?; + compute_ratio(&mut self.price_sortino_1w.height, starting_indexes_height, &returns.price_return._1w.height, &returns.price_downside_24h_sd_1w.sd.height, exit)?; + compute_ratio(&mut self.price_sortino_1m.height, starting_indexes_height, &returns.price_return._1m.height, &returns.price_downside_24h_sd_1m.sd.height, exit)?; + compute_ratio(&mut self.price_sortino_1y.height, starting_indexes_height, &returns.price_return._1y.height, &returns.price_downside_24h_sd_1y.sd.height, exit)?; Ok(()) } diff --git a/crates/brk_computer/src/market/volatility/import.rs b/crates/brk_computer/src/market/volatility/import.rs index 824c3ddf6..f8242fcd5 100644 --- a/crates/brk_computer/src/market/volatility/import.rs +++ b/crates/brk_computer/src/market/volatility/import.rs @@ -18,51 +18,51 @@ impl Vecs { ) -> Result { let v2 = Version::TWO; - let price_1w_volatility = LazyFromHeight::from_computed::>( - "price_1w_volatility", + let price_volatility_1w = LazyFromHeight::from_computed::>( + "price_volatility_1w", version + v2, - returns._1d_returns_1w_sd.sd.height.read_only_boxed_clone(), - &returns._1d_returns_1w_sd.sd, + returns.price_return_24h_sd_1w.sd.height.read_only_boxed_clone(), + &returns.price_return_24h_sd_1w.sd, ); - let price_1m_volatility = LazyFromHeight::from_computed::>( - "price_1m_volatility", + let price_volatility_1m = LazyFromHeight::from_computed::>( + "price_volatility_1m", version + v2, - returns._1d_returns_1m_sd.sd.height.read_only_boxed_clone(), - &returns._1d_returns_1m_sd.sd, + returns.price_return_24h_sd_1m.sd.height.read_only_boxed_clone(), + &returns.price_return_24h_sd_1m.sd, ); - let price_1y_volatility = LazyFromHeight::from_computed::>( - "price_1y_volatility", + let price_volatility_1y = LazyFromHeight::from_computed::>( + "price_volatility_1y", version + v2, - returns._1d_returns_1y_sd.sd.height.read_only_boxed_clone(), - &returns._1d_returns_1y_sd.sd, + returns.price_return_24h_sd_1y.sd.height.read_only_boxed_clone(), + &returns.price_return_24h_sd_1y.sd, ); - let sharpe_1w = - ComputedFromHeight::forced_import(db, "sharpe_1w", version + v2, indexes)?; - let sharpe_1m = - ComputedFromHeight::forced_import(db, "sharpe_1m", version + v2, indexes)?; - let sharpe_1y = - ComputedFromHeight::forced_import(db, "sharpe_1y", version + v2, indexes)?; + let price_sharpe_1w = + ComputedFromHeight::forced_import(db, "price_sharpe_1w", version + v2, indexes)?; + let price_sharpe_1m = + ComputedFromHeight::forced_import(db, "price_sharpe_1m", version + v2, indexes)?; + let price_sharpe_1y = + ComputedFromHeight::forced_import(db, "price_sharpe_1y", version + v2, indexes)?; - let sortino_1w = - ComputedFromHeight::forced_import(db, "sortino_1w", version + v2, indexes)?; - let sortino_1m = - ComputedFromHeight::forced_import(db, "sortino_1m", version + v2, indexes)?; - let sortino_1y = - ComputedFromHeight::forced_import(db, "sortino_1y", version + v2, indexes)?; + let price_sortino_1w = + ComputedFromHeight::forced_import(db, "price_sortino_1w", version + v2, indexes)?; + let price_sortino_1m = + ComputedFromHeight::forced_import(db, "price_sortino_1m", version + v2, indexes)?; + let price_sortino_1y = + ComputedFromHeight::forced_import(db, "price_sortino_1y", version + v2, indexes)?; Ok(Self { - price_1w_volatility, - price_1m_volatility, - price_1y_volatility, - sharpe_1w, - sharpe_1m, - sharpe_1y, - sortino_1w, - sortino_1m, - sortino_1y, + price_volatility_1w, + price_volatility_1m, + price_volatility_1y, + price_sharpe_1w, + price_sharpe_1m, + price_sharpe_1y, + price_sortino_1w, + price_sortino_1m, + price_sortino_1y, }) } } diff --git a/crates/brk_computer/src/market/volatility/vecs.rs b/crates/brk_computer/src/market/volatility/vecs.rs index 2b2ba6bd5..b2578b169 100644 --- a/crates/brk_computer/src/market/volatility/vecs.rs +++ b/crates/brk_computer/src/market/volatility/vecs.rs @@ -8,15 +8,15 @@ use brk_types::StoredF32; /// Price volatility metrics (derived from returns standard deviation) #[derive(Traversable)] pub struct Vecs { - pub price_1w_volatility: LazyFromHeight, - pub price_1m_volatility: LazyFromHeight, - pub price_1y_volatility: LazyFromHeight, + pub price_volatility_1w: LazyFromHeight, + pub price_volatility_1m: LazyFromHeight, + pub price_volatility_1y: LazyFromHeight, - pub sharpe_1w: ComputedFromHeight, - pub sharpe_1m: ComputedFromHeight, - pub sharpe_1y: ComputedFromHeight, + pub price_sharpe_1w: ComputedFromHeight, + pub price_sharpe_1m: ComputedFromHeight, + pub price_sharpe_1y: ComputedFromHeight, - pub sortino_1w: ComputedFromHeight, - pub sortino_1m: ComputedFromHeight, - pub sortino_1y: ComputedFromHeight, + pub price_sortino_1w: ComputedFromHeight, + pub price_sortino_1m: ComputedFromHeight, + pub price_sortino_1y: ComputedFromHeight, } diff --git a/crates/brk_computer/src/mining/hashrate/compute.rs b/crates/brk_computer/src/mining/hashrate/compute.rs index 8851f5a81..add44a014 100644 --- a/crates/brk_computer/src/mining/hashrate/compute.rs +++ b/crates/brk_computer/src/mining/hashrate/compute.rs @@ -35,28 +35,28 @@ impl Vecs { exit, )?; - self.hash_rate_1w_sma.height.compute_rolling_average( + self.hash_rate_sma_1w.height.compute_rolling_average( starting_indexes.height, &count_vecs.height_1w_ago, &self.hash_rate.height, exit, )?; - self.hash_rate_1m_sma.height.compute_rolling_average( + self.hash_rate_sma_1m.height.compute_rolling_average( starting_indexes.height, &count_vecs.height_1m_ago, &self.hash_rate.height, exit, )?; - self.hash_rate_2m_sma.height.compute_rolling_average( + self.hash_rate_sma_2m.height.compute_rolling_average( starting_indexes.height, &count_vecs.height_2m_ago, &self.hash_rate.height, exit, )?; - self.hash_rate_1y_sma.height.compute_rolling_average( + self.hash_rate_sma_1y.height.compute_rolling_average( starting_indexes.height, &count_vecs.height_1y_ago, &self.hash_rate.height, @@ -69,7 +69,7 @@ impl Vecs { exit, )?; - self.hash_rate_drawdown.height.compute_drawdown( + self.hash_rate_drawdown.compute_drawdown( starting_indexes.height, &self.hash_rate.height, &self.hash_rate_ath.height, diff --git a/crates/brk_computer/src/mining/hashrate/import.rs b/crates/brk_computer/src/mining/hashrate/import.rs index f1c8a941e..1869cca77 100644 --- a/crates/brk_computer/src/mining/hashrate/import.rs +++ b/crates/brk_computer/src/mining/hashrate/import.rs @@ -5,7 +5,7 @@ use vecdb::Database; use super::Vecs; use crate::{ indexes, - internal::ComputedFromHeight, + internal::{Bps16ToFloat, Bps16ToPercent, ComputedFromHeight, PercentFromHeight}, }; impl Vecs { @@ -19,27 +19,27 @@ impl Vecs { Ok(Self { hash_rate: ComputedFromHeight::forced_import(db, "hash_rate", version + v5, indexes)?, - hash_rate_1w_sma: ComputedFromHeight::forced_import( + hash_rate_sma_1w: ComputedFromHeight::forced_import( db, - "hash_rate_1w_sma", + "hash_rate_sma_1w", version, indexes, )?, - hash_rate_1m_sma: ComputedFromHeight::forced_import( + hash_rate_sma_1m: ComputedFromHeight::forced_import( db, - "hash_rate_1m_sma", + "hash_rate_sma_1m", version, indexes, )?, - hash_rate_2m_sma: ComputedFromHeight::forced_import( + hash_rate_sma_2m: ComputedFromHeight::forced_import( db, - "hash_rate_2m_sma", + "hash_rate_sma_2m", version, indexes, )?, - hash_rate_1y_sma: ComputedFromHeight::forced_import( + hash_rate_sma_1y: ComputedFromHeight::forced_import( db, - "hash_rate_1y_sma", + "hash_rate_sma_1y", version, indexes, )?, @@ -49,7 +49,7 @@ impl Vecs { version, indexes, )?, - hash_rate_drawdown: ComputedFromHeight::forced_import( + hash_rate_drawdown: PercentFromHeight::forced_import::( db, "hash_rate_drawdown", version, diff --git a/crates/brk_computer/src/mining/hashrate/vecs.rs b/crates/brk_computer/src/mining/hashrate/vecs.rs index da66f1cbb..512c0d544 100644 --- a/crates/brk_computer/src/mining/hashrate/vecs.rs +++ b/crates/brk_computer/src/mining/hashrate/vecs.rs @@ -1,19 +1,19 @@ use brk_traversable::Traversable; -use brk_types::{StoredF32, StoredF64}; +use brk_types::{BasisPointsSigned16, StoredF32, StoredF64}; use vecdb::{Rw, StorageMode}; -use crate::internal::ComputedFromHeight; +use crate::internal::{ComputedFromHeight, PercentFromHeight}; /// Mining-related metrics: hash rate, hash price, hash value #[derive(Traversable)] pub struct Vecs { pub hash_rate: ComputedFromHeight, - pub hash_rate_1w_sma: ComputedFromHeight, - pub hash_rate_1m_sma: ComputedFromHeight, - pub hash_rate_2m_sma: ComputedFromHeight, - pub hash_rate_1y_sma: ComputedFromHeight, + pub hash_rate_sma_1w: ComputedFromHeight, + pub hash_rate_sma_1m: ComputedFromHeight, + pub hash_rate_sma_2m: ComputedFromHeight, + pub hash_rate_sma_1y: ComputedFromHeight, pub hash_rate_ath: ComputedFromHeight, - pub hash_rate_drawdown: ComputedFromHeight, + pub hash_rate_drawdown: PercentFromHeight, pub hash_price_ths: ComputedFromHeight, pub hash_price_ths_min: ComputedFromHeight, pub hash_price_phs: ComputedFromHeight, diff --git a/crates/brk_computer/src/mining/rewards/compute.rs b/crates/brk_computer/src/mining/rewards/compute.rs index d32fe4441..1e82072a5 100644 --- a/crates/brk_computer/src/mining/rewards/compute.rs +++ b/crates/brk_computer/src/mining/rewards/compute.rs @@ -1,10 +1,10 @@ use brk_error::Result; use brk_indexer::Indexer; -use brk_types::{CheckedSub, HalvingEpoch, Sats, StoredF32}; +use brk_types::{BasisPoints16, CheckedSub, HalvingEpoch, Sats}; use vecdb::{Exit, ReadableVec, VecIndex}; use super::Vecs; -use crate::{ComputeIndexes, blocks, indexes, prices, transactions}; +use crate::{ComputeIndexes, blocks, indexes, internal::RatioSatsBp16, prices, transactions}; impl Vecs { #[allow(clippy::too_many_arguments)] @@ -122,14 +122,14 @@ impl Vecs { )?; // All-time cumulative fee dominance - self.fee_dominance.height.compute_percentage( + self.fee_dominance.compute_binary::( starting_indexes.height, &self.fees.cumulative.sats.height, &self.coinbase.cumulative.sats.height, exit, )?; - // Rolling fee dominance = sum(fees) / sum(coinbase) * 100 + // Rolling fee dominance = sum(fees) / sum(coinbase) for ((fee_dom, fees_w), coinbase_w) in self .fee_dominance_rolling .as_mut_array() @@ -137,7 +137,7 @@ impl Vecs { .zip(self.fees.rolling.as_array()) .zip(self.coinbase.rolling.as_array()) { - fee_dom.height.compute_percentage( + fee_dom.compute_binary::( starting_indexes.height, &fees_w.sum.sats.height, &coinbase_w.sum.sats.height, @@ -146,30 +146,29 @@ impl Vecs { } // All-time cumulative subsidy dominance - self.subsidy_dominance.height.compute_percentage( + self.subsidy_dominance.compute_binary::( starting_indexes.height, &self.subsidy.cumulative.sats.height, &self.coinbase.cumulative.sats.height, exit, )?; - // Rolling subsidy dominance = 100 - fee_dominance - let hundred = StoredF32::from(100u8); + // Rolling subsidy dominance = 1 - fee_dominance for (sub_dom, fee_dom) in self .subsidy_dominance_rolling .as_mut_array() .into_iter() .zip(self.fee_dominance_rolling.as_array()) { - sub_dom.height.compute_transform( + sub_dom.bps.height.compute_transform( starting_indexes.height, - &fee_dom.height, - |(height, fee_dom, _)| (height, hundred - fee_dom), + &fee_dom.bps.height, + |(height, fee, _)| (height, BasisPoints16::ONE - fee), exit, )?; } - self.subsidy_usd_1y_sma.cents.height.compute_rolling_average( + self.subsidy_sma_1y.cents.height.compute_rolling_average( starting_indexes.height, &count_vecs.height_1y_ago, &self.subsidy.base.cents.height, diff --git a/crates/brk_computer/src/mining/rewards/import.rs b/crates/brk_computer/src/mining/rewards/import.rs index 510094571..f28cb24b1 100644 --- a/crates/brk_computer/src/mining/rewards/import.rs +++ b/crates/brk_computer/src/mining/rewards/import.rs @@ -6,8 +6,8 @@ use super::Vecs; use crate::{ indexes, internal::{ - ComputedFromHeight, FiatFromHeight, RollingWindows, ValueFromHeightFull, - ValueFromHeightCumulativeSum, + Bp16ToFloat, Bp16ToPercent, FiatFromHeight, PercentFromHeight, PercentRollingWindows, + ValueFromHeightFull, ValueFromHeightCumulativeSum, }, }; @@ -27,33 +27,33 @@ impl Vecs { version, indexes, )?, - fee_dominance: ComputedFromHeight::forced_import( + fee_dominance: PercentFromHeight::forced_import::( db, "fee_dominance", version, indexes, )?, - fee_dominance_rolling: RollingWindows::forced_import( + fee_dominance_rolling: PercentRollingWindows::forced_import::( db, "fee_dominance", version, indexes, )?, - subsidy_dominance: ComputedFromHeight::forced_import( + subsidy_dominance: PercentFromHeight::forced_import::( db, "subsidy_dominance", version, indexes, )?, - subsidy_dominance_rolling: RollingWindows::forced_import( + subsidy_dominance_rolling: PercentRollingWindows::forced_import::( db, "subsidy_dominance", version, indexes, )?, - subsidy_usd_1y_sma: FiatFromHeight::forced_import( + subsidy_sma_1y: FiatFromHeight::forced_import( db, - "subsidy_usd_1y_sma", + "subsidy_sma_1y", version, indexes, )?, diff --git a/crates/brk_computer/src/mining/rewards/vecs.rs b/crates/brk_computer/src/mining/rewards/vecs.rs index c47aabf9a..dcaa76b8f 100644 --- a/crates/brk_computer/src/mining/rewards/vecs.rs +++ b/crates/brk_computer/src/mining/rewards/vecs.rs @@ -1,10 +1,10 @@ use brk_traversable::Traversable; -use brk_types::{Cents, StoredF32}; +use brk_types::{BasisPoints16, Cents}; use vecdb::{Rw, StorageMode}; use crate::internal::{ - ComputedFromHeight, FiatFromHeight, RollingWindows, ValueFromHeightFull, - ValueFromHeightCumulativeSum, + FiatFromHeight, PercentFromHeight, PercentRollingWindows, RollingWindows, + ValueFromHeightFull, ValueFromHeightCumulativeSum, }; /// Coinbase/subsidy/rewards metrics @@ -14,9 +14,9 @@ pub struct Vecs { pub subsidy: ValueFromHeightFull, pub fees: ValueFromHeightFull, pub unclaimed_rewards: ValueFromHeightCumulativeSum, - pub fee_dominance: ComputedFromHeight, - pub fee_dominance_rolling: RollingWindows, - pub subsidy_dominance: ComputedFromHeight, - pub subsidy_dominance_rolling: RollingWindows, - pub subsidy_usd_1y_sma: FiatFromHeight, + pub fee_dominance: PercentFromHeight, + pub fee_dominance_rolling: PercentRollingWindows, + pub subsidy_dominance: PercentFromHeight, + pub subsidy_dominance_rolling: PercentRollingWindows, + pub subsidy_sma_1y: FiatFromHeight, } diff --git a/crates/brk_computer/src/pools/vecs.rs b/crates/brk_computer/src/pools/vecs.rs index c40d683e4..816a45d5a 100644 --- a/crates/brk_computer/src/pools/vecs.rs +++ b/crates/brk_computer/src/pools/vecs.rs @@ -1,14 +1,15 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Height, PoolSlug, StoredF32, StoredU16, StoredU32}; +use brk_types::{BasisPoints16, Height, PoolSlug, StoredU32}; use vecdb::{AnyVec, BinaryTransform, Database, Exit, ReadableVec, Rw, StorageMode, VecIndex, Version}; use crate::{ blocks, indexes::{self, ComputeIndexes}, internal::{ - ComputedFromHeightCumulativeSum, ComputedFromHeight, MaskSats, PercentageU32F32, - RollingWindows, ValueFromHeightCumulativeSum, + Bp16ToFloat, Bp16ToPercent, ComputedFromHeightCumulativeSum, ComputedFromHeight, MaskSats, + PercentFromHeight, PercentRollingWindows, RatioU32Bp16, RollingWindows, + ValueFromHeightCumulativeSum, }, mining, prices, }; @@ -22,10 +23,9 @@ pub struct Vecs { pub subsidy: ValueFromHeightCumulativeSum, pub fee: ValueFromHeightCumulativeSum, pub coinbase: ValueFromHeightCumulativeSum, - pub dominance: ComputedFromHeight, - pub dominance_rolling: RollingWindows, - pub blocks_since_block: ComputedFromHeight, - pub days_since_block: ComputedFromHeight, + pub dominance: PercentFromHeight, + pub dominance_rolling: PercentRollingWindows, + pub blocks_since_last_mined: ComputedFromHeight, } impl Vecs { @@ -58,9 +58,9 @@ impl Vecs { ValueFromHeightCumulativeSum::forced_import(db, &suffix("coinbase"), version, indexes)?; let dominance = - ComputedFromHeight::forced_import(db, &suffix("dominance"), version, indexes)?; + PercentFromHeight::forced_import::(db, &suffix("dominance"), version, indexes)?; let dominance_rolling = - RollingWindows::forced_import(db, &suffix("dominance"), version, indexes)?; + PercentRollingWindows::forced_import::(db, &suffix("dominance"), version, indexes)?; Ok(Self { dominance, @@ -71,15 +71,9 @@ impl Vecs { coinbase, subsidy, fee, - blocks_since_block: ComputedFromHeight::forced_import( + blocks_since_last_mined: ComputedFromHeight::forced_import( db, - &suffix("blocks_since_block"), - version, - indexes, - )?, - days_since_block: ComputedFromHeight::forced_import( - db, - &suffix("days_since_block"), + &suffix("blocks_since_last_mined"), version, indexes, )?, @@ -126,7 +120,7 @@ impl Vecs { )?; self.dominance - .compute_binary::( + .compute_binary::( starting_indexes.height, &self.blocks_mined.cumulative.height, &blocks.count.block_count.cumulative.height, @@ -140,7 +134,7 @@ impl Vecs { .zip(self.blocks_mined_sum.as_array()) .zip(blocks.count.block_count_sum.as_array()) { - dom.compute_binary::( + dom.compute_binary::( starting_indexes.height, &mined.height, &total.height, @@ -198,19 +192,19 @@ impl Vecs { { let resume_from = self - .blocks_since_block + .blocks_since_last_mined .height .len() .min(starting_indexes.height.to_usize()); let mut prev = if resume_from > 0 { - self.blocks_since_block + self.blocks_since_last_mined .height .collect_one_at(resume_from - 1) .unwrap() } else { StoredU32::ZERO }; - self.blocks_since_block.height.compute_transform( + self.blocks_since_last_mined.height.compute_transform( starting_indexes.height, &self.blocks_mined.height, |(h, mined, ..)| { @@ -226,18 +220,6 @@ impl Vecs { )?; } - self.days_since_block.height.compute_transform( - starting_indexes.height, - &self.blocks_since_block.height, - |(h, blocks, ..)| { - ( - h, - StoredU16::from(u16::try_from(*blocks).unwrap_or(u16::MAX)), - ) - }, - exit, - )?; - Ok(()) } } diff --git a/crates/brk_computer/src/scripts/adoption.rs b/crates/brk_computer/src/scripts/adoption.rs index 43aea878d..067c9383c 100644 --- a/crates/brk_computer/src/scripts/adoption.rs +++ b/crates/brk_computer/src/scripts/adoption.rs @@ -1,16 +1,20 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{StoredF32, Version}; +use brk_types::{BasisPoints16, Version}; use vecdb::{Database, Exit, Rw, StorageMode}; -use crate::{ComputeIndexes, indexes, internal::{ComputedFromHeight, RatioU64F32}, outputs}; +use crate::{ + ComputeIndexes, indexes, + internal::{Bp16ToFloat, Bp16ToPercent, PercentFromHeight, RatioU64Bp16}, + outputs, +}; use super::count::Vecs as CountVecs; #[derive(Traversable)] pub struct Vecs { - pub taproot: ComputedFromHeight, - pub segwit: ComputedFromHeight, + pub taproot: PercentFromHeight, + pub segwit: PercentFromHeight, } impl Vecs { @@ -20,13 +24,18 @@ impl Vecs { indexes: &indexes::Vecs, ) -> Result { Ok(Self { - taproot: ComputedFromHeight::forced_import( + taproot: PercentFromHeight::forced_import::( db, "taproot_adoption", version, indexes, )?, - segwit: ComputedFromHeight::forced_import(db, "segwit_adoption", version, indexes)?, + segwit: PercentFromHeight::forced_import::( + db, + "segwit_adoption", + version, + indexes, + )?, }) } @@ -37,14 +46,14 @@ impl Vecs { starting_indexes: &ComputeIndexes, exit: &Exit, ) -> Result<()> { - self.taproot.compute_binary::<_, _, RatioU64F32>( + self.taproot.compute_binary::<_, _, RatioU64Bp16>( starting_indexes.height, &count.p2tr.height, &outputs_count.total_count.full.sum_cumulative.sum.0, exit, )?; - self.segwit.compute_binary::<_, _, RatioU64F32>( + self.segwit.compute_binary::<_, _, RatioU64Bp16>( starting_indexes.height, &count.segwit.height, &outputs_count.total_count.full.sum_cumulative.sum.0, diff --git a/crates/brk_computer/src/supply/compute.rs b/crates/brk_computer/src/supply/compute.rs index 989159f42..8ffb924c9 100644 --- a/crates/brk_computer/src/supply/compute.rs +++ b/crates/brk_computer/src/supply/compute.rs @@ -1,5 +1,4 @@ use brk_error::Result; -use brk_types::StoredF32; use vecdb::Exit; use super::Vecs; @@ -22,9 +21,9 @@ impl Vecs { self.burned .compute(scripts, mining, &blocks.count, prices, starting_indexes, exit)?; - // 2. Compute inflation rate at height level: (supply[h] - supply[1y_ago]) / supply[1y_ago] * 100 + // 2. Compute inflation rate: (supply[h] / supply[1y_ago]) - 1 let circulating_supply = &distribution.utxo_cohorts.all.metrics.supply.total.sats; - self.inflation.height.compute_rolling_percentage_change( + self.inflation_rate.bps.height.compute_rolling_ratio_change( starting_indexes.height, &blocks.count.height_1y_ago, &circulating_supply.height, @@ -35,10 +34,11 @@ impl Vecs { self.velocity .compute(blocks, transactions, distribution, starting_indexes, exit)?; - // 4. Compute cap growth rates at height level using 1y lookback + // 4. Compute cap growth rates using 1y lookback self.market_cap_growth_rate + .bps .height - .compute_rolling_percentage_change( + .compute_rolling_ratio_change( starting_indexes.height, &blocks.count.height_1y_ago, &self.market_cap.height, @@ -46,20 +46,20 @@ impl Vecs { )?; self.realized_cap_growth_rate + .bps .height - .compute_rolling_percentage_change( + .compute_rolling_ratio_change( starting_indexes.height, &blocks.count.height_1y_ago, &distribution.utxo_cohorts.all.metrics.realized.realized_cap.height, exit, )?; - // 5. Compute cap growth rate diff: market_cap_growth_rate - realized_cap_growth_rate - self.cap_growth_rate_diff.height.compute_transform2( + // 5. Compute cap growth rate diff: market - realized + self.market_minus_realized_cap_growth_rate.height.compute_subtract( starting_indexes.height, - &self.market_cap_growth_rate.height, - &self.realized_cap_growth_rate.height, - |(h, a, b, ..)| (h, StoredF32::from(*a - *b)), + &self.market_cap_growth_rate.bps.height, + &self.realized_cap_growth_rate.bps.height, exit, )?; diff --git a/crates/brk_computer/src/supply/import.rs b/crates/brk_computer/src/supply/import.rs index 50a0cd8cb..ca2f2bbfb 100644 --- a/crates/brk_computer/src/supply/import.rs +++ b/crates/brk_computer/src/supply/import.rs @@ -9,7 +9,8 @@ use super::Vecs; use crate::{ distribution, indexes, internal::{ - ComputedFromHeight, Identity, LazyFromHeight, LazyValueFromHeight, SatsToBitcoin, + Bps32ToFloat, Bps32ToPercent, ComputedFromHeight, Identity, LazyFromHeight, + LazyValueFromHeight, PercentFromHeight, SatsToBitcoin, }, }; @@ -40,8 +41,8 @@ impl Vecs { let burned = super::burned::Vecs::forced_import(&db, version, indexes)?; // Inflation rate - let inflation = - ComputedFromHeight::forced_import(&db, "inflation_rate", version, indexes)?; + let inflation_rate = + PercentFromHeight::forced_import::(&db, "inflation_rate", version, indexes)?; // Velocity let velocity = super::velocity::Vecs::forced_import(&db, version, indexes)?; @@ -54,31 +55,31 @@ impl Vecs { ); // Growth rates - let market_cap_growth_rate = ComputedFromHeight::forced_import( + let market_cap_growth_rate = PercentFromHeight::forced_import::( &db, "market_cap_growth_rate", version + Version::ONE, indexes, )?; - let realized_cap_growth_rate = ComputedFromHeight::forced_import( + let realized_cap_growth_rate = PercentFromHeight::forced_import::( &db, "realized_cap_growth_rate", version + Version::ONE, indexes, )?; - let cap_growth_rate_diff = - ComputedFromHeight::forced_import(&db, "cap_growth_rate_diff", version, indexes)?; + let market_minus_realized_cap_growth_rate = + ComputedFromHeight::forced_import(&db, "market_minus_realized_cap_growth_rate", version, indexes)?; let this = Self { db, circulating, burned, - inflation, + inflation_rate, velocity, market_cap, market_cap_growth_rate, realized_cap_growth_rate, - cap_growth_rate_diff, + market_minus_realized_cap_growth_rate, }; this.db.retain_regions( diff --git a/crates/brk_computer/src/supply/vecs.rs b/crates/brk_computer/src/supply/vecs.rs index fe405cdc3..4eec0ee01 100644 --- a/crates/brk_computer/src/supply/vecs.rs +++ b/crates/brk_computer/src/supply/vecs.rs @@ -1,10 +1,10 @@ use brk_traversable::Traversable; -use brk_types::{Dollars, StoredF32}; +use brk_types::{BasisPointsSigned32, Dollars}; use vecdb::{Database, Rw, StorageMode}; use super::{burned, velocity}; use crate::internal::{ - ComputedFromHeight, LazyFromHeight, LazyValueFromHeight, + ComputedFromHeight, LazyFromHeight, LazyValueFromHeight, PercentFromHeight, }; #[derive(Traversable)] @@ -14,10 +14,10 @@ pub struct Vecs { pub circulating: LazyValueFromHeight, pub burned: burned::Vecs, - pub inflation: ComputedFromHeight, + pub inflation_rate: PercentFromHeight, pub velocity: velocity::Vecs, pub market_cap: LazyFromHeight, - pub market_cap_growth_rate: ComputedFromHeight, - pub realized_cap_growth_rate: ComputedFromHeight, - pub cap_growth_rate_diff: ComputedFromHeight, + pub market_cap_growth_rate: PercentFromHeight, + pub realized_cap_growth_rate: PercentFromHeight, + pub market_minus_realized_cap_growth_rate: ComputedFromHeight, } diff --git a/crates/brk_computer/src/supply/velocity/import.rs b/crates/brk_computer/src/supply/velocity/import.rs index c8d1e78d2..5ccf0b6a1 100644 --- a/crates/brk_computer/src/supply/velocity/import.rs +++ b/crates/brk_computer/src/supply/velocity/import.rs @@ -12,8 +12,8 @@ impl Vecs { indexes: &indexes::Vecs, ) -> Result { Ok(Self { - btc: ComputedFromHeight::forced_import(db, "btc_velocity", version, indexes)?, - usd: ComputedFromHeight::forced_import(db, "usd_velocity", version, indexes)?, + btc: ComputedFromHeight::forced_import(db, "velocity_btc", version, indexes)?, + usd: ComputedFromHeight::forced_import(db, "velocity_usd", version, indexes)?, }) } } diff --git a/crates/brk_computer/src/traits/mod.rs b/crates/brk_computer/src/traits/mod.rs index 4ec19b20e..467b366e9 100644 --- a/crates/brk_computer/src/traits/mod.rs +++ b/crates/brk_computer/src/traits/mod.rs @@ -1,5 +1,5 @@ use brk_error::Result; -use brk_types::StoredF32; +use brk_types::{BasisPointsSigned16, StoredF32}; use vecdb::{ AnyStoredVec, AnyVec, EagerVec, Exit, PcoVec, PcoVecValue, ReadableVec, VecIndex, VecValue, WritableVec, @@ -243,3 +243,38 @@ where Ok(()) } } + +impl ComputeDrawdown for EagerVec> +where + I: VecIndex, +{ + fn compute_drawdown( + &mut self, + max_from: I, + current: &impl ReadableVec, + ath: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> + where + C: VecValue, + A: VecValue, + f64: From + From, + { + self.compute_transform2( + max_from, + current, + ath, + |(i, current, ath, _)| { + let ath_f64 = f64::from(ath); + let drawdown = if ath_f64 == 0.0 { + BasisPointsSigned16::default() + } else { + BasisPointsSigned16::from((f64::from(current) - ath_f64) / ath_f64) + }; + (i, drawdown) + }, + exit, + )?; + Ok(()) + } +} diff --git a/crates/brk_types/src/basis_points_16.rs b/crates/brk_types/src/basis_points_16.rs index c715064a4..097e44c42 100644 --- a/crates/brk_types/src/basis_points_16.rs +++ b/crates/brk_types/src/basis_points_16.rs @@ -1,4 +1,4 @@ -use std::ops::{Add, AddAssign, Div}; +use std::ops::{Add, AddAssign, Div, Sub}; use derive_more::Deref; use schemars::JsonSchema; @@ -8,8 +8,8 @@ use vecdb::{CheckedSub, Formattable, Pco}; use super::StoredF32; /// Unsigned basis points stored as u16. -/// 1 bp = 0.01%. Range: 0–655.35%. -/// Use for bounded 0–100% values (dominance, adoption, RSI, etc.). +/// 1 bp = 0.0001. Range: 0–6.5535. +/// Use for bounded 0–1 ratios (dominance, adoption, liveliness, etc.). #[derive( Debug, Deref, @@ -30,6 +30,7 @@ pub struct BasisPoints16(u16); impl BasisPoints16 { pub const ZERO: Self = Self(0); + pub const ONE: Self = Self(10000); #[inline] pub const fn new(value: u16) -> Self { @@ -41,16 +42,17 @@ impl BasisPoints16 { self.0 } - /// Convert to f32: divide by 100. + /// Convert to f32: divide by 10000. #[inline] pub fn to_f32(self) -> f32 { - self.0 as f32 / 100.0 + self.0 as f32 / 10000.0 } } impl From for BasisPoints16 { #[inline] fn from(value: usize) -> Self { + debug_assert!(value <= u16::MAX as usize, "usize out of BasisPoints16 range: {value}"); Self(value as u16) } } @@ -69,20 +71,20 @@ impl From for u16 { } } -/// Convert from float: multiply by 100 and round. -/// Input is in "display" form (e.g., 45.23 for 45.23%). +/// Convert from float: multiply by 10000 and round. +/// Input is in ratio form (e.g., 0.4523 for 45.23%). impl From for BasisPoints16 { #[inline] fn from(value: f64) -> Self { - debug_assert!(value >= 0.0 && value <= u16::MAX as f64 / 100.0, "f64 out of BasisPoints16 range: {value}"); - Self((value * 100.0).round() as u16) + debug_assert!(value >= 0.0 && value <= u16::MAX as f64 / 10000.0, "f64 out of BasisPoints16 range: {value}"); + Self((value * 10000.0).round() as u16) } } impl From for f64 { #[inline] fn from(value: BasisPoints16) -> Self { - value.0 as f64 / 100.0 + value.0 as f64 / 10000.0 } } @@ -101,6 +103,14 @@ impl Add for BasisPoints16 { } } +impl Sub for BasisPoints16 { + type Output = Self; + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } +} + impl AddAssign for BasisPoints16 { #[inline] fn add_assign(&mut self, rhs: Self) { @@ -112,6 +122,7 @@ 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) } } diff --git a/crates/brk_types/src/basis_points_32.rs b/crates/brk_types/src/basis_points_32.rs index f1d377438..49036707e 100644 --- a/crates/brk_types/src/basis_points_32.rs +++ b/crates/brk_types/src/basis_points_32.rs @@ -8,7 +8,7 @@ use vecdb::{CheckedSub, Formattable, Pco}; use super::StoredF32; /// Unsigned basis points stored as u32. -/// 1 bp = 0.01%. Range: 0–42,949,672.95%. +/// 1 bp = 0.0001. Range: 0–429,496.7295. /// Use for unbounded unsigned ratios (MVRV, NVT, SOPR, etc.). #[derive( Debug, @@ -41,16 +41,17 @@ impl BasisPoints32 { self.0 } - /// Convert to f32: divide by 100. + /// Convert to f32: divide by 10000. #[inline] pub fn to_f32(self) -> f32 { - self.0 as f32 / 100.0 + self.0 as f32 / 10000.0 } } impl From for BasisPoints32 { #[inline] fn from(value: usize) -> Self { + debug_assert!(value <= u32::MAX as usize, "usize out of BasisPoints32 range: {value}"); Self(value as u32) } } @@ -69,23 +70,23 @@ impl From for u32 { } } -/// Convert from float: multiply by 100 and round. -/// Input is in "display" form (e.g., 450.23 for 450.23%). +/// Convert from float: multiply by 10000 and round. +/// Input is in ratio form (e.g., 2.5 for MVRV of 2.5). impl From for BasisPoints32 { #[inline] fn from(value: f64) -> Self { debug_assert!( - value >= 0.0 && value <= u32::MAX as f64 / 100.0, + value >= 0.0 && value <= u32::MAX as f64 / 10000.0, "f64 out of BasisPoints32 range: {value}" ); - Self((value * 100.0).round() as u32) + Self((value * 10000.0).round() as u32) } } impl From for f64 { #[inline] fn from(value: BasisPoints32) -> Self { - value.0 as f64 / 100.0 + value.0 as f64 / 10000.0 } } @@ -115,6 +116,7 @@ impl Div for BasisPoints32 { type Output = Self; #[inline] fn div(self, rhs: usize) -> Self::Output { + debug_assert!(rhs <= u32::MAX as usize, "divisor out of u32 range: {rhs}"); Self(self.0 / rhs as u32) } } diff --git a/crates/brk_types/src/basis_points_signed_16.rs b/crates/brk_types/src/basis_points_signed_16.rs index 8c2af427b..0b878f853 100644 --- a/crates/brk_types/src/basis_points_signed_16.rs +++ b/crates/brk_types/src/basis_points_signed_16.rs @@ -8,8 +8,8 @@ use vecdb::{CheckedSub, Formattable, Pco}; use super::StoredF32; /// Signed basis points stored as i16. -/// 1 bp = 0.01%. Range: -327.67% to +327.67%. -/// Use for signed bounded values (NUPL, net PnL ratios, etc.). +/// 1 bp = 0.0001. Range: -3.2767 to +3.2767. +/// Use for signed bounded ratios (NUPL, net PnL ratios, etc.). #[derive( Debug, Deref, @@ -46,16 +46,17 @@ impl BasisPointsSigned16 { self.0 < 0 } - /// Convert to f32: divide by 100. + /// Convert to f32: divide by 10000. #[inline] pub fn to_f32(self) -> f32 { - self.0 as f32 / 100.0 + self.0 as f32 / 10000.0 } } impl From for BasisPointsSigned16 { #[inline] fn from(value: usize) -> Self { + debug_assert!(value <= i16::MAX as usize, "usize out of BasisPointsSigned16 range: {value}"); Self(value as i16) } } @@ -74,23 +75,23 @@ impl From for i16 { } } -/// Convert from float: multiply by 100 and round. -/// Input is in "display" form (e.g., -45.23 for -45.23%). +/// Convert from float: multiply by 10000 and round. +/// Input is in ratio form (e.g., -0.4523 for -45.23%). impl From for BasisPointsSigned16 { #[inline] fn from(value: f64) -> Self { debug_assert!( - value >= i16::MIN as f64 / 100.0 && value <= i16::MAX as f64 / 100.0, + value >= i16::MIN as f64 / 10000.0 && value <= i16::MAX as f64 / 10000.0, "f64 out of BasisPointsSigned16 range: {value}" ); - Self((value * 100.0).round() as i16) + Self((value * 10000.0).round() as i16) } } impl From for f64 { #[inline] fn from(value: BasisPointsSigned16) -> Self { - value.0 as f64 / 100.0 + value.0 as f64 / 10000.0 } } @@ -135,6 +136,7 @@ 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) } } diff --git a/crates/brk_types/src/basis_points_signed_32.rs b/crates/brk_types/src/basis_points_signed_32.rs index cf703c725..28a78bcd1 100644 --- a/crates/brk_types/src/basis_points_signed_32.rs +++ b/crates/brk_types/src/basis_points_signed_32.rs @@ -8,7 +8,7 @@ use vecdb::{CheckedSub, Formattable, Pco}; use super::StoredF32; /// Signed basis points stored as i32. -/// 1 bp = 0.01%. Range: -21,474,836.47% to +21,474,836.47%. +/// 1 bp = 0.0001. Range: -214,748.3647 to +214,748.3647. /// Use for unbounded signed values (returns, growth rates, volatility, z-scores, etc.). #[derive( Debug, @@ -46,16 +46,17 @@ impl BasisPointsSigned32 { self.0 < 0 } - /// Convert to f32: divide by 100. + /// Convert to f32: divide by 10000. #[inline] pub fn to_f32(self) -> f32 { - self.0 as f32 / 100.0 + self.0 as f32 / 10000.0 } } impl From for BasisPointsSigned32 { #[inline] fn from(value: usize) -> Self { + debug_assert!(value <= i32::MAX as usize, "usize out of BasisPointsSigned32 range: {value}"); Self(value as i32) } } @@ -74,23 +75,32 @@ impl From for i32 { } } -/// Convert from float: multiply by 100 and round. -/// Input is in "display" form (e.g., -5000.23 for -5000.23%). +/// Convert from float: multiply by 10000 and round. +/// Input is in ratio form (e.g., 50.0 for +5000%). impl From for BasisPointsSigned32 { #[inline] fn from(value: f64) -> Self { debug_assert!( - value >= i32::MIN as f64 / 100.0 && value <= i32::MAX as f64 / 100.0, + value >= i32::MIN as f64 / 10000.0 && value <= i32::MAX as f64 / 10000.0, "f64 out of BasisPointsSigned32 range: {value}" ); - Self((value * 100.0).round() as i32) + Self((value * 10000.0).round() as i32) + } +} + +/// Convert from f32 ratio form: multiply by 10000 and round. +/// Input is in ratio form (e.g., 0.5 for +50% → 5000 bps). +impl From for BasisPointsSigned32 { + #[inline] + fn from(value: f32) -> Self { + Self((value * 10000.0).round() as i32) } } impl From for f64 { #[inline] fn from(value: BasisPointsSigned32) -> Self { - value.0 as f64 / 100.0 + value.0 as f64 / 10000.0 } } @@ -135,6 +145,7 @@ impl Div for BasisPointsSigned32 { type Output = Self; #[inline] fn div(self, rhs: usize) -> Self::Output { + debug_assert!(rhs <= i32::MAX as usize, "divisor out of i32 range: {rhs}"); Self(self.0 / rhs as i32) } }