diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index 830009184..2c858a70c 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -958,8 +958,8 @@ pub struct CapCapitulationGrossInvestorLossLowerMvrvNegNetPeakProfitRealizedSell pub neg_realized_loss: MetricPattern1, pub net_pnl_change_1m_rel_to_market_cap: BpsPercentRatioPattern, pub net_pnl_change_1m_rel_to_realized_cap: BpsPercentRatioPattern, - pub net_pnl_delta: ChangeRatePattern, - pub net_pnl_delta_extended: _24hChangeRatePattern, + pub net_pnl_delta: ChangeRatePattern3, + pub net_pnl_delta_extended: _24hChangeRatePattern, pub net_realized_pnl: MetricPattern1, pub net_realized_pnl_cumulative: MetricPattern1, pub net_realized_pnl_rel_to_realized_cap: BpsPercentRatioPattern, @@ -974,8 +974,8 @@ pub struct CapCapitulationGrossInvestorLossLowerMvrvNegNetPeakProfitRealizedSell pub profit_value_destroyed_sum: _1m1w1y24hPattern, pub realized_cap: MetricPattern1, pub realized_cap_cents: MetricPattern1, - pub realized_cap_delta: ChangeRatePattern, - pub realized_cap_delta_extended: _24hChangeRatePattern, + pub realized_cap_delta: ChangeRatePattern3, + pub realized_cap_delta_extended: _24hChangeRatePattern, pub realized_cap_rel_to_own_market_cap: BpsPercentRatioPattern, pub realized_loss: CumulativeHeightPattern, pub realized_loss_rel_to_realized_cap: BpsPercentRatioPattern, @@ -1029,7 +1029,7 @@ impl CapCapitulationGrossInvestorLossLowerMvrvNegNetPeakProfitRealizedSellSentSo neg_realized_loss: MetricPattern1::new(client.clone(), _m(&acc, "neg_realized_loss")), net_pnl_change_1m_rel_to_market_cap: BpsPercentRatioPattern::new(client.clone(), _m(&acc, "net_pnl_change_1m_rel_to_market_cap")), net_pnl_change_1m_rel_to_realized_cap: BpsPercentRatioPattern::new(client.clone(), _m(&acc, "net_pnl_change_1m_rel_to_realized_cap")), - net_pnl_delta: ChangeRatePattern::new(client.clone(), _m(&acc, "net_pnl_delta")), + net_pnl_delta: ChangeRatePattern3::new(client.clone(), _m(&acc, "net_pnl_delta")), net_pnl_delta_extended: _24hChangeRatePattern::new(client.clone(), _m(&acc, "net_pnl_delta")), net_realized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_realized_pnl")), net_realized_pnl_cumulative: MetricPattern1::new(client.clone(), _m(&acc, "net_realized_pnl_cumulative")), @@ -1045,7 +1045,7 @@ impl CapCapitulationGrossInvestorLossLowerMvrvNegNetPeakProfitRealizedSellSentSo profit_value_destroyed_sum: _1m1w1y24hPattern::new(client.clone(), _m(&acc, "profit_value_destroyed")), realized_cap: MetricPattern1::new(client.clone(), _m(&acc, "realized_cap")), realized_cap_cents: MetricPattern1::new(client.clone(), _m(&acc, "realized_cap_cents")), - realized_cap_delta: ChangeRatePattern::new(client.clone(), _m(&acc, "realized_cap_delta")), + realized_cap_delta: ChangeRatePattern3::new(client.clone(), _m(&acc, "realized_cap_delta")), realized_cap_delta_extended: _24hChangeRatePattern::new(client.clone(), _m(&acc, "realized_cap_delta")), realized_cap_rel_to_own_market_cap: BpsPercentRatioPattern::new(client.clone(), _m(&acc, "realized_cap_rel_to_own_market_cap")), realized_loss: CumulativeHeightPattern::new(client.clone(), _m(&acc, "realized_loss")), @@ -1157,7 +1157,7 @@ pub struct MvrvNegNetRealizedSentSoprValuePattern { pub net_realized_pnl_sum: _24hPattern, pub realized_cap: MetricPattern1, pub realized_cap_cents: MetricPattern1, - pub realized_cap_delta: ChangeRatePattern, + pub realized_cap_delta: ChangeRatePattern3, pub realized_loss: CumulativeHeightPattern, pub realized_loss_sum: _24hPattern, pub realized_price: CentsSatsUsdPattern, @@ -1185,7 +1185,7 @@ impl MvrvNegNetRealizedSentSoprValuePattern { net_realized_pnl_sum: _24hPattern::new(client.clone(), _m(&acc, "net_realized_pnl_24h")), realized_cap: MetricPattern1::new(client.clone(), _m(&acc, "realized_cap")), realized_cap_cents: MetricPattern1::new(client.clone(), _m(&acc, "realized_cap_cents")), - realized_cap_delta: ChangeRatePattern::new(client.clone(), _m(&acc, "realized_cap_delta")), + realized_cap_delta: ChangeRatePattern3::new(client.clone(), _m(&acc, "realized_cap_delta")), realized_loss: CumulativeHeightPattern::new(client.clone(), _m(&acc, "realized_loss")), realized_loss_sum: _24hPattern::new(client.clone(), _m(&acc, "realized_loss_24h")), realized_price: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "realized_price")), @@ -1263,7 +1263,7 @@ pub struct MvrvNegNetRealizedSoprValuePattern { pub net_realized_pnl_sum: _24hPattern, pub realized_cap: MetricPattern1, pub realized_cap_cents: MetricPattern1, - pub realized_cap_delta: ChangeRatePattern, + pub realized_cap_delta: ChangeRatePattern3, pub realized_loss: CumulativeHeightPattern, pub realized_loss_sum: _24hPattern, pub realized_price: CentsSatsUsdPattern, @@ -1287,7 +1287,7 @@ impl MvrvNegNetRealizedSoprValuePattern { net_realized_pnl_sum: _24hPattern::new(client.clone(), _m(&acc, "net_realized_pnl_24h")), realized_cap: MetricPattern1::new(client.clone(), _m(&acc, "realized_cap")), realized_cap_cents: MetricPattern1::new(client.clone(), _m(&acc, "realized_cap_cents")), - realized_cap_delta: ChangeRatePattern::new(client.clone(), _m(&acc, "realized_cap_delta")), + realized_cap_delta: ChangeRatePattern3::new(client.clone(), _m(&acc, "realized_cap_delta")), realized_loss: CumulativeHeightPattern::new(client.clone(), _m(&acc, "realized_loss")), realized_loss_sum: _24hPattern::new(client.clone(), _m(&acc, "realized_loss_24h")), realized_price: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "realized_price")), @@ -2039,6 +2039,28 @@ impl NegNetSupplyUnrealizedPattern { } } +/// Pattern struct for repeated tree structure. +pub struct _24hChangeRatePattern { + pub _24h: BpsCentsPercentRatioUsdPattern, + pub change_1w: CentsUsdPattern, + pub change_1y: CentsUsdPattern, + pub rate_1w: BpsPercentRatioPattern, + pub rate_1y: BpsPercentRatioPattern, +} + +impl _24hChangeRatePattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + _24h: BpsCentsPercentRatioUsdPattern::new(client.clone(), acc.clone()), + change_1w: CentsUsdPattern::new(client.clone(), _m(&acc, "change_1w")), + change_1y: CentsUsdPattern::new(client.clone(), _m(&acc, "change_1y")), + rate_1w: BpsPercentRatioPattern::new(client.clone(), _m(&acc, "rate_1w")), + rate_1y: BpsPercentRatioPattern::new(client.clone(), _m(&acc, "rate_1y")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct ActivityOutputsRealizedSupplyUnrealizedPattern { pub activity: SentPattern, @@ -2083,6 +2105,28 @@ impl BaseBtcCentsSatsUsdPattern { } } +/// Pattern struct for repeated tree structure. +pub struct BpsCentsPercentRatioUsdPattern { + pub bps: MetricPattern1, + pub cents: MetricPattern1, + pub percent: MetricPattern1, + pub ratio: MetricPattern1, + pub usd: MetricPattern1, +} + +impl BpsCentsPercentRatioUsdPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + bps: MetricPattern1::new(client.clone(), _m(&acc, "rate_24h_bps")), + cents: MetricPattern1::new(client.clone(), _m(&acc, "change_24h_cents")), + percent: MetricPattern1::new(client.clone(), _m(&acc, "rate_24h")), + ratio: MetricPattern1::new(client.clone(), _m(&acc, "rate_24h_ratio")), + usd: MetricPattern1::new(client.clone(), _m(&acc, "change_24h_usd")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct EmaHistogramLineSignalPattern { pub ema_fast: MetricPattern1, @@ -2128,7 +2172,7 @@ impl _1m1w1y24hHeightPattern { } /// Pattern struct for repeated tree structure. -pub struct _24hChangeRatePattern { +pub struct _24hChangeRatePattern2 { pub _24h: BaseBpsPercentRatioPattern, pub change_1w: MetricPattern1, pub change_1y: MetricPattern1, @@ -2136,7 +2180,7 @@ pub struct _24hChangeRatePattern { pub rate_1y: BpsPercentRatioPattern, } -impl _24hChangeRatePattern { +impl _24hChangeRatePattern2 { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { @@ -2211,7 +2255,7 @@ impl _1m1w1y24hPattern5 { /// Pattern struct for repeated tree structure. pub struct BaseBpsPercentRatioPattern { - pub base: MetricPattern1, + pub base: MetricPattern1, pub bps: MetricPattern1, pub percent: MetricPattern1, pub ratio: MetricPattern1, @@ -2609,6 +2653,22 @@ impl ChangeRatePattern2 { } } +/// Pattern struct for repeated tree structure. +pub struct ChangeRatePattern3 { + pub change_1m: CentsUsdPattern, + pub rate_1m: BpsPercentRatioPattern, +} + +impl ChangeRatePattern3 { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + change_1m: CentsUsdPattern::new(client.clone(), _m(&acc, "change_1m")), + rate_1m: BpsPercentRatioPattern::new(client.clone(), _m(&acc, "rate_1m")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct HeightSumPattern { pub height: MetricPattern18, @@ -2625,6 +2685,22 @@ impl HeightSumPattern { } } +/// Pattern struct for repeated tree structure. +pub struct RealizedSupplyPattern { + pub realized_cap: MetricPattern1, + pub supply: MetricPattern1, +} + +impl RealizedSupplyPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + realized_cap: MetricPattern1::new(client.clone(), _m(&acc, "realized_cap")), + supply: MetricPattern1::new(client.clone(), _m(&acc, "supply")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct SdSmaPattern { pub sd: MetricPattern1, @@ -5568,6 +5644,7 @@ pub struct MetricsTree_Distribution_UtxoCohorts { pub amount_range: MetricsTree_Distribution_UtxoCohorts_AmountRange, pub lt_amount: MetricsTree_Distribution_UtxoCohorts_LtAmount, pub type_: MetricsTree_Distribution_UtxoCohorts_Type, + pub profitability: MetricsTree_Distribution_UtxoCohorts_Profitability, } impl MetricsTree_Distribution_UtxoCohorts { @@ -5585,6 +5662,7 @@ impl MetricsTree_Distribution_UtxoCohorts { amount_range: MetricsTree_Distribution_UtxoCohorts_AmountRange::new(client.clone(), format!("{base_path}_amount_range")), lt_amount: MetricsTree_Distribution_UtxoCohorts_LtAmount::new(client.clone(), format!("{base_path}_lt_amount")), type_: MetricsTree_Distribution_UtxoCohorts_Type::new(client.clone(), format!("{base_path}_type_")), + profitability: MetricsTree_Distribution_UtxoCohorts_Profitability::new(client.clone(), format!("{base_path}_profitability")), } } } @@ -5601,8 +5679,8 @@ pub struct MetricsTree_Distribution_UtxoCohorts_All { pub relative: MetricsTree_Distribution_UtxoCohorts_All_Relative, pub dormancy: MetricPattern1, pub velocity: MetricPattern1, - pub supply_delta_extended: _24hChangeRatePattern, - pub utxo_count_delta_extended: _24hChangeRatePattern, + pub supply_delta_extended: _24hChangeRatePattern2, + pub utxo_count_delta_extended: _24hChangeRatePattern2, } impl MetricsTree_Distribution_UtxoCohorts_All { @@ -5618,8 +5696,8 @@ impl MetricsTree_Distribution_UtxoCohorts_All { relative: MetricsTree_Distribution_UtxoCohorts_All_Relative::new(client.clone(), format!("{base_path}_relative")), dormancy: MetricPattern1::new(client.clone(), "dormancy".to_string()), velocity: MetricPattern1::new(client.clone(), "velocity".to_string()), - supply_delta_extended: _24hChangeRatePattern::new(client.clone(), "supply_delta".to_string()), - utxo_count_delta_extended: _24hChangeRatePattern::new(client.clone(), "utxo_count_delta".to_string()), + supply_delta_extended: _24hChangeRatePattern2::new(client.clone(), "supply_delta".to_string()), + utxo_count_delta_extended: _24hChangeRatePattern2::new(client.clone(), "utxo_count_delta".to_string()), } } } @@ -5685,8 +5763,8 @@ pub struct MetricsTree_Distribution_UtxoCohorts_Sth { pub relative: NetNuplSupplyUnrealizedPattern2, pub dormancy: MetricPattern1, pub velocity: MetricPattern1, - pub supply_delta_extended: _24hChangeRatePattern, - pub utxo_count_delta_extended: _24hChangeRatePattern, + pub supply_delta_extended: _24hChangeRatePattern2, + pub utxo_count_delta_extended: _24hChangeRatePattern2, pub adjusted_value_created: MetricPattern1, pub adjusted_value_destroyed: MetricPattern1, pub adjusted_value_created_sum: _1m1w1y24hPattern, @@ -5706,8 +5784,8 @@ impl MetricsTree_Distribution_UtxoCohorts_Sth { relative: NetNuplSupplyUnrealizedPattern2::new(client.clone(), "sth".to_string()), dormancy: MetricPattern1::new(client.clone(), "sth_dormancy".to_string()), velocity: MetricPattern1::new(client.clone(), "sth_velocity".to_string()), - supply_delta_extended: _24hChangeRatePattern::new(client.clone(), "sth_supply_delta".to_string()), - utxo_count_delta_extended: _24hChangeRatePattern::new(client.clone(), "sth_utxo_count_delta".to_string()), + supply_delta_extended: _24hChangeRatePattern2::new(client.clone(), "sth_supply_delta".to_string()), + utxo_count_delta_extended: _24hChangeRatePattern2::new(client.clone(), "sth_utxo_count_delta".to_string()), adjusted_value_created: MetricPattern1::new(client.clone(), "sth_adjusted_value_created".to_string()), adjusted_value_destroyed: MetricPattern1::new(client.clone(), "sth_adjusted_value_destroyed".to_string()), adjusted_value_created_sum: _1m1w1y24hPattern::new(client.clone(), "sth_adjusted_value_created".to_string()), @@ -5728,8 +5806,8 @@ pub struct MetricsTree_Distribution_UtxoCohorts_Lth { pub relative: NetNuplSupplyUnrealizedPattern2, pub dormancy: MetricPattern1, pub velocity: MetricPattern1, - pub supply_delta_extended: _24hChangeRatePattern, - pub utxo_count_delta_extended: _24hChangeRatePattern, + pub supply_delta_extended: _24hChangeRatePattern2, + pub utxo_count_delta_extended: _24hChangeRatePattern2, } impl MetricsTree_Distribution_UtxoCohorts_Lth { @@ -5744,8 +5822,8 @@ impl MetricsTree_Distribution_UtxoCohorts_Lth { relative: NetNuplSupplyUnrealizedPattern2::new(client.clone(), "lth".to_string()), dormancy: MetricPattern1::new(client.clone(), "lth_dormancy".to_string()), velocity: MetricPattern1::new(client.clone(), "lth_velocity".to_string()), - supply_delta_extended: _24hChangeRatePattern::new(client.clone(), "lth_supply_delta".to_string()), - utxo_count_delta_extended: _24hChangeRatePattern::new(client.clone(), "lth_utxo_count_delta".to_string()), + supply_delta_extended: _24hChangeRatePattern2::new(client.clone(), "lth_supply_delta".to_string()), + utxo_count_delta_extended: _24hChangeRatePattern2::new(client.clone(), "lth_utxo_count_delta".to_string()), } } } @@ -6113,6 +6191,156 @@ impl MetricsTree_Distribution_UtxoCohorts_Type { } } +/// Metrics tree node. +pub struct MetricsTree_Distribution_UtxoCohorts_Profitability { + pub range: MetricsTree_Distribution_UtxoCohorts_Profitability_Range, + pub profit: MetricsTree_Distribution_UtxoCohorts_Profitability_Profit, + pub loss: MetricsTree_Distribution_UtxoCohorts_Profitability_Loss, +} + +impl MetricsTree_Distribution_UtxoCohorts_Profitability { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + range: MetricsTree_Distribution_UtxoCohorts_Profitability_Range::new(client.clone(), format!("{base_path}_range")), + profit: MetricsTree_Distribution_UtxoCohorts_Profitability_Profit::new(client.clone(), format!("{base_path}_profit")), + loss: MetricsTree_Distribution_UtxoCohorts_Profitability_Loss::new(client.clone(), format!("{base_path}_loss")), + } + } +} + +/// Metrics tree node. +pub struct MetricsTree_Distribution_UtxoCohorts_Profitability_Range { + pub profit_over_1000: RealizedSupplyPattern, + pub profit_500_to_1000: RealizedSupplyPattern, + pub profit_300_to_500: RealizedSupplyPattern, + pub profit_200_to_300: RealizedSupplyPattern, + pub profit_100_to_200: RealizedSupplyPattern, + pub profit_90_to_100: RealizedSupplyPattern, + pub profit_80_to_90: RealizedSupplyPattern, + pub profit_70_to_80: RealizedSupplyPattern, + pub profit_60_to_70: RealizedSupplyPattern, + pub profit_50_to_60: RealizedSupplyPattern, + pub profit_40_to_50: RealizedSupplyPattern, + pub profit_30_to_40: RealizedSupplyPattern, + pub profit_20_to_30: RealizedSupplyPattern, + pub profit_10_to_20: RealizedSupplyPattern, + pub profit_0_to_10: RealizedSupplyPattern, + pub loss_0_to_10: RealizedSupplyPattern, + pub loss_10_to_20: RealizedSupplyPattern, + pub loss_20_to_30: RealizedSupplyPattern, + pub loss_30_to_40: RealizedSupplyPattern, + pub loss_40_to_50: RealizedSupplyPattern, + pub loss_50_to_60: RealizedSupplyPattern, + pub loss_60_to_70: RealizedSupplyPattern, + pub loss_70_to_80: RealizedSupplyPattern, + pub loss_80_to_90: RealizedSupplyPattern, + pub loss_90_to_100: RealizedSupplyPattern, +} + +impl MetricsTree_Distribution_UtxoCohorts_Profitability_Range { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + profit_over_1000: RealizedSupplyPattern::new(client.clone(), "profit_over_1000pct".to_string()), + profit_500_to_1000: RealizedSupplyPattern::new(client.clone(), "profit_500_to_1000pct".to_string()), + profit_300_to_500: RealizedSupplyPattern::new(client.clone(), "profit_300_to_500pct".to_string()), + profit_200_to_300: RealizedSupplyPattern::new(client.clone(), "profit_200_to_300pct".to_string()), + profit_100_to_200: RealizedSupplyPattern::new(client.clone(), "profit_100_to_200pct".to_string()), + profit_90_to_100: RealizedSupplyPattern::new(client.clone(), "profit_90_to_100pct".to_string()), + profit_80_to_90: RealizedSupplyPattern::new(client.clone(), "profit_80_to_90pct".to_string()), + profit_70_to_80: RealizedSupplyPattern::new(client.clone(), "profit_70_to_80pct".to_string()), + profit_60_to_70: RealizedSupplyPattern::new(client.clone(), "profit_60_to_70pct".to_string()), + profit_50_to_60: RealizedSupplyPattern::new(client.clone(), "profit_50_to_60pct".to_string()), + profit_40_to_50: RealizedSupplyPattern::new(client.clone(), "profit_40_to_50pct".to_string()), + profit_30_to_40: RealizedSupplyPattern::new(client.clone(), "profit_30_to_40pct".to_string()), + profit_20_to_30: RealizedSupplyPattern::new(client.clone(), "profit_20_to_30pct".to_string()), + profit_10_to_20: RealizedSupplyPattern::new(client.clone(), "profit_10_to_20pct".to_string()), + profit_0_to_10: RealizedSupplyPattern::new(client.clone(), "profit_0_to_10pct".to_string()), + loss_0_to_10: RealizedSupplyPattern::new(client.clone(), "loss_0_to_10pct".to_string()), + loss_10_to_20: RealizedSupplyPattern::new(client.clone(), "loss_10_to_20pct".to_string()), + loss_20_to_30: RealizedSupplyPattern::new(client.clone(), "loss_20_to_30pct".to_string()), + loss_30_to_40: RealizedSupplyPattern::new(client.clone(), "loss_30_to_40pct".to_string()), + loss_40_to_50: RealizedSupplyPattern::new(client.clone(), "loss_40_to_50pct".to_string()), + loss_50_to_60: RealizedSupplyPattern::new(client.clone(), "loss_50_to_60pct".to_string()), + loss_60_to_70: RealizedSupplyPattern::new(client.clone(), "loss_60_to_70pct".to_string()), + loss_70_to_80: RealizedSupplyPattern::new(client.clone(), "loss_70_to_80pct".to_string()), + loss_80_to_90: RealizedSupplyPattern::new(client.clone(), "loss_80_to_90pct".to_string()), + loss_90_to_100: RealizedSupplyPattern::new(client.clone(), "loss_90_to_100pct".to_string()), + } + } +} + +/// Metrics tree node. +pub struct MetricsTree_Distribution_UtxoCohorts_Profitability_Profit { + pub breakeven: RealizedSupplyPattern, + pub _10pct: RealizedSupplyPattern, + pub _20pct: RealizedSupplyPattern, + pub _30pct: RealizedSupplyPattern, + pub _40pct: RealizedSupplyPattern, + pub _50pct: RealizedSupplyPattern, + pub _60pct: RealizedSupplyPattern, + pub _70pct: RealizedSupplyPattern, + pub _80pct: RealizedSupplyPattern, + pub _90pct: RealizedSupplyPattern, + pub _100pct: RealizedSupplyPattern, + pub _200pct: RealizedSupplyPattern, + pub _300pct: RealizedSupplyPattern, + pub _500pct: RealizedSupplyPattern, + pub _1000pct: RealizedSupplyPattern, +} + +impl MetricsTree_Distribution_UtxoCohorts_Profitability_Profit { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + breakeven: RealizedSupplyPattern::new(client.clone(), "profit_ge_breakeven".to_string()), + _10pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_10pct".to_string()), + _20pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_20pct".to_string()), + _30pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_30pct".to_string()), + _40pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_40pct".to_string()), + _50pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_50pct".to_string()), + _60pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_60pct".to_string()), + _70pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_70pct".to_string()), + _80pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_80pct".to_string()), + _90pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_90pct".to_string()), + _100pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_100pct".to_string()), + _200pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_200pct".to_string()), + _300pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_300pct".to_string()), + _500pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_500pct".to_string()), + _1000pct: RealizedSupplyPattern::new(client.clone(), "profit_ge_1000pct".to_string()), + } + } +} + +/// Metrics tree node. +pub struct MetricsTree_Distribution_UtxoCohorts_Profitability_Loss { + pub breakeven: RealizedSupplyPattern, + pub _10pct: RealizedSupplyPattern, + pub _20pct: RealizedSupplyPattern, + pub _30pct: RealizedSupplyPattern, + pub _40pct: RealizedSupplyPattern, + pub _50pct: RealizedSupplyPattern, + pub _60pct: RealizedSupplyPattern, + pub _70pct: RealizedSupplyPattern, + pub _80pct: RealizedSupplyPattern, + pub _90pct: RealizedSupplyPattern, +} + +impl MetricsTree_Distribution_UtxoCohorts_Profitability_Loss { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + breakeven: RealizedSupplyPattern::new(client.clone(), "loss_ge_breakeven".to_string()), + _10pct: RealizedSupplyPattern::new(client.clone(), "loss_ge_10pct".to_string()), + _20pct: RealizedSupplyPattern::new(client.clone(), "loss_ge_20pct".to_string()), + _30pct: RealizedSupplyPattern::new(client.clone(), "loss_ge_30pct".to_string()), + _40pct: RealizedSupplyPattern::new(client.clone(), "loss_ge_40pct".to_string()), + _50pct: RealizedSupplyPattern::new(client.clone(), "loss_ge_50pct".to_string()), + _60pct: RealizedSupplyPattern::new(client.clone(), "loss_ge_60pct".to_string()), + _70pct: RealizedSupplyPattern::new(client.clone(), "loss_ge_70pct".to_string()), + _80pct: RealizedSupplyPattern::new(client.clone(), "loss_ge_80pct".to_string()), + _90pct: RealizedSupplyPattern::new(client.clone(), "loss_ge_90pct".to_string()), + } + } +} + /// Metrics tree node. pub struct MetricsTree_Distribution_AddressCohorts { pub ge_amount: MetricsTree_Distribution_AddressCohorts_GeAmount, @@ -6338,9 +6566,8 @@ pub struct MetricsTree_Supply { pub burned: MetricsTree_Supply_Burned, pub inflation_rate: BpsPercentRatioPattern, pub velocity: MetricsTree_Supply_Velocity, - pub market_cap: MetricPattern1, - pub market_cap_growth_rate: _1m1w1y24hPattern2, - pub realized_cap_growth_rate: _1m1w1y24hPattern2, + pub market_cap: CentsUsdPattern, + pub market_cap_delta: MetricsTree_Supply_MarketCapDelta, pub market_minus_realized_cap_growth_rate: _1m1w1y24hPattern, } @@ -6351,9 +6578,8 @@ impl MetricsTree_Supply { burned: MetricsTree_Supply_Burned::new(client.clone(), format!("{base_path}_burned")), inflation_rate: BpsPercentRatioPattern::new(client.clone(), "inflation_rate".to_string()), velocity: MetricsTree_Supply_Velocity::new(client.clone(), format!("{base_path}_velocity")), - market_cap: MetricPattern1::new(client.clone(), "market_cap".to_string()), - market_cap_growth_rate: _1m1w1y24hPattern2::new(client.clone(), "market_cap_growth_rate".to_string()), - realized_cap_growth_rate: _1m1w1y24hPattern2::new(client.clone(), "realized_cap_growth_rate".to_string()), + market_cap: CentsUsdPattern::new(client.clone(), "market_cap".to_string()), + market_cap_delta: MetricsTree_Supply_MarketCapDelta::new(client.clone(), format!("{base_path}_market_cap_delta")), market_minus_realized_cap_growth_rate: _1m1w1y24hPattern::new(client.clone(), "market_minus_realized_cap_growth_rate".to_string()), } } @@ -6389,6 +6615,27 @@ impl MetricsTree_Supply_Velocity { } } +/// Metrics tree node. +pub struct MetricsTree_Supply_MarketCapDelta { + pub change_24h: CentsUsdPattern, + pub change_1w: CentsUsdPattern, + pub change_1m: CentsUsdPattern, + pub change_1y: CentsUsdPattern, + pub rate: _1m1w1y24hPattern2, +} + +impl MetricsTree_Supply_MarketCapDelta { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + change_24h: CentsUsdPattern::new(client.clone(), "market_cap_delta_change_24h".to_string()), + change_1w: CentsUsdPattern::new(client.clone(), "market_cap_delta_change_1w".to_string()), + change_1m: CentsUsdPattern::new(client.clone(), "market_cap_delta_change_1m".to_string()), + change_1y: CentsUsdPattern::new(client.clone(), "market_cap_delta_change_1y".to_string()), + rate: _1m1w1y24hPattern2::new(client.clone(), "market_cap_delta_rate".to_string()), + } + } +} + /// Main BRK client with metrics tree and API methods. pub struct BrkClient { base: Arc, diff --git a/crates/brk_cohort/src/by_loss.rs b/crates/brk_cohort/src/by_loss.rs new file mode 100644 index 000000000..79315d5d4 --- /dev/null +++ b/crates/brk_cohort/src/by_loss.rs @@ -0,0 +1,152 @@ +use brk_traversable::Traversable; +use rayon::prelude::*; +use serde::Serialize; + +use super::CohortName; + +/// "At least X% loss" threshold names (10 thresholds). +pub const LOSS_NAMES: ByLoss = ByLoss { + breakeven: CohortName::new("loss_ge_breakeven", "<0%", "In Loss (Below Breakeven)"), + _10pct: CohortName::new("loss_ge_10pct", "≥10%L", "10%+ Loss"), + _20pct: CohortName::new("loss_ge_20pct", "≥20%L", "20%+ Loss"), + _30pct: CohortName::new("loss_ge_30pct", "≥30%L", "30%+ Loss"), + _40pct: CohortName::new("loss_ge_40pct", "≥40%L", "40%+ Loss"), + _50pct: CohortName::new("loss_ge_50pct", "≥50%L", "50%+ Loss"), + _60pct: CohortName::new("loss_ge_60pct", "≥60%L", "60%+ Loss"), + _70pct: CohortName::new("loss_ge_70pct", "≥70%L", "70%+ Loss"), + _80pct: CohortName::new("loss_ge_80pct", "≥80%L", "80%+ Loss"), + _90pct: CohortName::new("loss_ge_90pct", "≥90%L", "90%+ Loss"), +}; + +/// Number of loss thresholds. +pub const LOSS_COUNT: usize = 10; + +impl ByLoss { + pub const fn names() -> &'static Self { + &LOSS_NAMES + } +} + +/// 10 "at least X% loss" aggregate thresholds. +/// +/// Each is a suffix sum over the profitability ranges, from most loss-making up. +#[derive(Default, Clone, Traversable, Serialize)] +pub struct ByLoss { + pub breakeven: T, + pub _10pct: T, + pub _20pct: T, + pub _30pct: T, + pub _40pct: T, + pub _50pct: T, + pub _60pct: T, + pub _70pct: T, + pub _80pct: T, + pub _90pct: T, +} + +impl ByLoss { + pub fn new(mut create: F) -> Self + where + F: FnMut(&'static str) -> T, + { + let n = &LOSS_NAMES; + Self { + breakeven: create(n.breakeven.id), + _10pct: create(n._10pct.id), + _20pct: create(n._20pct.id), + _30pct: create(n._30pct.id), + _40pct: create(n._40pct.id), + _50pct: create(n._50pct.id), + _60pct: create(n._60pct.id), + _70pct: create(n._70pct.id), + _80pct: create(n._80pct.id), + _90pct: create(n._90pct.id), + } + } + + pub fn try_new(mut create: F) -> Result + where + F: FnMut(&'static str) -> Result, + { + let n = &LOSS_NAMES; + Ok(Self { + breakeven: create(n.breakeven.id)?, + _10pct: create(n._10pct.id)?, + _20pct: create(n._20pct.id)?, + _30pct: create(n._30pct.id)?, + _40pct: create(n._40pct.id)?, + _50pct: create(n._50pct.id)?, + _60pct: create(n._60pct.id)?, + _70pct: create(n._70pct.id)?, + _80pct: create(n._80pct.id)?, + _90pct: create(n._90pct.id)?, + }) + } + + pub fn iter(&self) -> impl Iterator { + [ + &self.breakeven, + &self._10pct, + &self._20pct, + &self._30pct, + &self._40pct, + &self._50pct, + &self._60pct, + &self._70pct, + &self._80pct, + &self._90pct, + ] + .into_iter() + } + + pub fn iter_mut(&mut self) -> impl Iterator { + [ + &mut self.breakeven, + &mut self._10pct, + &mut self._20pct, + &mut self._30pct, + &mut self._40pct, + &mut self._50pct, + &mut self._60pct, + &mut self._70pct, + &mut self._80pct, + &mut self._90pct, + ] + .into_iter() + } + + pub fn par_iter_mut(&mut self) -> impl ParallelIterator + where + T: Send + Sync, + { + [ + &mut self.breakeven, + &mut self._10pct, + &mut self._20pct, + &mut self._30pct, + &mut self._40pct, + &mut self._50pct, + &mut self._60pct, + &mut self._70pct, + &mut self._80pct, + &mut self._90pct, + ] + .into_par_iter() + } + + /// Access as array for indexed accumulation. + pub fn as_array_mut(&mut self) -> [&mut T; LOSS_COUNT] { + [ + &mut self.breakeven, + &mut self._10pct, + &mut self._20pct, + &mut self._30pct, + &mut self._40pct, + &mut self._50pct, + &mut self._60pct, + &mut self._70pct, + &mut self._80pct, + &mut self._90pct, + ] + } +} diff --git a/crates/brk_cohort/src/by_profit.rs b/crates/brk_cohort/src/by_profit.rs new file mode 100644 index 000000000..2ea09429e --- /dev/null +++ b/crates/brk_cohort/src/by_profit.rs @@ -0,0 +1,192 @@ +use brk_traversable::Traversable; +use rayon::prelude::*; +use serde::Serialize; + +use super::CohortName; + +/// "At least X% profit" threshold names (15 thresholds). +pub const PROFIT_NAMES: ByProfit = ByProfit { + breakeven: CohortName::new("profit_ge_breakeven", "≥0%", "In Profit (Breakeven+)"), + _10pct: CohortName::new("profit_ge_10pct", "≥10%", "10%+ Profit"), + _20pct: CohortName::new("profit_ge_20pct", "≥20%", "20%+ Profit"), + _30pct: CohortName::new("profit_ge_30pct", "≥30%", "30%+ Profit"), + _40pct: CohortName::new("profit_ge_40pct", "≥40%", "40%+ Profit"), + _50pct: CohortName::new("profit_ge_50pct", "≥50%", "50%+ Profit"), + _60pct: CohortName::new("profit_ge_60pct", "≥60%", "60%+ Profit"), + _70pct: CohortName::new("profit_ge_70pct", "≥70%", "70%+ Profit"), + _80pct: CohortName::new("profit_ge_80pct", "≥80%", "80%+ Profit"), + _90pct: CohortName::new("profit_ge_90pct", "≥90%", "90%+ Profit"), + _100pct: CohortName::new("profit_ge_100pct", "≥100%", "100%+ Profit"), + _200pct: CohortName::new("profit_ge_200pct", "≥200%", "200%+ Profit"), + _300pct: CohortName::new("profit_ge_300pct", "≥300%", "300%+ Profit"), + _500pct: CohortName::new("profit_ge_500pct", "≥500%", "500%+ Profit"), + _1000pct: CohortName::new("profit_ge_1000pct", "≥1000%", "1000%+ Profit"), +}; + +/// Number of profit thresholds. +pub const PROFIT_COUNT: usize = 15; + +impl ByProfit { + pub const fn names() -> &'static Self { + &PROFIT_NAMES + } +} + +/// 15 "at least X% profit" aggregate thresholds. +/// +/// Each is a prefix sum over the profitability ranges, from most profitable down. +#[derive(Default, Clone, Traversable, Serialize)] +pub struct ByProfit { + pub breakeven: T, + pub _10pct: T, + pub _20pct: T, + pub _30pct: T, + pub _40pct: T, + pub _50pct: T, + pub _60pct: T, + pub _70pct: T, + pub _80pct: T, + pub _90pct: T, + pub _100pct: T, + pub _200pct: T, + pub _300pct: T, + pub _500pct: T, + pub _1000pct: T, +} + +impl ByProfit { + pub fn new(mut create: F) -> Self + where + F: FnMut(&'static str) -> T, + { + let n = &PROFIT_NAMES; + Self { + breakeven: create(n.breakeven.id), + _10pct: create(n._10pct.id), + _20pct: create(n._20pct.id), + _30pct: create(n._30pct.id), + _40pct: create(n._40pct.id), + _50pct: create(n._50pct.id), + _60pct: create(n._60pct.id), + _70pct: create(n._70pct.id), + _80pct: create(n._80pct.id), + _90pct: create(n._90pct.id), + _100pct: create(n._100pct.id), + _200pct: create(n._200pct.id), + _300pct: create(n._300pct.id), + _500pct: create(n._500pct.id), + _1000pct: create(n._1000pct.id), + } + } + + pub fn try_new(mut create: F) -> Result + where + F: FnMut(&'static str) -> Result, + { + let n = &PROFIT_NAMES; + Ok(Self { + breakeven: create(n.breakeven.id)?, + _10pct: create(n._10pct.id)?, + _20pct: create(n._20pct.id)?, + _30pct: create(n._30pct.id)?, + _40pct: create(n._40pct.id)?, + _50pct: create(n._50pct.id)?, + _60pct: create(n._60pct.id)?, + _70pct: create(n._70pct.id)?, + _80pct: create(n._80pct.id)?, + _90pct: create(n._90pct.id)?, + _100pct: create(n._100pct.id)?, + _200pct: create(n._200pct.id)?, + _300pct: create(n._300pct.id)?, + _500pct: create(n._500pct.id)?, + _1000pct: create(n._1000pct.id)?, + }) + } + + pub fn iter(&self) -> impl Iterator { + [ + &self.breakeven, + &self._10pct, + &self._20pct, + &self._30pct, + &self._40pct, + &self._50pct, + &self._60pct, + &self._70pct, + &self._80pct, + &self._90pct, + &self._100pct, + &self._200pct, + &self._300pct, + &self._500pct, + &self._1000pct, + ] + .into_iter() + } + + pub fn iter_mut(&mut self) -> impl Iterator { + [ + &mut self.breakeven, + &mut self._10pct, + &mut self._20pct, + &mut self._30pct, + &mut self._40pct, + &mut self._50pct, + &mut self._60pct, + &mut self._70pct, + &mut self._80pct, + &mut self._90pct, + &mut self._100pct, + &mut self._200pct, + &mut self._300pct, + &mut self._500pct, + &mut self._1000pct, + ] + .into_iter() + } + + pub fn par_iter_mut(&mut self) -> impl ParallelIterator + where + T: Send + Sync, + { + [ + &mut self.breakeven, + &mut self._10pct, + &mut self._20pct, + &mut self._30pct, + &mut self._40pct, + &mut self._50pct, + &mut self._60pct, + &mut self._70pct, + &mut self._80pct, + &mut self._90pct, + &mut self._100pct, + &mut self._200pct, + &mut self._300pct, + &mut self._500pct, + &mut self._1000pct, + ] + .into_par_iter() + } + + /// Access as array for indexed accumulation. + pub fn as_array_mut(&mut self) -> [&mut T; PROFIT_COUNT] { + [ + &mut self.breakeven, + &mut self._10pct, + &mut self._20pct, + &mut self._30pct, + &mut self._40pct, + &mut self._50pct, + &mut self._60pct, + &mut self._70pct, + &mut self._80pct, + &mut self._90pct, + &mut self._100pct, + &mut self._200pct, + &mut self._300pct, + &mut self._500pct, + &mut self._1000pct, + ] + } +} diff --git a/crates/brk_cohort/src/by_profitability_range.rs b/crates/brk_cohort/src/by_profitability_range.rs new file mode 100644 index 000000000..b69e5db98 --- /dev/null +++ b/crates/brk_cohort/src/by_profitability_range.rs @@ -0,0 +1,381 @@ +use brk_traversable::Traversable; +use brk_types::Cents; +use rayon::prelude::*; +use serde::Serialize; + +use super::CohortName; + +/// Number of profitability range boundaries (24 boundaries → 25 buckets). +pub const PROFITABILITY_BOUNDARY_COUNT: usize = 24; + +/// Compute 24 boundary prices from spot price for profitability bucketing. +/// +/// Boundaries are returned in ascending price order (most profitable first → least profitable last). +/// Bucket assignment: prices ascending in k-way merge means we start at the most-profitable bucket +/// (lowest cost basis = highest profit) and advance the cursor as price crosses each boundary. +/// +/// For P% profit: boundary = spot × 100 / (100 + P) +/// For L% loss: boundary = spot × 100 / (100 - L) +/// +/// Returns boundaries in ascending order: +/// [spot/11, spot/6, spot/4, spot/3, spot/2, spot×100/190, spot×100/180, ..., spot×100/10] +pub fn compute_profitability_boundaries(spot: Cents) -> [Cents; PROFITABILITY_BOUNDARY_COUNT] { + let s = spot.as_u128(); + // Divisors in ascending boundary order (ascending price): + // profit_over_1000: price < spot/11 → boundary at spot*100/1100 = spot/11 + // profit_500_to_1000: spot/11 ≤ p < spot/6 → boundary at spot*100/600 = spot/6 + // profit_300_to_500: spot/6 ≤ p < spot/4 → boundary at spot*100/400 = spot/4 + // profit_200_to_300: spot/4 ≤ p < spot/3 → boundary at spot*100/300 = spot/3 + // profit_100_to_200: spot/3 ≤ p < spot/2 → boundary at spot*100/200 = spot/2 + // profit_90_to_100: spot/2 ≤ p < spot*100/190 → boundary at spot*100/190 + // profit_80_to_90: → boundary at spot*100/180 + // profit_70_to_80: → boundary at spot*100/170 + // profit_60_to_70: → boundary at spot*100/160 + // profit_50_to_60: → boundary at spot*100/150 + // profit_40_to_50: → boundary at spot*100/140 + // profit_30_to_40: → boundary at spot*100/130 + // profit_20_to_30: → boundary at spot*100/120 + // profit_10_to_20: → boundary at spot*100/110 + // profit_0_to_10: → boundary at spot (= spot*100/100) + // loss_0_to_10: spot ≤ p < spot*100/90 → boundary at spot*100/90 + // loss_10_to_20: → boundary at spot*100/80 + // loss_20_to_30: → boundary at spot*100/70 + // loss_30_to_40: → boundary at spot*100/60 + // loss_40_to_50: → boundary at spot*100/50 = spot*2 + // loss_50_to_60: → boundary at spot*100/40 = spot*5/2 + // loss_60_to_70: → boundary at spot*100/30 = spot*10/3 + // loss_70_to_80: → boundary at spot*100/20 = spot*5 + // loss_80_to_90: → boundary at spot*100/10 = spot*10 + // loss_90_to_100: spot*10 ≤ p (no upper boundary) + let divisors: [u128; PROFITABILITY_BOUNDARY_COUNT] = [ + 1100, // >1000% profit upper bound (spot/11) + 600, // 500-1000% profit upper bound (spot/6) + 400, // 300-500% profit upper bound (spot/4) + 300, // 200-300% profit upper bound (spot/3) + 200, // 100-200% profit upper bound (spot/2) + 190, // 90-100% profit upper bound + 180, // 80-90% profit upper bound + 170, // 70-80% profit upper bound + 160, // 60-70% profit upper bound + 150, // 50-60% profit upper bound + 140, // 40-50% profit upper bound + 130, // 30-40% profit upper bound + 120, // 20-30% profit upper bound + 110, // 10-20% profit upper bound + 100, // 0-10% profit upper bound (= spot) + 90, // 0-10% loss upper bound + 80, // 10-20% loss upper bound + 70, // 20-30% loss upper bound + 60, // 30-40% loss upper bound + 50, // 40-50% loss upper bound + 40, // 50-60% loss upper bound + 30, // 60-70% loss upper bound + 20, // 70-80% loss upper bound + 10, // 80-90% loss upper bound + ]; + + let mut boundaries = [Cents::ZERO; PROFITABILITY_BOUNDARY_COUNT]; + for (i, &d) in divisors.iter().enumerate() { + boundaries[i] = Cents::from(s * 100 / d); + } + boundaries +} + +/// Profitability range names (25 ranges, from most profitable to most in loss) +pub const PROFITABILITY_RANGE_NAMES: ByProfitabilityRange = ByProfitabilityRange { + profit_over_1000: CohortName::new("profit_over_1000pct", ">1000%", "Over 1000% Profit"), + profit_500_to_1000: CohortName::new("profit_500_to_1000pct", "500-1000%", "500-1000% Profit"), + profit_300_to_500: CohortName::new("profit_300_to_500pct", "300-500%", "300-500% Profit"), + profit_200_to_300: CohortName::new("profit_200_to_300pct", "200-300%", "200-300% Profit"), + profit_100_to_200: CohortName::new("profit_100_to_200pct", "100-200%", "100-200% Profit"), + profit_90_to_100: CohortName::new("profit_90_to_100pct", "90-100%", "90-100% Profit"), + profit_80_to_90: CohortName::new("profit_80_to_90pct", "80-90%", "80-90% Profit"), + profit_70_to_80: CohortName::new("profit_70_to_80pct", "70-80%", "70-80% Profit"), + profit_60_to_70: CohortName::new("profit_60_to_70pct", "60-70%", "60-70% Profit"), + profit_50_to_60: CohortName::new("profit_50_to_60pct", "50-60%", "50-60% Profit"), + profit_40_to_50: CohortName::new("profit_40_to_50pct", "40-50%", "40-50% Profit"), + profit_30_to_40: CohortName::new("profit_30_to_40pct", "30-40%", "30-40% Profit"), + profit_20_to_30: CohortName::new("profit_20_to_30pct", "20-30%", "20-30% Profit"), + profit_10_to_20: CohortName::new("profit_10_to_20pct", "10-20%", "10-20% Profit"), + profit_0_to_10: CohortName::new("profit_0_to_10pct", "0-10%", "0-10% Profit"), + loss_0_to_10: CohortName::new("loss_0_to_10pct", "0-10%L", "0-10% Loss"), + loss_10_to_20: CohortName::new("loss_10_to_20pct", "10-20%L", "10-20% Loss"), + loss_20_to_30: CohortName::new("loss_20_to_30pct", "20-30%L", "20-30% Loss"), + loss_30_to_40: CohortName::new("loss_30_to_40pct", "30-40%L", "30-40% Loss"), + loss_40_to_50: CohortName::new("loss_40_to_50pct", "40-50%L", "40-50% Loss"), + loss_50_to_60: CohortName::new("loss_50_to_60pct", "50-60%L", "50-60% Loss"), + loss_60_to_70: CohortName::new("loss_60_to_70pct", "60-70%L", "60-70% Loss"), + loss_70_to_80: CohortName::new("loss_70_to_80pct", "70-80%L", "70-80% Loss"), + loss_80_to_90: CohortName::new("loss_80_to_90pct", "80-90%L", "80-90% Loss"), + loss_90_to_100: CohortName::new("loss_90_to_100pct", "90-100%L", "90-100% Loss"), +}; + +impl ByProfitabilityRange { + pub const fn names() -> &'static Self { + &PROFITABILITY_RANGE_NAMES + } +} + +/// 25 profitability range buckets ordered from most profitable to most in loss. +/// +/// During the k-way merge (ascending price order), the cursor starts at bucket 0 +/// (profit_over_1000, lowest cost basis) and advances as price crosses each boundary. +#[derive(Default, Clone, Traversable, Serialize)] +pub struct ByProfitabilityRange { + pub profit_over_1000: T, + pub profit_500_to_1000: T, + pub profit_300_to_500: T, + pub profit_200_to_300: T, + pub profit_100_to_200: T, + pub profit_90_to_100: T, + pub profit_80_to_90: T, + pub profit_70_to_80: T, + pub profit_60_to_70: T, + pub profit_50_to_60: T, + pub profit_40_to_50: T, + pub profit_30_to_40: T, + pub profit_20_to_30: T, + pub profit_10_to_20: T, + pub profit_0_to_10: T, + pub loss_0_to_10: T, + pub loss_10_to_20: T, + pub loss_20_to_30: T, + pub loss_30_to_40: T, + pub loss_40_to_50: T, + pub loss_50_to_60: T, + pub loss_60_to_70: T, + pub loss_70_to_80: T, + pub loss_80_to_90: T, + pub loss_90_to_100: T, +} + +/// Number of profitability range buckets. +pub const PROFITABILITY_RANGE_COUNT: usize = 25; + +impl ByProfitabilityRange { + pub fn new(mut create: F) -> Self + where + F: FnMut(&'static str) -> T, + { + let n = &PROFITABILITY_RANGE_NAMES; + Self { + profit_over_1000: create(n.profit_over_1000.id), + profit_500_to_1000: create(n.profit_500_to_1000.id), + profit_300_to_500: create(n.profit_300_to_500.id), + profit_200_to_300: create(n.profit_200_to_300.id), + profit_100_to_200: create(n.profit_100_to_200.id), + profit_90_to_100: create(n.profit_90_to_100.id), + profit_80_to_90: create(n.profit_80_to_90.id), + profit_70_to_80: create(n.profit_70_to_80.id), + profit_60_to_70: create(n.profit_60_to_70.id), + profit_50_to_60: create(n.profit_50_to_60.id), + profit_40_to_50: create(n.profit_40_to_50.id), + profit_30_to_40: create(n.profit_30_to_40.id), + profit_20_to_30: create(n.profit_20_to_30.id), + profit_10_to_20: create(n.profit_10_to_20.id), + profit_0_to_10: create(n.profit_0_to_10.id), + loss_0_to_10: create(n.loss_0_to_10.id), + loss_10_to_20: create(n.loss_10_to_20.id), + loss_20_to_30: create(n.loss_20_to_30.id), + loss_30_to_40: create(n.loss_30_to_40.id), + loss_40_to_50: create(n.loss_40_to_50.id), + loss_50_to_60: create(n.loss_50_to_60.id), + loss_60_to_70: create(n.loss_60_to_70.id), + loss_70_to_80: create(n.loss_70_to_80.id), + loss_80_to_90: create(n.loss_80_to_90.id), + loss_90_to_100: create(n.loss_90_to_100.id), + } + } + + pub fn try_new(mut create: F) -> Result + where + F: FnMut(&'static str) -> Result, + { + let n = &PROFITABILITY_RANGE_NAMES; + Ok(Self { + profit_over_1000: create(n.profit_over_1000.id)?, + profit_500_to_1000: create(n.profit_500_to_1000.id)?, + profit_300_to_500: create(n.profit_300_to_500.id)?, + profit_200_to_300: create(n.profit_200_to_300.id)?, + profit_100_to_200: create(n.profit_100_to_200.id)?, + profit_90_to_100: create(n.profit_90_to_100.id)?, + profit_80_to_90: create(n.profit_80_to_90.id)?, + profit_70_to_80: create(n.profit_70_to_80.id)?, + profit_60_to_70: create(n.profit_60_to_70.id)?, + profit_50_to_60: create(n.profit_50_to_60.id)?, + profit_40_to_50: create(n.profit_40_to_50.id)?, + profit_30_to_40: create(n.profit_30_to_40.id)?, + profit_20_to_30: create(n.profit_20_to_30.id)?, + profit_10_to_20: create(n.profit_10_to_20.id)?, + profit_0_to_10: create(n.profit_0_to_10.id)?, + loss_0_to_10: create(n.loss_0_to_10.id)?, + loss_10_to_20: create(n.loss_10_to_20.id)?, + loss_20_to_30: create(n.loss_20_to_30.id)?, + loss_30_to_40: create(n.loss_30_to_40.id)?, + loss_40_to_50: create(n.loss_40_to_50.id)?, + loss_50_to_60: create(n.loss_50_to_60.id)?, + loss_60_to_70: create(n.loss_60_to_70.id)?, + loss_70_to_80: create(n.loss_70_to_80.id)?, + loss_80_to_90: create(n.loss_80_to_90.id)?, + loss_90_to_100: create(n.loss_90_to_100.id)?, + }) + } + + pub fn iter(&self) -> impl Iterator { + [ + &self.profit_over_1000, + &self.profit_500_to_1000, + &self.profit_300_to_500, + &self.profit_200_to_300, + &self.profit_100_to_200, + &self.profit_90_to_100, + &self.profit_80_to_90, + &self.profit_70_to_80, + &self.profit_60_to_70, + &self.profit_50_to_60, + &self.profit_40_to_50, + &self.profit_30_to_40, + &self.profit_20_to_30, + &self.profit_10_to_20, + &self.profit_0_to_10, + &self.loss_0_to_10, + &self.loss_10_to_20, + &self.loss_20_to_30, + &self.loss_30_to_40, + &self.loss_40_to_50, + &self.loss_50_to_60, + &self.loss_60_to_70, + &self.loss_70_to_80, + &self.loss_80_to_90, + &self.loss_90_to_100, + ] + .into_iter() + } + + pub fn iter_mut(&mut self) -> impl Iterator { + [ + &mut self.profit_over_1000, + &mut self.profit_500_to_1000, + &mut self.profit_300_to_500, + &mut self.profit_200_to_300, + &mut self.profit_100_to_200, + &mut self.profit_90_to_100, + &mut self.profit_80_to_90, + &mut self.profit_70_to_80, + &mut self.profit_60_to_70, + &mut self.profit_50_to_60, + &mut self.profit_40_to_50, + &mut self.profit_30_to_40, + &mut self.profit_20_to_30, + &mut self.profit_10_to_20, + &mut self.profit_0_to_10, + &mut self.loss_0_to_10, + &mut self.loss_10_to_20, + &mut self.loss_20_to_30, + &mut self.loss_30_to_40, + &mut self.loss_40_to_50, + &mut self.loss_50_to_60, + &mut self.loss_60_to_70, + &mut self.loss_70_to_80, + &mut self.loss_80_to_90, + &mut self.loss_90_to_100, + ] + .into_iter() + } + + pub fn par_iter_mut(&mut self) -> impl ParallelIterator + where + T: Send + Sync, + { + [ + &mut self.profit_over_1000, + &mut self.profit_500_to_1000, + &mut self.profit_300_to_500, + &mut self.profit_200_to_300, + &mut self.profit_100_to_200, + &mut self.profit_90_to_100, + &mut self.profit_80_to_90, + &mut self.profit_70_to_80, + &mut self.profit_60_to_70, + &mut self.profit_50_to_60, + &mut self.profit_40_to_50, + &mut self.profit_30_to_40, + &mut self.profit_20_to_30, + &mut self.profit_10_to_20, + &mut self.profit_0_to_10, + &mut self.loss_0_to_10, + &mut self.loss_10_to_20, + &mut self.loss_20_to_30, + &mut self.loss_30_to_40, + &mut self.loss_40_to_50, + &mut self.loss_50_to_60, + &mut self.loss_60_to_70, + &mut self.loss_70_to_80, + &mut self.loss_80_to_90, + &mut self.loss_90_to_100, + ] + .into_par_iter() + } + + /// Access as a fixed-size array of references (for indexed access during merge). + pub fn as_array(&self) -> [&T; PROFITABILITY_RANGE_COUNT] { + [ + &self.profit_over_1000, + &self.profit_500_to_1000, + &self.profit_300_to_500, + &self.profit_200_to_300, + &self.profit_100_to_200, + &self.profit_90_to_100, + &self.profit_80_to_90, + &self.profit_70_to_80, + &self.profit_60_to_70, + &self.profit_50_to_60, + &self.profit_40_to_50, + &self.profit_30_to_40, + &self.profit_20_to_30, + &self.profit_10_to_20, + &self.profit_0_to_10, + &self.loss_0_to_10, + &self.loss_10_to_20, + &self.loss_20_to_30, + &self.loss_30_to_40, + &self.loss_40_to_50, + &self.loss_50_to_60, + &self.loss_60_to_70, + &self.loss_70_to_80, + &self.loss_80_to_90, + &self.loss_90_to_100, + ] + } + + /// Access as a fixed-size array of mutable references (for indexed access during merge). + pub fn as_array_mut(&mut self) -> [&mut T; PROFITABILITY_RANGE_COUNT] { + [ + &mut self.profit_over_1000, + &mut self.profit_500_to_1000, + &mut self.profit_300_to_500, + &mut self.profit_200_to_300, + &mut self.profit_100_to_200, + &mut self.profit_90_to_100, + &mut self.profit_80_to_90, + &mut self.profit_70_to_80, + &mut self.profit_60_to_70, + &mut self.profit_50_to_60, + &mut self.profit_40_to_50, + &mut self.profit_30_to_40, + &mut self.profit_20_to_30, + &mut self.profit_10_to_20, + &mut self.profit_0_to_10, + &mut self.loss_0_to_10, + &mut self.loss_10_to_20, + &mut self.loss_20_to_30, + &mut self.loss_30_to_40, + &mut self.loss_40_to_50, + &mut self.loss_50_to_60, + &mut self.loss_60_to_70, + &mut self.loss_70_to_80, + &mut self.loss_80_to_90, + &mut self.loss_90_to_100, + ] + } +} diff --git a/crates/brk_cohort/src/lib.rs b/crates/brk_cohort/src/lib.rs index 20edff925..2d9fa1ef2 100644 --- a/crates/brk_cohort/src/lib.rs +++ b/crates/brk_cohort/src/lib.rs @@ -8,9 +8,12 @@ mod by_amount_range; mod by_any_address; mod by_epoch; mod by_ge_amount; +mod by_loss; mod by_lt_amount; mod by_max_age; mod by_min_age; +mod by_profitability_range; +mod by_profit; mod by_spendable_type; mod by_term; mod by_type; @@ -34,9 +37,12 @@ pub use by_amount_range::*; pub use by_any_address::*; pub use by_epoch::*; pub use by_ge_amount::*; +pub use by_loss::*; pub use by_lt_amount::*; pub use by_max_age::*; pub use by_min_age::*; +pub use by_profitability_range::*; +pub use by_profit::*; pub use by_spendable_type::*; pub use by_term::*; pub use by_type::*; diff --git a/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs b/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs index ef13d7c27..a735d3dda 100644 --- a/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs +++ b/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs @@ -15,7 +15,7 @@ use crate::{blocks, distribution::DynCohortVecs, indexes, prices}; use crate::distribution::metrics::{ AllCohortMetrics, BasicCohortMetrics, CohortMetricsBase, CoreCohortMetrics, ExtendedAdjustedCohortMetrics, ExtendedCohortMetrics, ImportConfig, MinimalCohortMetrics, - SupplyMetrics, + ProfitabilityMetrics, SupplyMetrics, }; use super::{percentiles::PercentileCache, vecs::UTXOCohortVecs}; @@ -39,6 +39,7 @@ pub struct UTXOCohorts { pub amount_range: ByAmountRange>>, pub lt_amount: ByLowerThanAmount>>, pub type_: BySpendableType>>, + pub profitability: ProfitabilityMetrics, #[traversable(skip)] pub(super) percentile_cache: PercentileCache, /// Cached partition_point positions for tick_tock boundary searches. @@ -141,6 +142,9 @@ impl UTXOCohorts { AllCohortMetrics::forced_import_with_supply(&all_cfg, all_supply)?, ); + // Phase 3b: Import profitability metrics (derived from "all" during k-way merge). + let profitability = ProfitabilityMetrics::forced_import(db, v, indexes)?; + // Phase 4: Import aggregate cohorts. // sth: ExtendedAdjustedCohortMetrics @@ -227,6 +231,7 @@ impl UTXOCohorts { amount_range, lt_amount, ge_amount, + profitability, percentile_cache: PercentileCache::default(), tick_tock_cached_positions: [0; 20], }) @@ -590,6 +595,7 @@ impl UTXOCohorts { for v in self.type_.iter_mut() { vecs.extend(v.metrics.collect_all_vecs_mut()); } + vecs.extend(self.profitability.collect_all_vecs_mut()); vecs.into_par_iter() } @@ -599,12 +605,13 @@ impl UTXOCohorts { .try_for_each(|v| v.write_state(height, cleanup)) } - /// Get minimum height from all separate cohorts' height-indexed vectors. + /// Get minimum height from all separate cohorts' + profitability height-indexed vectors. pub(crate) fn min_separate_stateful_height_len(&self) -> Height { self.iter_separate() .map(|v| Height::from(v.min_stateful_height_len())) .min() .unwrap_or_default() + .min(Height::from(self.profitability.min_stateful_height_len())) } /// Import state for all separate cohorts at or before given height. @@ -650,7 +657,6 @@ impl UTXOCohorts { for v in self.lt_amount.iter_mut() { v.metrics.validate_computed_versions(base_version)?; } - Ok(()) } } diff --git a/crates/brk_computer/src/distribution/cohorts/utxo/percentiles.rs b/crates/brk_computer/src/distribution/cohorts/utxo/percentiles.rs index d673c4941..ad374b501 100644 --- a/crates/brk_computer/src/distribution/cohorts/utxo/percentiles.rs +++ b/crates/brk_computer/src/distribution/cohorts/utxo/percentiles.rs @@ -1,12 +1,15 @@ use std::{cmp::Reverse, collections::BinaryHeap, fs, path::Path}; -use brk_cohort::{Filtered, TERM_NAMES}; +use brk_cohort::{ + compute_profitability_boundaries, Filtered, PROFITABILITY_BOUNDARY_COUNT, + PROFITABILITY_RANGE_COUNT, PROFIT_COUNT, TERM_NAMES, +}; use brk_error::Result; -use brk_types::{Cents, CentsCompact, CostBasisDistribution, Date, Height, Sats}; +use brk_types::{Cents, CentsCompact, CostBasisDistribution, Date, Dollars, Height, Sats}; use crate::internal::{PERCENTILES, PERCENTILES_LEN}; -use crate::distribution::metrics::{CohortMetricsBase, CostBasis}; +use crate::distribution::metrics::{CostBasis, ProfitabilityMetrics}; use super::groups::UTXOCohorts; @@ -27,45 +30,47 @@ impl CachedPercentiles { } } -/// Cached percentile results for all/sth/lth. +/// Cached percentile + profitability results for all/sth/lth. /// Avoids re-merging 21 BTreeMaps on every block. #[derive(Clone, Default)] pub(super) struct PercentileCache { all: CachedPercentiles, sth: CachedPercentiles, lth: CachedPercentiles, + profitability: [(u64, u128); PROFITABILITY_RANGE_COUNT], initialized: bool, } impl UTXOCohorts { - /// Compute and push percentiles for aggregate cohorts (all, sth, lth). + /// Compute and push percentiles + profitability for aggregate cohorts. /// /// Full K-way merge only runs at day boundaries or when the cache is empty. - /// For intermediate blocks, pushes cached percentile arrays. + /// For intermediate blocks, pushes cached values. pub(crate) fn truncate_push_aggregate_percentiles( &mut self, height: Height, + spot_price: Cents, date_opt: Option, states_path: &Path, ) -> Result<()> { if date_opt.is_some() || !self.percentile_cache.initialized { - self.merge_and_push_percentiles(height, date_opt, states_path) - } else { - self.push_cached_percentiles(height) + self.recompute_cache(spot_price, date_opt, states_path)?; } + self.push_cached(height) } - /// Full K-way merge: compute percentiles from scratch, update cache, push. - fn merge_and_push_percentiles( + /// Full K-way merge: recompute percentiles + profitability from scratch, update cache. + fn recompute_cache( &mut self, - height: Height, + spot_price: Cents, date_opt: Option, states_path: &Path, ) -> Result<()> { let collect_merged = date_opt.is_some(); + let boundaries = compute_profitability_boundaries(spot_price); let targets = { - let sth_filter = self.sth.metrics.filter().clone(); + let sth_filter = self.sth.metrics.filter.clone(); let mut totals = AllSthLth::<(u64, u128)>::default(); let maps: Vec<_> = self @@ -101,31 +106,26 @@ impl UTXOCohorts { }; let all_has_data = totals.all.0 > 0; let mut targets = totals.map(|(sats, usd)| PercTarget::new(sats, usd, cap)); + self.percentile_cache.profitability = Default::default(); if all_has_data { - merge_k_way(&maps, &mut targets, collect_merged); + merge_k_way( + &maps, + &mut targets, + &boundaries, + &mut self.percentile_cache.profitability, + collect_merged, + ); } targets }; - // Update cache + push self.percentile_cache.all = targets.all.to_cached(); self.percentile_cache.sth = targets.sth.to_cached(); self.percentile_cache.lth = targets.lth.to_cached(); self.percentile_cache.initialized = true; - self.percentile_cache - .all - .push(height, &mut self.all.metrics.cost_basis)?; - self.percentile_cache - .sth - .push(height, &mut self.sth.metrics.cost_basis)?; - self.percentile_cache - .lth - .push(height, &mut self.lth.metrics.cost_basis)?; - - // Serialize full distribution at day boundaries if let Some(date) = date_opt { write_distribution(states_path, "all", date, targets.all.merged)?; write_distribution(states_path, TERM_NAMES.short.id, date, targets.sth.merged)?; @@ -135,8 +135,8 @@ impl UTXOCohorts { Ok(()) } - /// Fast path: push cached percentile arrays. - fn push_cached_percentiles(&mut self, height: Height) -> Result<()> { + /// Push cached percentile + profitability values. + fn push_cached(&mut self, height: Height) -> Result<()> { self.percentile_cache .all .push(height, &mut self.all.metrics.cost_basis)?; @@ -146,10 +146,60 @@ impl UTXOCohorts { self.percentile_cache .lth .push(height, &mut self.lth.metrics.cost_basis)?; - Ok(()) + push_profitability( + height, + &self.percentile_cache.profitability, + &mut self.profitability, + ) } } +/// Convert raw (cents × sats) accumulator to Dollars (÷ 100 for cents→dollars, ÷ 1e8 for sats). +#[inline] +fn raw_usd_to_dollars(raw: u128) -> Dollars { + Dollars::from(raw as f64 / 1e10) +} + +/// Push profitability range + profit/loss aggregate values to vecs. +fn push_profitability( + height: Height, + buckets: &[(u64, u128); PROFITABILITY_RANGE_COUNT], + metrics: &mut ProfitabilityMetrics, +) -> Result<()> { + // Push 25 range buckets + for (i, bucket) in metrics.range.as_array_mut().into_iter().enumerate() { + let (sats, usd_raw) = buckets[i]; + bucket.truncate_push(height, Sats::from(sats), raw_usd_to_dollars(usd_raw))?; + } + + // ByProfit: forward cumulative sum over ranges[0..15], pushed in reverse. + // profit[0] (breakeven) = sum(0..=14), ..., profit[14] (_1000pct) = ranges[0] + let profit_arr = metrics.profit.as_array_mut(); + let mut cum_sats = 0u64; + let mut cum_usd = 0u128; + for i in 0..PROFIT_COUNT { + cum_sats += buckets[i].0; + cum_usd += buckets[i].1; + profit_arr[PROFIT_COUNT - 1 - i] + .truncate_push(height, Sats::from(cum_sats), raw_usd_to_dollars(cum_usd))?; + } + + // ByLoss: backward cumulative sum over ranges[15..25], pushed in reverse. + // loss[0] (breakeven) = sum(15..=24), ..., loss[9] (_90pct) = ranges[24] + let loss_arr = metrics.loss.as_array_mut(); + let loss_count = loss_arr.len(); + cum_sats = 0; + cum_usd = 0; + for i in 0..loss_count { + cum_sats += buckets[PROFITABILITY_RANGE_COUNT - 1 - i].0; + cum_usd += buckets[PROFITABILITY_RANGE_COUNT - 1 - i].1; + loss_arr[loss_count - 1 - i] + .truncate_push(height, Sats::from(cum_sats), raw_usd_to_dollars(cum_usd))?; + } + + Ok(()) +} + fn write_distribution( states_path: &Path, name: &str, @@ -166,9 +216,12 @@ fn write_distribution( } /// K-way merge via BinaryHeap over BTreeMap iterators. +/// Also accumulates profitability buckets for the "all" target using cursor approach. fn merge_k_way( maps: &[(&std::collections::BTreeMap, bool)], targets: &mut AllSthLth, + boundaries: &[Cents; PROFITABILITY_BOUNDARY_COUNT], + prof: &mut [(u64, u128); PROFITABILITY_RANGE_COUNT], collect_merged: bool, ) { let mut iters: Vec<_> = maps @@ -185,36 +238,40 @@ fn merge_k_way( } let mut current_price: Option = None; - let mut early_exit = false; + let mut boundary_idx = 0usize; while let Some(Reverse((price, ci))) = heap.pop() { let (ref mut iter, is_sth) = iters[ci]; let (_, &sats) = iter.next().unwrap(); let amount = u64::from(sats); - let usd = Cents::from(price).as_u128() * amount as u128; + let price_cents = Cents::from(price); + let usd = price_cents.as_u128() * amount as u128; if let Some(prev) = current_price && prev != price { targets.for_each_mut(|t| t.finalize_price(prev.into(), collect_merged)); - if !collect_merged && targets.all_match(|t| t.done()) { - early_exit = true; - break; - } } current_price = Some(price); targets.all.accumulate(amount, usd); targets.term_mut(is_sth).accumulate(amount, usd); + // Profitability: advance cursor past boundaries (prices are ascending) + while boundary_idx < PROFITABILITY_BOUNDARY_COUNT + && price_cents >= boundaries[boundary_idx] + { + boundary_idx += 1; + } + prof[boundary_idx].0 += amount; + prof[boundary_idx].1 += usd; + if let Some(&(&next_price, _)) = iter.peek() { heap.push(Reverse((next_price, ci))); } } - if !early_exit - && let Some(price) = current_price - { + if let Some(price) = current_price { targets.for_each_mut(|t| t.finalize_price(price.into(), collect_merged)); } } @@ -254,9 +311,6 @@ impl AllSthLth { f(&mut self.lth); } - fn all_match(&self, mut f: impl FnMut(&T) -> bool) -> bool { - f(&self.all) && f(&self.sth) && f(&self.lth) - } } struct PercTarget { @@ -362,8 +416,4 @@ impl PercTarget { self.price_usd = 0; } - fn done(&self) -> bool { - (self.total_sats == 0 || self.sat_idx >= PERCENTILES_LEN) - && (self.total_usd == 0 || self.usd_idx >= PERCENTILES_LEN) - } } diff --git a/crates/brk_computer/src/distribution/compute/block_loop.rs b/crates/brk_computer/src/distribution/compute/block_loop.rs index cfec2099d..b7460c03c 100644 --- a/crates/brk_computer/src/distribution/compute/block_loop.rs +++ b/crates/brk_computer/src/distribution/compute/block_loop.rs @@ -438,6 +438,7 @@ pub(crate) fn process_blocks( vecs.utxo_cohorts.truncate_push_aggregate_percentiles( height, + block_price, date_opt, &vecs.states_path, )?; diff --git a/crates/brk_computer/src/distribution/metrics/config.rs b/crates/brk_computer/src/distribution/metrics/config.rs index 45e4c18f8..0649f676d 100644 --- a/crates/brk_computer/src/distribution/metrics/config.rs +++ b/crates/brk_computer/src/distribution/metrics/config.rs @@ -10,10 +10,10 @@ use crate::{ indexes, internal::{ CentsType, ComputedFromHeight, ComputedFromHeightCumulative, - ComputedFromHeightCumulativeSum, ComputedFromHeightRatio, FiatFromHeight, NumericValue, - PercentFromHeight, PercentRollingWindows, Price, RollingDelta1m, RollingDeltaExcept1m, - RollingWindow24h, RollingWindows, RollingWindowsFrom1w, - ValueFromHeight, ValueFromHeightCumulative, + ComputedFromHeightCumulativeSum, ComputedFromHeightRatio, FiatFromHeight, + FiatRollingDelta1m, FiatRollingDeltaExcept1m, NumericValue, PercentFromHeight, + PercentRollingWindows, Price, RollingDelta1m, RollingDeltaExcept1m, RollingWindow24h, + RollingWindows, RollingWindowsFrom1w, ValueFromHeight, ValueFromHeightCumulative, }, }; @@ -96,6 +96,16 @@ impl ConfigImport Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes) } } +impl ConfigImport for FiatRollingDelta1m { + fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result { + Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes) + } +} +impl ConfigImport for FiatRollingDeltaExcept1m { + fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result { + Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes) + } +} impl ConfigImport for BytesVec { fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result { Ok(Self::forced_import( diff --git a/crates/brk_computer/src/distribution/metrics/mod.rs b/crates/brk_computer/src/distribution/metrics/mod.rs index 063c4ce3e..b8d40eb01 100644 --- a/crates/brk_computer/src/distribution/metrics/mod.rs +++ b/crates/brk_computer/src/distribution/metrics/mod.rs @@ -36,6 +36,7 @@ mod cohort; mod config; mod cost_basis; mod outputs; +mod profitability; mod realized; mod relative; mod supply; @@ -48,6 +49,7 @@ pub use cohort::{ }; pub use config::ImportConfig; pub use cost_basis::CostBasis; +pub use profitability::ProfitabilityMetrics; pub use outputs::OutputsMetrics; pub use realized::{ RealizedAdjusted, RealizedBase, RealizedCore, RealizedFull, RealizedLike, RealizedMinimal, diff --git a/crates/brk_computer/src/distribution/metrics/profitability.rs b/crates/brk_computer/src/distribution/metrics/profitability.rs new file mode 100644 index 000000000..4695a2976 --- /dev/null +++ b/crates/brk_computer/src/distribution/metrics/profitability.rs @@ -0,0 +1,124 @@ +use brk_cohort::{ByLoss, ByProfit, ByProfitabilityRange}; +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{Dollars, Height, Sats, Version}; +use vecdb::{AnyStoredVec, AnyVec, Database, Rw, StorageMode, WritableVec}; + +use crate::{indexes, internal::ComputedFromHeight}; + +/// Supply + realized cap for a single profitability bucket. +#[derive(Traversable)] +pub struct ProfitabilityBucket { + pub supply: ComputedFromHeight, + pub realized_cap: ComputedFromHeight, +} + +impl ProfitabilityBucket { + fn min_len(&self) -> usize { + self.supply.height.len().min(self.realized_cap.height.len()) + } +} + +impl ProfitabilityBucket { + fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + Ok(Self { + supply: ComputedFromHeight::forced_import( + db, + &format!("{name}_supply"), + version, + indexes, + )?, + realized_cap: ComputedFromHeight::forced_import( + db, + &format!("{name}_realized_cap"), + version, + indexes, + )?, + }) + } + + pub(crate) fn truncate_push( + &mut self, + height: Height, + supply: Sats, + realized_cap: Dollars, + ) -> Result<()> { + self.supply.height.truncate_push(height, supply)?; + self.realized_cap + .height + .truncate_push(height, realized_cap)?; + Ok(()) + } + + pub(crate) fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> { + vec![ + &mut self.supply.height as &mut dyn AnyStoredVec, + &mut self.realized_cap.height as &mut dyn AnyStoredVec, + ] + } +} + +/// All profitability metrics: 25 ranges + 15 profit thresholds + 10 loss thresholds. +#[derive(Traversable)] +pub struct ProfitabilityMetrics { + pub range: ByProfitabilityRange>, + pub profit: ByProfit>, + pub loss: ByLoss>, +} + +impl ProfitabilityMetrics { + pub(crate) fn min_stateful_height_len(&self) -> usize { + self.range.iter() + .chain(self.profit.iter()) + .chain(self.loss.iter()) + .map(|b| b.min_len()) + .min() + .unwrap_or(0) + } +} + +impl ProfitabilityMetrics { + pub(crate) fn forced_import( + db: &Database, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let range = ByProfitabilityRange::try_new(|name| { + ProfitabilityBucket::forced_import(db, name, version, indexes) + })?; + + let profit = ByProfit::try_new(|name| { + ProfitabilityBucket::forced_import(db, name, version, indexes) + })?; + + let loss = ByLoss::try_new(|name| { + ProfitabilityBucket::forced_import(db, name, version, indexes) + })?; + + Ok(Self { + range, + profit, + loss, + }) + } + + pub(crate) fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> { + let mut vecs = Vec::new(); + for bucket in self.range.iter_mut() { + vecs.extend(bucket.collect_all_vecs_mut()); + } + for bucket in self.profit.iter_mut() { + vecs.extend(bucket.collect_all_vecs_mut()); + } + for bucket in self.loss.iter_mut() { + vecs.extend(bucket.collect_all_vecs_mut()); + } + vecs + } + +} diff --git a/crates/brk_computer/src/distribution/metrics/realized/core.rs b/crates/brk_computer/src/distribution/metrics/realized/core.rs index b04f505a3..ccfee9217 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/core.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/core.rs @@ -10,8 +10,8 @@ use crate::{ blocks, distribution::state::RealizedOps, internal::{ - ComputedFromHeight, LazyFromHeight, NegCentsUnsignedToDollars, RatioCents64, - RollingDelta1m, RollingWindow24h, + ComputedFromHeight, FiatRollingDelta1m, LazyFromHeight, NegCentsUnsignedToDollars, + RatioCents64, RollingWindow24h, }, prices, }; @@ -27,7 +27,7 @@ pub struct RealizedCore { #[traversable(flatten)] pub minimal: RealizedMinimal, - pub realized_cap_delta: RollingDelta1m, + pub realized_cap_delta: FiatRollingDelta1m, pub neg_realized_loss: LazyFromHeight, pub net_realized_pnl: ComputedFromHeight, diff --git a/crates/brk_computer/src/distribution/metrics/realized/full.rs b/crates/brk_computer/src/distribution/metrics/realized/full.rs index c08982e47..9c2df09da 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/full.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/full.rs @@ -14,12 +14,12 @@ use crate::{ blocks, distribution::state::RealizedState, internal::{ - CentsUnsignedToDollars, ComputedFromHeight, ComputedFromHeightCumulative, FiatFromHeight, + CentsUnsignedToDollars, ComputedFromHeight, ComputedFromHeightCumulative, ComputedFromHeightRatio, ComputedFromHeightRatioPercentiles, - ComputedFromHeightRatioStdDevBands, LazyFromHeight, PercentFromHeight, - PercentRollingWindows, Price, RatioCents64, RatioCentsBp32, - RatioCentsSignedCentsBps32, RatioCentsSignedDollarsBps32, RatioDollarsBp32, - RollingDelta1m, RollingDeltaExcept1m, RollingWindows, RollingWindowsFrom1w, + ComputedFromHeightRatioStdDevBands, FiatFromHeight, FiatRollingDelta1m, + FiatRollingDeltaExcept1m, LazyFromHeight, PercentFromHeight, PercentRollingWindows, Price, + RatioCents64, RatioCentsBp32, RatioCentsSignedCentsBps32, RatioCentsSignedDollarsBps32, + RatioDollarsBp32, RollingWindows, RollingWindowsFrom1w, }, prices, }; @@ -59,12 +59,12 @@ pub struct RealizedFull { pub net_realized_pnl_cumulative: ComputedFromHeight, pub net_realized_pnl_sum_extended: RollingWindowsFrom1w, - pub net_pnl_delta: RollingDelta1m, - pub net_pnl_delta_extended: RollingDeltaExcept1m, + pub net_pnl_delta: FiatRollingDelta1m, + pub net_pnl_delta_extended: FiatRollingDeltaExcept1m, pub net_pnl_change_1m_rel_to_realized_cap: PercentFromHeight, pub net_pnl_change_1m_rel_to_market_cap: PercentFromHeight, - pub realized_cap_delta_extended: RollingDeltaExcept1m, + pub realized_cap_delta_extended: FiatRollingDeltaExcept1m, pub investor_price: Price>, pub investor_price_ratio: ComputedFromHeightRatio, @@ -468,14 +468,14 @@ impl RealizedFull { self.net_pnl_change_1m_rel_to_realized_cap .compute_binary::( starting_indexes.height, - &self.net_pnl_delta.change_1m.height, + &self.net_pnl_delta.change_1m.cents.height, &self.base.core.minimal.realized_cap_cents.height, exit, )?; self.net_pnl_change_1m_rel_to_market_cap .compute_binary::( starting_indexes.height, - &self.net_pnl_delta.change_1m.height, + &self.net_pnl_delta.change_1m.cents.height, height_to_market_cap, exit, )?; diff --git a/crates/brk_computer/src/internal/from_height/computed/delta.rs b/crates/brk_computer/src/internal/from_height/computed/delta.rs index 9f49049c2..cf21e694c 100644 --- a/crates/brk_computer/src/internal/from_height/computed/delta.rs +++ b/crates/brk_computer/src/internal/from_height/computed/delta.rs @@ -25,7 +25,7 @@ use crate::{ /// Pre-collect source data from the earliest needed offset. /// Returns (source_data, offset) for use in compute_delta_window. -fn collect_source( +pub(super) fn collect_source( source: &impl ReadableVec, skip: usize, earliest_starts: &impl ReadableVec, @@ -40,7 +40,7 @@ fn collect_source( } /// Shared computation: change = current - ago, rate = change / ago. -fn compute_delta_window( +pub(super) fn compute_delta_window( change_h: &mut EagerVec>, rate_bps_h: &mut EagerVec>, max_from: Height, diff --git a/crates/brk_computer/src/internal/from_height/computed/fiat_delta.rs b/crates/brk_computer/src/internal/from_height/computed/fiat_delta.rs new file mode 100644 index 000000000..433901596 --- /dev/null +++ b/crates/brk_computer/src/internal/from_height/computed/fiat_delta.rs @@ -0,0 +1,275 @@ +//! Fiat delta variants — same as RollingDelta* but change is FiatFromHeight +//! (stored cents + lazy USD) instead of ComputedFromHeight (stored cents only). + +use brk_error::Result; +use brk_traversable::Traversable; +use brk_types::{BasisPointsSigned32, Height, Version}; +use schemars::JsonSchema; +use vecdb::{AnyVec, Database, Exit, ReadableVec, Rw, StorageMode}; + +use crate::{ + indexes, + internal::{ + CentsType, FiatFromHeight, NumericValue, PercentFromHeight, PercentRollingWindows, + WindowStarts, + }, +}; + +use super::delta::{collect_source, compute_delta_window}; + +/// Fiat 1m-only delta: fiat change (cents + usd) + rate for the 1-month window. +#[derive(Traversable)] +pub struct FiatRollingDelta1m +where + S: NumericValue + JsonSchema, + C: CentsType, +{ + pub change_1m: FiatFromHeight, + pub rate_1m: PercentFromHeight, + _phantom: std::marker::PhantomData, +} + +impl FiatRollingDelta1m +where + S: NumericValue + JsonSchema, + C: CentsType, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + Ok(Self { + change_1m: FiatFromHeight::forced_import( + db, + &format!("{name}_change_1m"), + version, + indexes, + )?, + rate_1m: PercentFromHeight::forced_import( + db, + &format!("{name}_rate_1m"), + version, + indexes, + )?, + _phantom: std::marker::PhantomData, + }) + } + + pub(crate) fn compute( + &mut self, + max_from: Height, + height_1m_ago: &impl ReadableVec, + source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + let skip = self.change_1m.cents.height.len(); + let (source_data, offset) = collect_source(source, skip, height_1m_ago); + + compute_delta_window( + &mut self.change_1m.cents.height, + &mut self.rate_1m.bps.height, + max_from, + height_1m_ago, + &source_data, + offset, + exit, + ) + } +} + +/// Fiat extended delta: 24h + 1w + 1y windows, fiat change (cents + usd) + rate. +#[derive(Traversable)] +pub struct FiatRollingDeltaExcept1m +where + S: NumericValue + JsonSchema, + C: CentsType, +{ + #[traversable(rename = "24h")] + pub change_24h: FiatFromHeight, + pub change_1w: FiatFromHeight, + pub change_1y: FiatFromHeight, + #[traversable(rename = "24h")] + pub rate_24h: PercentFromHeight, + pub rate_1w: PercentFromHeight, + pub rate_1y: PercentFromHeight, + _phantom: std::marker::PhantomData, +} + +impl FiatRollingDeltaExcept1m +where + S: NumericValue + JsonSchema, + C: CentsType, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + Ok(Self { + change_24h: FiatFromHeight::forced_import( + db, + &format!("{name}_change_24h"), + version, + indexes, + )?, + change_1w: FiatFromHeight::forced_import( + db, + &format!("{name}_change_1w"), + version, + indexes, + )?, + change_1y: FiatFromHeight::forced_import( + db, + &format!("{name}_change_1y"), + version, + indexes, + )?, + rate_24h: PercentFromHeight::forced_import( + db, + &format!("{name}_rate_24h"), + version, + indexes, + )?, + rate_1w: PercentFromHeight::forced_import( + db, + &format!("{name}_rate_1w"), + version, + indexes, + )?, + rate_1y: PercentFromHeight::forced_import( + db, + &format!("{name}_rate_1y"), + version, + indexes, + )?, + _phantom: std::marker::PhantomData, + }) + } + + pub(crate) fn compute( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + let skip = self.change_24h.cents.height.len(); + let (source_data, offset) = collect_source(source, skip, windows._1y); + + let changes: [&mut FiatFromHeight; 3] = + [&mut self.change_24h, &mut self.change_1w, &mut self.change_1y]; + let rates = [&mut self.rate_24h, &mut self.rate_1w, &mut self.rate_1y]; + let starts = [windows._24h, windows._1w, windows._1y]; + + for ((change_w, rate_w), starts) in changes.into_iter().zip(rates).zip(starts) { + compute_delta_window( + &mut change_w.cents.height, + &mut rate_w.bps.height, + max_from, + starts, + &source_data, + offset, + exit, + )?; + } + Ok(()) + } +} + +/// Fiat rolling delta: all 4 windows, fiat change (cents + usd) + rate. +#[derive(Traversable)] +pub struct FiatRollingDelta +where + S: NumericValue + JsonSchema, + C: CentsType, +{ + pub change_24h: FiatFromHeight, + pub change_1w: FiatFromHeight, + pub change_1m: FiatFromHeight, + pub change_1y: FiatFromHeight, + pub rate: PercentRollingWindows, + _phantom: std::marker::PhantomData, +} + +impl FiatRollingDelta +where + S: NumericValue + JsonSchema, + C: CentsType, +{ + pub(crate) fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + Ok(Self { + change_24h: FiatFromHeight::forced_import( + db, + &format!("{name}_change_24h"), + version, + indexes, + )?, + change_1w: FiatFromHeight::forced_import( + db, + &format!("{name}_change_1w"), + version, + indexes, + )?, + change_1m: FiatFromHeight::forced_import( + db, + &format!("{name}_change_1m"), + version, + indexes, + )?, + change_1y: FiatFromHeight::forced_import( + db, + &format!("{name}_change_1y"), + version, + indexes, + )?, + rate: PercentRollingWindows::forced_import( + db, + &format!("{name}_rate"), + version, + indexes, + )?, + _phantom: std::marker::PhantomData, + }) + } + + pub(crate) fn compute( + &mut self, + max_from: Height, + windows: &WindowStarts<'_>, + source: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + let skip = self.change_24h.cents.height.len(); + let (source_data, offset) = collect_source(source, skip, windows._1y); + + let changes: [&mut FiatFromHeight; 4] = [ + &mut self.change_24h, + &mut self.change_1w, + &mut self.change_1m, + &mut self.change_1y, + ]; + let rates = self.rate.0.as_mut_array(); + let starts = windows.as_array(); + + for ((change_w, rate_w), starts) in changes.into_iter().zip(rates).zip(starts) { + compute_delta_window( + &mut change_w.cents.height, + &mut rate_w.bps.height, + max_from, + *starts, + &source_data, + offset, + exit, + )?; + } + Ok(()) + } +} diff --git a/crates/brk_computer/src/internal/from_height/computed/mod.rs b/crates/brk_computer/src/internal/from_height/computed/mod.rs index 702958f80..91c98b93f 100644 --- a/crates/brk_computer/src/internal/from_height/computed/mod.rs +++ b/crates/brk_computer/src/internal/from_height/computed/mod.rs @@ -2,6 +2,7 @@ mod aggregated; mod cumulative; mod cumulative_sum; mod delta; +mod fiat_delta; mod full; mod rolling_average; mod sum; @@ -10,6 +11,7 @@ pub use aggregated::*; pub use cumulative::*; pub use cumulative_sum::*; pub use delta::*; +pub use fiat_delta::*; pub use full::*; pub use rolling_average::*; pub use sum::*; diff --git a/crates/brk_computer/src/internal/from_height/lazy_fiat.rs b/crates/brk_computer/src/internal/from_height/lazy_fiat.rs new file mode 100644 index 000000000..8faebd434 --- /dev/null +++ b/crates/brk_computer/src/internal/from_height/lazy_fiat.rs @@ -0,0 +1,41 @@ +use brk_traversable::Traversable; +use brk_types::{Dollars, Version}; +use vecdb::ReadableCloneableVec; + +use super::{ComputedFromHeight, LazyFromHeight}; +use crate::internal::{Identity, NumericValue}; + +use super::fiat::CentsType; + +/// Lazy fiat: both cents and usd are lazy views of a stored source. +/// Zero extra stored vecs. +#[derive(Clone, Traversable)] +pub struct LazyFiatFromHeight { + pub cents: LazyFromHeight, + pub usd: LazyFromHeight, +} + +impl LazyFiatFromHeight { + pub(crate) fn from_computed( + name: &str, + version: Version, + source: &ComputedFromHeight, + ) -> Self + where + C: NumericValue, + { + let cents = LazyFromHeight::from_computed::>( + &format!("{name}_cents"), + version, + source.height.read_only_boxed_clone(), + source, + ); + let usd = LazyFromHeight::from_computed::( + &format!("{name}_usd"), + version, + source.height.read_only_boxed_clone(), + source, + ); + Self { cents, usd } + } +} diff --git a/crates/brk_computer/src/internal/from_height/mod.rs b/crates/brk_computer/src/internal/from_height/mod.rs index 62a416856..449ea8098 100644 --- a/crates/brk_computer/src/internal/from_height/mod.rs +++ b/crates/brk_computer/src/internal/from_height/mod.rs @@ -4,6 +4,7 @@ mod computed; mod constant; mod fiat; mod lazy; +mod lazy_fiat; mod percent; mod percentiles; mod price; @@ -17,6 +18,7 @@ pub use computed::*; pub use constant::*; pub use fiat::*; pub use lazy::*; +pub use lazy_fiat::*; pub use percent::*; pub use percentiles::*; pub use price::*; diff --git a/crates/brk_computer/src/supply/compute.rs b/crates/brk_computer/src/supply/compute.rs index c81e91c4e..61b750658 100644 --- a/crates/brk_computer/src/supply/compute.rs +++ b/crates/brk_computer/src/supply/compute.rs @@ -44,39 +44,34 @@ impl Vecs { self.velocity .compute(blocks, transactions, distribution, starting_indexes, exit)?; - // 4. Compute cap growth rates across 4 windows + // 4. Compute market cap delta (change + rate across 4 windows) let window_starts = blocks.count.window_starts(); - let realized_cap = &distribution - .utxo_cohorts - .all - .metrics - .realized - .realized_cap - .height; + self.market_cap_delta.compute( + starting_indexes.height, + &window_starts, + &self.market_cap.cents.height, + exit, + )?; - let mcgr_arr = self.market_cap_growth_rate.0.as_mut_array(); - let rcgr_arr = self.realized_cap_growth_rate.0.as_mut_array(); + // 5. market_cap_rate - realized_cap_rate per window + let all_realized = &distribution.utxo_cohorts.all.metrics.realized; + let mcr_arr = self.market_cap_delta.rate.0.as_array(); let diff_arr = self.market_minus_realized_cap_growth_rate.0.as_mut_array(); - let starts_arr = window_starts.as_array(); + + // 24h, 1w, 1y from extended; 1m from core delta + let rcr_rates = [ + &all_realized.realized_cap_delta_extended.rate_24h.bps.height, + &all_realized.realized_cap_delta_extended.rate_1w.bps.height, + &all_realized.realized_cap_delta.rate_1m.bps.height, + &all_realized.realized_cap_delta_extended.rate_1y.bps.height, + ]; for i in 0..4 { - mcgr_arr[i].bps.height.compute_rolling_ratio_change( - starting_indexes.height, - *starts_arr[i], - &self.market_cap.height, - exit, - )?; - rcgr_arr[i].bps.height.compute_rolling_ratio_change( - starting_indexes.height, - *starts_arr[i], - realized_cap, - exit, - )?; diff_arr[i].height.compute_subtract( starting_indexes.height, - &mcgr_arr[i].bps.height, - &rcgr_arr[i].bps.height, + &mcr_arr[i].bps.height, + rcr_rates[i], exit, )?; } diff --git a/crates/brk_computer/src/supply/import.rs b/crates/brk_computer/src/supply/import.rs index 6356e5d11..c0702cd1d 100644 --- a/crates/brk_computer/src/supply/import.rs +++ b/crates/brk_computer/src/supply/import.rs @@ -6,7 +6,7 @@ use brk_types::{Cents, Dollars, Sats, Version}; use crate::{ distribution, indexes, internal::{ - Identity, LazyFromHeight, LazyValueFromHeight, PercentFromHeight, PercentRollingWindows, + FiatRollingDelta, Identity, LazyFiatFromHeight, LazyValueFromHeight, PercentFromHeight, RollingWindows, SatsToBitcoin, finalize_db, open_db, }, }; @@ -45,30 +45,22 @@ impl Vecs { // Velocity let velocity = super::velocity::Vecs::forced_import(&db, version, indexes)?; - // Market cap - lazy identity from distribution supply in USD - let market_cap = LazyFromHeight::from_lazy::, Cents>( - "market_cap", - version, - &supply_metrics.total.usd, - ); + // Market cap - lazy fiat (cents + usd) from distribution supply + let market_cap = + LazyFiatFromHeight::from_computed("market_cap", version, &supply_metrics.total.cents); - // Growth rates (4 windows: 24h, 1w, 1m, 1y) - let market_cap_growth_rate = PercentRollingWindows::forced_import( + // Market cap delta (change + rate across 4 windows) + let market_cap_delta = FiatRollingDelta::forced_import( &db, - "market_cap_growth_rate", - version + Version::TWO, - indexes, - )?; - let realized_cap_growth_rate = PercentRollingWindows::forced_import( - &db, - "realized_cap_growth_rate", - version + Version::TWO, + "market_cap_delta", + version + Version::new(3), indexes, )?; + let market_minus_realized_cap_growth_rate = RollingWindows::forced_import( &db, "market_minus_realized_cap_growth_rate", - version + Version::ONE, + version + Version::TWO, indexes, )?; @@ -79,8 +71,7 @@ impl Vecs { inflation_rate, velocity, market_cap, - market_cap_growth_rate, - realized_cap_growth_rate, + market_cap_delta, market_minus_realized_cap_growth_rate, }; finalize_db(&this.db, &this)?; diff --git a/crates/brk_computer/src/supply/vecs.rs b/crates/brk_computer/src/supply/vecs.rs index 0e8e789fc..1de061832 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::{BasisPointsSigned32, Dollars}; +use brk_types::{BasisPointsSigned32, Cents, CentsSigned}; use vecdb::{Database, Rw, StorageMode}; use super::{burned, velocity}; use crate::internal::{ - LazyFromHeight, LazyValueFromHeight, PercentFromHeight, PercentRollingWindows, RollingWindows, + FiatRollingDelta, LazyFiatFromHeight, LazyValueFromHeight, PercentFromHeight, RollingWindows, }; #[derive(Traversable)] @@ -16,8 +16,7 @@ pub struct Vecs { pub burned: burned::Vecs, pub inflation_rate: PercentFromHeight, pub velocity: velocity::Vecs, - pub market_cap: LazyFromHeight, - pub market_cap_growth_rate: PercentRollingWindows, - pub realized_cap_growth_rate: PercentRollingWindows, + pub market_cap: LazyFiatFromHeight, + pub market_cap_delta: FiatRollingDelta, pub market_minus_realized_cap_growth_rate: RollingWindows, } diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index 23499770a..8a45bb913 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -1593,8 +1593,8 @@ function createMetricPattern35(client, name) { return /** @type {MetricPattern35 * @property {MetricPattern1} negRealizedLoss * @property {BpsPercentRatioPattern} netPnlChange1mRelToMarketCap * @property {BpsPercentRatioPattern} netPnlChange1mRelToRealizedCap - * @property {ChangeRatePattern} netPnlDelta - * @property {_24hChangeRatePattern} netPnlDeltaExtended + * @property {ChangeRatePattern3} netPnlDelta + * @property {_24hChangeRatePattern} netPnlDeltaExtended * @property {MetricPattern1} netRealizedPnl * @property {MetricPattern1} netRealizedPnlCumulative * @property {BpsPercentRatioPattern} netRealizedPnlRelToRealizedCap @@ -1609,8 +1609,8 @@ function createMetricPattern35(client, name) { return /** @type {MetricPattern35 * @property {_1m1w1y24hPattern} profitValueDestroyedSum * @property {MetricPattern1} realizedCap * @property {MetricPattern1} realizedCapCents - * @property {ChangeRatePattern} realizedCapDelta - * @property {_24hChangeRatePattern} realizedCapDeltaExtended + * @property {ChangeRatePattern3} realizedCapDelta + * @property {_24hChangeRatePattern} realizedCapDeltaExtended * @property {BpsPercentRatioPattern} realizedCapRelToOwnMarketCap * @property {CumulativeHeightPattern} realizedLoss * @property {BpsPercentRatioPattern} realizedLossRelToRealizedCap @@ -1668,7 +1668,7 @@ function createCapCapitulationGrossInvestorLossLowerMvrvNegNetPeakProfitRealized negRealizedLoss: createMetricPattern1(client, _m(acc, 'neg_realized_loss')), netPnlChange1mRelToMarketCap: createBpsPercentRatioPattern(client, _m(acc, 'net_pnl_change_1m_rel_to_market_cap')), netPnlChange1mRelToRealizedCap: createBpsPercentRatioPattern(client, _m(acc, 'net_pnl_change_1m_rel_to_realized_cap')), - netPnlDelta: createChangeRatePattern(client, _m(acc, 'net_pnl_delta')), + netPnlDelta: createChangeRatePattern3(client, _m(acc, 'net_pnl_delta')), netPnlDeltaExtended: create_24hChangeRatePattern(client, _m(acc, 'net_pnl_delta')), netRealizedPnl: createMetricPattern1(client, _m(acc, 'net_realized_pnl')), netRealizedPnlCumulative: createMetricPattern1(client, _m(acc, 'net_realized_pnl_cumulative')), @@ -1684,7 +1684,7 @@ function createCapCapitulationGrossInvestorLossLowerMvrvNegNetPeakProfitRealized profitValueDestroyedSum: create_1m1w1y24hPattern(client, _m(acc, 'profit_value_destroyed')), realizedCap: createMetricPattern1(client, _m(acc, 'realized_cap')), realizedCapCents: createMetricPattern1(client, _m(acc, 'realized_cap_cents')), - realizedCapDelta: createChangeRatePattern(client, _m(acc, 'realized_cap_delta')), + realizedCapDelta: createChangeRatePattern3(client, _m(acc, 'realized_cap_delta')), realizedCapDeltaExtended: create_24hChangeRatePattern(client, _m(acc, 'realized_cap_delta')), realizedCapRelToOwnMarketCap: createBpsPercentRatioPattern(client, _m(acc, 'realized_cap_rel_to_own_market_cap')), realizedLoss: createCumulativeHeightPattern(client, _m(acc, 'realized_loss')), @@ -1798,7 +1798,7 @@ function create_0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdSmaZscorePattern(client * @property {_24hPattern} netRealizedPnlSum * @property {MetricPattern1} realizedCap * @property {MetricPattern1} realizedCapCents - * @property {ChangeRatePattern} realizedCapDelta + * @property {ChangeRatePattern3} realizedCapDelta * @property {CumulativeHeightPattern} realizedLoss * @property {_24hPattern} realizedLossSum * @property {CentsSatsUsdPattern} realizedPrice @@ -1830,7 +1830,7 @@ function createMvrvNegNetRealizedSentSoprValuePattern(client, acc) { netRealizedPnlSum: create_24hPattern(client, _m(acc, 'net_realized_pnl_24h')), realizedCap: createMetricPattern1(client, _m(acc, 'realized_cap')), realizedCapCents: createMetricPattern1(client, _m(acc, 'realized_cap_cents')), - realizedCapDelta: createChangeRatePattern(client, _m(acc, 'realized_cap_delta')), + realizedCapDelta: createChangeRatePattern3(client, _m(acc, 'realized_cap_delta')), realizedLoss: createCumulativeHeightPattern(client, _m(acc, 'realized_loss')), realizedLossSum: create_24hPattern(client, _m(acc, 'realized_loss_24h')), realizedPrice: createCentsSatsUsdPattern(client, _m(acc, 'realized_price')), @@ -1910,7 +1910,7 @@ function createPct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65 * @property {_24hPattern} netRealizedPnlSum * @property {MetricPattern1} realizedCap * @property {MetricPattern1} realizedCapCents - * @property {ChangeRatePattern} realizedCapDelta + * @property {ChangeRatePattern3} realizedCapDelta * @property {CumulativeHeightPattern} realizedLoss * @property {_24hPattern} realizedLossSum * @property {CentsSatsUsdPattern} realizedPrice @@ -1938,7 +1938,7 @@ function createMvrvNegNetRealizedSoprValuePattern(client, acc) { netRealizedPnlSum: create_24hPattern(client, _m(acc, 'net_realized_pnl_24h')), realizedCap: createMetricPattern1(client, _m(acc, 'realized_cap')), realizedCapCents: createMetricPattern1(client, _m(acc, 'realized_cap_cents')), - realizedCapDelta: createChangeRatePattern(client, _m(acc, 'realized_cap_delta')), + realizedCapDelta: createChangeRatePattern3(client, _m(acc, 'realized_cap_delta')), realizedLoss: createCumulativeHeightPattern(client, _m(acc, 'realized_loss')), realizedLossSum: create_24hPattern(client, _m(acc, 'realized_loss_24h')), realizedPrice: createCentsSatsUsdPattern(client, _m(acc, 'realized_price')), @@ -2760,6 +2760,31 @@ function createNegNetSupplyUnrealizedPattern(client, acc) { }; } +/** + * @typedef {Object} _24hChangeRatePattern + * @property {BpsCentsPercentRatioUsdPattern} _24h + * @property {CentsUsdPattern} change1w + * @property {CentsUsdPattern} change1y + * @property {BpsPercentRatioPattern} rate1w + * @property {BpsPercentRatioPattern} rate1y + */ + +/** + * Create a _24hChangeRatePattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {_24hChangeRatePattern} + */ +function create_24hChangeRatePattern(client, acc) { + return { + _24h: createBpsCentsPercentRatioUsdPattern(client, acc), + change1w: createCentsUsdPattern(client, _m(acc, 'change_1w')), + change1y: createCentsUsdPattern(client, _m(acc, 'change_1y')), + rate1w: createBpsPercentRatioPattern(client, _m(acc, 'rate_1w')), + rate1y: createBpsPercentRatioPattern(client, _m(acc, 'rate_1y')), + }; +} + /** * @typedef {Object} ActivityOutputsRealizedSupplyUnrealizedPattern * @property {SentPattern} activity @@ -2810,6 +2835,31 @@ function createBaseBtcCentsSatsUsdPattern(client, acc) { }; } +/** + * @typedef {Object} BpsCentsPercentRatioUsdPattern + * @property {MetricPattern1} bps + * @property {MetricPattern1} cents + * @property {MetricPattern1} percent + * @property {MetricPattern1} ratio + * @property {MetricPattern1} usd + */ + +/** + * Create a BpsCentsPercentRatioUsdPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {BpsCentsPercentRatioUsdPattern} + */ +function createBpsCentsPercentRatioUsdPattern(client, acc) { + return { + bps: createMetricPattern1(client, _m(acc, 'rate_24h_bps')), + cents: createMetricPattern1(client, _m(acc, 'change_24h_cents')), + percent: createMetricPattern1(client, _m(acc, 'rate_24h')), + ratio: createMetricPattern1(client, _m(acc, 'rate_24h_ratio')), + usd: createMetricPattern1(client, _m(acc, 'change_24h_usd')), + }; +} + /** * @typedef {Object} EmaHistogramLineSignalPattern * @property {MetricPattern1} emaFast @@ -2864,7 +2914,7 @@ function create_1m1w1y24hHeightPattern(client, acc) { /** * @template T - * @typedef {Object} _24hChangeRatePattern + * @typedef {Object} _24hChangeRatePattern2 * @property {BaseBpsPercentRatioPattern} _24h * @property {MetricPattern1} change1w * @property {MetricPattern1} change1y @@ -2873,13 +2923,13 @@ function create_1m1w1y24hHeightPattern(client, acc) { */ /** - * Create a _24hChangeRatePattern pattern node + * Create a _24hChangeRatePattern2 pattern node * @template T * @param {BrkClientBase} client * @param {string} acc - Accumulated metric name - * @returns {_24hChangeRatePattern} + * @returns {_24hChangeRatePattern2} */ -function create_24hChangeRatePattern(client, acc) { +function create_24hChangeRatePattern2(client, acc) { return { _24h: createBaseBpsPercentRatioPattern(client, acc), change1w: createMetricPattern1(client, _m(acc, 'change_1w')), @@ -2960,7 +3010,7 @@ function create_1m1w1y24hPattern5(client, acc) { /** * @typedef {Object} BaseBpsPercentRatioPattern - * @property {MetricPattern1} base + * @property {MetricPattern1} base * @property {MetricPattern1} bps * @property {MetricPattern1} percent * @property {MetricPattern1} ratio @@ -3432,6 +3482,25 @@ function createChangeRatePattern2(client, acc) { }; } +/** + * @typedef {Object} ChangeRatePattern3 + * @property {CentsUsdPattern} change1m + * @property {BpsPercentRatioPattern} rate1m + */ + +/** + * Create a ChangeRatePattern3 pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {ChangeRatePattern3} + */ +function createChangeRatePattern3(client, acc) { + return { + change1m: createCentsUsdPattern(client, _m(acc, 'change_1m')), + rate1m: createBpsPercentRatioPattern(client, _m(acc, 'rate_1m')), + }; +} + /** * @typedef {Object} HeightSumPattern * @property {MetricPattern18} height @@ -3451,6 +3520,25 @@ function createHeightSumPattern(client, acc) { }; } +/** + * @typedef {Object} RealizedSupplyPattern + * @property {MetricPattern1} realizedCap + * @property {MetricPattern1} supply + */ + +/** + * Create a RealizedSupplyPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {RealizedSupplyPattern} + */ +function createRealizedSupplyPattern(client, acc) { + return { + realizedCap: createMetricPattern1(client, _m(acc, 'realized_cap')), + supply: createMetricPattern1(client, _m(acc, 'supply')), + }; +} + /** * @typedef {Object} SdSmaPattern * @property {MetricPattern1} sd @@ -4852,6 +4940,7 @@ function create_24hPattern(client, acc) { * @property {MetricsTree_Distribution_UtxoCohorts_AmountRange} amountRange * @property {MetricsTree_Distribution_UtxoCohorts_LtAmount} ltAmount * @property {MetricsTree_Distribution_UtxoCohorts_Type} type + * @property {MetricsTree_Distribution_UtxoCohorts_Profitability} profitability */ /** @@ -4866,8 +4955,8 @@ function create_24hPattern(client, acc) { * @property {MetricsTree_Distribution_UtxoCohorts_All_Relative} relative * @property {MetricPattern1} dormancy * @property {MetricPattern1} velocity - * @property {_24hChangeRatePattern} supplyDeltaExtended - * @property {_24hChangeRatePattern} utxoCountDeltaExtended + * @property {_24hChangeRatePattern2} supplyDeltaExtended + * @property {_24hChangeRatePattern2} utxoCountDeltaExtended */ /** @@ -4903,8 +4992,8 @@ function create_24hPattern(client, acc) { * @property {NetNuplSupplyUnrealizedPattern2} relative * @property {MetricPattern1} dormancy * @property {MetricPattern1} velocity - * @property {_24hChangeRatePattern} supplyDeltaExtended - * @property {_24hChangeRatePattern} utxoCountDeltaExtended + * @property {_24hChangeRatePattern2} supplyDeltaExtended + * @property {_24hChangeRatePattern2} utxoCountDeltaExtended * @property {MetricPattern1} adjustedValueCreated * @property {MetricPattern1} adjustedValueDestroyed * @property {_1m1w1y24hPattern} adjustedValueCreatedSum @@ -4923,8 +5012,8 @@ function create_24hPattern(client, acc) { * @property {NetNuplSupplyUnrealizedPattern2} relative * @property {MetricPattern1} dormancy * @property {MetricPattern1} velocity - * @property {_24hChangeRatePattern} supplyDeltaExtended - * @property {_24hChangeRatePattern} utxoCountDeltaExtended + * @property {_24hChangeRatePattern2} supplyDeltaExtended + * @property {_24hChangeRatePattern2} utxoCountDeltaExtended */ /** @@ -5095,6 +5184,75 @@ function create_24hPattern(client, acc) { * @property {ActivityOutputsRealizedSupplyUnrealizedPattern} empty */ +/** + * @typedef {Object} MetricsTree_Distribution_UtxoCohorts_Profitability + * @property {MetricsTree_Distribution_UtxoCohorts_Profitability_Range} range + * @property {MetricsTree_Distribution_UtxoCohorts_Profitability_Profit} profit + * @property {MetricsTree_Distribution_UtxoCohorts_Profitability_Loss} loss + */ + +/** + * @typedef {Object} MetricsTree_Distribution_UtxoCohorts_Profitability_Range + * @property {RealizedSupplyPattern} profitOver1000 + * @property {RealizedSupplyPattern} profit500To1000 + * @property {RealizedSupplyPattern} profit300To500 + * @property {RealizedSupplyPattern} profit200To300 + * @property {RealizedSupplyPattern} profit100To200 + * @property {RealizedSupplyPattern} profit90To100 + * @property {RealizedSupplyPattern} profit80To90 + * @property {RealizedSupplyPattern} profit70To80 + * @property {RealizedSupplyPattern} profit60To70 + * @property {RealizedSupplyPattern} profit50To60 + * @property {RealizedSupplyPattern} profit40To50 + * @property {RealizedSupplyPattern} profit30To40 + * @property {RealizedSupplyPattern} profit20To30 + * @property {RealizedSupplyPattern} profit10To20 + * @property {RealizedSupplyPattern} profit0To10 + * @property {RealizedSupplyPattern} loss0To10 + * @property {RealizedSupplyPattern} loss10To20 + * @property {RealizedSupplyPattern} loss20To30 + * @property {RealizedSupplyPattern} loss30To40 + * @property {RealizedSupplyPattern} loss40To50 + * @property {RealizedSupplyPattern} loss50To60 + * @property {RealizedSupplyPattern} loss60To70 + * @property {RealizedSupplyPattern} loss70To80 + * @property {RealizedSupplyPattern} loss80To90 + * @property {RealizedSupplyPattern} loss90To100 + */ + +/** + * @typedef {Object} MetricsTree_Distribution_UtxoCohorts_Profitability_Profit + * @property {RealizedSupplyPattern} breakeven + * @property {RealizedSupplyPattern} _10pct + * @property {RealizedSupplyPattern} _20pct + * @property {RealizedSupplyPattern} _30pct + * @property {RealizedSupplyPattern} _40pct + * @property {RealizedSupplyPattern} _50pct + * @property {RealizedSupplyPattern} _60pct + * @property {RealizedSupplyPattern} _70pct + * @property {RealizedSupplyPattern} _80pct + * @property {RealizedSupplyPattern} _90pct + * @property {RealizedSupplyPattern} _100pct + * @property {RealizedSupplyPattern} _200pct + * @property {RealizedSupplyPattern} _300pct + * @property {RealizedSupplyPattern} _500pct + * @property {RealizedSupplyPattern} _1000pct + */ + +/** + * @typedef {Object} MetricsTree_Distribution_UtxoCohorts_Profitability_Loss + * @property {RealizedSupplyPattern} breakeven + * @property {RealizedSupplyPattern} _10pct + * @property {RealizedSupplyPattern} _20pct + * @property {RealizedSupplyPattern} _30pct + * @property {RealizedSupplyPattern} _40pct + * @property {RealizedSupplyPattern} _50pct + * @property {RealizedSupplyPattern} _60pct + * @property {RealizedSupplyPattern} _70pct + * @property {RealizedSupplyPattern} _80pct + * @property {RealizedSupplyPattern} _90pct + */ + /** * @typedef {Object} MetricsTree_Distribution_AddressCohorts * @property {MetricsTree_Distribution_AddressCohorts_GeAmount} geAmount @@ -5200,9 +5358,8 @@ function create_24hPattern(client, acc) { * @property {MetricsTree_Supply_Burned} burned * @property {BpsPercentRatioPattern} inflationRate * @property {MetricsTree_Supply_Velocity} velocity - * @property {MetricPattern1} marketCap - * @property {_1m1w1y24hPattern2} marketCapGrowthRate - * @property {_1m1w1y24hPattern2} realizedCapGrowthRate + * @property {CentsUsdPattern} marketCap + * @property {MetricsTree_Supply_MarketCapDelta} marketCapDelta * @property {_1m1w1y24hPattern} marketMinusRealizedCapGrowthRate */ @@ -5218,6 +5375,15 @@ function create_24hPattern(client, acc) { * @property {MetricPattern1} usd */ +/** + * @typedef {Object} MetricsTree_Supply_MarketCapDelta + * @property {CentsUsdPattern} change24h + * @property {CentsUsdPattern} change1w + * @property {CentsUsdPattern} change1m + * @property {CentsUsdPattern} change1y + * @property {_1m1w1y24hPattern2} rate + */ + /** * Main BRK client with metrics tree and API methods * @extends BrkClientBase @@ -7106,8 +7272,8 @@ class BrkClient extends BrkClientBase { }, dormancy: createMetricPattern1(this, 'dormancy'), velocity: createMetricPattern1(this, 'velocity'), - supplyDeltaExtended: create_24hChangeRatePattern(this, 'supply_delta'), - utxoCountDeltaExtended: create_24hChangeRatePattern(this, 'utxo_count_delta'), + supplyDeltaExtended: create_24hChangeRatePattern2(this, 'supply_delta'), + utxoCountDeltaExtended: create_24hChangeRatePattern2(this, 'utxo_count_delta'), }, sth: { supply: createDeltaHalvedTotalPattern(this, 'sth_supply'), @@ -7119,8 +7285,8 @@ class BrkClient extends BrkClientBase { relative: createNetNuplSupplyUnrealizedPattern2(this, 'sth'), dormancy: createMetricPattern1(this, 'sth_dormancy'), velocity: createMetricPattern1(this, 'sth_velocity'), - supplyDeltaExtended: create_24hChangeRatePattern(this, 'sth_supply_delta'), - utxoCountDeltaExtended: create_24hChangeRatePattern(this, 'sth_utxo_count_delta'), + supplyDeltaExtended: create_24hChangeRatePattern2(this, 'sth_supply_delta'), + utxoCountDeltaExtended: create_24hChangeRatePattern2(this, 'sth_utxo_count_delta'), adjustedValueCreated: createMetricPattern1(this, 'sth_adjusted_value_created'), adjustedValueDestroyed: createMetricPattern1(this, 'sth_adjusted_value_destroyed'), adjustedValueCreatedSum: create_1m1w1y24hPattern(this, 'sth_adjusted_value_created'), @@ -7137,8 +7303,8 @@ class BrkClient extends BrkClientBase { relative: createNetNuplSupplyUnrealizedPattern2(this, 'lth'), dormancy: createMetricPattern1(this, 'lth_dormancy'), velocity: createMetricPattern1(this, 'lth_velocity'), - supplyDeltaExtended: create_24hChangeRatePattern(this, 'lth_supply_delta'), - utxoCountDeltaExtended: create_24hChangeRatePattern(this, 'lth_utxo_count_delta'), + supplyDeltaExtended: create_24hChangeRatePattern2(this, 'lth_supply_delta'), + utxoCountDeltaExtended: create_24hChangeRatePattern2(this, 'lth_utxo_count_delta'), }, ageRange: { upTo1h: createActivityOutputsRealizedRelativeSupplyUnrealizedPattern(this, 'utxos_under_1h_old'), @@ -7290,6 +7456,64 @@ class BrkClient extends BrkClientBase { unknown: createActivityOutputsRealizedSupplyUnrealizedPattern(this, 'unknown_outputs'), empty: createActivityOutputsRealizedSupplyUnrealizedPattern(this, 'empty_outputs'), }, + profitability: { + range: { + profitOver1000: createRealizedSupplyPattern(this, 'profit_over_1000pct'), + profit500To1000: createRealizedSupplyPattern(this, 'profit_500_to_1000pct'), + profit300To500: createRealizedSupplyPattern(this, 'profit_300_to_500pct'), + profit200To300: createRealizedSupplyPattern(this, 'profit_200_to_300pct'), + profit100To200: createRealizedSupplyPattern(this, 'profit_100_to_200pct'), + profit90To100: createRealizedSupplyPattern(this, 'profit_90_to_100pct'), + profit80To90: createRealizedSupplyPattern(this, 'profit_80_to_90pct'), + profit70To80: createRealizedSupplyPattern(this, 'profit_70_to_80pct'), + profit60To70: createRealizedSupplyPattern(this, 'profit_60_to_70pct'), + profit50To60: createRealizedSupplyPattern(this, 'profit_50_to_60pct'), + profit40To50: createRealizedSupplyPattern(this, 'profit_40_to_50pct'), + profit30To40: createRealizedSupplyPattern(this, 'profit_30_to_40pct'), + profit20To30: createRealizedSupplyPattern(this, 'profit_20_to_30pct'), + profit10To20: createRealizedSupplyPattern(this, 'profit_10_to_20pct'), + profit0To10: createRealizedSupplyPattern(this, 'profit_0_to_10pct'), + loss0To10: createRealizedSupplyPattern(this, 'loss_0_to_10pct'), + loss10To20: createRealizedSupplyPattern(this, 'loss_10_to_20pct'), + loss20To30: createRealizedSupplyPattern(this, 'loss_20_to_30pct'), + loss30To40: createRealizedSupplyPattern(this, 'loss_30_to_40pct'), + loss40To50: createRealizedSupplyPattern(this, 'loss_40_to_50pct'), + loss50To60: createRealizedSupplyPattern(this, 'loss_50_to_60pct'), + loss60To70: createRealizedSupplyPattern(this, 'loss_60_to_70pct'), + loss70To80: createRealizedSupplyPattern(this, 'loss_70_to_80pct'), + loss80To90: createRealizedSupplyPattern(this, 'loss_80_to_90pct'), + loss90To100: createRealizedSupplyPattern(this, 'loss_90_to_100pct'), + }, + profit: { + breakeven: createRealizedSupplyPattern(this, 'profit_ge_breakeven'), + _10pct: createRealizedSupplyPattern(this, 'profit_ge_10pct'), + _20pct: createRealizedSupplyPattern(this, 'profit_ge_20pct'), + _30pct: createRealizedSupplyPattern(this, 'profit_ge_30pct'), + _40pct: createRealizedSupplyPattern(this, 'profit_ge_40pct'), + _50pct: createRealizedSupplyPattern(this, 'profit_ge_50pct'), + _60pct: createRealizedSupplyPattern(this, 'profit_ge_60pct'), + _70pct: createRealizedSupplyPattern(this, 'profit_ge_70pct'), + _80pct: createRealizedSupplyPattern(this, 'profit_ge_80pct'), + _90pct: createRealizedSupplyPattern(this, 'profit_ge_90pct'), + _100pct: createRealizedSupplyPattern(this, 'profit_ge_100pct'), + _200pct: createRealizedSupplyPattern(this, 'profit_ge_200pct'), + _300pct: createRealizedSupplyPattern(this, 'profit_ge_300pct'), + _500pct: createRealizedSupplyPattern(this, 'profit_ge_500pct'), + _1000pct: createRealizedSupplyPattern(this, 'profit_ge_1000pct'), + }, + loss: { + breakeven: createRealizedSupplyPattern(this, 'loss_ge_breakeven'), + _10pct: createRealizedSupplyPattern(this, 'loss_ge_10pct'), + _20pct: createRealizedSupplyPattern(this, 'loss_ge_20pct'), + _30pct: createRealizedSupplyPattern(this, 'loss_ge_30pct'), + _40pct: createRealizedSupplyPattern(this, 'loss_ge_40pct'), + _50pct: createRealizedSupplyPattern(this, 'loss_ge_50pct'), + _60pct: createRealizedSupplyPattern(this, 'loss_ge_60pct'), + _70pct: createRealizedSupplyPattern(this, 'loss_ge_70pct'), + _80pct: createRealizedSupplyPattern(this, 'loss_ge_80pct'), + _90pct: createRealizedSupplyPattern(this, 'loss_ge_90pct'), + }, + }, }, addressCohorts: { geAmount: { @@ -7390,9 +7614,14 @@ class BrkClient extends BrkClientBase { btc: createMetricPattern1(this, 'velocity_btc'), usd: createMetricPattern1(this, 'velocity_usd'), }, - marketCap: createMetricPattern1(this, 'market_cap'), - marketCapGrowthRate: create_1m1w1y24hPattern2(this, 'market_cap_growth_rate'), - realizedCapGrowthRate: create_1m1w1y24hPattern2(this, 'realized_cap_growth_rate'), + marketCap: createCentsUsdPattern(this, 'market_cap'), + marketCapDelta: { + change24h: createCentsUsdPattern(this, 'market_cap_delta_change_24h'), + change1w: createCentsUsdPattern(this, 'market_cap_delta_change_1w'), + change1m: createCentsUsdPattern(this, 'market_cap_delta_change_1m'), + change1y: createCentsUsdPattern(this, 'market_cap_delta_change_1y'), + rate: create_1m1w1y24hPattern2(this, 'market_cap_delta_rate'), + }, marketMinusRealizedCapGrowthRate: create_1m1w1y24hPattern(this, 'market_minus_realized_cap_growth_rate'), }, }; diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index 55bf42390..4a963c803 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -2088,8 +2088,8 @@ class CapCapitulationGrossInvestorLossLowerMvrvNegNetPeakProfitRealizedSellSentS self.neg_realized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'neg_realized_loss')) self.net_pnl_change_1m_rel_to_market_cap: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'net_pnl_change_1m_rel_to_market_cap')) self.net_pnl_change_1m_rel_to_realized_cap: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'net_pnl_change_1m_rel_to_realized_cap')) - self.net_pnl_delta: ChangeRatePattern[CentsSigned] = ChangeRatePattern(client, _m(acc, 'net_pnl_delta')) - self.net_pnl_delta_extended: _24hChangeRatePattern[CentsSigned] = _24hChangeRatePattern(client, _m(acc, 'net_pnl_delta')) + self.net_pnl_delta: ChangeRatePattern3 = ChangeRatePattern3(client, _m(acc, 'net_pnl_delta')) + self.net_pnl_delta_extended: _24hChangeRatePattern = _24hChangeRatePattern(client, _m(acc, 'net_pnl_delta')) self.net_realized_pnl: MetricPattern1[CentsSigned] = MetricPattern1(client, _m(acc, 'net_realized_pnl')) self.net_realized_pnl_cumulative: MetricPattern1[CentsSigned] = MetricPattern1(client, _m(acc, 'net_realized_pnl_cumulative')) self.net_realized_pnl_rel_to_realized_cap: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'net_realized_pnl_rel_to_realized_cap')) @@ -2104,8 +2104,8 @@ class CapCapitulationGrossInvestorLossLowerMvrvNegNetPeakProfitRealizedSellSentS self.profit_value_destroyed_sum: _1m1w1y24hPattern[Cents] = _1m1w1y24hPattern(client, _m(acc, 'profit_value_destroyed')) self.realized_cap: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'realized_cap')) self.realized_cap_cents: MetricPattern1[Cents] = MetricPattern1(client, _m(acc, 'realized_cap_cents')) - self.realized_cap_delta: ChangeRatePattern[CentsSigned] = ChangeRatePattern(client, _m(acc, 'realized_cap_delta')) - self.realized_cap_delta_extended: _24hChangeRatePattern[CentsSigned] = _24hChangeRatePattern(client, _m(acc, 'realized_cap_delta')) + self.realized_cap_delta: ChangeRatePattern3 = ChangeRatePattern3(client, _m(acc, 'realized_cap_delta')) + self.realized_cap_delta_extended: _24hChangeRatePattern = _24hChangeRatePattern(client, _m(acc, 'realized_cap_delta')) self.realized_cap_rel_to_own_market_cap: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'realized_cap_rel_to_own_market_cap')) self.realized_loss: CumulativeHeightPattern[Cents] = CumulativeHeightPattern(client, _m(acc, 'realized_loss')) self.realized_loss_rel_to_realized_cap: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'realized_loss_rel_to_realized_cap')) @@ -2182,7 +2182,7 @@ class MvrvNegNetRealizedSentSoprValuePattern: self.net_realized_pnl_sum: _24hPattern[CentsSigned] = _24hPattern(client, _m(acc, 'net_realized_pnl_24h')) self.realized_cap: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'realized_cap')) self.realized_cap_cents: MetricPattern1[Cents] = MetricPattern1(client, _m(acc, 'realized_cap_cents')) - self.realized_cap_delta: ChangeRatePattern[CentsSigned] = ChangeRatePattern(client, _m(acc, 'realized_cap_delta')) + self.realized_cap_delta: ChangeRatePattern3 = ChangeRatePattern3(client, _m(acc, 'realized_cap_delta')) self.realized_loss: CumulativeHeightPattern[Cents] = CumulativeHeightPattern(client, _m(acc, 'realized_loss')) self.realized_loss_sum: _24hPattern[Cents] = _24hPattern(client, _m(acc, 'realized_loss_24h')) self.realized_price: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'realized_price')) @@ -2235,7 +2235,7 @@ class MvrvNegNetRealizedSoprValuePattern: self.net_realized_pnl_sum: _24hPattern[CentsSigned] = _24hPattern(client, _m(acc, 'net_realized_pnl_24h')) self.realized_cap: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'realized_cap')) self.realized_cap_cents: MetricPattern1[Cents] = MetricPattern1(client, _m(acc, 'realized_cap_cents')) - self.realized_cap_delta: ChangeRatePattern[CentsSigned] = ChangeRatePattern(client, _m(acc, 'realized_cap_delta')) + self.realized_cap_delta: ChangeRatePattern3 = ChangeRatePattern3(client, _m(acc, 'realized_cap_delta')) self.realized_loss: CumulativeHeightPattern[Cents] = CumulativeHeightPattern(client, _m(acc, 'realized_loss')) self.realized_loss_sum: _24hPattern[Cents] = _24hPattern(client, _m(acc, 'realized_loss_24h')) self.realized_price: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'realized_price')) @@ -2616,6 +2616,17 @@ class NegNetSupplyUnrealizedPattern: self.unrealized_loss: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'unrealized_loss')) self.unrealized_profit: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'unrealized_profit')) +class _24hChangeRatePattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self._24h: BpsCentsPercentRatioUsdPattern = BpsCentsPercentRatioUsdPattern(client, acc) + self.change_1w: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'change_1w')) + self.change_1y: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'change_1y')) + self.rate_1w: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'rate_1w')) + self.rate_1y: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'rate_1y')) + class ActivityOutputsRealizedSupplyUnrealizedPattern: """Pattern struct for repeated tree structure.""" @@ -2638,6 +2649,17 @@ class BaseBtcCentsSatsUsdPattern: self.sats: MetricPattern1[Sats] = MetricPattern1(client, _m(acc, 'rewards_cumulative')) self.usd: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'rewards_cumulative_usd')) +class BpsCentsPercentRatioUsdPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.bps: MetricPattern1[BasisPointsSigned32] = MetricPattern1(client, _m(acc, 'rate_24h_bps')) + self.cents: MetricPattern1[CentsSigned] = MetricPattern1(client, _m(acc, 'change_24h_cents')) + self.percent: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'rate_24h')) + self.ratio: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'rate_24h_ratio')) + self.usd: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'change_24h_usd')) + class EmaHistogramLineSignalPattern: """Pattern struct for repeated tree structure.""" @@ -2660,7 +2682,7 @@ class _1m1w1y24hHeightPattern(Generic[T]): self._24h: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'average_24h')) self.height: MetricPattern18[T] = MetricPattern18(client, acc) -class _24hChangeRatePattern(Generic[T]): +class _24hChangeRatePattern2(Generic[T]): """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): @@ -2706,7 +2728,7 @@ class BaseBpsPercentRatioPattern: def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" - self.base: MetricPattern1[CentsSigned] = MetricPattern1(client, _m(acc, 'change_24h')) + self.base: MetricPattern1[SatsSigned] = MetricPattern1(client, _m(acc, 'change_24h')) self.bps: MetricPattern1[BasisPointsSigned32] = MetricPattern1(client, _m(acc, 'rate_24h_bps')) self.percent: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'rate_24h')) self.ratio: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'rate_24h_ratio')) @@ -2901,6 +2923,14 @@ class ChangeRatePattern2: self.change: _1m1w1y24hPattern[StoredI64] = _1m1w1y24hPattern(client, _m(acc, 'change')) self.rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, _m(acc, 'rate')) +class ChangeRatePattern3: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.change_1m: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'change_1m')) + self.rate_1m: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'rate_1m')) + class HeightSumPattern: """Pattern struct for repeated tree structure.""" @@ -2909,6 +2939,14 @@ class HeightSumPattern: self.height: MetricPattern18[StoredU64] = MetricPattern18(client, acc) self.sum: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'sum')) +class RealizedSupplyPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.realized_cap: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'realized_cap')) + self.supply: MetricPattern1[Sats] = MetricPattern1(client, _m(acc, 'supply')) + class SdSmaPattern: """Pattern struct for repeated tree structure.""" @@ -4320,8 +4358,8 @@ class MetricsTree_Distribution_UtxoCohorts_All: self.relative: MetricsTree_Distribution_UtxoCohorts_All_Relative = MetricsTree_Distribution_UtxoCohorts_All_Relative(client) self.dormancy: MetricPattern1[StoredF32] = MetricPattern1(client, 'dormancy') self.velocity: MetricPattern1[StoredF32] = MetricPattern1(client, 'velocity') - self.supply_delta_extended: _24hChangeRatePattern[SatsSigned] = _24hChangeRatePattern(client, 'supply_delta') - self.utxo_count_delta_extended: _24hChangeRatePattern[StoredI64] = _24hChangeRatePattern(client, 'utxo_count_delta') + self.supply_delta_extended: _24hChangeRatePattern2[SatsSigned] = _24hChangeRatePattern2(client, 'supply_delta') + self.utxo_count_delta_extended: _24hChangeRatePattern2[StoredI64] = _24hChangeRatePattern2(client, 'utxo_count_delta') class MetricsTree_Distribution_UtxoCohorts_Sth: """Metrics tree node.""" @@ -4336,8 +4374,8 @@ class MetricsTree_Distribution_UtxoCohorts_Sth: self.relative: NetNuplSupplyUnrealizedPattern2 = NetNuplSupplyUnrealizedPattern2(client, 'sth') self.dormancy: MetricPattern1[StoredF32] = MetricPattern1(client, 'sth_dormancy') self.velocity: MetricPattern1[StoredF32] = MetricPattern1(client, 'sth_velocity') - self.supply_delta_extended: _24hChangeRatePattern[SatsSigned] = _24hChangeRatePattern(client, 'sth_supply_delta') - self.utxo_count_delta_extended: _24hChangeRatePattern[StoredI64] = _24hChangeRatePattern(client, 'sth_utxo_count_delta') + self.supply_delta_extended: _24hChangeRatePattern2[SatsSigned] = _24hChangeRatePattern2(client, 'sth_supply_delta') + self.utxo_count_delta_extended: _24hChangeRatePattern2[StoredI64] = _24hChangeRatePattern2(client, 'sth_utxo_count_delta') self.adjusted_value_created: MetricPattern1[Cents] = MetricPattern1(client, 'sth_adjusted_value_created') self.adjusted_value_destroyed: MetricPattern1[Cents] = MetricPattern1(client, 'sth_adjusted_value_destroyed') self.adjusted_value_created_sum: _1m1w1y24hPattern[Cents] = _1m1w1y24hPattern(client, 'sth_adjusted_value_created') @@ -4357,8 +4395,8 @@ class MetricsTree_Distribution_UtxoCohorts_Lth: self.relative: NetNuplSupplyUnrealizedPattern2 = NetNuplSupplyUnrealizedPattern2(client, 'lth') self.dormancy: MetricPattern1[StoredF32] = MetricPattern1(client, 'lth_dormancy') self.velocity: MetricPattern1[StoredF32] = MetricPattern1(client, 'lth_velocity') - self.supply_delta_extended: _24hChangeRatePattern[SatsSigned] = _24hChangeRatePattern(client, 'lth_supply_delta') - self.utxo_count_delta_extended: _24hChangeRatePattern[StoredI64] = _24hChangeRatePattern(client, 'lth_utxo_count_delta') + self.supply_delta_extended: _24hChangeRatePattern2[SatsSigned] = _24hChangeRatePattern2(client, 'lth_supply_delta') + self.utxo_count_delta_extended: _24hChangeRatePattern2[StoredI64] = _24hChangeRatePattern2(client, 'lth_utxo_count_delta') class MetricsTree_Distribution_UtxoCohorts_AgeRange: """Metrics tree node.""" @@ -4537,6 +4575,79 @@ class MetricsTree_Distribution_UtxoCohorts_Type: self.unknown: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'unknown_outputs') self.empty: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'empty_outputs') +class MetricsTree_Distribution_UtxoCohorts_Profitability_Range: + """Metrics tree node.""" + + def __init__(self, client: BrkClientBase, base_path: str = ''): + self.profit_over_1000: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_over_1000pct') + self.profit_500_to_1000: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_500_to_1000pct') + self.profit_300_to_500: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_300_to_500pct') + self.profit_200_to_300: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_200_to_300pct') + self.profit_100_to_200: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_100_to_200pct') + self.profit_90_to_100: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_90_to_100pct') + self.profit_80_to_90: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_80_to_90pct') + self.profit_70_to_80: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_70_to_80pct') + self.profit_60_to_70: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_60_to_70pct') + self.profit_50_to_60: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_50_to_60pct') + self.profit_40_to_50: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_40_to_50pct') + self.profit_30_to_40: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_30_to_40pct') + self.profit_20_to_30: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_20_to_30pct') + self.profit_10_to_20: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_10_to_20pct') + self.profit_0_to_10: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_0_to_10pct') + self.loss_0_to_10: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_0_to_10pct') + self.loss_10_to_20: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_10_to_20pct') + self.loss_20_to_30: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_20_to_30pct') + self.loss_30_to_40: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_30_to_40pct') + self.loss_40_to_50: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_40_to_50pct') + self.loss_50_to_60: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_50_to_60pct') + self.loss_60_to_70: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_60_to_70pct') + self.loss_70_to_80: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_70_to_80pct') + self.loss_80_to_90: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_80_to_90pct') + self.loss_90_to_100: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_90_to_100pct') + +class MetricsTree_Distribution_UtxoCohorts_Profitability_Profit: + """Metrics tree node.""" + + def __init__(self, client: BrkClientBase, base_path: str = ''): + self.breakeven: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_breakeven') + self._10pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_10pct') + self._20pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_20pct') + self._30pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_30pct') + self._40pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_40pct') + self._50pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_50pct') + self._60pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_60pct') + self._70pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_70pct') + self._80pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_80pct') + self._90pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_90pct') + self._100pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_100pct') + self._200pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_200pct') + self._300pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_300pct') + self._500pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_500pct') + self._1000pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'profit_ge_1000pct') + +class MetricsTree_Distribution_UtxoCohorts_Profitability_Loss: + """Metrics tree node.""" + + def __init__(self, client: BrkClientBase, base_path: str = ''): + self.breakeven: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_ge_breakeven') + self._10pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_ge_10pct') + self._20pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_ge_20pct') + self._30pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_ge_30pct') + self._40pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_ge_40pct') + self._50pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_ge_50pct') + self._60pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_ge_60pct') + self._70pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_ge_70pct') + self._80pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_ge_80pct') + self._90pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'loss_ge_90pct') + +class MetricsTree_Distribution_UtxoCohorts_Profitability: + """Metrics tree node.""" + + def __init__(self, client: BrkClientBase, base_path: str = ''): + self.range: MetricsTree_Distribution_UtxoCohorts_Profitability_Range = MetricsTree_Distribution_UtxoCohorts_Profitability_Range(client) + self.profit: MetricsTree_Distribution_UtxoCohorts_Profitability_Profit = MetricsTree_Distribution_UtxoCohorts_Profitability_Profit(client) + self.loss: MetricsTree_Distribution_UtxoCohorts_Profitability_Loss = MetricsTree_Distribution_UtxoCohorts_Profitability_Loss(client) + class MetricsTree_Distribution_UtxoCohorts: """Metrics tree node.""" @@ -4553,6 +4664,7 @@ class MetricsTree_Distribution_UtxoCohorts: self.amount_range: MetricsTree_Distribution_UtxoCohorts_AmountRange = MetricsTree_Distribution_UtxoCohorts_AmountRange(client) self.lt_amount: MetricsTree_Distribution_UtxoCohorts_LtAmount = MetricsTree_Distribution_UtxoCohorts_LtAmount(client) self.type_: MetricsTree_Distribution_UtxoCohorts_Type = MetricsTree_Distribution_UtxoCohorts_Type(client) + self.profitability: MetricsTree_Distribution_UtxoCohorts_Profitability = MetricsTree_Distribution_UtxoCohorts_Profitability(client) class MetricsTree_Distribution_AddressCohorts_GeAmount: """Metrics tree node.""" @@ -4692,6 +4804,16 @@ class MetricsTree_Supply_Velocity: self.btc: MetricPattern1[StoredF64] = MetricPattern1(client, 'velocity_btc') self.usd: MetricPattern1[StoredF64] = MetricPattern1(client, 'velocity_usd') +class MetricsTree_Supply_MarketCapDelta: + """Metrics tree node.""" + + def __init__(self, client: BrkClientBase, base_path: str = ''): + self.change_24h: CentsUsdPattern = CentsUsdPattern(client, 'market_cap_delta_change_24h') + self.change_1w: CentsUsdPattern = CentsUsdPattern(client, 'market_cap_delta_change_1w') + self.change_1m: CentsUsdPattern = CentsUsdPattern(client, 'market_cap_delta_change_1m') + self.change_1y: CentsUsdPattern = CentsUsdPattern(client, 'market_cap_delta_change_1y') + self.rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, 'market_cap_delta_rate') + class MetricsTree_Supply: """Metrics tree node.""" @@ -4700,9 +4822,8 @@ class MetricsTree_Supply: self.burned: MetricsTree_Supply_Burned = MetricsTree_Supply_Burned(client) self.inflation_rate: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'inflation_rate') self.velocity: MetricsTree_Supply_Velocity = MetricsTree_Supply_Velocity(client) - self.market_cap: MetricPattern1[Dollars] = MetricPattern1(client, 'market_cap') - self.market_cap_growth_rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, 'market_cap_growth_rate') - self.realized_cap_growth_rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, 'realized_cap_growth_rate') + self.market_cap: CentsUsdPattern = CentsUsdPattern(client, 'market_cap') + self.market_cap_delta: MetricsTree_Supply_MarketCapDelta = MetricsTree_Supply_MarketCapDelta(client) self.market_minus_realized_cap_growth_rate: _1m1w1y24hPattern[BasisPointsSigned32] = _1m1w1y24hPattern(client, 'market_minus_realized_cap_growth_rate') class MetricsTree: