mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-17 22:18:14 -07:00
global: add cohorts by entry
This commit is contained in:
@@ -6,9 +6,9 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use brk_cohort::{
|
||||
AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, CLASS_NAMES, EPOCH_NAMES, LOSS_NAMES, OVER_AGE_NAMES,
|
||||
OVER_AMOUNT_NAMES, PROFIT_NAMES, PROFITABILITY_RANGE_NAMES, SPENDABLE_TYPE_NAMES, TERM_NAMES,
|
||||
UNDER_AGE_NAMES, UNDER_AMOUNT_NAMES,
|
||||
AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, CLASS_NAMES, ENTRY_NAMES, EPOCH_NAMES, LOSS_NAMES,
|
||||
OVER_AGE_NAMES, OVER_AMOUNT_NAMES, PROFIT_NAMES, PROFITABILITY_RANGE_NAMES,
|
||||
SPENDABLE_TYPE_NAMES, TERM_NAMES, UNDER_AGE_NAMES, UNDER_AMOUNT_NAMES,
|
||||
};
|
||||
use brk_types::{Index, pools};
|
||||
use serde::Serialize;
|
||||
@@ -59,6 +59,7 @@ impl CohortConstants {
|
||||
("TERM_NAMES", to_value(&TERM_NAMES)),
|
||||
("EPOCH_NAMES", to_value(&EPOCH_NAMES)),
|
||||
("CLASS_NAMES", to_value(&CLASS_NAMES)),
|
||||
("ENTRY_NAMES", to_value(&ENTRY_NAMES)),
|
||||
("SPENDABLE_TYPE_NAMES", to_value(&SPENDABLE_TYPE_NAMES)),
|
||||
("AGE_RANGE_NAMES", to_value(&AGE_RANGE_NAMES)),
|
||||
("UNDER_AGE_NAMES", to_value(&UNDER_AGE_NAMES)),
|
||||
|
||||
+603
-18
@@ -1191,6 +1191,22 @@ pub struct CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern {
|
||||
pub sopr: AdjustedRatioValuePattern,
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2 {
|
||||
pub cap: CentsDeltaToUsdPattern,
|
||||
pub capitalized: PricePattern,
|
||||
pub gross_pnl: BlockCumulativeSumPattern,
|
||||
pub loss: BlockCumulativeNegativeSumPattern,
|
||||
pub mvrv: SeriesPattern1<StoredF32>,
|
||||
pub net_pnl: BlockChangeCumulativeDeltaSumPattern,
|
||||
pub peak_regret: BlockCumulativeSumPattern,
|
||||
pub price: BpsCentsPercentilesRatioSatsSmaStdUsdPattern,
|
||||
pub profit: BlockCumulativeSumPattern,
|
||||
pub profit_to_loss_ratio: _1m1w1y24hPattern<StoredF64>,
|
||||
pub sell_side_risk_ratio: _1m1w1y24hPattern8,
|
||||
pub sopr: RatioValuePattern2,
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct EmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2 {
|
||||
pub empty: _1m1w1y24hBpsPercentRatioPattern,
|
||||
@@ -1658,6 +1674,17 @@ pub struct ActiveInputOutputSpendablePattern {
|
||||
pub spendable_output_to_reused_addr_share: _1m1w1y24hBpsPercentRatioPattern,
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2 {
|
||||
pub activity: CoindaysCoinyearsDormancyTransferPattern,
|
||||
pub cost_basis: InMaxMinPerSupplyPattern,
|
||||
pub invested_capital: InPattern,
|
||||
pub outputs: SpendingSpentUnspentPattern,
|
||||
pub realized: CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2,
|
||||
pub supply: DeltaDominanceHalfInTotalPattern2,
|
||||
pub unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2,
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CapLossMvrvNetPriceProfitSoprPattern {
|
||||
pub cap: CentsDeltaUsdPattern,
|
||||
@@ -3408,6 +3435,22 @@ impl PriceRatioPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RatioValuePattern2 {
|
||||
pub ratio: _1m1w1y24hPattern<StoredF64>,
|
||||
pub value_destroyed: AverageBlockCumulativeSumPattern<Cents>,
|
||||
}
|
||||
|
||||
impl RatioValuePattern2 {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
ratio: _1m1w1y24hPattern::new(client.clone(), _m(&acc, "sopr")),
|
||||
value_destroyed: AverageBlockCumulativeSumPattern::new(client.clone(), _m(&acc, "value_destroyed")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RatioValuePattern {
|
||||
pub ratio: _24hPattern,
|
||||
@@ -7199,6 +7242,7 @@ pub struct SeriesTree_Cohorts_Utxo {
|
||||
pub over_age: SeriesTree_Cohorts_Utxo_OverAge,
|
||||
pub epoch: SeriesTree_Cohorts_Utxo_Epoch,
|
||||
pub class: SeriesTree_Cohorts_Utxo_Class,
|
||||
pub entry: SeriesTree_Cohorts_Utxo_Entry,
|
||||
pub over_amount: SeriesTree_Cohorts_Utxo_OverAmount,
|
||||
pub amount_range: SeriesTree_Cohorts_Utxo_AmountRange,
|
||||
pub under_amount: SeriesTree_Cohorts_Utxo_UnderAmount,
|
||||
@@ -7218,6 +7262,7 @@ impl SeriesTree_Cohorts_Utxo {
|
||||
over_age: SeriesTree_Cohorts_Utxo_OverAge::new(client.clone(), format!("{base_path}_over_age")),
|
||||
epoch: SeriesTree_Cohorts_Utxo_Epoch::new(client.clone(), format!("{base_path}_epoch")),
|
||||
class: SeriesTree_Cohorts_Utxo_Class::new(client.clone(), format!("{base_path}_class")),
|
||||
entry: SeriesTree_Cohorts_Utxo_Entry::new(client.clone(), format!("{base_path}_entry")),
|
||||
over_amount: SeriesTree_Cohorts_Utxo_OverAmount::new(client.clone(), format!("{base_path}_over_amount")),
|
||||
amount_range: SeriesTree_Cohorts_Utxo_AmountRange::new(client.clone(), format!("{base_path}_amount_range")),
|
||||
under_amount: SeriesTree_Cohorts_Utxo_UnderAmount::new(client.clone(), format!("{base_path}_under_amount")),
|
||||
@@ -7999,7 +8044,7 @@ pub struct SeriesTree_Cohorts_Utxo_Lth_Realized {
|
||||
pub price: SeriesTree_Cohorts_Utxo_Lth_Realized_Price,
|
||||
pub mvrv: SeriesPattern1<StoredF32>,
|
||||
pub net_pnl: BlockChangeCumulativeDeltaSumPattern,
|
||||
pub sopr: SeriesTree_Cohorts_Utxo_Lth_Realized_Sopr,
|
||||
pub sopr: RatioValuePattern2,
|
||||
pub gross_pnl: BlockCumulativeSumPattern,
|
||||
pub sell_side_risk_ratio: _1m1w1y24hPattern8,
|
||||
pub peak_regret: BlockCumulativeSumPattern,
|
||||
@@ -8016,7 +8061,7 @@ impl SeriesTree_Cohorts_Utxo_Lth_Realized {
|
||||
price: SeriesTree_Cohorts_Utxo_Lth_Realized_Price::new(client.clone(), format!("{base_path}_price")),
|
||||
mvrv: SeriesPattern1::new(client.clone(), "lth_mvrv".to_string()),
|
||||
net_pnl: BlockChangeCumulativeDeltaSumPattern::new(client.clone(), "lth_net".to_string()),
|
||||
sopr: SeriesTree_Cohorts_Utxo_Lth_Realized_Sopr::new(client.clone(), format!("{base_path}_sopr")),
|
||||
sopr: RatioValuePattern2::new(client.clone(), "lth".to_string()),
|
||||
gross_pnl: BlockCumulativeSumPattern::new(client.clone(), "lth_realized_gross_pnl".to_string()),
|
||||
sell_side_risk_ratio: _1m1w1y24hPattern8::new(client.clone(), "lth_sell_side_risk_ratio".to_string()),
|
||||
peak_regret: BlockCumulativeSumPattern::new(client.clone(), "lth_realized_peak_regret".to_string()),
|
||||
@@ -8236,21 +8281,6 @@ impl SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_1y {
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Sopr {
|
||||
pub value_destroyed: AverageBlockCumulativeSumPattern<Cents>,
|
||||
pub ratio: _1m1w1y24hPattern<StoredF64>,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Lth_Realized_Sopr {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
value_destroyed: AverageBlockCumulativeSumPattern::new(client.clone(), "lth_value_destroyed".to_string()),
|
||||
ratio: _1m1w1y24hPattern::new(client.clone(), "lth_sopr".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_AgeRange {
|
||||
pub under_1h: ActivityOutputsRealizedSupplyUnrealizedPattern,
|
||||
@@ -8466,6 +8496,561 @@ impl SeriesTree_Cohorts_Utxo_Class {
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry {
|
||||
pub discount: SeriesTree_Cohorts_Utxo_Entry_Discount,
|
||||
pub premium: SeriesTree_Cohorts_Utxo_Entry_Premium,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
discount: SeriesTree_Cohorts_Utxo_Entry_Discount::new(client.clone(), format!("{base_path}_discount")),
|
||||
premium: SeriesTree_Cohorts_Utxo_Entry_Premium::new(client.clone(), format!("{base_path}_premium")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Discount {
|
||||
pub supply: DeltaDominanceHalfInTotalPattern2,
|
||||
pub outputs: SpendingSpentUnspentPattern,
|
||||
pub activity: CoindaysCoinyearsDormancyTransferPattern,
|
||||
pub realized: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized,
|
||||
pub cost_basis: InMaxMinPerSupplyPattern,
|
||||
pub unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2,
|
||||
pub invested_capital: InPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Discount {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
supply: DeltaDominanceHalfInTotalPattern2::new(client.clone(), "veteran_supply".to_string()),
|
||||
outputs: SpendingSpentUnspentPattern::new(client.clone(), "veteran".to_string()),
|
||||
activity: CoindaysCoinyearsDormancyTransferPattern::new(client.clone(), "veteran".to_string()),
|
||||
realized: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized::new(client.clone(), format!("{base_path}_realized")),
|
||||
cost_basis: InMaxMinPerSupplyPattern::new(client.clone(), "veteran".to_string()),
|
||||
unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2::new(client.clone(), "veteran".to_string()),
|
||||
invested_capital: InPattern::new(client.clone(), "veteran_invested_capital_in".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Discount_Realized {
|
||||
pub cap: CentsDeltaToUsdPattern,
|
||||
pub profit: BlockCumulativeSumPattern,
|
||||
pub loss: BlockCumulativeNegativeSumPattern,
|
||||
pub price: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price,
|
||||
pub mvrv: SeriesPattern1<StoredF32>,
|
||||
pub net_pnl: BlockChangeCumulativeDeltaSumPattern,
|
||||
pub sopr: RatioValuePattern2,
|
||||
pub gross_pnl: BlockCumulativeSumPattern,
|
||||
pub sell_side_risk_ratio: _1m1w1y24hPattern8,
|
||||
pub peak_regret: BlockCumulativeSumPattern,
|
||||
pub capitalized: PricePattern,
|
||||
pub profit_to_loss_ratio: _1m1w1y24hPattern<StoredF64>,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Discount_Realized {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
cap: CentsDeltaToUsdPattern::new(client.clone(), "veteran_realized_cap".to_string()),
|
||||
profit: BlockCumulativeSumPattern::new(client.clone(), "veteran_realized_profit".to_string()),
|
||||
loss: BlockCumulativeNegativeSumPattern::new(client.clone(), "veteran_realized_loss".to_string()),
|
||||
price: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price::new(client.clone(), format!("{base_path}_price")),
|
||||
mvrv: SeriesPattern1::new(client.clone(), "veteran_mvrv".to_string()),
|
||||
net_pnl: BlockChangeCumulativeDeltaSumPattern::new(client.clone(), "veteran_net".to_string()),
|
||||
sopr: RatioValuePattern2::new(client.clone(), "veteran".to_string()),
|
||||
gross_pnl: BlockCumulativeSumPattern::new(client.clone(), "veteran_realized_gross_pnl".to_string()),
|
||||
sell_side_risk_ratio: _1m1w1y24hPattern8::new(client.clone(), "veteran_sell_side_risk_ratio".to_string()),
|
||||
peak_regret: BlockCumulativeSumPattern::new(client.clone(), "veteran_realized_peak_regret".to_string()),
|
||||
capitalized: PricePattern::new(client.clone(), "veteran_capitalized_price".to_string()),
|
||||
profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), "veteran_realized_profit_to_loss_ratio".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price {
|
||||
pub usd: SeriesPattern1<Dollars>,
|
||||
pub cents: SeriesPattern1<Cents>,
|
||||
pub sats: SeriesPattern1<SatsFract>,
|
||||
pub bps: SeriesPattern1<BasisPoints32>,
|
||||
pub ratio: SeriesPattern1<StoredF32>,
|
||||
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
|
||||
pub sma: _1m1w1y2y4yAllPattern,
|
||||
pub std_dev: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
usd: SeriesPattern1::new(client.clone(), "veteran_realized_price".to_string()),
|
||||
cents: SeriesPattern1::new(client.clone(), "veteran_realized_price_cents".to_string()),
|
||||
sats: SeriesPattern1::new(client.clone(), "veteran_realized_price_sats".to_string()),
|
||||
bps: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_bps".to_string()),
|
||||
ratio: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio".to_string()),
|
||||
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "veteran_realized_price".to_string()),
|
||||
sma: _1m1w1y2y4yAllPattern::new(client.clone(), "veteran_realized_price_ratio_sma".to_string()),
|
||||
std_dev: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev::new(client.clone(), format!("{base_path}_std_dev")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev {
|
||||
pub all: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_All,
|
||||
pub _4y: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_4y,
|
||||
pub _2y: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_2y,
|
||||
pub _1y: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_1y,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
all: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_All::new(client.clone(), format!("{base_path}_all")),
|
||||
_4y: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_4y::new(client.clone(), format!("{base_path}_4y")),
|
||||
_2y: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_2y::new(client.clone(), format!("{base_path}_2y")),
|
||||
_1y: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_1y::new(client.clone(), format!("{base_path}_1y")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_All {
|
||||
pub sd: SeriesPattern1<StoredF32>,
|
||||
pub zscore: SeriesPattern1<StoredF32>,
|
||||
pub _0sd: CentsSatsUsdPattern,
|
||||
pub p0_5sd: PriceRatioPattern,
|
||||
pub p1sd: PriceRatioPattern,
|
||||
pub p1_5sd: PriceRatioPattern,
|
||||
pub p2sd: PriceRatioPattern,
|
||||
pub p2_5sd: PriceRatioPattern,
|
||||
pub p3sd: PriceRatioPattern,
|
||||
pub m0_5sd: PriceRatioPattern,
|
||||
pub m1sd: PriceRatioPattern,
|
||||
pub m1_5sd: PriceRatioPattern,
|
||||
pub m2sd: PriceRatioPattern,
|
||||
pub m2_5sd: PriceRatioPattern,
|
||||
pub m3sd: PriceRatioPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_All {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
sd: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_sd".to_string()),
|
||||
zscore: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_zscore".to_string()),
|
||||
_0sd: CentsSatsUsdPattern::new(client.clone(), "veteran_realized_price_0sd".to_string()),
|
||||
p0_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p0_5sd".to_string()),
|
||||
p1sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p1sd".to_string()),
|
||||
p1_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p1_5sd".to_string()),
|
||||
p2sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p2sd".to_string()),
|
||||
p2_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p2_5sd".to_string()),
|
||||
p3sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p3sd".to_string()),
|
||||
m0_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m0_5sd".to_string()),
|
||||
m1sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m1sd".to_string()),
|
||||
m1_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m1_5sd".to_string()),
|
||||
m2sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m2sd".to_string()),
|
||||
m2_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m2_5sd".to_string()),
|
||||
m3sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m3sd".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_4y {
|
||||
pub sd: SeriesPattern1<StoredF32>,
|
||||
pub zscore: SeriesPattern1<StoredF32>,
|
||||
pub _0sd: CentsSatsUsdPattern,
|
||||
pub p0_5sd: PriceRatioPattern,
|
||||
pub p1sd: PriceRatioPattern,
|
||||
pub p1_5sd: PriceRatioPattern,
|
||||
pub p2sd: PriceRatioPattern,
|
||||
pub p2_5sd: PriceRatioPattern,
|
||||
pub p3sd: PriceRatioPattern,
|
||||
pub m0_5sd: PriceRatioPattern,
|
||||
pub m1sd: PriceRatioPattern,
|
||||
pub m1_5sd: PriceRatioPattern,
|
||||
pub m2sd: PriceRatioPattern,
|
||||
pub m2_5sd: PriceRatioPattern,
|
||||
pub m3sd: PriceRatioPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_4y {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
sd: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_sd_4y".to_string()),
|
||||
zscore: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_zscore_4y".to_string()),
|
||||
_0sd: CentsSatsUsdPattern::new(client.clone(), "veteran_realized_price_0sd_4y".to_string()),
|
||||
p0_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p0_5sd_4y".to_string()),
|
||||
p1sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p1sd_4y".to_string()),
|
||||
p1_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p1_5sd_4y".to_string()),
|
||||
p2sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p2sd_4y".to_string()),
|
||||
p2_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p2_5sd_4y".to_string()),
|
||||
p3sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p3sd_4y".to_string()),
|
||||
m0_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m0_5sd_4y".to_string()),
|
||||
m1sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m1sd_4y".to_string()),
|
||||
m1_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m1_5sd_4y".to_string()),
|
||||
m2sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m2sd_4y".to_string()),
|
||||
m2_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m2_5sd_4y".to_string()),
|
||||
m3sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m3sd_4y".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_2y {
|
||||
pub sd: SeriesPattern1<StoredF32>,
|
||||
pub zscore: SeriesPattern1<StoredF32>,
|
||||
pub _0sd: CentsSatsUsdPattern,
|
||||
pub p0_5sd: PriceRatioPattern,
|
||||
pub p1sd: PriceRatioPattern,
|
||||
pub p1_5sd: PriceRatioPattern,
|
||||
pub p2sd: PriceRatioPattern,
|
||||
pub p2_5sd: PriceRatioPattern,
|
||||
pub p3sd: PriceRatioPattern,
|
||||
pub m0_5sd: PriceRatioPattern,
|
||||
pub m1sd: PriceRatioPattern,
|
||||
pub m1_5sd: PriceRatioPattern,
|
||||
pub m2sd: PriceRatioPattern,
|
||||
pub m2_5sd: PriceRatioPattern,
|
||||
pub m3sd: PriceRatioPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_2y {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
sd: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_sd_2y".to_string()),
|
||||
zscore: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_zscore_2y".to_string()),
|
||||
_0sd: CentsSatsUsdPattern::new(client.clone(), "veteran_realized_price_0sd_2y".to_string()),
|
||||
p0_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p0_5sd_2y".to_string()),
|
||||
p1sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p1sd_2y".to_string()),
|
||||
p1_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p1_5sd_2y".to_string()),
|
||||
p2sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p2sd_2y".to_string()),
|
||||
p2_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p2_5sd_2y".to_string()),
|
||||
p3sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p3sd_2y".to_string()),
|
||||
m0_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m0_5sd_2y".to_string()),
|
||||
m1sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m1sd_2y".to_string()),
|
||||
m1_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m1_5sd_2y".to_string()),
|
||||
m2sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m2sd_2y".to_string()),
|
||||
m2_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m2_5sd_2y".to_string()),
|
||||
m3sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m3sd_2y".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_1y {
|
||||
pub sd: SeriesPattern1<StoredF32>,
|
||||
pub zscore: SeriesPattern1<StoredF32>,
|
||||
pub _0sd: CentsSatsUsdPattern,
|
||||
pub p0_5sd: PriceRatioPattern,
|
||||
pub p1sd: PriceRatioPattern,
|
||||
pub p1_5sd: PriceRatioPattern,
|
||||
pub p2sd: PriceRatioPattern,
|
||||
pub p2_5sd: PriceRatioPattern,
|
||||
pub p3sd: PriceRatioPattern,
|
||||
pub m0_5sd: PriceRatioPattern,
|
||||
pub m1sd: PriceRatioPattern,
|
||||
pub m1_5sd: PriceRatioPattern,
|
||||
pub m2sd: PriceRatioPattern,
|
||||
pub m2_5sd: PriceRatioPattern,
|
||||
pub m3sd: PriceRatioPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_1y {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
sd: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_sd_1y".to_string()),
|
||||
zscore: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_zscore_1y".to_string()),
|
||||
_0sd: CentsSatsUsdPattern::new(client.clone(), "veteran_realized_price_0sd_1y".to_string()),
|
||||
p0_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p0_5sd_1y".to_string()),
|
||||
p1sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p1sd_1y".to_string()),
|
||||
p1_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p1_5sd_1y".to_string()),
|
||||
p2sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p2sd_1y".to_string()),
|
||||
p2_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p2_5sd_1y".to_string()),
|
||||
p3sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "p3sd_1y".to_string()),
|
||||
m0_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m0_5sd_1y".to_string()),
|
||||
m1sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m1sd_1y".to_string()),
|
||||
m1_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m1_5sd_1y".to_string()),
|
||||
m2sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m2sd_1y".to_string()),
|
||||
m2_5sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m2_5sd_1y".to_string()),
|
||||
m3sd: PriceRatioPattern::new(client.clone(), "veteran_realized_price".to_string(), "m3sd_1y".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Premium {
|
||||
pub supply: DeltaDominanceHalfInTotalPattern2,
|
||||
pub outputs: SpendingSpentUnspentPattern,
|
||||
pub activity: CoindaysCoinyearsDormancyTransferPattern,
|
||||
pub realized: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized,
|
||||
pub cost_basis: InMaxMinPerSupplyPattern,
|
||||
pub unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2,
|
||||
pub invested_capital: InPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Premium {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
supply: DeltaDominanceHalfInTotalPattern2::new(client.clone(), "rookie_supply".to_string()),
|
||||
outputs: SpendingSpentUnspentPattern::new(client.clone(), "rookie".to_string()),
|
||||
activity: CoindaysCoinyearsDormancyTransferPattern::new(client.clone(), "rookie".to_string()),
|
||||
realized: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized::new(client.clone(), format!("{base_path}_realized")),
|
||||
cost_basis: InMaxMinPerSupplyPattern::new(client.clone(), "rookie".to_string()),
|
||||
unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2::new(client.clone(), "rookie".to_string()),
|
||||
invested_capital: InPattern::new(client.clone(), "rookie_invested_capital_in".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Premium_Realized {
|
||||
pub cap: CentsDeltaToUsdPattern,
|
||||
pub profit: BlockCumulativeSumPattern,
|
||||
pub loss: BlockCumulativeNegativeSumPattern,
|
||||
pub price: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price,
|
||||
pub mvrv: SeriesPattern1<StoredF32>,
|
||||
pub net_pnl: BlockChangeCumulativeDeltaSumPattern,
|
||||
pub sopr: RatioValuePattern2,
|
||||
pub gross_pnl: BlockCumulativeSumPattern,
|
||||
pub sell_side_risk_ratio: _1m1w1y24hPattern8,
|
||||
pub peak_regret: BlockCumulativeSumPattern,
|
||||
pub capitalized: PricePattern,
|
||||
pub profit_to_loss_ratio: _1m1w1y24hPattern<StoredF64>,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Premium_Realized {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
cap: CentsDeltaToUsdPattern::new(client.clone(), "rookie_realized_cap".to_string()),
|
||||
profit: BlockCumulativeSumPattern::new(client.clone(), "rookie_realized_profit".to_string()),
|
||||
loss: BlockCumulativeNegativeSumPattern::new(client.clone(), "rookie_realized_loss".to_string()),
|
||||
price: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price::new(client.clone(), format!("{base_path}_price")),
|
||||
mvrv: SeriesPattern1::new(client.clone(), "rookie_mvrv".to_string()),
|
||||
net_pnl: BlockChangeCumulativeDeltaSumPattern::new(client.clone(), "rookie_net".to_string()),
|
||||
sopr: RatioValuePattern2::new(client.clone(), "rookie".to_string()),
|
||||
gross_pnl: BlockCumulativeSumPattern::new(client.clone(), "rookie_realized_gross_pnl".to_string()),
|
||||
sell_side_risk_ratio: _1m1w1y24hPattern8::new(client.clone(), "rookie_sell_side_risk_ratio".to_string()),
|
||||
peak_regret: BlockCumulativeSumPattern::new(client.clone(), "rookie_realized_peak_regret".to_string()),
|
||||
capitalized: PricePattern::new(client.clone(), "rookie_capitalized_price".to_string()),
|
||||
profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), "rookie_realized_profit_to_loss_ratio".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price {
|
||||
pub usd: SeriesPattern1<Dollars>,
|
||||
pub cents: SeriesPattern1<Cents>,
|
||||
pub sats: SeriesPattern1<SatsFract>,
|
||||
pub bps: SeriesPattern1<BasisPoints32>,
|
||||
pub ratio: SeriesPattern1<StoredF32>,
|
||||
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
|
||||
pub sma: _1m1w1y2y4yAllPattern,
|
||||
pub std_dev: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
usd: SeriesPattern1::new(client.clone(), "rookie_realized_price".to_string()),
|
||||
cents: SeriesPattern1::new(client.clone(), "rookie_realized_price_cents".to_string()),
|
||||
sats: SeriesPattern1::new(client.clone(), "rookie_realized_price_sats".to_string()),
|
||||
bps: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_bps".to_string()),
|
||||
ratio: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio".to_string()),
|
||||
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "rookie_realized_price".to_string()),
|
||||
sma: _1m1w1y2y4yAllPattern::new(client.clone(), "rookie_realized_price_ratio_sma".to_string()),
|
||||
std_dev: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev::new(client.clone(), format!("{base_path}_std_dev")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev {
|
||||
pub all: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_All,
|
||||
pub _4y: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_4y,
|
||||
pub _2y: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_2y,
|
||||
pub _1y: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_1y,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
all: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_All::new(client.clone(), format!("{base_path}_all")),
|
||||
_4y: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_4y::new(client.clone(), format!("{base_path}_4y")),
|
||||
_2y: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_2y::new(client.clone(), format!("{base_path}_2y")),
|
||||
_1y: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_1y::new(client.clone(), format!("{base_path}_1y")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_All {
|
||||
pub sd: SeriesPattern1<StoredF32>,
|
||||
pub zscore: SeriesPattern1<StoredF32>,
|
||||
pub _0sd: CentsSatsUsdPattern,
|
||||
pub p0_5sd: PriceRatioPattern,
|
||||
pub p1sd: PriceRatioPattern,
|
||||
pub p1_5sd: PriceRatioPattern,
|
||||
pub p2sd: PriceRatioPattern,
|
||||
pub p2_5sd: PriceRatioPattern,
|
||||
pub p3sd: PriceRatioPattern,
|
||||
pub m0_5sd: PriceRatioPattern,
|
||||
pub m1sd: PriceRatioPattern,
|
||||
pub m1_5sd: PriceRatioPattern,
|
||||
pub m2sd: PriceRatioPattern,
|
||||
pub m2_5sd: PriceRatioPattern,
|
||||
pub m3sd: PriceRatioPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_All {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
sd: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_sd".to_string()),
|
||||
zscore: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_zscore".to_string()),
|
||||
_0sd: CentsSatsUsdPattern::new(client.clone(), "rookie_realized_price_0sd".to_string()),
|
||||
p0_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p0_5sd".to_string()),
|
||||
p1sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p1sd".to_string()),
|
||||
p1_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p1_5sd".to_string()),
|
||||
p2sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p2sd".to_string()),
|
||||
p2_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p2_5sd".to_string()),
|
||||
p3sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p3sd".to_string()),
|
||||
m0_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m0_5sd".to_string()),
|
||||
m1sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m1sd".to_string()),
|
||||
m1_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m1_5sd".to_string()),
|
||||
m2sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m2sd".to_string()),
|
||||
m2_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m2_5sd".to_string()),
|
||||
m3sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m3sd".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_4y {
|
||||
pub sd: SeriesPattern1<StoredF32>,
|
||||
pub zscore: SeriesPattern1<StoredF32>,
|
||||
pub _0sd: CentsSatsUsdPattern,
|
||||
pub p0_5sd: PriceRatioPattern,
|
||||
pub p1sd: PriceRatioPattern,
|
||||
pub p1_5sd: PriceRatioPattern,
|
||||
pub p2sd: PriceRatioPattern,
|
||||
pub p2_5sd: PriceRatioPattern,
|
||||
pub p3sd: PriceRatioPattern,
|
||||
pub m0_5sd: PriceRatioPattern,
|
||||
pub m1sd: PriceRatioPattern,
|
||||
pub m1_5sd: PriceRatioPattern,
|
||||
pub m2sd: PriceRatioPattern,
|
||||
pub m2_5sd: PriceRatioPattern,
|
||||
pub m3sd: PriceRatioPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_4y {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
sd: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_sd_4y".to_string()),
|
||||
zscore: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_zscore_4y".to_string()),
|
||||
_0sd: CentsSatsUsdPattern::new(client.clone(), "rookie_realized_price_0sd_4y".to_string()),
|
||||
p0_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p0_5sd_4y".to_string()),
|
||||
p1sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p1sd_4y".to_string()),
|
||||
p1_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p1_5sd_4y".to_string()),
|
||||
p2sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p2sd_4y".to_string()),
|
||||
p2_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p2_5sd_4y".to_string()),
|
||||
p3sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p3sd_4y".to_string()),
|
||||
m0_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m0_5sd_4y".to_string()),
|
||||
m1sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m1sd_4y".to_string()),
|
||||
m1_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m1_5sd_4y".to_string()),
|
||||
m2sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m2sd_4y".to_string()),
|
||||
m2_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m2_5sd_4y".to_string()),
|
||||
m3sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m3sd_4y".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_2y {
|
||||
pub sd: SeriesPattern1<StoredF32>,
|
||||
pub zscore: SeriesPattern1<StoredF32>,
|
||||
pub _0sd: CentsSatsUsdPattern,
|
||||
pub p0_5sd: PriceRatioPattern,
|
||||
pub p1sd: PriceRatioPattern,
|
||||
pub p1_5sd: PriceRatioPattern,
|
||||
pub p2sd: PriceRatioPattern,
|
||||
pub p2_5sd: PriceRatioPattern,
|
||||
pub p3sd: PriceRatioPattern,
|
||||
pub m0_5sd: PriceRatioPattern,
|
||||
pub m1sd: PriceRatioPattern,
|
||||
pub m1_5sd: PriceRatioPattern,
|
||||
pub m2sd: PriceRatioPattern,
|
||||
pub m2_5sd: PriceRatioPattern,
|
||||
pub m3sd: PriceRatioPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_2y {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
sd: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_sd_2y".to_string()),
|
||||
zscore: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_zscore_2y".to_string()),
|
||||
_0sd: CentsSatsUsdPattern::new(client.clone(), "rookie_realized_price_0sd_2y".to_string()),
|
||||
p0_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p0_5sd_2y".to_string()),
|
||||
p1sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p1sd_2y".to_string()),
|
||||
p1_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p1_5sd_2y".to_string()),
|
||||
p2sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p2sd_2y".to_string()),
|
||||
p2_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p2_5sd_2y".to_string()),
|
||||
p3sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p3sd_2y".to_string()),
|
||||
m0_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m0_5sd_2y".to_string()),
|
||||
m1sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m1sd_2y".to_string()),
|
||||
m1_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m1_5sd_2y".to_string()),
|
||||
m2sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m2sd_2y".to_string()),
|
||||
m2_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m2_5sd_2y".to_string()),
|
||||
m3sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m3sd_2y".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_1y {
|
||||
pub sd: SeriesPattern1<StoredF32>,
|
||||
pub zscore: SeriesPattern1<StoredF32>,
|
||||
pub _0sd: CentsSatsUsdPattern,
|
||||
pub p0_5sd: PriceRatioPattern,
|
||||
pub p1sd: PriceRatioPattern,
|
||||
pub p1_5sd: PriceRatioPattern,
|
||||
pub p2sd: PriceRatioPattern,
|
||||
pub p2_5sd: PriceRatioPattern,
|
||||
pub p3sd: PriceRatioPattern,
|
||||
pub m0_5sd: PriceRatioPattern,
|
||||
pub m1sd: PriceRatioPattern,
|
||||
pub m1_5sd: PriceRatioPattern,
|
||||
pub m2sd: PriceRatioPattern,
|
||||
pub m2_5sd: PriceRatioPattern,
|
||||
pub m3sd: PriceRatioPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_1y {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
sd: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_sd_1y".to_string()),
|
||||
zscore: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_zscore_1y".to_string()),
|
||||
_0sd: CentsSatsUsdPattern::new(client.clone(), "rookie_realized_price_0sd_1y".to_string()),
|
||||
p0_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p0_5sd_1y".to_string()),
|
||||
p1sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p1sd_1y".to_string()),
|
||||
p1_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p1_5sd_1y".to_string()),
|
||||
p2sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p2sd_1y".to_string()),
|
||||
p2_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p2_5sd_1y".to_string()),
|
||||
p3sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "p3sd_1y".to_string()),
|
||||
m0_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m0_5sd_1y".to_string()),
|
||||
m1sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m1sd_1y".to_string()),
|
||||
m1_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m1_5sd_1y".to_string()),
|
||||
m2sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m2sd_1y".to_string()),
|
||||
m2_5sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m2_5sd_1y".to_string()),
|
||||
m3sd: PriceRatioPattern::new(client.clone(), "rookie_realized_price".to_string(), "m3sd_1y".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Cohorts_Utxo_OverAmount {
|
||||
pub _1sat: ActivityOutputsRealizedSupplyUnrealizedPattern2,
|
||||
@@ -8953,7 +9538,7 @@ pub struct BrkClient {
|
||||
|
||||
impl BrkClient {
|
||||
/// Client version.
|
||||
pub const VERSION: &'static str = "v0.3.2";
|
||||
pub const VERSION: &'static str = "v0.3.3";
|
||||
|
||||
/// Create a new client with the given base URL.
|
||||
pub fn new(base_url: impl Into<String>) -> Self {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
use brk_traversable::Traversable;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{CohortName, Filter};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EntryPrice {
|
||||
Discount,
|
||||
Premium,
|
||||
}
|
||||
|
||||
impl EntryPrice {
|
||||
#[inline]
|
||||
pub const fn from_is_discount(is_discount: bool) -> Self {
|
||||
if is_discount {
|
||||
Self::Discount
|
||||
} else {
|
||||
Self::Premium
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn is_discount(self) -> bool {
|
||||
matches!(self, Self::Discount)
|
||||
}
|
||||
}
|
||||
|
||||
pub const ENTRY_FILTERS: ByEntry<Filter> = ByEntry {
|
||||
discount: Filter::Entry(EntryPrice::Discount),
|
||||
premium: Filter::Entry(EntryPrice::Premium),
|
||||
};
|
||||
|
||||
pub const ENTRY_NAMES: ByEntry<CohortName> = ByEntry {
|
||||
discount: CohortName::new("veteran", "Veteran", "Veteran Coins"),
|
||||
premium: CohortName::new("rookie", "Rookie", "Rookie Coins"),
|
||||
};
|
||||
|
||||
#[derive(Default, Clone, Traversable, Serialize)]
|
||||
pub struct ByEntry<T> {
|
||||
pub discount: T,
|
||||
pub premium: T,
|
||||
}
|
||||
|
||||
impl ByEntry<CohortName> {
|
||||
pub const fn names() -> &'static Self {
|
||||
&ENTRY_NAMES
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ByEntry<T> {
|
||||
pub fn new<F>(mut create: F) -> Self
|
||||
where
|
||||
F: FnMut(Filter, &'static str) -> T,
|
||||
{
|
||||
let f = ENTRY_FILTERS;
|
||||
let n = ENTRY_NAMES;
|
||||
Self {
|
||||
discount: create(f.discount, n.discount.id),
|
||||
premium: create(f.premium, n.premium.id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnMut(Filter, &'static str) -> Result<T, E>,
|
||||
{
|
||||
let f = ENTRY_FILTERS;
|
||||
let n = ENTRY_NAMES;
|
||||
Ok(Self {
|
||||
discount: create(f.discount, n.discount.id)?,
|
||||
premium: create(f.premium, n.premium.id)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get(&self, entry: EntryPrice) -> &T {
|
||||
match entry {
|
||||
EntryPrice::Discount => &self.discount,
|
||||
EntryPrice::Premium => &self.premium,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, entry: EntryPrice) -> &mut T {
|
||||
match entry {
|
||||
EntryPrice::Discount => &mut self.discount,
|
||||
EntryPrice::Premium => &mut self.premium,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||
[&self.discount, &self.premium].into_iter()
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||
[&mut self.discount, &mut self.premium].into_iter()
|
||||
}
|
||||
|
||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut T>
|
||||
where
|
||||
T: Send + Sync,
|
||||
{
|
||||
[&mut self.discount, &mut self.premium].into_par_iter()
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ impl CohortContext {
|
||||
/// Build full name for a filter, adding prefix only for Time/Amount filters.
|
||||
///
|
||||
/// Prefix rules:
|
||||
/// - No prefix: `All`, `Term`, `Epoch`, `Class`, `Type`
|
||||
/// - No prefix: `All`, `Term`, `Epoch`, `Class`, `Entry`, `Type`
|
||||
/// - Context prefix: `Time`, `Amount`
|
||||
pub fn full_name(&self, filter: &Filter, name: &str) -> String {
|
||||
match filter {
|
||||
@@ -32,6 +32,7 @@ impl CohortContext {
|
||||
| Filter::Term(_)
|
||||
| Filter::Epoch(_)
|
||||
| Filter::Class(_)
|
||||
| Filter::Entry(_)
|
||||
| Filter::Type(_) => name.to_string(),
|
||||
Filter::Time(_) | Filter::Amount(_) => self.prefixed(name),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_types::{Halving, OutputType, Sats, Year};
|
||||
|
||||
use super::{AmountFilter, CohortContext, Term, TimeFilter};
|
||||
use super::{AmountFilter, CohortContext, EntryPrice, Term, TimeFilter};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Filter {
|
||||
@@ -10,6 +10,7 @@ pub enum Filter {
|
||||
Amount(AmountFilter),
|
||||
Epoch(Halving),
|
||||
Class(Year),
|
||||
Entry(EntryPrice),
|
||||
Type(OutputType),
|
||||
}
|
||||
|
||||
@@ -68,7 +69,8 @@ impl Filter {
|
||||
}
|
||||
|
||||
/// Whether to compute extended metrics (realized cap ratios, profit/loss ratios, percentiles)
|
||||
/// For UTXO context: true only for age range cohorts (Range) and aggregate cohorts (All, Term)
|
||||
/// For UTXO context: true for age range cohorts (Range), aggregate cohorts (All, Term),
|
||||
/// and immutable entry valuation cohorts.
|
||||
/// For address context: always false
|
||||
pub fn is_extended(&self, context: CohortContext) -> bool {
|
||||
match context {
|
||||
@@ -76,7 +78,10 @@ impl Filter {
|
||||
CohortContext::Utxo => {
|
||||
matches!(
|
||||
self,
|
||||
Filter::All | Filter::Term(_) | Filter::Time(TimeFilter::Range(_))
|
||||
Filter::All
|
||||
| Filter::Term(_)
|
||||
| Filter::Time(TimeFilter::Range(_))
|
||||
| Filter::Entry(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ mod amount_range;
|
||||
mod by_addr_type;
|
||||
mod by_any_addr;
|
||||
mod by_epoch;
|
||||
mod by_entry;
|
||||
mod by_term;
|
||||
mod by_type;
|
||||
mod class;
|
||||
@@ -36,6 +37,7 @@ pub use amount_range::*;
|
||||
pub use by_addr_type::*;
|
||||
pub use by_any_addr::*;
|
||||
pub use by_epoch::*;
|
||||
pub use by_entry::*;
|
||||
pub use by_term::*;
|
||||
pub use by_type::*;
|
||||
pub use class::*;
|
||||
|
||||
@@ -2,8 +2,8 @@ use brk_traversable::Traversable;
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::{
|
||||
AgeRange, AmountRange, ByEpoch, ByTerm, Class, Filter, OverAge, OverAmount, SpendableType,
|
||||
UnderAge, UnderAmount,
|
||||
AgeRange, AmountRange, ByEntry, ByEpoch, ByTerm, Class, Filter, OverAge, OverAmount,
|
||||
SpendableType, UnderAge, UnderAmount,
|
||||
};
|
||||
|
||||
#[derive(Default, Clone, Traversable)]
|
||||
@@ -12,6 +12,7 @@ pub struct UTXOGroups<T> {
|
||||
pub age_range: AgeRange<T>,
|
||||
pub epoch: ByEpoch<T>,
|
||||
pub class: Class<T>,
|
||||
pub entry: ByEntry<T>,
|
||||
pub over_age: OverAge<T>,
|
||||
pub over_amount: OverAmount<T>,
|
||||
pub amount_range: AmountRange<T>,
|
||||
@@ -31,6 +32,7 @@ impl<T> UTXOGroups<T> {
|
||||
age_range: AgeRange::new(&mut create),
|
||||
epoch: ByEpoch::new(&mut create),
|
||||
class: Class::new(&mut create),
|
||||
entry: ByEntry::new(&mut create),
|
||||
over_age: OverAge::new(&mut create),
|
||||
over_amount: OverAmount::new(&mut create),
|
||||
amount_range: AmountRange::new(&mut create),
|
||||
@@ -51,6 +53,7 @@ impl<T> UTXOGroups<T> {
|
||||
.chain(self.age_range.iter())
|
||||
.chain(self.epoch.iter())
|
||||
.chain(self.class.iter())
|
||||
.chain(self.entry.iter())
|
||||
.chain(self.amount_range.iter())
|
||||
.chain(self.under_amount.iter())
|
||||
.chain(self.type_.iter())
|
||||
@@ -66,6 +69,7 @@ impl<T> UTXOGroups<T> {
|
||||
.chain(self.age_range.iter_mut())
|
||||
.chain(self.epoch.iter_mut())
|
||||
.chain(self.class.iter_mut())
|
||||
.chain(self.entry.iter_mut())
|
||||
.chain(self.amount_range.iter_mut())
|
||||
.chain(self.under_amount.iter_mut())
|
||||
.chain(self.type_.iter_mut())
|
||||
@@ -84,6 +88,7 @@ impl<T> UTXOGroups<T> {
|
||||
.chain(self.age_range.par_iter_mut())
|
||||
.chain(self.epoch.par_iter_mut())
|
||||
.chain(self.class.par_iter_mut())
|
||||
.chain(self.entry.par_iter_mut())
|
||||
.chain(self.amount_range.par_iter_mut())
|
||||
.chain(self.under_amount.par_iter_mut())
|
||||
.chain(self.type_.par_iter_mut())
|
||||
@@ -94,6 +99,7 @@ impl<T> UTXOGroups<T> {
|
||||
.iter()
|
||||
.chain(self.epoch.iter())
|
||||
.chain(self.class.iter())
|
||||
.chain(self.entry.iter())
|
||||
.chain(self.amount_range.iter())
|
||||
.chain(self.type_.iter())
|
||||
}
|
||||
@@ -103,6 +109,7 @@ impl<T> UTXOGroups<T> {
|
||||
.iter_mut()
|
||||
.chain(self.epoch.iter_mut())
|
||||
.chain(self.class.iter_mut())
|
||||
.chain(self.entry.iter_mut())
|
||||
.chain(self.amount_range.iter_mut())
|
||||
.chain(self.type_.iter_mut())
|
||||
}
|
||||
@@ -115,6 +122,7 @@ impl<T> UTXOGroups<T> {
|
||||
.par_iter_mut()
|
||||
.chain(self.epoch.par_iter_mut())
|
||||
.chain(self.class.par_iter_mut())
|
||||
.chain(self.entry.par_iter_mut())
|
||||
.chain(self.amount_range.par_iter_mut())
|
||||
.chain(self.type_.par_iter_mut())
|
||||
}
|
||||
|
||||
@@ -30,18 +30,34 @@ const TREE_SIZE: usize = TIER0_COUNT + TIER1_COUNT + OVERFLOW; // 190,001
|
||||
pub(super) struct CostBasisNode {
|
||||
all_sats: i64,
|
||||
sth_sats: i64,
|
||||
discount_sats: i64,
|
||||
all_usd: i128,
|
||||
sth_usd: i128,
|
||||
discount_usd: i128,
|
||||
}
|
||||
|
||||
impl CostBasisNode {
|
||||
#[inline(always)]
|
||||
fn new(sats: i64, usd: i128, is_sth: bool) -> Self {
|
||||
fn new_supply(sats: i64, usd: i128, is_sth: bool) -> Self {
|
||||
Self {
|
||||
all_sats: sats,
|
||||
sth_sats: if is_sth { sats } else { 0 },
|
||||
discount_sats: 0,
|
||||
all_usd: usd,
|
||||
sth_usd: if is_sth { usd } else { 0 },
|
||||
discount_usd: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn new_discount(sats: i64, usd: i128) -> Self {
|
||||
Self {
|
||||
all_sats: 0,
|
||||
sth_sats: 0,
|
||||
discount_sats: sats,
|
||||
all_usd: 0,
|
||||
sth_usd: 0,
|
||||
discount_usd: usd,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,8 +67,10 @@ impl FenwickNode for CostBasisNode {
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
self.all_sats += other.all_sats;
|
||||
self.sth_sats += other.sth_sats;
|
||||
self.discount_sats += other.discount_sats;
|
||||
self.all_usd += other.all_usd;
|
||||
self.sth_usd += other.sth_usd;
|
||||
self.discount_usd += other.discount_usd;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,16 +169,34 @@ impl CostBasisFenwick {
|
||||
}
|
||||
let bucket = price_to_bucket(price);
|
||||
let delta =
|
||||
CostBasisNode::new(net_sats, price.as_u128() as i128 * net_sats as i128, is_sth);
|
||||
CostBasisNode::new_supply(net_sats, price.as_u128() as i128 * net_sats as i128, is_sth);
|
||||
self.tree.add(bucket, &delta);
|
||||
self.totals.add_assign(&delta);
|
||||
}
|
||||
|
||||
/// Bulk-initialize from BTreeMaps (one per age-range cohort).
|
||||
/// Call after state import when all pending maps have been drained.
|
||||
pub(super) fn bulk_init<'a>(
|
||||
/// Apply a net delta from the discount-entry cohort.
|
||||
///
|
||||
/// Supply totals are maintained from the age-range cohorts; this updates
|
||||
/// only the discount-entry partition so premium can be derived as all - discount.
|
||||
pub(super) fn apply_discount_delta(&mut self, price: CentsCompact, pending: &PendingDelta) {
|
||||
let net_sats = u64::from(pending.inc) as i64 - u64::from(pending.dec) as i64;
|
||||
if net_sats == 0 {
|
||||
return;
|
||||
}
|
||||
let bucket = price_to_bucket(price);
|
||||
let delta =
|
||||
CostBasisNode::new_discount(net_sats, price.as_u128() as i128 * net_sats as i128);
|
||||
self.tree.add(bucket, &delta);
|
||||
self.totals.add_assign(&delta);
|
||||
}
|
||||
|
||||
/// Bulk-initialize from age-range maps plus the discount-entry map.
|
||||
/// Age-range maps maintain all/STH/LTH totals; the discount-entry map
|
||||
/// maintains only the discount partition used to derive premium.
|
||||
pub(super) fn bulk_init_with_discount<'a>(
|
||||
&mut self,
|
||||
maps: impl Iterator<Item = (&'a std::collections::BTreeMap<CentsCompact, Sats>, bool)>,
|
||||
discount_maps: impl Iterator<Item = &'a std::collections::BTreeMap<CentsCompact, Sats>>,
|
||||
) {
|
||||
self.tree.reset();
|
||||
self.totals = CostBasisNode::default();
|
||||
@@ -169,7 +205,18 @@ impl CostBasisFenwick {
|
||||
for (&price, &sats) in map.iter() {
|
||||
let bucket = price_to_bucket(price);
|
||||
let s = u64::from(sats) as i64;
|
||||
let node = CostBasisNode::new(s, price.as_u128() as i128 * s as i128, is_sth);
|
||||
let node =
|
||||
CostBasisNode::new_supply(s, price.as_u128() as i128 * s as i128, is_sth);
|
||||
self.tree.add_raw(bucket, &node);
|
||||
self.totals.add_assign(&node);
|
||||
}
|
||||
}
|
||||
|
||||
for map in discount_maps {
|
||||
for (&price, &sats) in map.iter() {
|
||||
let bucket = price_to_bucket(price);
|
||||
let s = u64::from(sats) as i64;
|
||||
let node = CostBasisNode::new_discount(s, price.as_u128() as i128 * s as i128);
|
||||
self.tree.add_raw(bucket, &node);
|
||||
self.totals.add_assign(&node);
|
||||
}
|
||||
@@ -212,6 +259,26 @@ impl CostBasisFenwick {
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute percentile prices for discount-entry cohort.
|
||||
pub(super) fn percentiles_discount_entry(&self) -> PercentileResult {
|
||||
self.compute_percentiles(
|
||||
self.totals.discount_sats,
|
||||
self.totals.discount_usd,
|
||||
|n| n.discount_sats,
|
||||
|n| n.discount_usd,
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute percentile prices for premium-entry cohort (all - discount).
|
||||
pub(super) fn percentiles_premium_entry(&self) -> PercentileResult {
|
||||
self.compute_percentiles(
|
||||
self.totals.all_sats - self.totals.discount_sats,
|
||||
self.totals.all_usd - self.totals.discount_usd,
|
||||
|n| n.all_sats - n.discount_sats,
|
||||
|n| n.all_usd - n.discount_usd,
|
||||
)
|
||||
}
|
||||
|
||||
fn compute_percentiles(
|
||||
&self,
|
||||
total_sats: i64,
|
||||
@@ -271,6 +338,37 @@ impl CostBasisFenwick {
|
||||
return (0, 0, 0);
|
||||
}
|
||||
|
||||
let range = self.density_range(spot_price);
|
||||
let all_range = range.all_sats.max(0);
|
||||
let sth_range = range.sth_sats.max(0);
|
||||
let lth_range = all_range - sth_range;
|
||||
|
||||
let lth_total = self.totals.all_sats - self.totals.sth_sats;
|
||||
(
|
||||
Self::to_bps(all_range, self.totals.all_sats),
|
||||
Self::to_bps(sth_range, self.totals.sth_sats),
|
||||
Self::to_bps(lth_range, lth_total),
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute supply density for entry cohorts: (discount_bps, premium_bps).
|
||||
pub(super) fn entry_density(&self, spot_price: Cents) -> (u16, u16) {
|
||||
if self.totals.all_sats <= 0 {
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
let range = self.density_range(spot_price);
|
||||
let discount_range = range.discount_sats.max(0);
|
||||
let premium_range = range.all_sats.max(0) - discount_range;
|
||||
let premium_total = self.totals.all_sats - self.totals.discount_sats;
|
||||
|
||||
(
|
||||
Self::to_bps(discount_range, self.totals.discount_sats),
|
||||
Self::to_bps(premium_range, premium_total),
|
||||
)
|
||||
}
|
||||
|
||||
fn density_range(&self, spot_price: Cents) -> CostBasisNode {
|
||||
let spot_f64 = u64::from(spot_price) as f64;
|
||||
let low = Cents::from((spot_f64 * 0.95) as u64);
|
||||
let high = Cents::from((spot_f64 * 1.05) as u64);
|
||||
@@ -285,24 +383,23 @@ impl CostBasisFenwick {
|
||||
CostBasisNode::default()
|
||||
};
|
||||
|
||||
let all_range = (cum_high.all_sats - cum_low.all_sats).max(0);
|
||||
let sth_range = (cum_high.sth_sats - cum_low.sth_sats).max(0);
|
||||
let lth_range = all_range - sth_range;
|
||||
CostBasisNode {
|
||||
all_sats: cum_high.all_sats - cum_low.all_sats,
|
||||
sth_sats: cum_high.sth_sats - cum_low.sth_sats,
|
||||
discount_sats: cum_high.discount_sats - cum_low.discount_sats,
|
||||
all_usd: cum_high.all_usd - cum_low.all_usd,
|
||||
sth_usd: cum_high.sth_usd - cum_low.sth_usd,
|
||||
discount_usd: cum_high.discount_usd - cum_low.discount_usd,
|
||||
}
|
||||
}
|
||||
|
||||
let to_bps = |range: i64, total: i64| -> u16 {
|
||||
if total <= 0 {
|
||||
0
|
||||
} else {
|
||||
(range as f64 / total as f64 * 10000.0).round() as u16
|
||||
}
|
||||
};
|
||||
|
||||
let lth_total = self.totals.all_sats - self.totals.sth_sats;
|
||||
(
|
||||
to_bps(all_range, self.totals.all_sats),
|
||||
to_bps(sth_range, self.totals.sth_sats),
|
||||
to_bps(lth_range, lth_total),
|
||||
)
|
||||
#[inline(always)]
|
||||
fn to_bps(range: i64, total: i64) -> u16 {
|
||||
if total <= 0 {
|
||||
0
|
||||
} else {
|
||||
(range as f64 / total as f64 * 10000.0).round() as u16
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_cohort::{
|
||||
AgeRange, AmountRange, ByEpoch, Class, CohortContext, Filter, Filtered, OverAge, OverAmount,
|
||||
SpendableType, Term, UnderAge, UnderAmount,
|
||||
AgeRange, AmountRange, ByEntry, ByEpoch, Class, CohortContext, Filter, Filtered, OverAge,
|
||||
OverAmount, SpendableType, Term, UnderAge, UnderAmount,
|
||||
};
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Lengths;
|
||||
@@ -16,7 +16,6 @@ use vecdb::{
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::{
|
||||
DynCohortVecs,
|
||||
metrics::{
|
||||
AllCohortMetrics, BasicCohortMetrics, CohortMetricsBase, CoreCohortMetrics,
|
||||
ExtendedAdjustedCohortMetrics, ExtendedCohortMetrics, ImportConfig,
|
||||
@@ -24,6 +23,7 @@ use crate::{
|
||||
TypeCohortMetrics,
|
||||
},
|
||||
state::UTXOCohortState,
|
||||
DynCohortVecs,
|
||||
},
|
||||
indexes,
|
||||
internal::{ValuePerBlockCumulativeRolling, WindowStartVec, Windows},
|
||||
@@ -45,6 +45,7 @@ pub struct UTXOCohorts<M: StorageMode = Rw> {
|
||||
pub over_age: OverAge<UTXOCohortVecs<CoreCohortMetrics<M>>>,
|
||||
pub epoch: ByEpoch<UTXOCohortVecs<CoreCohortMetrics<M>>>,
|
||||
pub class: Class<UTXOCohortVecs<CoreCohortMetrics<M>>>,
|
||||
pub entry: ByEntry<UTXOCohortVecs<ExtendedCohortMetrics<M>>>,
|
||||
pub over_amount: OverAmount<UTXOCohortVecs<MinimalCohortMetrics<M>>>,
|
||||
pub amount_range: AmountRange<UTXOCohortVecs<MinimalCohortMetrics<M>>>,
|
||||
pub under_amount: UnderAmount<UTXOCohortVecs<MinimalCohortMetrics<M>>>,
|
||||
@@ -67,8 +68,10 @@ pub(crate) struct UTXOCohortsTransientState {
|
||||
}
|
||||
|
||||
impl UTXOCohorts<Rw> {
|
||||
/// ~71 separate cohorts (21 age + 5 epoch + 18 class + 15 amount + 12 type)
|
||||
const SEPARATE_COHORT_CAPACITY: usize = 80;
|
||||
/// Separate cohorts currently total 72:
|
||||
/// 21 age + 5 epoch + 18 class + 2 entry + 15 amount + 11 spendable type.
|
||||
/// Keep small headroom because this is only Vec allocation capacity.
|
||||
const SEPARATE_COHORT_CAPACITY: usize = 82;
|
||||
|
||||
/// Import all UTXO cohorts from database.
|
||||
pub(crate) fn forced_import(
|
||||
@@ -136,6 +139,26 @@ impl UTXOCohorts<Rw> {
|
||||
let epoch = ByEpoch::try_new(&core_separate)?;
|
||||
let class = Class::try_new(&core_separate)?;
|
||||
|
||||
let extended_separate =
|
||||
|f: Filter, name: &'static str| -> Result<UTXOCohortVecs<ExtendedCohortMetrics>> {
|
||||
let full_name = CohortContext::Utxo.full_name(&f, name);
|
||||
let cfg = ImportConfig {
|
||||
db,
|
||||
filter: &f,
|
||||
full_name: &full_name,
|
||||
version: v,
|
||||
indexes,
|
||||
cached_starts,
|
||||
};
|
||||
let state = Some(Box::new(UTXOCohortState::new(states_path, &full_name)));
|
||||
Ok(UTXOCohortVecs::new(
|
||||
state,
|
||||
ExtendedCohortMetrics::forced_import(&cfg)?,
|
||||
))
|
||||
};
|
||||
|
||||
let entry = ByEntry::try_new(&extended_separate)?;
|
||||
|
||||
// Helper for separate cohorts with MinimalCohortMetrics + MinimalRealizedState
|
||||
let minimal_separate =
|
||||
|f: Filter, name: &'static str| -> Result<UTXOCohortVecs<MinimalCohortMetrics>> {
|
||||
@@ -281,6 +304,7 @@ impl UTXOCohorts<Rw> {
|
||||
lth,
|
||||
epoch,
|
||||
class,
|
||||
entry,
|
||||
type_,
|
||||
under_age,
|
||||
over_age,
|
||||
@@ -309,6 +333,7 @@ impl UTXOCohorts<Rw> {
|
||||
sth,
|
||||
caches,
|
||||
age_range,
|
||||
entry,
|
||||
..
|
||||
} = self;
|
||||
caches
|
||||
@@ -327,7 +352,15 @@ impl UTXOCohorts<Rw> {
|
||||
Some((map, caches.fenwick.is_sth_at(i)))
|
||||
})
|
||||
.collect();
|
||||
caches.fenwick.bulk_init(maps.into_iter());
|
||||
let discount_maps = entry
|
||||
.discount
|
||||
.state
|
||||
.as_ref()
|
||||
.map(|state| state.cost_basis_map())
|
||||
.into_iter();
|
||||
caches
|
||||
.fenwick
|
||||
.bulk_init_with_discount(maps.into_iter(), discount_maps);
|
||||
}
|
||||
|
||||
/// Apply pending deltas from all age-range cohorts to the Fenwick tree.
|
||||
@@ -338,7 +371,10 @@ impl UTXOCohorts<Rw> {
|
||||
}
|
||||
// Destructure to get separate borrows on caches and age_range
|
||||
let Self {
|
||||
caches, age_range, ..
|
||||
caches,
|
||||
age_range,
|
||||
entry,
|
||||
..
|
||||
} = self;
|
||||
for (i, sub) in age_range.iter().enumerate() {
|
||||
if let Some(state) = sub.state.as_ref() {
|
||||
@@ -348,6 +384,11 @@ impl UTXOCohorts<Rw> {
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(state) = entry.discount.state.as_ref() {
|
||||
state.for_each_cost_basis_pending(|&price, delta| {
|
||||
caches.fenwick.apply_discount_delta(price, delta);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Push maturation sats to the matured vecs for the given height.
|
||||
@@ -365,6 +406,7 @@ impl UTXOCohorts<Rw> {
|
||||
age_range,
|
||||
epoch,
|
||||
class,
|
||||
entry,
|
||||
amount_range,
|
||||
type_,
|
||||
..
|
||||
@@ -374,6 +416,7 @@ impl UTXOCohorts<Rw> {
|
||||
.map(|x| x as &mut dyn DynCohortVecs)
|
||||
.chain(epoch.par_iter_mut().map(|x| x as &mut dyn DynCohortVecs))
|
||||
.chain(class.par_iter_mut().map(|x| x as &mut dyn DynCohortVecs))
|
||||
.chain(entry.par_iter_mut().map(|x| x as &mut dyn DynCohortVecs))
|
||||
.chain(
|
||||
amount_range
|
||||
.par_iter_mut()
|
||||
@@ -389,6 +432,7 @@ impl UTXOCohorts<Rw> {
|
||||
age_range,
|
||||
epoch,
|
||||
class,
|
||||
entry,
|
||||
amount_range,
|
||||
type_,
|
||||
..
|
||||
@@ -398,6 +442,7 @@ impl UTXOCohorts<Rw> {
|
||||
.map(|x| x as &mut dyn DynCohortVecs)
|
||||
.chain(epoch.iter_mut().map(|x| x as &mut dyn DynCohortVecs))
|
||||
.chain(class.iter_mut().map(|x| x as &mut dyn DynCohortVecs))
|
||||
.chain(entry.iter_mut().map(|x| x as &mut dyn DynCohortVecs))
|
||||
.chain(amount_range.iter_mut().map(|x| x as &mut dyn DynCohortVecs))
|
||||
.chain(type_.iter_mut().map(|x| x as &mut dyn DynCohortVecs))
|
||||
}
|
||||
@@ -409,6 +454,7 @@ impl UTXOCohorts<Rw> {
|
||||
.map(|x| x as &dyn DynCohortVecs)
|
||||
.chain(self.epoch.iter().map(|x| x as &dyn DynCohortVecs))
|
||||
.chain(self.class.iter().map(|x| x as &dyn DynCohortVecs))
|
||||
.chain(self.entry.iter().map(|x| x as &dyn DynCohortVecs))
|
||||
.chain(self.amount_range.iter().map(|x| x as &dyn DynCohortVecs))
|
||||
.chain(self.type_.iter().map(|x| x as &dyn DynCohortVecs))
|
||||
}
|
||||
@@ -516,6 +562,7 @@ impl UTXOCohorts<Rw> {
|
||||
);
|
||||
all.extend(self.epoch.iter_mut().map(|x| x as &mut dyn DynCohortVecs));
|
||||
all.extend(self.class.iter_mut().map(|x| x as &mut dyn DynCohortVecs));
|
||||
all.extend(self.entry.iter_mut().map(|x| x as &mut dyn DynCohortVecs));
|
||||
all.extend(
|
||||
self.amount_range
|
||||
.iter_mut()
|
||||
@@ -604,6 +651,7 @@ impl UTXOCohorts<Rw> {
|
||||
under_amount,
|
||||
epoch,
|
||||
class,
|
||||
entry,
|
||||
type_,
|
||||
..
|
||||
} = self;
|
||||
@@ -676,6 +724,19 @@ impl UTXOCohorts<Rw> {
|
||||
.compute_rest_part2(prices, starting_lengths, ss, au, exit)
|
||||
})
|
||||
}),
|
||||
Box::new(|| {
|
||||
entry.par_iter_mut().try_for_each(|v| {
|
||||
v.metrics.compute_rest_part2(
|
||||
blocks,
|
||||
prices,
|
||||
starting_lengths,
|
||||
height_to_market_cap,
|
||||
ss,
|
||||
au,
|
||||
exit,
|
||||
)
|
||||
})
|
||||
}),
|
||||
Box::new(|| {
|
||||
amount_range.par_iter_mut().try_for_each(|v| {
|
||||
v.metrics
|
||||
@@ -730,6 +791,9 @@ impl UTXOCohorts<Rw> {
|
||||
for v in self.class.iter_mut() {
|
||||
vecs.extend(v.metrics.collect_all_vecs_mut());
|
||||
}
|
||||
for v in self.entry.iter_mut() {
|
||||
vecs.extend(v.metrics.collect_all_vecs_mut());
|
||||
}
|
||||
for v in self.amount_range.iter_mut() {
|
||||
vecs.extend(v.metrics.collect_all_vecs_mut());
|
||||
}
|
||||
@@ -813,7 +877,7 @@ impl UTXOCohorts<Rw> {
|
||||
|
||||
/// Aggregate RealizedFull fields from age_range states and push to all/sth/lth.
|
||||
/// Called during the block loop after separate cohorts' push_state but before reset.
|
||||
pub(crate) fn push_overlapping(&mut self, height_price: Cents) {
|
||||
pub(crate) fn push_overlapping(&mut self, height_price: Cents) -> Cents {
|
||||
let Self {
|
||||
all,
|
||||
sth,
|
||||
@@ -852,7 +916,7 @@ impl UTXOCohorts<Rw> {
|
||||
}
|
||||
}
|
||||
|
||||
all.metrics.realized.push_accum(&all_acc);
|
||||
let all_capitalized_price = all.metrics.realized.push_accum(&all_acc);
|
||||
sth.metrics.realized.push_accum(&sth_acc);
|
||||
lth.metrics.realized.push_accum(<h_acc);
|
||||
|
||||
@@ -880,6 +944,8 @@ impl UTXOCohorts<Rw> {
|
||||
.unrealized
|
||||
.capitalized_cap_in_loss_raw
|
||||
.push(CentsSquaredSats::new(lth_ccap.1));
|
||||
|
||||
all_capitalized_price
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,22 @@ impl UTXOCohorts {
|
||||
let lth = self.caches.fenwick.percentiles_lth();
|
||||
push_cost_basis(<h, lth_d, &mut self.lth.metrics.cost_basis);
|
||||
|
||||
let (discount_d, premium_d) = self.caches.fenwick.entry_density(spot_price);
|
||||
|
||||
let discount = self.caches.fenwick.percentiles_discount_entry();
|
||||
push_cost_basis(
|
||||
&discount,
|
||||
discount_d,
|
||||
&mut self.entry.discount.metrics.cost_basis,
|
||||
);
|
||||
|
||||
let premium = self.caches.fenwick.percentiles_premium_entry();
|
||||
push_cost_basis(
|
||||
&premium,
|
||||
premium_d,
|
||||
&mut self.entry.premium.metrics.cost_basis,
|
||||
);
|
||||
|
||||
let prof = self.caches.fenwick.profitability(spot_price);
|
||||
push_profitability(&prof, &mut self.profitability);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use brk_cohort::EntryPrice;
|
||||
use brk_types::{Cents, CostBasisSnapshot, Height, Timestamp};
|
||||
use vecdb::Rw;
|
||||
|
||||
@@ -12,6 +13,7 @@ impl UTXOCohorts<Rw> {
|
||||
/// - The "under_1h" age cohort (all new UTXOs start at 0 hours old)
|
||||
/// - The appropriate epoch cohort based on block height
|
||||
/// - The appropriate class cohort based on block timestamp
|
||||
/// - The immutable entry valuation cohort based on creation price versus anchor
|
||||
/// - The appropriate output type cohort (P2PKH, P2SH, etc.)
|
||||
/// - The appropriate amount range cohort based on value
|
||||
pub(crate) fn receive(
|
||||
@@ -20,13 +22,14 @@ impl UTXOCohorts<Rw> {
|
||||
height: Height,
|
||||
timestamp: Timestamp,
|
||||
price: Cents,
|
||||
entry: EntryPrice,
|
||||
) {
|
||||
let supply_state = received.spendable_supply;
|
||||
|
||||
// Pre-compute snapshot once for the 3 cohorts sharing the same supply_state
|
||||
// Pre-compute snapshot once for cohorts sharing the block-level supply_state
|
||||
let snapshot = CostBasisSnapshot::from_utxo(price, &supply_state);
|
||||
|
||||
// New UTXOs go into under_1h, current epoch, and current class
|
||||
// New UTXOs go into under_1h plus immutable creation cohorts
|
||||
self.age_range
|
||||
.under_1h
|
||||
.state
|
||||
@@ -45,6 +48,12 @@ impl UTXOCohorts<Rw> {
|
||||
.unwrap()
|
||||
.receive_utxo_snapshot(&supply_state, &snapshot);
|
||||
}
|
||||
self.entry
|
||||
.get_mut(entry)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive_utxo_snapshot(&supply_state, &snapshot);
|
||||
|
||||
// Update output type cohorts (skip types with no outputs this block)
|
||||
self.type_.iter_typed_mut().for_each(|(output_type, vecs)| {
|
||||
|
||||
@@ -49,7 +49,7 @@ impl UTXOCohorts<Rw> {
|
||||
// This is the max price between receive and send heights
|
||||
let peak_price = price_range_max.max_between(receive_height, send_height);
|
||||
|
||||
// Pre-compute once for age_range, epoch, year (all share sent.spendable_supply)
|
||||
// Pre-compute once for cohorts sharing the sent supply.
|
||||
if let Some(pre) = SendPrecomputed::new(
|
||||
&sent.spendable_supply,
|
||||
current_price,
|
||||
@@ -75,6 +75,12 @@ impl UTXOCohorts<Rw> {
|
||||
.unwrap()
|
||||
.send_utxo_precomputed(&sent.spendable_supply, &pre);
|
||||
}
|
||||
self.entry
|
||||
.get_mut(block_state.entry)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send_utxo_precomputed(&sent.spendable_supply, &pre);
|
||||
} else if sent.spendable_supply.utxo_count > 0 {
|
||||
// Zero-value UTXOs: just subtract supply
|
||||
self.age_range.get_mut(age).state.as_mut().unwrap().supply -=
|
||||
@@ -85,6 +91,12 @@ impl UTXOCohorts<Rw> {
|
||||
if let Some(v) = self.class.mut_vec_from_timestamp(block_state.timestamp) {
|
||||
v.state.as_mut().unwrap().supply -= &sent.spendable_supply;
|
||||
}
|
||||
self.entry
|
||||
.get_mut(block_state.entry)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.supply -= &sent.spendable_supply;
|
||||
}
|
||||
|
||||
// Update output type cohorts (skip zero-supply entries)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use brk_cohort::ByAddrType;
|
||||
use brk_cohort::{ByAddrType, EntryPrice};
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{
|
||||
@@ -46,6 +46,7 @@ pub(crate) fn process_blocks(
|
||||
last_height: Height,
|
||||
chain_state: &mut Vec<BlockState>,
|
||||
tx_index_to_height: &mut RangeMap<TxIndex, Height>,
|
||||
mut entry_anchor: Cents,
|
||||
cached_prices: &[Cents],
|
||||
cached_timestamps: &[Timestamp],
|
||||
cached_price_range_max: &PriceRangeMax,
|
||||
@@ -370,9 +371,14 @@ pub(crate) fn process_blocks(
|
||||
.iterate(Sats::FIFTY_BTC, OutputType::P2PK65);
|
||||
}
|
||||
|
||||
let entry = EntryPrice::from_is_discount(
|
||||
entry_anchor == Cents::ZERO || block_price <= entry_anchor,
|
||||
);
|
||||
|
||||
// Push current block state before processing cohort updates
|
||||
chain_state.push(BlockState {
|
||||
supply: transacted.spendable_supply,
|
||||
entry,
|
||||
price: block_price,
|
||||
timestamp,
|
||||
});
|
||||
@@ -411,7 +417,7 @@ pub(crate) fn process_blocks(
|
||||
|| {
|
||||
// UTXO cohorts receive/send
|
||||
vecs.utxo_cohorts
|
||||
.receive(transacted, height, timestamp, block_price);
|
||||
.receive(transacted, height, timestamp, block_price, entry);
|
||||
if let Some(min_h) =
|
||||
vecs.utxo_cohorts
|
||||
.send(height_to_sent, chain_state, ctx.price_range_max)
|
||||
@@ -460,7 +466,7 @@ pub(crate) fn process_blocks(
|
||||
let is_last_of_day = is_last_of_day[offset];
|
||||
let date_opt = is_last_of_day.then(|| Date::from(timestamp));
|
||||
|
||||
push_cohort_states(
|
||||
entry_anchor = push_cohort_states(
|
||||
&mut vecs.utxo_cohorts,
|
||||
&mut vecs.addr_cohorts,
|
||||
height,
|
||||
@@ -527,7 +533,7 @@ fn push_cohort_states(
|
||||
addr_cohorts: &mut AddrCohorts,
|
||||
height: Height,
|
||||
height_price: Cents,
|
||||
) {
|
||||
) -> Cents {
|
||||
// Phase 1: push + unrealized (no reset yet, states still needed for aggregation)
|
||||
rayon::join(
|
||||
|| {
|
||||
@@ -545,7 +551,7 @@ fn push_cohort_states(
|
||||
);
|
||||
|
||||
// Phase 2: aggregate age_range states → push to overlapping cohorts
|
||||
utxo_cohorts.push_overlapping(height_price);
|
||||
let all_capitalized_price = utxo_cohorts.push_overlapping(height_price);
|
||||
|
||||
// Phase 3: reset per-block values
|
||||
utxo_cohorts
|
||||
@@ -554,4 +560,6 @@ fn push_cohort_states(
|
||||
addr_cohorts
|
||||
.iter_separate_mut()
|
||||
.for_each(|v| v.reset_single_iteration_values());
|
||||
|
||||
all_capitalized_price
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ impl RealizedFull {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_accum(&mut self, accum: &RealizedFullAccum) {
|
||||
pub(crate) fn push_accum(&mut self, accum: &RealizedFullAccum) -> Cents {
|
||||
self.cap_raw.push(accum.cap_raw);
|
||||
self.capitalized.cap_raw.push(accum.capitalized_cap_raw);
|
||||
|
||||
@@ -221,6 +221,8 @@ impl RealizedFull {
|
||||
self.capitalized.price.cents.height.push(capitalized_price);
|
||||
|
||||
self.peak_regret.value.block.cents.push(accum.peak_regret());
|
||||
|
||||
capitalized_price
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest_part1(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::ops::{Add, AddAssign, SubAssign};
|
||||
|
||||
use brk_cohort::EntryPrice;
|
||||
use brk_types::{Cents, SupplyState, Timestamp};
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -8,6 +9,8 @@ pub struct BlockState {
|
||||
#[serde(flatten)]
|
||||
pub supply: SupplyState,
|
||||
#[serde(skip)]
|
||||
pub entry: EntryPrice,
|
||||
#[serde(skip)]
|
||||
pub price: Cents,
|
||||
#[serde(skip)]
|
||||
pub timestamp: Timestamp,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use brk_cohort::{ByAddrType, Filter};
|
||||
use brk_cohort::{ByAddrType, EntryPrice, Filter};
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_traversable::Traversable;
|
||||
@@ -436,13 +436,34 @@ impl Vecs {
|
||||
let end = usize::from(recovered_height);
|
||||
debug!("building supply_state vec for {} heights", recovered_height);
|
||||
let supply_state_data: Vec<_> = self.supply_state.collect_range_at(0, end);
|
||||
let capitalized_price_data: Vec<_> = self
|
||||
.utxo_cohorts
|
||||
.all
|
||||
.metrics
|
||||
.realized
|
||||
.capitalized
|
||||
.price
|
||||
.cents
|
||||
.height
|
||||
.collect_range_at(0, end);
|
||||
|
||||
let mut entry_anchor = Cents::ZERO;
|
||||
chain_state = supply_state_data
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(h, supply)| BlockState {
|
||||
supply,
|
||||
price: self.caches.prices[h],
|
||||
timestamp: self.caches.timestamps[h],
|
||||
.map(|(h, supply)| {
|
||||
let price = self.caches.prices[h];
|
||||
let entry = EntryPrice::from_is_discount(
|
||||
entry_anchor == Cents::ZERO || price <= entry_anchor,
|
||||
);
|
||||
entry_anchor = capitalized_price_data[h];
|
||||
|
||||
BlockState {
|
||||
supply,
|
||||
entry,
|
||||
price,
|
||||
timestamp: self.caches.timestamps[h],
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
debug!("chain_state rebuilt");
|
||||
@@ -474,6 +495,20 @@ impl Vecs {
|
||||
let prices = std::mem::take(&mut self.caches.prices);
|
||||
let timestamps = std::mem::take(&mut self.caches.timestamps);
|
||||
let price_range_max = std::mem::take(&mut self.caches.price_range_max);
|
||||
let entry_anchor = starting_height
|
||||
.decremented()
|
||||
.and_then(|height| {
|
||||
self.utxo_cohorts
|
||||
.all
|
||||
.metrics
|
||||
.realized
|
||||
.capitalized
|
||||
.price
|
||||
.cents
|
||||
.height
|
||||
.collect_one(height)
|
||||
})
|
||||
.unwrap_or(Cents::ZERO);
|
||||
|
||||
process_blocks(
|
||||
self,
|
||||
@@ -486,6 +521,7 @@ impl Vecs {
|
||||
last_height,
|
||||
&mut chain_state,
|
||||
&mut tx_index_to_height,
|
||||
entry_anchor,
|
||||
&prices,
|
||||
×tamps,
|
||||
&price_range_max,
|
||||
|
||||
Reference in New Issue
Block a user