diff --git a/crates/brk_bindgen/src/analysis/positions.rs b/crates/brk_bindgen/src/analysis/positions.rs index 07d69b1c0..ee8ee6ed2 100644 --- a/crates/brk_bindgen/src/analysis/positions.rs +++ b/crates/brk_bindgen/src/analysis/positions.rs @@ -1131,8 +1131,8 @@ mod tests { #[test] fn test_all_empty_same_type_marks_outlier() { - // RatioPerBlockStdDevBands: all children are the same type (StdDevPerBlockExtended) - // and all return the same base → all-empty field_parts. + // A wrapper whose children share one type and return the same base + // produces all-empty field_parts. // Should be marked as outlier so the tree inlines instead of using a // factory that can't differentiate the children. let mut child_bases = BTreeMap::new(); @@ -1184,7 +1184,8 @@ mod tests { #[test] fn test_extract_disc_from_instance() { - // StdDevPerBlockExtended 4y instance: field_parts include "0sd_4y", "p1sd_4y", "ratio_sd_4y". + // A discriminated instance has field_parts such as "0sd_4y", "p1sd_4y", + // and "ratio_sd_4y". // Templates are "0sd{disc}", "p1sd{disc}", "ratio_sd{disc}". // The extracted disc should be "_4y", not "0sd_4y" (the shortest field_part). use crate::StructuralPattern; diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index 69b369854..d2829aff8 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -1072,6 +1072,60 @@ impl SeriesPattern for SeriesPattern35 { fn get(&self // Reusable pattern structs +/// Pattern struct for repeated tree structure. +pub struct IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern { + pub index: SeriesPattern1, + pub pct0_01: CentsSatsUsdPattern, + pub pct0_5: CentsSatsUsdPattern, + pub pct1: CentsSatsUsdPattern, + pub pct10: CentsSatsUsdPattern, + pub pct2: CentsSatsUsdPattern, + pub pct20: CentsSatsUsdPattern, + pub pct30: CentsSatsUsdPattern, + pub pct40: CentsSatsUsdPattern, + pub pct5: CentsSatsUsdPattern, + pub pct50: CentsSatsUsdPattern, + pub pct60: CentsSatsUsdPattern, + pub pct70: CentsSatsUsdPattern, + pub pct80: CentsSatsUsdPattern, + pub pct90: CentsSatsUsdPattern, + pub pct95: CentsSatsUsdPattern, + pub pct98: CentsSatsUsdPattern, + pub pct99: CentsSatsUsdPattern, + pub pct99_5: CentsSatsUsdPattern, + pub pct99_9: CentsSatsUsdPattern, + pub score: SeriesPattern1, +} + +impl IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern { + /// Create a new pattern node with accumulated series name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + index: SeriesPattern1::new(client.clone(), _m(&acc, "index")), + pct0_01: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct0_01")), + pct0_5: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct0_5")), + pct1: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct01")), + pct10: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct10")), + pct2: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct02")), + pct20: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct20")), + pct30: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct30")), + pct40: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct40")), + pct5: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct05")), + pct50: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct50")), + pct60: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct60")), + pct70: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct70")), + pct80: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct80")), + pct90: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct90")), + pct95: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct95")), + pct98: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct98")), + pct99: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct99")), + pct99_5: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct99_5")), + pct99_9: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct99_9")), + score: SeriesPattern1::new(client.clone(), _m(&acc, "score")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern { pub pct05: CentsSatsUsdPattern, @@ -1123,22 +1177,53 @@ impl Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75 } /// Pattern struct for repeated tree structure. -pub struct _0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern { - pub _0sd: CentsSatsUsdPattern, - pub m0_5sd: PriceRatioPattern, - pub m1_5sd: PriceRatioPattern, - pub m1sd: PriceRatioPattern, - pub m2_5sd: PriceRatioPattern, - pub m2sd: PriceRatioPattern, - pub m3sd: PriceRatioPattern, - pub p0_5sd: PriceRatioPattern, - pub p1_5sd: PriceRatioPattern, - pub p1sd: PriceRatioPattern, - pub p2_5sd: PriceRatioPattern, - pub p2sd: PriceRatioPattern, - pub p3sd: PriceRatioPattern, - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, +pub struct Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern { + pub pct0_01: PpmPriceRatioPattern, + pub pct0_5: PpmPriceRatioPattern, + pub pct1: PpmPriceRatioPattern, + pub pct10: PpmPriceRatioPattern, + pub pct2: PpmPriceRatioPattern, + pub pct20: PpmPriceRatioPattern, + pub pct30: PpmPriceRatioPattern, + pub pct40: PpmPriceRatioPattern, + pub pct5: PpmPriceRatioPattern, + pub pct50: PpmPriceRatioPattern, + pub pct60: PpmPriceRatioPattern, + pub pct70: PpmPriceRatioPattern, + pub pct80: PpmPriceRatioPattern, + pub pct90: PpmPriceRatioPattern, + pub pct95: PpmPriceRatioPattern, + pub pct98: PpmPriceRatioPattern, + pub pct99: PpmPriceRatioPattern, + pub pct99_5: PpmPriceRatioPattern, + pub pct99_9: PpmPriceRatioPattern, +} + +impl Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern { + /// Create a new pattern node with accumulated series name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + pct0_01: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct0_01".to_string()), + pct0_5: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct0_5".to_string()), + pct1: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct1".to_string()), + pct10: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct10".to_string()), + pct2: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct2".to_string()), + pct20: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct20".to_string()), + pct30: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct30".to_string()), + pct40: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct40".to_string()), + pct5: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct5".to_string()), + pct50: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct50".to_string()), + pct60: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct60".to_string()), + pct70: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct70".to_string()), + pct80: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct80".to_string()), + pct90: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct90".to_string()), + pct95: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct95".to_string()), + pct98: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct98".to_string()), + pct99: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct99".to_string()), + pct99_5: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct99_5".to_string()), + pct99_9: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct99_9".to_string()), + } + } } /// Pattern struct for repeated tree structure. @@ -1276,13 +1361,33 @@ pub struct CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern { pub mvrv: SeriesPattern1, pub net_pnl: BlockChangeCumulativeDeltaSumPattern, pub peak_regret: BlockCumulativeSumPattern, - pub price: CentsPercentilesPpmRatioSatsSmaStdUsdPattern, + pub price: CentsPpmRatioSatsUsdPattern, pub profit: BlockCumulativeSumPattern, pub profit_to_loss_ratio: _1m1w1y24hPattern, pub sell_side_risk_ratio: _1m1w1y24hPattern8, pub sopr: AdjustedRatioValuePattern, } +impl CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern { + /// Create a new pattern node with accumulated series name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + cap: CentsDeltaToUsdPattern::new(client.clone(), _m(&acc, "realized_cap")), + capitalized: PricePattern::new(client.clone(), _m(&acc, "capitalized_price")), + gross_pnl: BlockCumulativeSumPattern::new(client.clone(), _m(&acc, "realized_gross_pnl")), + loss: BlockCumulativeNegativeSumPattern::new(client.clone(), _m(&acc, "realized_loss")), + mvrv: SeriesPattern1::new(client.clone(), _m(&acc, "mvrv")), + net_pnl: BlockChangeCumulativeDeltaSumPattern::new(client.clone(), _m(&acc, "net")), + peak_regret: BlockCumulativeSumPattern::new(client.clone(), _m(&acc, "realized_peak_regret")), + price: CentsPpmRatioSatsUsdPattern::new(client.clone(), _m(&acc, "realized_price")), + profit: BlockCumulativeSumPattern::new(client.clone(), _m(&acc, "realized_profit")), + profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), _m(&acc, "realized_profit_to_loss_ratio")), + sell_side_risk_ratio: _1m1w1y24hPattern8::new(client.clone(), _m(&acc, "sell_side_risk_ratio")), + sopr: AdjustedRatioValuePattern::new(client.clone(), acc.clone()), + } + } +} + /// Pattern struct for repeated tree structure. pub struct CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2 { pub cap: CentsDeltaToUsdPattern, @@ -1292,13 +1397,33 @@ pub struct CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2 { pub mvrv: SeriesPattern1, pub net_pnl: BlockChangeCumulativeDeltaSumPattern, pub peak_regret: BlockCumulativeSumPattern, - pub price: CentsPercentilesPpmRatioSatsSmaStdUsdPattern, + pub price: CentsPpmRatioSatsUsdPattern, pub profit: BlockCumulativeSumPattern, pub profit_to_loss_ratio: _1m1w1y24hPattern, pub sell_side_risk_ratio: _1m1w1y24hPattern8, pub sopr: RatioValuePattern2, } +impl CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2 { + /// Create a new pattern node with accumulated series name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + cap: CentsDeltaToUsdPattern::new(client.clone(), _m(&acc, "realized_cap")), + capitalized: PricePattern::new(client.clone(), _m(&acc, "capitalized_price")), + gross_pnl: BlockCumulativeSumPattern::new(client.clone(), _m(&acc, "realized_gross_pnl")), + loss: BlockCumulativeNegativeSumPattern::new(client.clone(), _m(&acc, "realized_loss")), + mvrv: SeriesPattern1::new(client.clone(), _m(&acc, "mvrv")), + net_pnl: BlockChangeCumulativeDeltaSumPattern::new(client.clone(), _m(&acc, "net")), + peak_regret: BlockCumulativeSumPattern::new(client.clone(), _m(&acc, "realized_peak_regret")), + price: CentsPpmRatioSatsUsdPattern::new(client.clone(), _m(&acc, "realized_price")), + profit: BlockCumulativeSumPattern::new(client.clone(), _m(&acc, "realized_profit")), + profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), _m(&acc, "realized_profit_to_loss_ratio")), + sell_side_risk_ratio: _1m1w1y24hPattern8::new(client.clone(), _m(&acc, "sell_side_risk_ratio")), + sopr: RatioValuePattern2::new(client.clone(), acc.clone()), + } + } +} + /// Pattern struct for repeated tree structure. pub struct EmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2 { pub empty: _1m1w1y24hPercentPpmRatioPattern, @@ -1451,38 +1576,6 @@ pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshSharePattern { pub share: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5, } -/// Pattern struct for repeated tree structure. -pub struct IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern { - pub index: SeriesPattern1, - pub pct0_5: CentsSatsUsdPattern, - pub pct1: CentsSatsUsdPattern, - pub pct2: CentsSatsUsdPattern, - pub pct5: CentsSatsUsdPattern, - pub pct95: CentsSatsUsdPattern, - pub pct98: CentsSatsUsdPattern, - pub pct99: CentsSatsUsdPattern, - pub pct99_5: CentsSatsUsdPattern, - pub score: SeriesPattern1, -} - -impl IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern { - /// Create a new pattern node with accumulated series name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - index: SeriesPattern1::new(client.clone(), _m(&acc, "index")), - pct0_5: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct0_5")), - pct1: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct01")), - pct2: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct02")), - pct5: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct05")), - pct95: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct95")), - pct98: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct98")), - pct99: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct99")), - pct99_5: CentsSatsUsdPattern::new(client.clone(), _m(&acc, "pct99_5")), - score: SeriesPattern1::new(client.clone(), _m(&acc, "score")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6 { pub all: AverageBlockCumulativeSumPattern, @@ -1693,46 +1786,6 @@ impl Pct10Pct20Pct30Pct40Pct50Pct60Pct70Pct80Pct90Pattern { } } -/// Pattern struct for repeated tree structure. -pub struct CentsPercentilesPpmRatioSatsSmaStdUsdPattern { - pub cents: SeriesPattern1, - pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern, - pub ppm: SeriesPattern1, - pub ratio: SeriesPattern1, - pub sats: SeriesPattern1, - pub sma: _1m1w1y2y4yAllPattern, - pub std_dev: _1y2y4yAllPattern, - pub usd: SeriesPattern1, -} - -/// Pattern struct for repeated tree structure. -pub struct Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern { - pub pct0_5: PpmPriceRatioPattern, - pub pct1: PpmPriceRatioPattern, - pub pct2: PpmPriceRatioPattern, - pub pct5: PpmPriceRatioPattern, - pub pct95: PpmPriceRatioPattern, - pub pct98: PpmPriceRatioPattern, - pub pct99: PpmPriceRatioPattern, - pub pct99_5: PpmPriceRatioPattern, -} - -impl Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern { - /// Create a new pattern node with accumulated series name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - pct0_5: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct0_5".to_string()), - pct1: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct1".to_string()), - pct2: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct2".to_string()), - pct5: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct5".to_string()), - pct95: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct95".to_string()), - pct98: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct98".to_string()), - pct99: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct99".to_string()), - pct99_5: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct99_5".to_string()), - } - } -} - /// Pattern struct for repeated tree structure. pub struct _10y2y3y4y5y6y8yPattern { pub _10y: PercentPpmRatioPattern, @@ -1807,6 +1860,21 @@ pub struct ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2 { pub unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2, } +impl ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2 { + /// Create a new pattern node with accumulated series name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + activity: CoindaysCoinyearsDormancyTransferPattern::new(client.clone(), acc.clone()), + cost_basis: InMaxMinPerSupplyPattern::new(client.clone(), acc.clone()), + invested_capital: InPattern::new(client.clone(), _m(&acc, "invested_capital_in")), + outputs: SpentUnspentUtxoPattern::new(client.clone(), acc.clone()), + realized: CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2::new(client.clone(), acc.clone()), + supply: DeltaDominanceHalfInTotalPattern2::new(client.clone(), _m(&acc, "supply")), + unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2::new(client.clone(), acc.clone()), + } + } +} + /// Pattern struct for repeated tree structure. pub struct CapLossMvrvNetPriceProfitSoprPattern { pub cap: CentsDeltaUsdPattern, @@ -1937,30 +2005,6 @@ impl MaxMedianMinPct10Pct25Pct75Pct90Pattern { } } -/// Pattern struct for repeated tree structure. -pub struct _1m1w1y2y4yAllPattern { - pub _1m: PpmRatioPattern2, - pub _1w: PpmRatioPattern2, - pub _1y: PpmRatioPattern2, - pub _2y: PpmRatioPattern2, - pub _4y: PpmRatioPattern2, - pub all: PpmRatioPattern2, -} - -impl _1m1w1y2y4yAllPattern { - /// Create a new pattern node with accumulated series name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - _1m: PpmRatioPattern2::new(client.clone(), _m(&acc, "1m")), - _1w: PpmRatioPattern2::new(client.clone(), _m(&acc, "1w")), - _1y: PpmRatioPattern2::new(client.clone(), _m(&acc, "1y")), - _2y: PpmRatioPattern2::new(client.clone(), _m(&acc, "2y")), - _4y: PpmRatioPattern2::new(client.clone(), _m(&acc, "4y")), - all: PpmRatioPattern2::new(client.clone(), _m(&acc, "all")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct ActivityAddrOutputsRealizedSupplyUnrealizedPattern { pub activity: TransferPattern, @@ -2033,30 +2077,6 @@ impl CentsNegativeToUsdPattern2 { } } -/// Pattern struct for repeated tree structure. -pub struct CentsPercentilesPpmRatioSatsUsdPattern { - pub cents: SeriesPattern1, - pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern, - pub ppm: SeriesPattern1, - pub ratio: SeriesPattern1, - pub sats: SeriesPattern1, - pub usd: SeriesPattern1, -} - -impl CentsPercentilesPpmRatioSatsUsdPattern { - /// Create a new pattern node with accumulated series name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - cents: SeriesPattern1::new(client.clone(), _m(&acc, "cents")), - percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), acc.clone()), - ppm: SeriesPattern1::new(client.clone(), _m(&acc, "ratio_ppm")), - ratio: SeriesPattern1::new(client.clone(), _m(&acc, "ratio")), - sats: SeriesPattern1::new(client.clone(), _m(&acc, "sats")), - usd: SeriesPattern1::new(client.clone(), acc.clone()), - } - } -} - /// Pattern struct for repeated tree structure. pub struct ChainDataOutputTxPattern { pub chain_share: PercentPpmRatioPattern2, @@ -2615,14 +2635,6 @@ impl _1m1w1y24hPattern8 { } } -/// Pattern struct for repeated tree structure. -pub struct _1y2y4yAllPattern { - pub _1y: _0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern, - pub _2y: _0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern, - pub _4y: _0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern, - pub all: _0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern, -} - /// Pattern struct for repeated tree structure. pub struct AverageBlockCumulativeSumPattern2 { pub average: _1m1w1y24hPattern, @@ -3690,22 +3702,6 @@ impl PpmRatioPattern { } } -/// Pattern struct for repeated tree structure. -pub struct PriceRatioPattern { - pub price: CentsSatsUsdPattern, - pub ratio: SeriesPattern1, -} - -impl PriceRatioPattern { - /// Create a new pattern node with accumulated series name. - pub fn new(client: Arc, acc: String, disc: String) -> Self { - Self { - price: CentsSatsUsdPattern::new(client.clone(), _m(&acc, &disc)), - ratio: SeriesPattern1::new(client.clone(), _m(&acc, &format!("ratio_{disc}", disc=disc))), - } - } -} - /// Pattern struct for repeated tree structure. pub struct RatioValuePattern2 { pub ratio: _1m1w1y24hPattern, @@ -3804,14 +3800,14 @@ impl NuplPattern { /// Pattern struct for repeated tree structure. pub struct PricePattern { - pub price: CentsPercentilesPpmRatioSatsUsdPattern, + pub price: CentsPpmRatioSatsUsdPattern, } impl PricePattern { /// Create a new pattern node with accumulated series name. pub fn new(client: Arc, acc: String) -> Self { Self { - price: CentsPercentilesPpmRatioSatsUsdPattern::new(client.clone(), acc.clone()), + price: CentsPpmRatioSatsUsdPattern::new(client.clone(), acc.clone()), } } } @@ -6034,19 +6030,19 @@ impl SeriesTree_Cointime_Cap { /// Series tree node. pub struct SeriesTree_Cointime_Prices { - pub vaulted: CentsPercentilesPpmRatioSatsUsdPattern, - pub active: CentsPercentilesPpmRatioSatsUsdPattern, - pub true_market_mean: CentsPercentilesPpmRatioSatsUsdPattern, - pub cointime: CentsPercentilesPpmRatioSatsUsdPattern, + pub vaulted: CentsPpmRatioSatsUsdPattern, + pub active: CentsPpmRatioSatsUsdPattern, + pub true_market_mean: CentsPpmRatioSatsUsdPattern, + pub cointime: CentsPpmRatioSatsUsdPattern, } impl SeriesTree_Cointime_Prices { pub fn new(client: Arc, base_path: String) -> Self { Self { - vaulted: CentsPercentilesPpmRatioSatsUsdPattern::new(client.clone(), "vaulted_price".to_string()), - active: CentsPercentilesPpmRatioSatsUsdPattern::new(client.clone(), "active_price".to_string()), - true_market_mean: CentsPercentilesPpmRatioSatsUsdPattern::new(client.clone(), "true_market_mean".to_string()), - cointime: CentsPercentilesPpmRatioSatsUsdPattern::new(client.clone(), "cointime_price".to_string()), + vaulted: CentsPpmRatioSatsUsdPattern::new(client.clone(), "vaulted_price".to_string()), + active: CentsPpmRatioSatsUsdPattern::new(client.clone(), "active_price".to_string()), + true_market_mean: CentsPpmRatioSatsUsdPattern::new(client.clone(), "true_market_mean".to_string()), + cointime: CentsPpmRatioSatsUsdPattern::new(client.clone(), "cointime_price".to_string()), } } } @@ -6920,17 +6916,60 @@ impl SeriesTree_Indicators_Dormancy { /// Series tree node. pub struct SeriesTree_Indicators_RarityMeter { - pub full: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern, - pub local: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern, - pub cycle: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern, + pub components: SeriesTree_Indicators_RarityMeter_Components, + pub full: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern, + pub local: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern, + pub cycle: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern, } impl SeriesTree_Indicators_RarityMeter { pub fn new(client: Arc, base_path: String) -> Self { Self { - full: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern::new(client.clone(), "rarity_meter".to_string()), - local: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern::new(client.clone(), "local_rarity_meter".to_string()), - cycle: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern::new(client.clone(), "cycle_rarity_meter".to_string()), + components: SeriesTree_Indicators_RarityMeter_Components::new(client.clone(), format!("{base_path}_components")), + full: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern::new(client.clone(), "rarity_meter".to_string()), + local: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern::new(client.clone(), "local_rarity_meter".to_string()), + cycle: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern::new(client.clone(), "cycle_rarity_meter".to_string()), + } + } +} + +/// Series tree node. +pub struct SeriesTree_Indicators_RarityMeter_Components { + pub realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub capitalized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub sth_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub sth_capitalized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub lth_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub lth_capitalized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub over_6m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub over_4m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub under_4m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub under_6m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub vaulted_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub active_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub true_market_mean_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub cointime_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, + pub coinflow_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern, +} + +impl SeriesTree_Indicators_RarityMeter_Components { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "realized_price".to_string()), + capitalized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "capitalized_price".to_string()), + sth_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "sth_realized_price".to_string()), + sth_capitalized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "sth_capitalized_price".to_string()), + lth_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "lth_realized_price".to_string()), + lth_capitalized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "lth_capitalized_price".to_string()), + over_6m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "over_6m_realized_price".to_string()), + over_4m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "over_4m_realized_price".to_string()), + under_4m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "under_4m_realized_price".to_string()), + under_6m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "under_6m_realized_price".to_string()), + vaulted_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "vaulted_price".to_string()), + active_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "active_price".to_string()), + true_market_mean_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "true_market_mean_price".to_string()), + cointime_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "cointime_price".to_string()), + coinflow_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern::new(client.clone(), "coinflow_price".to_string()), } } } @@ -8134,7 +8173,7 @@ impl SeriesTree_Cohorts { pub struct SeriesTree_Cohorts_Utxo { pub all: SeriesTree_Cohorts_Utxo_All, pub sth: SeriesTree_Cohorts_Utxo_Sth, - pub lth: SeriesTree_Cohorts_Utxo_Lth, + pub lth: ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2, pub age_range: SeriesTree_Cohorts_Utxo_AgeRange, pub under_age: SeriesTree_Cohorts_Utxo_UnderAge, pub over_age: SeriesTree_Cohorts_Utxo_OverAge, @@ -8154,7 +8193,7 @@ impl SeriesTree_Cohorts_Utxo { Self { all: SeriesTree_Cohorts_Utxo_All::new(client.clone(), format!("{base_path}_all")), sth: SeriesTree_Cohorts_Utxo_Sth::new(client.clone(), format!("{base_path}_sth")), - lth: SeriesTree_Cohorts_Utxo_Lth::new(client.clone(), format!("{base_path}_lth")), + lth: ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2::new(client.clone(), "lth".to_string()), age_range: SeriesTree_Cohorts_Utxo_AgeRange::new(client.clone(), format!("{base_path}_age_range")), under_age: SeriesTree_Cohorts_Utxo_UnderAge::new(client.clone(), format!("{base_path}_under_age")), over_age: SeriesTree_Cohorts_Utxo_OverAge::new(client.clone(), format!("{base_path}_over_age")), @@ -8237,7 +8276,7 @@ pub struct SeriesTree_Cohorts_Utxo_All_Realized { pub cap: CentsDeltaToUsdPattern, pub profit: BlockCumulativeSumPattern, pub loss: BlockCumulativeNegativeSumPattern, - pub price: SeriesTree_Cohorts_Utxo_All_Realized_Price, + pub price: CentsPpmRatioSatsUsdPattern, pub mvrv: SeriesPattern1, pub net_pnl: BlockChangeCumulativeDeltaSumPattern, pub sopr: SeriesTree_Cohorts_Utxo_All_Realized_Sopr, @@ -8254,7 +8293,7 @@ impl SeriesTree_Cohorts_Utxo_All_Realized { cap: CentsDeltaToUsdPattern::new(client.clone(), "realized_cap".to_string()), profit: BlockCumulativeSumPattern::new(client.clone(), "realized_profit".to_string()), loss: BlockCumulativeNegativeSumPattern::new(client.clone(), "realized_loss".to_string()), - price: SeriesTree_Cohorts_Utxo_All_Realized_Price::new(client.clone(), format!("{base_path}_price")), + price: CentsPpmRatioSatsUsdPattern::new(client.clone(), "realized_price".to_string()), mvrv: SeriesPattern1::new(client.clone(), "mvrv".to_string()), net_pnl: BlockChangeCumulativeDeltaSumPattern::new(client.clone(), "net".to_string()), sopr: SeriesTree_Cohorts_Utxo_All_Realized_Sopr::new(client.clone(), format!("{base_path}_sopr")), @@ -8267,216 +8306,6 @@ impl SeriesTree_Cohorts_Utxo_All_Realized { } } -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_All_Realized_Price { - pub usd: SeriesPattern1, - pub cents: SeriesPattern1, - pub sats: SeriesPattern1, - pub ppm: SeriesPattern1, - pub ratio: SeriesPattern1, - pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern, - pub sma: _1m1w1y2y4yAllPattern, - pub std_dev: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev, -} - -impl SeriesTree_Cohorts_Utxo_All_Realized_Price { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - usd: SeriesPattern1::new(client.clone(), "realized_price".to_string()), - cents: SeriesPattern1::new(client.clone(), "realized_price_cents".to_string()), - sats: SeriesPattern1::new(client.clone(), "realized_price_sats".to_string()), - ppm: SeriesPattern1::new(client.clone(), "realized_price_ratio_ppm".to_string()), - ratio: SeriesPattern1::new(client.clone(), "realized_price_ratio".to_string()), - percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "realized_price".to_string()), - sma: _1m1w1y2y4yAllPattern::new(client.clone(), "realized_price_ratio_sma".to_string()), - std_dev: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev::new(client.clone(), format!("{base_path}_std_dev")), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev { - pub all: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All, - pub _4y: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y, - pub _2y: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y, - pub _1y: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_1y, -} - -impl SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - all: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All::new(client.clone(), format!("{base_path}_all")), - _4y: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y::new(client.clone(), format!("{base_path}_4y")), - _2y: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y::new(client.clone(), format!("{base_path}_2y")), - _1y: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_1y::new(client.clone(), format!("{base_path}_1y")), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_All_Realized_Price_StdDev_All { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "realized_price_ratio_sd".to_string()), - zscore: SeriesPattern1::new(client.clone(), "realized_price_ratio_zscore".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "realized_price_0sd".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p0_5sd".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p1sd".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p1_5sd".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p2sd".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p2_5sd".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p3sd".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m0_5sd".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m1sd".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m1_5sd".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m2sd".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m2_5sd".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m3sd".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_All_Realized_Price_StdDev_4y { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "realized_price_ratio_sd_4y".to_string()), - zscore: SeriesPattern1::new(client.clone(), "realized_price_ratio_zscore_4y".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "realized_price_0sd_4y".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p0_5sd_4y".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p1sd_4y".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p1_5sd_4y".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p2sd_4y".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p2_5sd_4y".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p3sd_4y".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m0_5sd_4y".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m1sd_4y".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m1_5sd_4y".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m2sd_4y".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m2_5sd_4y".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m3sd_4y".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_All_Realized_Price_StdDev_2y { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "realized_price_ratio_sd_2y".to_string()), - zscore: SeriesPattern1::new(client.clone(), "realized_price_ratio_zscore_2y".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "realized_price_0sd_2y".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p0_5sd_2y".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p1sd_2y".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p1_5sd_2y".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p2sd_2y".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p2_5sd_2y".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p3sd_2y".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m0_5sd_2y".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m1sd_2y".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m1_5sd_2y".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m2sd_2y".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m2_5sd_2y".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m3sd_2y".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_1y { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_All_Realized_Price_StdDev_1y { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "realized_price_ratio_sd_1y".to_string()), - zscore: SeriesPattern1::new(client.clone(), "realized_price_ratio_zscore_1y".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "realized_price_0sd_1y".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p0_5sd_1y".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p1sd_1y".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p1_5sd_1y".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p2sd_1y".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p2_5sd_1y".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "p3sd_1y".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m0_5sd_1y".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m1sd_1y".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m1_5sd_1y".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m2sd_1y".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m2_5sd_1y".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "realized_price".to_string(), "m3sd_1y".to_string()), - } - } -} - /// Series tree node. pub struct SeriesTree_Cohorts_Utxo_All_Realized_Sopr { pub value_destroyed: AverageBlockCumulativeSumPattern, @@ -8644,7 +8473,7 @@ pub struct SeriesTree_Cohorts_Utxo_Sth { pub supply: DeltaDominanceHalfInTotalPattern2, pub outputs: SpentUnspentUtxoPattern, pub activity: CoindaysCoinyearsDormancyTransferPattern, - pub realized: SeriesTree_Cohorts_Utxo_Sth_Realized, + pub realized: CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern, pub cost_basis: InMaxMinPerSupplyPattern, pub unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2, pub invested_capital: InPattern, @@ -8656,7 +8485,7 @@ impl SeriesTree_Cohorts_Utxo_Sth { supply: DeltaDominanceHalfInTotalPattern2::new(client.clone(), "sth_supply".to_string()), outputs: SpentUnspentUtxoPattern::new(client.clone(), "sth".to_string()), activity: CoindaysCoinyearsDormancyTransferPattern::new(client.clone(), "sth".to_string()), - realized: SeriesTree_Cohorts_Utxo_Sth_Realized::new(client.clone(), format!("{base_path}_realized")), + realized: CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern::new(client.clone(), "sth".to_string()), cost_basis: InMaxMinPerSupplyPattern::new(client.clone(), "sth".to_string()), unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2::new(client.clone(), "sth".to_string()), invested_capital: InPattern::new(client.clone(), "sth_invested_capital_in".to_string()), @@ -8664,521 +8493,6 @@ impl SeriesTree_Cohorts_Utxo_Sth { } } -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Sth_Realized { - pub cap: CentsDeltaToUsdPattern, - pub profit: BlockCumulativeSumPattern, - pub loss: BlockCumulativeNegativeSumPattern, - pub price: SeriesTree_Cohorts_Utxo_Sth_Realized_Price, - pub mvrv: SeriesPattern1, - pub net_pnl: BlockChangeCumulativeDeltaSumPattern, - pub sopr: AdjustedRatioValuePattern, - pub gross_pnl: BlockCumulativeSumPattern, - pub sell_side_risk_ratio: _1m1w1y24hPattern8, - pub peak_regret: BlockCumulativeSumPattern, - pub capitalized: PricePattern, - pub profit_to_loss_ratio: _1m1w1y24hPattern, -} - -impl SeriesTree_Cohorts_Utxo_Sth_Realized { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - cap: CentsDeltaToUsdPattern::new(client.clone(), "sth_realized_cap".to_string()), - profit: BlockCumulativeSumPattern::new(client.clone(), "sth_realized_profit".to_string()), - loss: BlockCumulativeNegativeSumPattern::new(client.clone(), "sth_realized_loss".to_string()), - price: SeriesTree_Cohorts_Utxo_Sth_Realized_Price::new(client.clone(), format!("{base_path}_price")), - mvrv: SeriesPattern1::new(client.clone(), "sth_mvrv".to_string()), - net_pnl: BlockChangeCumulativeDeltaSumPattern::new(client.clone(), "sth_net".to_string()), - sopr: AdjustedRatioValuePattern::new(client.clone(), "sth".to_string()), - gross_pnl: BlockCumulativeSumPattern::new(client.clone(), "sth_realized_gross_pnl".to_string()), - sell_side_risk_ratio: _1m1w1y24hPattern8::new(client.clone(), "sth_sell_side_risk_ratio".to_string()), - peak_regret: BlockCumulativeSumPattern::new(client.clone(), "sth_realized_peak_regret".to_string()), - capitalized: PricePattern::new(client.clone(), "sth_capitalized_price".to_string()), - profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), "sth_realized_profit_to_loss_ratio".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Sth_Realized_Price { - pub usd: SeriesPattern1, - pub cents: SeriesPattern1, - pub sats: SeriesPattern1, - pub ppm: SeriesPattern1, - pub ratio: SeriesPattern1, - pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern, - pub sma: _1m1w1y2y4yAllPattern, - pub std_dev: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev, -} - -impl SeriesTree_Cohorts_Utxo_Sth_Realized_Price { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - usd: SeriesPattern1::new(client.clone(), "sth_realized_price".to_string()), - cents: SeriesPattern1::new(client.clone(), "sth_realized_price_cents".to_string()), - sats: SeriesPattern1::new(client.clone(), "sth_realized_price_sats".to_string()), - ppm: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_ppm".to_string()), - ratio: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio".to_string()), - percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "sth_realized_price".to_string()), - sma: _1m1w1y2y4yAllPattern::new(client.clone(), "sth_realized_price_ratio_sma".to_string()), - std_dev: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev::new(client.clone(), format!("{base_path}_std_dev")), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev { - pub all: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All, - pub _4y: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y, - pub _2y: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y, - pub _1y: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_1y, -} - -impl SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - all: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All::new(client.clone(), format!("{base_path}_all")), - _4y: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y::new(client.clone(), format!("{base_path}_4y")), - _2y: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y::new(client.clone(), format!("{base_path}_2y")), - _1y: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_1y::new(client.clone(), format!("{base_path}_1y")), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_Sth_Realized_Price_StdDev_All { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_sd".to_string()), - zscore: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_zscore".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "sth_realized_price_0sd".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p0_5sd".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p1sd".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p1_5sd".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p2sd".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p2_5sd".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p3sd".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m0_5sd".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m1sd".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m1_5sd".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m2sd".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m2_5sd".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m3sd".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_Sth_Realized_Price_StdDev_4y { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_sd_4y".to_string()), - zscore: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_zscore_4y".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "sth_realized_price_0sd_4y".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p0_5sd_4y".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p1sd_4y".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p1_5sd_4y".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p2sd_4y".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p2_5sd_4y".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p3sd_4y".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m0_5sd_4y".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m1sd_4y".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m1_5sd_4y".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m2sd_4y".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m2_5sd_4y".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m3sd_4y".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_Sth_Realized_Price_StdDev_2y { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_sd_2y".to_string()), - zscore: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_zscore_2y".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "sth_realized_price_0sd_2y".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p0_5sd_2y".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p1sd_2y".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p1_5sd_2y".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p2sd_2y".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p2_5sd_2y".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p3sd_2y".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m0_5sd_2y".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m1sd_2y".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m1_5sd_2y".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m2sd_2y".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m2_5sd_2y".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m3sd_2y".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_1y { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_Sth_Realized_Price_StdDev_1y { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_sd_1y".to_string()), - zscore: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_zscore_1y".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "sth_realized_price_0sd_1y".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p0_5sd_1y".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p1sd_1y".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p1_5sd_1y".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p2sd_1y".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p2_5sd_1y".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "p3sd_1y".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m0_5sd_1y".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m1sd_1y".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m1_5sd_1y".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m2sd_1y".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m2_5sd_1y".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "sth_realized_price".to_string(), "m3sd_1y".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Lth { - pub supply: DeltaDominanceHalfInTotalPattern2, - pub outputs: SpentUnspentUtxoPattern, - pub activity: CoindaysCoinyearsDormancyTransferPattern, - pub realized: SeriesTree_Cohorts_Utxo_Lth_Realized, - pub cost_basis: InMaxMinPerSupplyPattern, - pub unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2, - pub invested_capital: InPattern, -} - -impl SeriesTree_Cohorts_Utxo_Lth { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - supply: DeltaDominanceHalfInTotalPattern2::new(client.clone(), "lth_supply".to_string()), - outputs: SpentUnspentUtxoPattern::new(client.clone(), "lth".to_string()), - activity: CoindaysCoinyearsDormancyTransferPattern::new(client.clone(), "lth".to_string()), - realized: SeriesTree_Cohorts_Utxo_Lth_Realized::new(client.clone(), format!("{base_path}_realized")), - cost_basis: InMaxMinPerSupplyPattern::new(client.clone(), "lth".to_string()), - unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2::new(client.clone(), "lth".to_string()), - invested_capital: InPattern::new(client.clone(), "lth_invested_capital_in".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Lth_Realized { - pub cap: CentsDeltaToUsdPattern, - pub profit: BlockCumulativeSumPattern, - pub loss: BlockCumulativeNegativeSumPattern, - pub price: SeriesTree_Cohorts_Utxo_Lth_Realized_Price, - pub mvrv: SeriesPattern1, - 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, -} - -impl SeriesTree_Cohorts_Utxo_Lth_Realized { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - cap: CentsDeltaToUsdPattern::new(client.clone(), "lth_realized_cap".to_string()), - profit: BlockCumulativeSumPattern::new(client.clone(), "lth_realized_profit".to_string()), - loss: BlockCumulativeNegativeSumPattern::new(client.clone(), "lth_realized_loss".to_string()), - 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: 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()), - capitalized: PricePattern::new(client.clone(), "lth_capitalized_price".to_string()), - profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), "lth_realized_profit_to_loss_ratio".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Price { - pub usd: SeriesPattern1, - pub cents: SeriesPattern1, - pub sats: SeriesPattern1, - pub ppm: SeriesPattern1, - pub ratio: SeriesPattern1, - pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern, - pub sma: _1m1w1y2y4yAllPattern, - pub std_dev: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev, -} - -impl SeriesTree_Cohorts_Utxo_Lth_Realized_Price { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - usd: SeriesPattern1::new(client.clone(), "lth_realized_price".to_string()), - cents: SeriesPattern1::new(client.clone(), "lth_realized_price_cents".to_string()), - sats: SeriesPattern1::new(client.clone(), "lth_realized_price_sats".to_string()), - ppm: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_ppm".to_string()), - ratio: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio".to_string()), - percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "lth_realized_price".to_string()), - sma: _1m1w1y2y4yAllPattern::new(client.clone(), "lth_realized_price_ratio_sma".to_string()), - std_dev: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev::new(client.clone(), format!("{base_path}_std_dev")), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev { - pub all: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All, - pub _4y: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y, - pub _2y: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y, - pub _1y: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_1y, -} - -impl SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - all: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All::new(client.clone(), format!("{base_path}_all")), - _4y: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y::new(client.clone(), format!("{base_path}_4y")), - _2y: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y::new(client.clone(), format!("{base_path}_2y")), - _1y: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_1y::new(client.clone(), format!("{base_path}_1y")), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_Lth_Realized_Price_StdDev_All { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_sd".to_string()), - zscore: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_zscore".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "lth_realized_price_0sd".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p0_5sd".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p1sd".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p1_5sd".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p2sd".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p2_5sd".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p3sd".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m0_5sd".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m1sd".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m1_5sd".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m2sd".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m2_5sd".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m3sd".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_Lth_Realized_Price_StdDev_4y { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_sd_4y".to_string()), - zscore: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_zscore_4y".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "lth_realized_price_0sd_4y".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p0_5sd_4y".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p1sd_4y".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p1_5sd_4y".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p2sd_4y".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p2_5sd_4y".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p3sd_4y".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m0_5sd_4y".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m1sd_4y".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m1_5sd_4y".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m2sd_4y".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m2_5sd_4y".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m3sd_4y".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_Lth_Realized_Price_StdDev_2y { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_sd_2y".to_string()), - zscore: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_zscore_2y".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "lth_realized_price_0sd_2y".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p0_5sd_2y".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p1sd_2y".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p1_5sd_2y".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p2sd_2y".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p2_5sd_2y".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p3sd_2y".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m0_5sd_2y".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m1sd_2y".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m1_5sd_2y".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m2sd_2y".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m2_5sd_2y".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m3sd_2y".to_string()), - } - } -} - -/// Series tree node. -pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_1y { - pub sd: SeriesPattern1, - pub zscore: SeriesPattern1, - 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_Lth_Realized_Price_StdDev_1y { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - sd: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_sd_1y".to_string()), - zscore: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_zscore_1y".to_string()), - _0sd: CentsSatsUsdPattern::new(client.clone(), "lth_realized_price_0sd_1y".to_string()), - p0_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p0_5sd_1y".to_string()), - p1sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p1sd_1y".to_string()), - p1_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p1_5sd_1y".to_string()), - p2sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p2sd_1y".to_string()), - p2_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p2_5sd_1y".to_string()), - p3sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "p3sd_1y".to_string()), - m0_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m0_5sd_1y".to_string()), - m1sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m1sd_1y".to_string()), - m1_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m1_5sd_1y".to_string()), - m2sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m2sd_1y".to_string()), - m2_5sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m2_5sd_1y".to_string()), - m3sd: PriceRatioPattern::new(client.clone(), "lth_realized_price".to_string(), "m3sd_1y".to_string()), - } - } -} - /// Series tree node. pub struct SeriesTree_Cohorts_Utxo_AgeRange { pub under_1h: ActivityOutputsRealizedSupplyUnrealizedPattern, @@ -9396,555 +8710,15 @@ 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, + pub discount: ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2, + pub premium: ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2, } impl SeriesTree_Cohorts_Utxo_Entry { pub fn new(client: Arc, 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: SpentUnspentUtxoPattern, - 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, base_path: String) -> Self { - Self { - supply: DeltaDominanceHalfInTotalPattern2::new(client.clone(), "veteran_supply".to_string()), - outputs: SpentUnspentUtxoPattern::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, - 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, -} - -impl SeriesTree_Cohorts_Utxo_Entry_Discount_Realized { - pub fn new(client: Arc, 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, - pub cents: SeriesPattern1, - pub sats: SeriesPattern1, - pub ppm: SeriesPattern1, - pub ratio: SeriesPattern1, - 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, 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()), - ppm: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_ppm".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, 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, - pub zscore: SeriesPattern1, - 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, 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, - pub zscore: SeriesPattern1, - 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, 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, - pub zscore: SeriesPattern1, - 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, 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, - pub zscore: SeriesPattern1, - 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, 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: SpentUnspentUtxoPattern, - 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, base_path: String) -> Self { - Self { - supply: DeltaDominanceHalfInTotalPattern2::new(client.clone(), "rookie_supply".to_string()), - outputs: SpentUnspentUtxoPattern::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, - 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, -} - -impl SeriesTree_Cohorts_Utxo_Entry_Premium_Realized { - pub fn new(client: Arc, 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, - pub cents: SeriesPattern1, - pub sats: SeriesPattern1, - pub ppm: SeriesPattern1, - pub ratio: SeriesPattern1, - 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, 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()), - ppm: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_ppm".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, 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, - pub zscore: SeriesPattern1, - 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, 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, - pub zscore: SeriesPattern1, - 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, 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, - pub zscore: SeriesPattern1, - 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, 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, - pub zscore: SeriesPattern1, - 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, 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()), + discount: ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2::new(client.clone(), "veteran".to_string()), + premium: ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2::new(client.clone(), "rookie".to_string()), } } } diff --git a/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs b/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs index 783b4fb6a..a458b372c 100644 --- a/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs +++ b/crates/brk_computer/src/distribution/cohorts/utxo/groups.rs @@ -14,7 +14,6 @@ use vecdb::{ }; use crate::{ - blocks, distribution::{ DynCohortVecs, metrics::{ @@ -592,7 +591,6 @@ impl UTXOCohorts { /// Second phase of post-processing: compute relative metrics. pub(crate) fn compute_rest_part2( &mut self, - blocks: &blocks::Vecs, prices: &price::Vecs, starting_lengths: &Lengths, height_to_market_cap: &impl ReadableVec, @@ -620,7 +618,6 @@ impl UTXOCohorts { // "all" cohort computed first (no all_supply_sats needed). self.all.metrics.compute_rest_part2( - blocks, prices, starting_lengths, height_to_market_cap, @@ -666,7 +663,6 @@ impl UTXOCohorts { let tasks: Vec Result<()> + Send + '_>> = vec![ Box::new(|| { sth.metrics.compute_rest_part2( - blocks, prices, starting_lengths, height_to_market_cap, @@ -679,7 +675,6 @@ impl UTXOCohorts { }), Box::new(|| { lth.metrics.compute_rest_part2( - blocks, prices, starting_lengths, height_to_market_cap, @@ -727,7 +722,6 @@ impl UTXOCohorts { Box::new(|| { entry.par_iter_mut().try_for_each(|v| { v.metrics.compute_rest_part2( - blocks, prices, starting_lengths, height_to_market_cap, diff --git a/crates/brk_computer/src/distribution/metrics/cohort/all.rs b/crates/brk_computer/src/distribution/metrics/cohort/all.rs index f07a886ff..dceabb076 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/all.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/all.rs @@ -6,7 +6,6 @@ use brk_types::{Cents, Dollars, Height, Version}; use vecdb::{AnyStoredVec, Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode}; use crate::{ - blocks, distribution::metrics::{ ActivityFull, AdjustedSopr, CohortMetricsBase, CostBasis, ImportConfig, OutputsBase, RealizedFull, RelativeForAll, SupplyCore, UnrealizedFull, @@ -99,7 +98,6 @@ impl AllCohortMetrics { #[allow(clippy::too_many_arguments)] pub(crate) fn compute_rest_part2( &mut self, - blocks: &blocks::Vecs, prices: &price::Vecs, starting_lengths: &Lengths, height_to_market_cap: &impl ReadableVec, @@ -108,7 +106,6 @@ impl AllCohortMetrics { exit: &Exit, ) -> Result<()> { self.realized.compute_rest_part2( - blocks, prices, starting_lengths, &self.supply.total.btc.height, diff --git a/crates/brk_computer/src/distribution/metrics/cohort/extended.rs b/crates/brk_computer/src/distribution/metrics/cohort/extended.rs index d36c37547..ee7990b35 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/extended.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/extended.rs @@ -7,7 +7,6 @@ use vecdb::AnyStoredVec; use vecdb::{Exit, ReadableVec, Rw, StorageMode}; use crate::{ - blocks, distribution::metrics::{ ActivityFull, CohortMetricsBase, CostBasis, ImportConfig, OutputsBase, RealizedFull, RelativeWithExtended, SupplyCore, UnrealizedFull, @@ -89,7 +88,6 @@ impl ExtendedCohortMetrics { #[allow(clippy::too_many_arguments)] pub(crate) fn compute_rest_part2( &mut self, - blocks: &blocks::Vecs, prices: &price::Vecs, starting_lengths: &Lengths, height_to_market_cap: &impl ReadableVec, @@ -98,7 +96,6 @@ impl ExtendedCohortMetrics { exit: &Exit, ) -> Result<()> { self.realized.compute_rest_part2( - blocks, prices, starting_lengths, &self.supply.total.btc.height, diff --git a/crates/brk_computer/src/distribution/metrics/cohort/extended_adjusted.rs b/crates/brk_computer/src/distribution/metrics/cohort/extended_adjusted.rs index 5e4beb751..685380b9b 100644 --- a/crates/brk_computer/src/distribution/metrics/cohort/extended_adjusted.rs +++ b/crates/brk_computer/src/distribution/metrics/cohort/extended_adjusted.rs @@ -6,7 +6,6 @@ use derive_more::{Deref, DerefMut}; use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode}; use crate::{ - blocks, distribution::metrics::{ ActivityFull, AdjustedSopr, CohortMetricsBase, ImportConfig, RealizedFull, UnrealizedFull, }, @@ -61,7 +60,6 @@ impl ExtendedAdjustedCohortMetrics { #[allow(clippy::too_many_arguments)] pub(crate) fn compute_rest_part2( &mut self, - blocks: &blocks::Vecs, prices: &price::Vecs, starting_lengths: &Lengths, height_to_market_cap: &impl ReadableVec, @@ -72,7 +70,6 @@ impl ExtendedAdjustedCohortMetrics { exit: &Exit, ) -> Result<()> { self.inner.compute_rest_part2( - blocks, prices, starting_lengths, height_to_market_cap, diff --git a/crates/brk_computer/src/distribution/metrics/config.rs b/crates/brk_computer/src/distribution/metrics/config.rs index 7f8789db8..52f73b1ff 100644 --- a/crates/brk_computer/src/distribution/metrics/config.rs +++ b/crates/brk_computer/src/distribution/metrics/config.rs @@ -11,9 +11,9 @@ use crate::{ internal::{ FiatPerBlock, FiatPerBlockCumulativeWithSums, FiatType, NumericValue, PerBlock, PerBlockCumulativeRolling, PercentPerBlock, PercentRollingWindows, Price, - PriceWithRatioExtendedPerBlock, PriceWithRatioPerBlock, RatioPerBlock, - RollingWindow24hPerBlock, RollingWindows, RollingWindowsFrom1w, ValuePerBlock, - ValuePerBlockCumulative, ValuePerBlockCumulativeRolling, WindowStartVec, Windows, + PriceWithRatioPerBlock, RatioPerBlock, RollingWindow24hPerBlock, RollingWindows, + RollingWindowsFrom1w, ValuePerBlock, ValuePerBlockCumulative, + ValuePerBlockCumulativeRolling, WindowStartVec, Windows, }, }; @@ -40,7 +40,6 @@ impl_config_import!( ValuePerBlock, ValuePerBlockCumulative, PriceWithRatioPerBlock, - PriceWithRatioExtendedPerBlock, RatioPerBlock, PercentPerBlock, PercentPerBlock, diff --git a/crates/brk_computer/src/distribution/metrics/realized/full.rs b/crates/brk_computer/src/distribution/metrics/realized/full.rs index 85bd9ef84..b51d6babd 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/full.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/full.rs @@ -9,13 +9,12 @@ use derive_more::{Deref, DerefMut}; use vecdb::{AnyStoredVec, AnyVec, BytesVec, Exit, ReadableVec, Rw, StorageMode, WritableVec}; use crate::{ - blocks, distribution::state::{CohortState, CostBasisData, RealizedState, WithCapital}, internal::{ FiatPerBlockCumulativeWithSums, PercentPerBlock, PercentRollingWindows, - PriceWithRatioExtendedPerBlock, RatioCents, RatioCents64, RatioCentsSignedCents, - RatioCentsSignedDollars, RatioDollars, RatioPerBlockPercentiles, RatioPerBlockStdDevBands, - RatioSma, RollingWindows, RollingWindowsFrom1w, ValuePerBlockCumulativeRolling, + PriceWithRatioPerBlock, RatioCents, RatioCents64, RatioCentsSignedCents, + RatioCentsSignedDollars, RatioDollars, RollingWindows, RollingWindowsFrom1w, + ValuePerBlockCumulativeRolling, }, price, }; @@ -46,7 +45,7 @@ pub struct RealizedPeakRegret { #[derive(Traversable)] pub struct RealizedCapitalized { - pub price: PriceWithRatioExtendedPerBlock, + pub price: PriceWithRatioPerBlock, #[traversable(hidden)] pub cap_raw: M::Stored>, } @@ -71,13 +70,6 @@ pub struct RealizedFull { pub cap_raw: M::Stored>, #[traversable(wrap = "cap", rename = "to_own_mcap")] pub cap_to_own_mcap: PercentPerBlock, - - #[traversable(wrap = "price", rename = "percentiles")] - pub price_ratio_percentiles: RatioPerBlockPercentiles, - #[traversable(wrap = "price", rename = "sma")] - pub price_ratio_sma: RatioSma, - #[traversable(wrap = "price", rename = "std_dev")] - pub price_ratio_std_dev: RatioPerBlockStdDevBands, } impl RealizedFull { @@ -114,10 +106,6 @@ impl RealizedFull { cap_raw: cfg.import("capitalized_cap_raw", v0)?, }; - // Price ratio stats - let realized_price_name = cfg.name("realized_price"); - let realized_price_version = cfg.version + v1; - Ok(Self { core, gross_pnl, @@ -129,24 +117,6 @@ impl RealizedFull { profit_to_loss_ratio: cfg.import("realized_profit_to_loss_ratio", v1)?, cap_raw: cfg.import("cap_raw", v0)?, cap_to_own_mcap: cfg.import("realized_cap_to_own_mcap", v1)?, - price_ratio_percentiles: RatioPerBlockPercentiles::forced_import( - cfg.db, - &realized_price_name, - realized_price_version, - cfg.indexes, - )?, - price_ratio_sma: RatioSma::forced_import( - cfg.db, - &realized_price_name, - realized_price_version, - cfg.indexes, - )?, - price_ratio_std_dev: RatioPerBlockStdDevBands::forced_import( - cfg.db, - &realized_price_name, - realized_price_version, - cfg.indexes, - )?, }) } @@ -240,7 +210,6 @@ impl RealizedFull { #[allow(clippy::too_many_arguments)] pub(crate) fn compute_rest_part2( &mut self, - blocks: &blocks::Vecs, prices: &price::Vecs, starting_lengths: &Lengths, height_to_supply: &impl ReadableVec, @@ -304,10 +273,10 @@ impl RealizedFull { exit, )?; - // Capitalized price ratio, percentiles and bands + // Capitalized price ratio self.capitalized .price - .compute_rest(prices, starting_lengths, exit)?; + .compute_ratio(starting_lengths, &prices.spot.cents.height, exit)?; // Sell-side risk ratios for (ssrr, rv) in self @@ -349,30 +318,6 @@ impl RealizedFull { )?; } - // Price ratio: percentiles, sma and std dev bands - self.price_ratio_percentiles.compute( - starting_lengths, - exit, - &self.core.minimal.price.ratio.height, - &self.core.minimal.price.cents.height, - )?; - - self.price_ratio_sma.compute( - blocks, - starting_lengths, - exit, - &self.core.minimal.price.ratio.height, - )?; - - self.price_ratio_std_dev.compute( - blocks, - starting_lengths, - exit, - &self.core.minimal.price.ratio.height, - &self.core.minimal.price.cents.height, - &self.price_ratio_sma, - )?; - Ok(()) } } diff --git a/crates/brk_computer/src/distribution/vecs.rs b/crates/brk_computer/src/distribution/vecs.rs index 55fde91bd..8e23b9e0f 100644 --- a/crates/brk_computer/src/distribution/vecs.rs +++ b/crates/brk_computer/src/distribution/vecs.rs @@ -16,7 +16,6 @@ use vecdb::{ }; use crate::{ - blocks, distribution::{ compute::{ PriceRangeMax, StartMode, determine_start_mode, process_blocks, recover_state, @@ -315,7 +314,6 @@ impl Vecs { inputs: &inputs::Vecs, outputs: &outputs::Vecs, transactions: &transactions::Vecs, - blocks: &blocks::Vecs, prices: &price::Vecs, exit: &Exit, ) -> Result<()> { @@ -668,7 +666,6 @@ impl Vecs { info!("Computing rest part 2..."); self.utxo_cohorts.compute_rest_part2( - blocks, prices, &starting_lengths, &height_to_market_cap, diff --git a/crates/brk_computer/src/frameworks/cointime/prices/import.rs b/crates/brk_computer/src/frameworks/cointime/prices/import.rs index dd4e0839c..26dd9392d 100644 --- a/crates/brk_computer/src/frameworks/cointime/prices/import.rs +++ b/crates/brk_computer/src/frameworks/cointime/prices/import.rs @@ -3,7 +3,7 @@ use brk_types::Version; use vecdb::Database; use super::Vecs; -use crate::{indexes, internal::PriceWithRatioExtendedPerBlock}; +use crate::{indexes, internal::PriceWithRatioPerBlock}; impl Vecs { pub(crate) fn forced_import( @@ -13,7 +13,7 @@ impl Vecs { ) -> Result { macro_rules! import { ($name:expr) => { - PriceWithRatioExtendedPerBlock::forced_import(db, $name, version, indexes)? + PriceWithRatioPerBlock::forced_import(db, $name, version, indexes)? }; } diff --git a/crates/brk_computer/src/frameworks/cointime/prices/vecs.rs b/crates/brk_computer/src/frameworks/cointime/prices/vecs.rs index 0c11af2a3..f1b4f958b 100644 --- a/crates/brk_computer/src/frameworks/cointime/prices/vecs.rs +++ b/crates/brk_computer/src/frameworks/cointime/prices/vecs.rs @@ -1,12 +1,12 @@ use brk_traversable::Traversable; use vecdb::{Rw, StorageMode}; -use crate::internal::PriceWithRatioExtendedPerBlock; +use crate::internal::PriceWithRatioPerBlock; #[derive(Traversable)] pub struct Vecs { - pub vaulted: PriceWithRatioExtendedPerBlock, - pub active: PriceWithRatioExtendedPerBlock, - pub true_market_mean: PriceWithRatioExtendedPerBlock, - pub cointime: PriceWithRatioExtendedPerBlock, + pub vaulted: PriceWithRatioPerBlock, + pub active: PriceWithRatioPerBlock, + pub true_market_mean: PriceWithRatioPerBlock, + pub cointime: PriceWithRatioPerBlock, } diff --git a/crates/brk_computer/src/indicators/rarity_meter/components.rs b/crates/brk_computer/src/indicators/rarity_meter/components.rs new file mode 100644 index 000000000..b9f69e294 --- /dev/null +++ b/crates/brk_computer/src/indicators/rarity_meter/components.rs @@ -0,0 +1,359 @@ +use brk_error::Result; +use brk_indexer::{Indexer, Lengths}; +use brk_traversable::Traversable; +use brk_types::{Cents, Height, PartsPerMillion32, Version}; +use vecdb::{ + AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex, + WritableVec, +}; + +use crate::{ + distribution, + frameworks::{coinflow, cointime}, + indexes, + internal::{PerBlock, Price, PriceTimesRatio, PriceWithRatioPerBlock, RatioPerBlock}, +}; + +use super::percentiles::{BlockDecayPercentiles, START_HEIGHT}; + +#[derive(Traversable)] +pub struct Band { + #[traversable(flatten)] + pub ratio: RatioPerBlock, + pub price: Price>, +} + +#[derive(Traversable)] +pub struct Component { + pub pct0_01: Band, + pub pct0_5: Band, + pub pct1: Band, + pub pct2: Band, + pub pct5: Band, + pub pct10: Band, + pub pct20: Band, + pub pct30: Band, + pub pct40: Band, + pub pct50: Band, + pub pct60: Band, + pub pct70: Band, + pub pct80: Band, + pub pct90: Band, + pub pct95: Band, + pub pct98: Band, + pub pct99: Band, + pub pct99_5: Band, + pub pct99_9: Band, + + #[traversable(skip)] + block_decay_pct: BlockDecayPercentiles, +} + +const VERSION: Version = Version::new(8); + +impl Component { + fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let version = version + VERSION; + + macro_rules! import_ratio { + ($suffix:expr) => { + RatioPerBlock::forced_import_ppm( + db, + &format!("{name}_{}", $suffix), + version, + indexes, + )? + }; + } + + macro_rules! import_price { + ($suffix:expr) => { + Price::forced_import(db, &format!("{name}_{}", $suffix), version, indexes)? + }; + } + + macro_rules! import_band { + ($pct:expr) => { + Band { + ratio: import_ratio!(concat!("ratio_", $pct)), + price: import_price!($pct), + } + }; + } + + Ok(Self { + pct0_01: import_band!("pct0_01"), + pct0_5: import_band!("pct0_5"), + pct1: import_band!("pct1"), + pct2: import_band!("pct2"), + pct5: import_band!("pct5"), + pct10: import_band!("pct10"), + pct20: import_band!("pct20"), + pct30: import_band!("pct30"), + pct40: import_band!("pct40"), + pct50: import_band!("pct50"), + pct60: import_band!("pct60"), + pct70: import_band!("pct70"), + pct80: import_band!("pct80"), + pct90: import_band!("pct90"), + pct95: import_band!("pct95"), + pct98: import_band!("pct98"), + pct99: import_band!("pct99"), + pct99_5: import_band!("pct99_5"), + pct99_9: import_band!("pct99_9"), + block_decay_pct: BlockDecayPercentiles::default(), + }) + } + + fn compute( + &mut self, + starting_lengths: &Lengths, + source: &PriceWithRatioPerBlock, + exit: &Exit, + ) -> Result<()> { + let ratio_source = &source.ratio.height; + let series_price = &source.cents.height; + let ratio_version = ratio_source.version(); + + self.mut_pct_vecs().try_for_each(|vec| -> Result<()> { + vec.validate_computed_version_or_reset(ratio_version)?; + Ok(()) + })?; + + let starting_height = self + .mut_pct_vecs() + .map(|vec| Height::from(vec.len())) + .min() + .unwrap() + .min(starting_lengths.height); + + let start = starting_height.to_usize(); + let ratio_len = ratio_source.len(); + + if ratio_len > start { + let expected_len = start.saturating_sub(START_HEIGHT); + if self.block_decay_pct.len() != expected_len { + self.block_decay_pct.reset(); + if start > START_HEIGHT { + let historical = ratio_source.collect_range_at(START_HEIGHT, start); + self.block_decay_pct.add_bulk(START_HEIGHT, &historical); + } + } + + let new_ratios = ratio_source.collect_range_at(start, ratio_len); + let mut pct_vecs: [&mut EagerVec>; 19] = [ + &mut self.pct0_01.ratio.ppm.height, + &mut self.pct0_5.ratio.ppm.height, + &mut self.pct1.ratio.ppm.height, + &mut self.pct2.ratio.ppm.height, + &mut self.pct5.ratio.ppm.height, + &mut self.pct10.ratio.ppm.height, + &mut self.pct20.ratio.ppm.height, + &mut self.pct30.ratio.ppm.height, + &mut self.pct40.ratio.ppm.height, + &mut self.pct50.ratio.ppm.height, + &mut self.pct60.ratio.ppm.height, + &mut self.pct70.ratio.ppm.height, + &mut self.pct80.ratio.ppm.height, + &mut self.pct90.ratio.ppm.height, + &mut self.pct95.ratio.ppm.height, + &mut self.pct98.ratio.ppm.height, + &mut self.pct99.ratio.ppm.height, + &mut self.pct99_5.ratio.ppm.height, + &mut self.pct99_9.ratio.ppm.height, + ]; + const PCTS: [f64; 19] = [ + 0.0001, 0.005, 0.01, 0.02, 0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, + 0.90, 0.95, 0.98, 0.99, 0.995, 0.999, + ]; + let mut out = [0.0; 19]; + + for vec in &mut pct_vecs { + vec.truncate_if_needed_at(start)?; + } + + for (offset, &ratio) in new_ratios.iter().enumerate() { + let height = start + offset; + if height >= START_HEIGHT { + self.block_decay_pct.add(height, *ratio); + } + self.block_decay_pct.quantiles(&PCTS, &mut out); + for (vec, &value) in pct_vecs.iter_mut().zip(&out) { + vec.push(PartsPerMillion32::from(value)); + } + } + } + + { + let _lock = exit.lock(); + self.mut_pct_vecs() + .try_for_each(|vec| vec.write().map(|_| ()))?; + } + + macro_rules! compute_price { + ($band:ident) => { + self.$band + .price + .cents + .compute_binary::>( + starting_lengths.height, + series_price, + &self.$band.ratio.ppm.height, + exit, + )?; + }; + } + + compute_price!(pct0_01); + compute_price!(pct0_5); + compute_price!(pct1); + compute_price!(pct2); + compute_price!(pct5); + compute_price!(pct10); + compute_price!(pct20); + compute_price!(pct30); + compute_price!(pct40); + compute_price!(pct50); + compute_price!(pct60); + compute_price!(pct70); + compute_price!(pct80); + compute_price!(pct90); + compute_price!(pct95); + compute_price!(pct98); + compute_price!(pct99); + compute_price!(pct99_5); + compute_price!(pct99_9); + + Ok(()) + } + + fn mut_pct_vecs( + &mut self, + ) -> impl Iterator>> { + [ + &mut self.pct0_01.ratio.ppm.height, + &mut self.pct0_5.ratio.ppm.height, + &mut self.pct1.ratio.ppm.height, + &mut self.pct2.ratio.ppm.height, + &mut self.pct5.ratio.ppm.height, + &mut self.pct10.ratio.ppm.height, + &mut self.pct20.ratio.ppm.height, + &mut self.pct30.ratio.ppm.height, + &mut self.pct40.ratio.ppm.height, + &mut self.pct50.ratio.ppm.height, + &mut self.pct60.ratio.ppm.height, + &mut self.pct70.ratio.ppm.height, + &mut self.pct80.ratio.ppm.height, + &mut self.pct90.ratio.ppm.height, + &mut self.pct95.ratio.ppm.height, + &mut self.pct98.ratio.ppm.height, + &mut self.pct99.ratio.ppm.height, + &mut self.pct99_5.ratio.ppm.height, + &mut self.pct99_9.ratio.ppm.height, + ] + .into_iter() + } +} + +#[derive(Traversable)] +pub struct Components { + pub realized_price: Component, + pub capitalized_price: Component, + pub sth_realized_price: Component, + pub sth_capitalized_price: Component, + pub lth_realized_price: Component, + pub lth_capitalized_price: Component, + pub over_6m_realized_price: Component, + pub over_4m_realized_price: Component, + pub under_4m_realized_price: Component, + pub under_6m_realized_price: Component, + pub vaulted_price: Component, + pub active_price: Component, + pub true_market_mean_price: Component, + pub cointime_price: Component, + pub coinflow_price: Component, +} + +impl Components { + pub(super) fn forced_import( + db: &Database, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let import = |name| Component::forced_import(db, name, version, indexes); + + Ok(Self { + realized_price: import("realized_price")?, + capitalized_price: import("capitalized_price")?, + sth_realized_price: import("sth_realized_price")?, + sth_capitalized_price: import("sth_capitalized_price")?, + lth_realized_price: import("lth_realized_price")?, + lth_capitalized_price: import("lth_capitalized_price")?, + over_6m_realized_price: import("over_6m_realized_price")?, + over_4m_realized_price: import("over_4m_realized_price")?, + under_4m_realized_price: import("under_4m_realized_price")?, + under_6m_realized_price: import("under_6m_realized_price")?, + vaulted_price: import("vaulted_price")?, + active_price: import("active_price")?, + true_market_mean_price: import("true_market_mean_price")?, + cointime_price: import("cointime_price")?, + coinflow_price: import("coinflow_price")?, + }) + } + + pub(super) fn compute( + &mut self, + indexer: &Indexer, + distribution: &distribution::Vecs, + cointime: &cointime::Vecs, + coinflow: &coinflow::Vecs, + exit: &Exit, + ) -> Result<()> { + let starting_lengths = indexer.safe_lengths(); + let utxos = &distribution.utxo_cohorts; + let all = &utxos.all.metrics.realized; + let sth = &utxos.sth.metrics.realized; + let lth = &utxos.lth.metrics.realized; + + macro_rules! compute { + ($component:ident, $source:expr) => { + self.$component.compute(&starting_lengths, $source, exit)?; + }; + } + + compute!(realized_price, &all.price); + compute!(capitalized_price, &all.capitalized.price); + compute!(sth_realized_price, &sth.price); + compute!(sth_capitalized_price, &sth.capitalized.price); + compute!(lth_realized_price, <h.price); + compute!(lth_capitalized_price, <h.capitalized.price); + compute!( + over_6m_realized_price, + &utxos.over_age._6m.metrics.realized.price + ); + compute!( + over_4m_realized_price, + &utxos.over_age._4m.metrics.realized.price + ); + compute!( + under_4m_realized_price, + &utxos.under_age._4m.metrics.realized.price + ); + compute!( + under_6m_realized_price, + &utxos.under_age._6m.metrics.realized.price + ); + compute!(vaulted_price, &cointime.prices.vaulted); + compute!(active_price, &cointime.prices.active); + compute!(true_market_mean_price, &cointime.prices.true_market_mean); + compute!(cointime_price, &cointime.prices.cointime); + compute!(coinflow_price, &coinflow.price); + + Ok(()) + } +} diff --git a/crates/brk_computer/src/indicators/rarity_meter/inner.rs b/crates/brk_computer/src/indicators/rarity_meter/inner.rs index 948c872f4..ef5d23d56 100644 --- a/crates/brk_computer/src/indicators/rarity_meter/inner.rs +++ b/crates/brk_computer/src/indicators/rarity_meter/inner.rs @@ -2,23 +2,36 @@ use brk_error::Result; use brk_indexer::Indexer; use brk_traversable::Traversable; use brk_types::{Cents, Height, StoredI8, Version}; -use vecdb::{AnyVec, Database, Exit, ReadableVec, Rw, StorageMode, WritableVec}; +use vecdb::{AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, WritableVec}; use crate::{ indexes, - internal::{PerBlock, Price, RatioPerBlockPercentiles}, + internal::{PerBlock, Price}, }; +use super::Component; + #[derive(Traversable)] pub struct RarityMeterInner { + pub pct0_01: Price>, pub pct0_5: Price>, pub pct1: Price>, pub pct2: Price>, pub pct5: Price>, + pub pct10: Price>, + pub pct20: Price>, + pub pct30: Price>, + pub pct40: Price>, + pub pct50: Price>, + pub pct60: Price>, + pub pct70: Price>, + pub pct80: Price>, + pub pct90: Price>, pub pct95: Price>, pub pct98: Price>, pub pct99: Price>, pub pct99_5: Price>, + pub pct99_9: Price>, pub index: PerBlock, pub score: PerBlock, } @@ -31,14 +44,25 @@ impl RarityMeterInner { indexes: &indexes::Vecs, ) -> Result { Ok(Self { + pct0_01: Price::forced_import(db, &format!("{prefix}_pct0_01"), version, indexes)?, pct0_5: Price::forced_import(db, &format!("{prefix}_pct0_5"), version, indexes)?, pct1: Price::forced_import(db, &format!("{prefix}_pct01"), version, indexes)?, pct2: Price::forced_import(db, &format!("{prefix}_pct02"), version, indexes)?, pct5: Price::forced_import(db, &format!("{prefix}_pct05"), version, indexes)?, + pct10: Price::forced_import(db, &format!("{prefix}_pct10"), version, indexes)?, + pct20: Price::forced_import(db, &format!("{prefix}_pct20"), version, indexes)?, + pct30: Price::forced_import(db, &format!("{prefix}_pct30"), version, indexes)?, + pct40: Price::forced_import(db, &format!("{prefix}_pct40"), version, indexes)?, + pct50: Price::forced_import(db, &format!("{prefix}_pct50"), version, indexes)?, + pct60: Price::forced_import(db, &format!("{prefix}_pct60"), version, indexes)?, + pct70: Price::forced_import(db, &format!("{prefix}_pct70"), version, indexes)?, + pct80: Price::forced_import(db, &format!("{prefix}_pct80"), version, indexes)?, + pct90: Price::forced_import(db, &format!("{prefix}_pct90"), version, indexes)?, pct95: Price::forced_import(db, &format!("{prefix}_pct95"), version, indexes)?, pct98: Price::forced_import(db, &format!("{prefix}_pct98"), version, indexes)?, pct99: Price::forced_import(db, &format!("{prefix}_pct99"), version, indexes)?, pct99_5: Price::forced_import(db, &format!("{prefix}_pct99_5"), version, indexes)?, + pct99_9: Price::forced_import(db, &format!("{prefix}_pct99_9"), version, indexes)?, index: PerBlock::forced_import(db, &format!("{prefix}_index"), version, indexes)?, score: PerBlock::forced_import(db, &format!("{prefix}_score"), version, indexes)?, }) @@ -46,63 +70,146 @@ impl RarityMeterInner { pub(super) fn compute( &mut self, - models: &[&RatioPerBlockPercentiles], + components: &[&Component], spot: &impl ReadableVec, indexer: &Indexer, exit: &Exit, ) -> Result<()> { let starting_height = indexer.safe_lengths().height; - let gather = |f: fn(&RatioPerBlockPercentiles) -> &_| -> Vec<_> { - models.iter().map(|m| f(m)).collect() + let gather = |f: fn(&Component) -> &_| -> Vec<_> { + components.iter().map(|component| f(component)).collect() }; // Lower percentiles: max across all models (tightest lower bound) + self.pct0_01.cents.height.compute_max_of_others( + starting_height, + &gather(|component| &component.pct0_01.price.cents.height), + exit, + )?; self.pct0_5.cents.height.compute_max_of_others( starting_height, - &gather(|m| &m.pct0_5.price.cents.height), + &gather(|component| &component.pct0_5.price.cents.height), exit, )?; self.pct1.cents.height.compute_max_of_others( starting_height, - &gather(|m| &m.pct1.price.cents.height), + &gather(|component| &component.pct1.price.cents.height), exit, )?; self.pct2.cents.height.compute_max_of_others( starting_height, - &gather(|m| &m.pct2.price.cents.height), + &gather(|component| &component.pct2.price.cents.height), exit, )?; self.pct5.cents.height.compute_max_of_others( starting_height, - &gather(|m| &m.pct5.price.cents.height), + &gather(|component| &component.pct5.price.cents.height), exit, )?; // Upper percentiles: min across all models (tightest upper bound) self.pct95.cents.height.compute_min_of_others( starting_height, - &gather(|m| &m.pct95.price.cents.height), + &gather(|component| &component.pct95.price.cents.height), exit, )?; self.pct98.cents.height.compute_min_of_others( starting_height, - &gather(|m| &m.pct98.price.cents.height), + &gather(|component| &component.pct98.price.cents.height), exit, )?; self.pct99.cents.height.compute_min_of_others( starting_height, - &gather(|m| &m.pct99.price.cents.height), + &gather(|component| &component.pct99.price.cents.height), exit, )?; self.pct99_5.cents.height.compute_min_of_others( starting_height, - &gather(|m| &m.pct99_5.price.cents.height), + &gather(|component| &component.pct99_5.price.cents.height), + exit, + )?; + self.pct99_9.cents.height.compute_min_of_others( + starting_height, + &gather(|component| &component.pct99_9.price.cents.height), + exit, + )?; + + compute_inner_percentile( + &mut self.pct10.cents.height, + starting_height, + &self.pct5.cents.height, + &self.pct95.cents.height, + 10, + exit, + )?; + compute_inner_percentile( + &mut self.pct20.cents.height, + starting_height, + &self.pct5.cents.height, + &self.pct95.cents.height, + 20, + exit, + )?; + compute_inner_percentile( + &mut self.pct30.cents.height, + starting_height, + &self.pct5.cents.height, + &self.pct95.cents.height, + 30, + exit, + )?; + compute_inner_percentile( + &mut self.pct40.cents.height, + starting_height, + &self.pct5.cents.height, + &self.pct95.cents.height, + 40, + exit, + )?; + compute_inner_percentile( + &mut self.pct50.cents.height, + starting_height, + &self.pct5.cents.height, + &self.pct95.cents.height, + 50, + exit, + )?; + compute_inner_percentile( + &mut self.pct60.cents.height, + starting_height, + &self.pct5.cents.height, + &self.pct95.cents.height, + 60, + exit, + )?; + compute_inner_percentile( + &mut self.pct70.cents.height, + starting_height, + &self.pct5.cents.height, + &self.pct95.cents.height, + 70, + exit, + )?; + compute_inner_percentile( + &mut self.pct80.cents.height, + starting_height, + &self.pct5.cents.height, + &self.pct95.cents.height, + 80, + exit, + )?; + compute_inner_percentile( + &mut self.pct90.cents.height, + starting_height, + &self.pct5.cents.height, + &self.pct95.cents.height, + 90, exit, )?; self.compute_index(spot, indexer, exit)?; - self.compute_score(models, spot, indexer, exit)?; + self.compute_score(components, spot, indexer, exit)?; Ok(()) } @@ -115,6 +222,7 @@ impl RarityMeterInner { ) -> Result<()> { let starting_height = indexer.safe_lengths().height; let bands = [ + &self.pct0_01.cents.height, &self.pct0_5.cents.height, &self.pct1.cents.height, &self.pct2.cents.height, @@ -123,6 +231,7 @@ impl RarityMeterInner { &self.pct98.cents.height, &self.pct99.cents.height, &self.pct99_5.cents.height, + &self.pct99_9.cents.height, ]; let dep_version: Version = @@ -143,38 +252,10 @@ impl RarityMeterInner { } let spot_batch = spot.collect_range_at(skip, end); - let b: [Vec; 8] = bands.each_ref().map(|v| v.collect_range_at(skip, end)); + let b: [Vec; 10] = bands.each_ref().map(|v| v.collect_range_at(skip, end)); for j in 0..(end - skip) { - let price = spot_batch[j]; - let mut score: i8 = 0; - - if price < b[3][j] { - score -= 1; - } - if price < b[2][j] { - score -= 1; - } - if price < b[1][j] { - score -= 1; - } - if price < b[0][j] { - score -= 1; - } - if price > b[4][j] { - score += 1; - } - if price > b[5][j] { - score += 1; - } - if price > b[6][j] { - score += 1; - } - if price > b[7][j] { - score += 1; - } - - vec.push(StoredI8::new(score)); + vec.push(StoredI8::new(score_at(spot_batch[j], &b, j))); } Ok(()) @@ -185,23 +266,25 @@ impl RarityMeterInner { fn compute_score( &mut self, - models: &[&RatioPerBlockPercentiles], + components: &[&Component], spot: &impl ReadableVec, indexer: &Indexer, exit: &Exit, ) -> Result<()> { let starting_height = indexer.safe_lengths().height; - let dep_version: Version = models + let dep_version: Version = components .iter() - .map(|p| { - p.pct0_5.price.cents.height.version() - + p.pct1.price.cents.height.version() - + p.pct2.price.cents.height.version() - + p.pct5.price.cents.height.version() - + p.pct95.price.cents.height.version() - + p.pct98.price.cents.height.version() - + p.pct99.price.cents.height.version() - + p.pct99_5.price.cents.height.version() + .map(|component| { + component.pct0_01.price.cents.height.version() + + component.pct0_5.price.cents.height.version() + + component.pct1.price.cents.height.version() + + component.pct2.price.cents.height.version() + + component.pct5.price.cents.height.version() + + component.pct95.price.cents.height.version() + + component.pct98.price.cents.height.version() + + component.pct99.price.cents.height.version() + + component.pct99_5.price.cents.height.version() + + component.pct99_9.price.cents.height.version() }) .sum::() + spot.version(); @@ -213,18 +296,20 @@ impl RarityMeterInner { self.score.height.repeat_until_complete(exit, |vec| { let skip = vec.len(); - let source_end = models + let source_end = components .iter() - .flat_map(|p| { + .flat_map(|component| { [ - p.pct0_5.price.cents.height.len(), - p.pct1.price.cents.height.len(), - p.pct2.price.cents.height.len(), - p.pct5.price.cents.height.len(), - p.pct95.price.cents.height.len(), - p.pct98.price.cents.height.len(), - p.pct99.price.cents.height.len(), - p.pct99_5.price.cents.height.len(), + component.pct0_01.price.cents.height.len(), + component.pct0_5.price.cents.height.len(), + component.pct1.price.cents.height.len(), + component.pct2.price.cents.height.len(), + component.pct5.price.cents.height.len(), + component.pct95.price.cents.height.len(), + component.pct98.price.cents.height.len(), + component.pct99.price.cents.height.len(), + component.pct99_5.price.cents.height.len(), + component.pct99_9.price.cents.height.len(), ] }) .min() @@ -238,18 +323,70 @@ impl RarityMeterInner { let spot_batch = spot.collect_range_at(skip, end); - let bands: Vec<[Vec; 8]> = models + let bands: Vec<[Vec; 10]> = components .iter() - .map(|p| { + .map(|component| { [ - p.pct0_5.price.cents.height.collect_range_at(skip, end), - p.pct1.price.cents.height.collect_range_at(skip, end), - p.pct2.price.cents.height.collect_range_at(skip, end), - p.pct5.price.cents.height.collect_range_at(skip, end), - p.pct95.price.cents.height.collect_range_at(skip, end), - p.pct98.price.cents.height.collect_range_at(skip, end), - p.pct99.price.cents.height.collect_range_at(skip, end), - p.pct99_5.price.cents.height.collect_range_at(skip, end), + component + .pct0_01 + .price + .cents + .height + .collect_range_at(skip, end), + component + .pct0_5 + .price + .cents + .height + .collect_range_at(skip, end), + component + .pct1 + .price + .cents + .height + .collect_range_at(skip, end), + component + .pct2 + .price + .cents + .height + .collect_range_at(skip, end), + component + .pct5 + .price + .cents + .height + .collect_range_at(skip, end), + component + .pct95 + .price + .cents + .height + .collect_range_at(skip, end), + component + .pct98 + .price + .cents + .height + .collect_range_at(skip, end), + component + .pct99 + .price + .cents + .height + .collect_range_at(skip, end), + component + .pct99_5 + .price + .cents + .height + .collect_range_at(skip, end), + component + .pct99_9 + .price + .cents + .height + .collect_range_at(skip, end), ] }) .collect(); @@ -258,31 +395,8 @@ impl RarityMeterInner { let price = spot_batch[j]; let mut total: i8 = 0; - for model in &bands { - if price < model[3][j] { - total -= 1; - } - if price < model[2][j] { - total -= 1; - } - if price < model[1][j] { - total -= 1; - } - if price < model[0][j] { - total -= 1; - } - if price > model[4][j] { - total += 1; - } - if price > model[5][j] { - total += 1; - } - if price > model[6][j] { - total += 1; - } - if price > model[7][j] { - total += 1; - } + for component in &bands { + total += score_at(price, component, j); } vec.push(StoredI8::new(total)); @@ -294,3 +408,50 @@ impl RarityMeterInner { Ok(()) } } + +fn compute_inner_percentile( + out: &mut EagerVec>, + max_from: Height, + lower: &EagerVec>, + upper: &EagerVec>, + percentile: u8, + exit: &Exit, +) -> Result<()> { + debug_assert!((5..=95).contains(&percentile)); + let position = (f64::from(percentile) - 5.0) / 90.0; + + out.validate_and_truncate(lower.version() + upper.version(), max_from)?; + + out.repeat_until_complete(exit, |vec| { + let skip = vec.len(); + let source_end = lower.len().min(upper.len()); + let end = vec.batch_end(source_end); + if skip >= end { + return Ok(()); + } + + let lower_batch = lower.collect_range_at(skip, end); + let upper_batch = upper.collect_range_at(skip, end); + for j in 0..(end - skip) { + let lower = f64::from(lower_batch[j]); + let upper = f64::from(upper_batch[j]); + let value = if lower > 0.0 && upper > 0.0 { + (lower.ln() + position * (upper.ln() - lower.ln())).exp() + } else { + lower + position * (upper - lower) + }; + vec.push(Cents::from(value.round())); + } + + Ok(()) + })?; + + Ok(()) +} + +fn score_at(price: Cents, bands: &[Vec; 10], index: usize) -> i8 { + let lower = bands[..5].iter().filter(|band| price < band[index]).count() as i8; + let upper = bands[5..].iter().filter(|band| price > band[index]).count() as i8; + + upper - lower +} diff --git a/crates/brk_computer/src/indicators/rarity_meter/mod.rs b/crates/brk_computer/src/indicators/rarity_meter/mod.rs index 3d9568c9b..12e653143 100644 --- a/crates/brk_computer/src/indicators/rarity_meter/mod.rs +++ b/crates/brk_computer/src/indicators/rarity_meter/mod.rs @@ -1,4 +1,6 @@ +mod components; mod inner; +mod percentiles; use brk_error::Result; use brk_indexer::Indexer; @@ -6,18 +8,24 @@ use brk_traversable::Traversable; use brk_types::Version; use vecdb::{Database, Exit, Rw, StorageMode}; -use crate::{distribution, indexes, price}; +use crate::{ + distribution, + frameworks::{coinflow, cointime}, + indexes, price, +}; +pub use components::{Component, Components}; pub use inner::RarityMeterInner; #[derive(Traversable)] pub struct RarityMeter { + pub components: Components, pub full: RarityMeterInner, pub local: RarityMeterInner, pub cycle: RarityMeterInner, } -const VERSION: Version = Version::new(4); +const VERSION: Version = Version::new(7); impl RarityMeter { pub(crate) fn forced_import( @@ -27,6 +35,7 @@ impl RarityMeter { ) -> Result { let v = version + VERSION; Ok(Self { + components: Components::forced_import(db, v, indexes)?, full: RarityMeterInner::forced_import(db, "rarity_meter", v, indexes)?, local: RarityMeterInner::forced_import(db, "local_rarity_meter", v, indexes)?, cycle: RarityMeterInner::forced_import(db, "cycle_rarity_meter", v, indexes)?, @@ -37,47 +46,57 @@ impl RarityMeter { &mut self, indexer: &Indexer, distribution: &distribution::Vecs, + cointime: &cointime::Vecs, + coinflow: &coinflow::Vecs, prices: &price::Vecs, exit: &Exit, ) -> Result<()> { - let realized = &distribution.utxo_cohorts.all.metrics.realized; - let sth_realized = &distribution.utxo_cohorts.sth.metrics.realized; - let lth_realized = &distribution.utxo_cohorts.lth.metrics.realized; let spot = &prices.spot.cents.height; - // Full: all + sth + lth (rp + cp), 6 models + self.components + .compute(indexer, distribution, cointime, coinflow, exit)?; + + // Full: all Rainbow components, 10 models self.full.compute( &[ - &realized.price_ratio_percentiles, - &realized.capitalized.price.percentiles, - &sth_realized.price_ratio_percentiles, - &sth_realized.capitalized.price.percentiles, - <h_realized.price_ratio_percentiles, - <h_realized.capitalized.price.percentiles, + &self.components.under_4m_realized_price, + &self.components.under_6m_realized_price, + &self.components.over_4m_realized_price, + &self.components.over_6m_realized_price, + &self.components.sth_realized_price, + &self.components.sth_capitalized_price, + &self.components.lth_realized_price, + &self.components.lth_capitalized_price, + &self.components.realized_price, + &self.components.capitalized_price, ], spot, indexer, exit, )?; - // Local: sth only, 2 models + // Local: young-coin and STH components, 4 models self.local.compute( &[ - &sth_realized.price_ratio_percentiles, - &sth_realized.capitalized.price.percentiles, + &self.components.under_4m_realized_price, + &self.components.under_6m_realized_price, + &self.components.sth_realized_price, + &self.components.sth_capitalized_price, ], spot, indexer, exit, )?; - // Cycle: all + lth, 4 models + // Cycle: old-coin, all, and LTH components, 6 models self.cycle.compute( &[ - &realized.price_ratio_percentiles, - &realized.capitalized.price.percentiles, - <h_realized.price_ratio_percentiles, - <h_realized.capitalized.price.percentiles, + &self.components.over_4m_realized_price, + &self.components.over_6m_realized_price, + &self.components.realized_price, + &self.components.capitalized_price, + &self.components.lth_realized_price, + &self.components.lth_capitalized_price, ], spot, indexer, diff --git a/crates/brk_computer/src/indicators/rarity_meter/percentiles.rs b/crates/brk_computer/src/indicators/rarity_meter/percentiles.rs new file mode 100644 index 000000000..25eac1325 --- /dev/null +++ b/crates/brk_computer/src/indicators/rarity_meter/percentiles.rs @@ -0,0 +1,177 @@ +use brk_types::StoredF32; + +use crate::internal::algo::FenwickTree; + +/// First block included in the Rarity Meter distribution. +pub const START_HEIGHT: usize = 210_000; + +/// Number of blocks after which an observation has half the weight of a new one. +pub const HALF_LIFE_BLOCKS: usize = 210_000; + +/// Block-decayed percentile tracker backed by a Fenwick tree. +/// +/// An observation at `height` receives weight +/// `2 ^ ((height - START_HEIGHT) / HALF_LIFE_BLOCKS)`. Multiplying every +/// observation by the same current-height decay factor does not change +/// quantiles, so this fixed scale is exactly equivalent to halving old weights +/// every 210,000 blocks without rescaling the tree on every block. +#[derive(Clone)] +pub(crate) struct BlockDecayPercentiles { + tree: FenwickTree, + len: usize, + mass: f64, +} + +const BUCKET_WIDTH: f64 = 0.001; +const MAX_RATIO: f64 = 43.0; +const TREE_SIZE: usize = (MAX_RATIO / BUCKET_WIDTH) as usize + 1; + +impl Default for BlockDecayPercentiles { + fn default() -> Self { + Self { + tree: FenwickTree::new(TREE_SIZE), + len: 0, + mass: 0.0, + } + } +} + +impl BlockDecayPercentiles { + pub fn len(&self) -> usize { + self.len + } + + pub fn reset(&mut self) { + self.tree.reset(); + self.len = 0; + self.mass = 0.0; + } + + #[inline] + fn to_bucket(value: f32) -> usize { + (value as f64 / BUCKET_WIDTH) + .round() + .clamp(0.0, (TREE_SIZE - 1) as f64) as usize + } + + #[inline] + fn weight(height: usize) -> f64 { + 2.0_f64.powf(height.saturating_sub(START_HEIGHT) as f64 / HALF_LIFE_BLOCKS as f64) + } + + /// Rebuild historical state in O(n + N). + pub fn add_bulk(&mut self, start_height: usize, values: &[StoredF32]) { + for (offset, &value) in values.iter().enumerate() { + self.len += 1; + let value = *value; + if value.is_nan() { + continue; + } + let weight = Self::weight(start_height + offset); + self.mass += weight; + self.tree.add_raw(Self::to_bucket(value), &weight); + } + self.tree.build_in_place(); + } + + /// Add the observation for one block. O(log N). + #[inline] + pub fn add(&mut self, height: usize, value: f32) { + self.len += 1; + if value.is_nan() { + return; + } + let weight = Self::weight(height); + self.mass += weight; + self.tree.add(Self::to_bucket(value), &weight); + } + + /// Compute sorted quantiles in one shared tree walk. + pub fn quantiles(&self, qs: &[f64; N], out: &mut [f64; N]) { + if self.mass == 0.0 { + out.fill(0.0); + return; + } + + let mut targets = [0.0; N]; + for (i, &q) in qs.iter().enumerate() { + targets[i] = (q * self.mass).next_down().max(0.0); + } + + let mut buckets = [0; N]; + self.tree + .kth(&targets, &|weight: &f64| *weight, &mut buckets); + for (i, bucket) in buckets.iter().enumerate() { + out[i] = *bucket as f64 * BUCKET_WIDTH; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn quantile(percentiles: &BlockDecayPercentiles, q: f64) -> f64 { + let mut out = [0.0; 8]; + percentiles.quantiles(&[q, q, q, q, q, q, q, q], &mut out); + out[0] + } + + #[test] + fn basic_quantiles() { + let mut percentiles = BlockDecayPercentiles::default(); + for i in 1..=1000 { + percentiles.add(START_HEIGHT + i, i as f32 / 1000.0); + } + assert_eq!(percentiles.len(), 1000); + + let median = quantile(&percentiles, 0.5); + assert!((median - 0.5).abs() < 0.01, "median was {median}"); + + let p99 = quantile(&percentiles, 0.99); + assert!((p99 - 0.99).abs() < 0.01, "p99 was {p99}"); + + let p01 = quantile(&percentiles, 0.01); + assert!((p01 - 0.01).abs() < 0.01, "p01 was {p01}"); + } + + #[test] + fn empty() { + let percentiles = BlockDecayPercentiles::default(); + assert_eq!(quantile(&percentiles, 0.5), 0.0); + } + + #[test] + fn one_half_life_doubles_relative_weight() { + let mut percentiles = BlockDecayPercentiles::default(); + percentiles.add(START_HEIGHT, 1.0); + percentiles.add(START_HEIGHT + HALF_LIFE_BLOCKS, 2.0); + + assert!((percentiles.mass - 3.0).abs() < f64::EPSILON); + assert_eq!(quantile(&percentiles, 0.5), 2.0); + } + + #[test] + fn bulk_recovery_matches_incremental_state() { + let values: Vec<_> = (0..1000) + .map(|i| StoredF32::from(i as f64 / 100.0)) + .collect(); + let mut incremental = BlockDecayPercentiles::default(); + for (offset, value) in values.iter().enumerate() { + incremental.add(START_HEIGHT + offset, **value); + } + + let mut recovered = BlockDecayPercentiles::default(); + recovered.add_bulk(START_HEIGHT, &values); + + let qs = [0.0001, 0.01, 0.1, 0.5, 0.9, 0.99, 0.999, 0.9999]; + let mut incremental_out = [0.0; 8]; + let mut recovered_out = [0.0; 8]; + incremental.quantiles(&qs, &mut incremental_out); + recovered.quantiles(&qs, &mut recovered_out); + + assert_eq!(incremental.len, recovered.len); + assert!((incremental.mass - recovered.mass).abs() < 1e-9); + assert_eq!(incremental_out, recovered_out); + } +} diff --git a/crates/brk_computer/src/internal/algo/expanding_percentiles.rs b/crates/brk_computer/src/internal/algo/expanding_percentiles.rs deleted file mode 100644 index d4d7852e2..000000000 --- a/crates/brk_computer/src/internal/algo/expanding_percentiles.rs +++ /dev/null @@ -1,148 +0,0 @@ -use brk_types::StoredF32; - -use super::fenwick::FenwickTree; - -/// Fast expanding percentile tracker using a Fenwick tree (Binary Indexed Tree). -/// -/// Values are discretized to 0.001 ratio resolution and tracked in -/// a fixed-size frequency array with Fenwick prefix sums. This gives: -/// - O(log N) insert (N = tree size, ~16 ops for 43k buckets) -/// - O(log N) percentile query via prefix-sum walk -/// - 0.1% value resolution (10 BPS granularity) -#[derive(Clone)] -pub(crate) struct ExpandingPercentiles { - tree: FenwickTree, - count: u32, -} - -const BUCKET_WIDTH: f64 = 0.001; -const MAX_RATIO: f64 = 43.0; -const TREE_SIZE: usize = (MAX_RATIO / BUCKET_WIDTH) as usize + 1; - -impl Default for ExpandingPercentiles { - fn default() -> Self { - Self { - tree: FenwickTree::new(TREE_SIZE), - count: 0, - } - } -} - -impl ExpandingPercentiles { - pub fn count(&self) -> u32 { - self.count - } - - pub fn reset(&mut self) { - self.tree.reset(); - self.count = 0; - } - - /// Convert f32 ratio to 0-indexed bucket. - #[inline] - fn to_bucket(value: f32) -> usize { - (value as f64 / BUCKET_WIDTH) - .round() - .clamp(0.0, (TREE_SIZE - 1) as f64) as usize - } - - /// Bulk-load values in O(n + N) instead of O(n log N). - /// Builds raw frequency counts, then converts to Fenwick in-place. - pub fn add_bulk(&mut self, values: &[StoredF32]) { - for &v in values { - let v = *v; - if v.is_nan() { - continue; - } - self.count += 1; - self.tree.add_raw(Self::to_bucket(v), &1); - } - self.tree.build_in_place(); - } - - /// Add a value. O(log N). - #[inline] - pub fn add(&mut self, value: f32) { - if value.is_nan() { - return; - } - self.count += 1; - self.tree.add(Self::to_bucket(value), &1); - } - - /// Compute 8 percentiles in one call via kth. O(8 × log N) but with - /// shared tree traversal across all 8 targets for better cache locality. - /// Quantiles q must be sorted ascending in (0, 1). Output values are ratios. - pub fn quantiles(&self, qs: &[f64; 8], out: &mut [f64; 8]) { - if self.count == 0 { - out.fill(0.0); - return; - } - let mut targets = [0u32; 8]; - for (i, &q) in qs.iter().enumerate() { - let k = ((q * self.count as f64).ceil() as u32).clamp(1, self.count); - targets[i] = k - 1; // 0-indexed - } - let mut buckets = [0usize; 8]; - self.tree.kth(&targets, &|n: &u32| *n, &mut buckets); - for (i, bucket) in buckets.iter().enumerate() { - out[i] = *bucket as f64 * BUCKET_WIDTH; - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn quantile(ep: &ExpandingPercentiles, q: f64) -> f64 { - let mut out = [0.0; 8]; - ep.quantiles(&[q, q, q, q, q, q, q, q], &mut out); - out[0] - } - - #[test] - fn basic_quantiles() { - let mut ep = ExpandingPercentiles::default(); - for i in 1..=1000 { - ep.add(i as f32 / 1000.0); - } - assert_eq!(ep.count(), 1000); - - let median = quantile(&ep, 0.5); - assert!((median - 0.5).abs() < 0.01, "median was {median}"); - - let p99 = quantile(&ep, 0.99); - assert!((p99 - 0.99).abs() < 0.01, "p99 was {p99}"); - - let p01 = quantile(&ep, 0.01); - assert!((p01 - 0.01).abs() < 0.01, "p01 was {p01}"); - } - - #[test] - fn empty() { - let ep = ExpandingPercentiles::default(); - assert_eq!(ep.count(), 0); - assert_eq!(quantile(&ep, 0.5), 0.0); - } - - #[test] - fn single_value() { - let mut ep = ExpandingPercentiles::default(); - ep.add(0.42); - let v = quantile(&ep, 0.5); - assert!((v - 0.42).abs() <= BUCKET_WIDTH, "got {v}"); - } - - #[test] - fn reset_works() { - let mut ep = ExpandingPercentiles::default(); - for i in 0..100 { - ep.add(i as f32 / 100.0); - } - assert_eq!(ep.count(), 100); - ep.reset(); - assert_eq!(ep.count(), 0); - assert_eq!(quantile(&ep, 0.5), 0.0); - } -} diff --git a/crates/brk_computer/src/internal/algo/fenwick.rs b/crates/brk_computer/src/internal/algo/fenwick.rs index 22d31a3a5..48e38b7b0 100644 --- a/crates/brk_computer/src/internal/algo/fenwick.rs +++ b/crates/brk_computer/src/internal/algo/fenwick.rs @@ -10,6 +10,13 @@ impl FenwickNode for u32 { } } +impl FenwickNode for f64 { + #[inline(always)] + fn add_assign(&mut self, other: &Self) { + *self += other; + } +} + /// Generic Fenwick tree (Binary Indexed Tree) over arbitrary node types. /// /// Uses 0-indexed buckets externally; 1-indexed internally. diff --git a/crates/brk_computer/src/internal/algo/mod.rs b/crates/brk_computer/src/internal/algo/mod.rs index 038de0808..12b1cdace 100644 --- a/crates/brk_computer/src/internal/algo/mod.rs +++ b/crates/brk_computer/src/internal/algo/mod.rs @@ -1,12 +1,10 @@ mod drawdown; -mod expanding_percentiles; mod fenwick; mod sliding_distribution; mod sliding_median; mod sliding_window; pub(crate) use drawdown::*; -pub(crate) use expanding_percentiles::*; pub(crate) use fenwick::*; pub(crate) use sliding_distribution::*; pub(crate) use sliding_median::*; diff --git a/crates/brk_computer/src/internal/per_block/ratio/mod.rs b/crates/brk_computer/src/internal/per_block/ratio/mod.rs index 3e8af3f50..f3cf939e9 100644 --- a/crates/brk_computer/src/internal/per_block/ratio/mod.rs +++ b/crates/brk_computer/src/internal/per_block/ratio/mod.rs @@ -1,13 +1,7 @@ mod base; -mod percentiles; -mod price_extended; -mod sma; -mod std_dev_bands; +mod price; mod windows; pub use base::*; -pub use percentiles::*; -pub use price_extended::*; -pub use sma::*; -pub use std_dev_bands::*; +pub use price::*; pub use windows::*; diff --git a/crates/brk_computer/src/internal/per_block/ratio/percentiles.rs b/crates/brk_computer/src/internal/per_block/ratio/percentiles.rs deleted file mode 100644 index 0693db251..000000000 --- a/crates/brk_computer/src/internal/per_block/ratio/percentiles.rs +++ /dev/null @@ -1,198 +0,0 @@ -use brk_error::Result; -use brk_indexer::Lengths; -use brk_traversable::Traversable; -use brk_types::{Cents, Height, PartsPerMillion32, StoredF32, Version}; -use vecdb::{ - AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex, - WritableVec, -}; - -use crate::{ - indexes, - internal::{Price, PriceTimesRatio, algo::ExpandingPercentiles}, -}; - -use super::{super::PerBlock, RatioPerBlock}; - -#[derive(Traversable)] -pub struct RatioBand { - #[traversable(flatten)] - pub ratio: RatioPerBlock, - pub price: Price>, -} - -#[derive(Traversable)] -pub struct RatioPerBlockPercentiles { - pub pct99_5: RatioBand, - pub pct99: RatioBand, - pub pct98: RatioBand, - pub pct95: RatioBand, - pub pct5: RatioBand, - pub pct2: RatioBand, - pub pct1: RatioBand, - pub pct0_5: RatioBand, - - #[traversable(skip)] - expanding_pct: ExpandingPercentiles, -} - -const VERSION: Version = Version::new(6); - -/// First height included in ratio percentile computation (first halving). -/// Earlier blocks lack meaningful market data and pollute the distribution. -const MIN_HEIGHT: usize = 210_000; - -impl RatioPerBlockPercentiles { - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let v = version + VERSION; - - macro_rules! import_ratio { - ($suffix:expr) => { - RatioPerBlock::forced_import_ppm(db, &format!("{name}_{}", $suffix), v, indexes)? - }; - } - - macro_rules! import_price { - ($suffix:expr) => { - Price::forced_import(db, &format!("{name}_{}", $suffix), v, indexes)? - }; - } - - macro_rules! import_band { - ($pct:expr) => { - RatioBand { - ratio: import_ratio!(concat!("ratio_", $pct)), - price: import_price!($pct), - } - }; - } - - Ok(Self { - pct99_5: import_band!("pct99_5"), - pct99: import_band!("pct99"), - pct98: import_band!("pct98"), - pct95: import_band!("pct95"), - pct5: import_band!("pct5"), - pct2: import_band!("pct2"), - pct1: import_band!("pct1"), - pct0_5: import_band!("pct0_5"), - expanding_pct: ExpandingPercentiles::default(), - }) - } - - pub(crate) fn compute( - &mut self, - starting_lengths: &Lengths, - exit: &Exit, - ratio_source: &impl ReadableVec, - series_price: &impl ReadableVec, - ) -> Result<()> { - let ratio_version = ratio_source.version(); - self.mut_pct_vecs().try_for_each(|v| -> Result<()> { - v.validate_computed_version_or_reset(ratio_version)?; - Ok(()) - })?; - - let starting_height = self - .mut_pct_vecs() - .map(|v| Height::from(v.len())) - .min() - .unwrap() - .min(starting_lengths.height); - - let start = starting_height.to_usize(); - let ratio_len = ratio_source.len(); - - if ratio_len > start { - let expected_count = start.saturating_sub(MIN_HEIGHT); - if self.expanding_pct.count() as usize != expected_count { - self.expanding_pct.reset(); - if start > MIN_HEIGHT { - let historical = ratio_source.collect_range_at(MIN_HEIGHT, start); - self.expanding_pct.add_bulk(&historical); - } - } - - let new_ratios = ratio_source.collect_range_at(start, ratio_len); - let mut pct_vecs: [&mut EagerVec>; 8] = [ - &mut self.pct0_5.ratio.ppm.height, - &mut self.pct1.ratio.ppm.height, - &mut self.pct2.ratio.ppm.height, - &mut self.pct5.ratio.ppm.height, - &mut self.pct95.ratio.ppm.height, - &mut self.pct98.ratio.ppm.height, - &mut self.pct99.ratio.ppm.height, - &mut self.pct99_5.ratio.ppm.height, - ]; - const PCTS: [f64; 8] = [0.005, 0.01, 0.02, 0.05, 0.95, 0.98, 0.99, 0.995]; - let mut out = [0.0; 8]; - - for vec in pct_vecs.iter_mut() { - vec.truncate_if_needed_at(start)?; - } - - for (i, &ratio) in new_ratios.iter().enumerate() { - if start + i >= MIN_HEIGHT { - self.expanding_pct.add(*ratio); - } - self.expanding_pct.quantiles(&PCTS, &mut out); - for (vec, &val) in pct_vecs.iter_mut().zip(out.iter()) { - vec.push(PartsPerMillion32::from(val)); - } - } - } - - { - let _lock = exit.lock(); - self.mut_pct_vecs() - .try_for_each(|v| v.write().map(|_| ()))?; - } - - // Cents bands - macro_rules! compute_band { - ($band:ident) => { - self.$band - .price - .cents - .compute_binary::>( - starting_lengths.height, - series_price, - &self.$band.ratio.ppm.height, - exit, - )?; - }; - } - - compute_band!(pct99_5); - compute_band!(pct99); - compute_band!(pct98); - compute_band!(pct95); - compute_band!(pct5); - compute_band!(pct2); - compute_band!(pct1); - compute_band!(pct0_5); - - Ok(()) - } - - fn mut_pct_vecs( - &mut self, - ) -> impl Iterator>> { - [ - &mut self.pct0_5.ratio.ppm.height, - &mut self.pct1.ratio.ppm.height, - &mut self.pct2.ratio.ppm.height, - &mut self.pct5.ratio.ppm.height, - &mut self.pct95.ratio.ppm.height, - &mut self.pct98.ratio.ppm.height, - &mut self.pct99.ratio.ppm.height, - &mut self.pct99_5.ratio.ppm.height, - ] - .into_iter() - } -} diff --git a/crates/brk_computer/src/internal/per_block/ratio/price_extended.rs b/crates/brk_computer/src/internal/per_block/ratio/price.rs similarity index 57% rename from crates/brk_computer/src/internal/per_block/ratio/price_extended.rs rename to crates/brk_computer/src/internal/per_block/ratio/price.rs index f1a40c1e5..ce3cfbe39 100644 --- a/crates/brk_computer/src/internal/per_block/ratio/price_extended.rs +++ b/crates/brk_computer/src/internal/per_block/ratio/price.rs @@ -2,13 +2,12 @@ use brk_error::Result; use brk_indexer::Lengths; use brk_traversable::Traversable; use brk_types::{Cents, Dollars, Height, PartsPerMillion64, SatsFract, StoredF32, Version}; -use derive_more::{Deref, DerefMut}; use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode}; use crate::internal::{LazyPerBlock, PerBlock, Price}; use crate::{indexes, price}; -use super::{RatioPerBlock, RatioPerBlockPercentiles}; +use super::RatioPerBlock; #[derive(Traversable)] pub struct PriceWithRatioPerBlock { @@ -78,60 +77,3 @@ impl PriceWithRatioPerBlock { self.compute_ratio(starting_lengths, &prices.spot.cents.height, exit) } } - -#[derive(Deref, DerefMut, Traversable)] -pub struct PriceWithRatioExtendedPerBlock { - #[deref] - #[deref_mut] - #[traversable(flatten)] - pub base: PriceWithRatioPerBlock, - pub percentiles: RatioPerBlockPercentiles, -} - -impl PriceWithRatioExtendedPerBlock { - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - Ok(Self { - base: PriceWithRatioPerBlock::forced_import(db, name, version, indexes)?, - percentiles: RatioPerBlockPercentiles::forced_import(db, name, version, indexes)?, - }) - } - - /// Compute ratio and percentiles from already-computed price cents. - pub(crate) fn compute_rest( - &mut self, - prices: &price::Vecs, - starting_lengths: &Lengths, - exit: &Exit, - ) -> Result<()> { - let close_price = &prices.spot.cents.height; - self.base - .compute_ratio(starting_lengths, close_price, exit)?; - self.percentiles.compute( - starting_lengths, - exit, - &self.base.ratio.height, - &self.base.cents.height, - )?; - Ok(()) - } - - /// Compute price via closure (in cents), then compute ratio and percentiles. - pub(crate) fn compute_all( - &mut self, - prices: &price::Vecs, - starting_lengths: &Lengths, - exit: &Exit, - mut compute_price: F, - ) -> Result<()> - where - F: FnMut(&mut EagerVec>) -> Result<()>, - { - compute_price(&mut self.base.cents.height)?; - self.compute_rest(prices, starting_lengths, exit) - } -} diff --git a/crates/brk_computer/src/internal/per_block/ratio/sma.rs b/crates/brk_computer/src/internal/per_block/ratio/sma.rs deleted file mode 100644 index 367170132..000000000 --- a/crates/brk_computer/src/internal/per_block/ratio/sma.rs +++ /dev/null @@ -1,87 +0,0 @@ -use brk_error::Result; -use brk_indexer::Lengths; -use brk_traversable::Traversable; -use brk_types::{Height, PartsPerMillion32, StoredF32, Version}; -use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode}; - -use crate::{blocks, indexes}; - -use super::RatioPerBlock; - -#[derive(Traversable)] -pub struct RatioSma { - pub all: RatioPerBlock, - pub _1w: RatioPerBlock, - pub _1m: RatioPerBlock, - pub _1y: RatioPerBlock, - pub _2y: RatioPerBlock, - pub _4y: RatioPerBlock, -} - -const VERSION: Version = Version::new(4); - -impl RatioSma { - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let v = version + VERSION; - - macro_rules! import { - ($suffix:expr) => { - RatioPerBlock::forced_import_ppm( - db, - &format!("{name}_ratio_sma_{}", $suffix), - v, - indexes, - )? - }; - } - - Ok(Self { - all: import!("all"), - _1w: import!("1w"), - _1m: import!("1m"), - _1y: import!("1y"), - _2y: import!("2y"), - _4y: import!("4y"), - }) - } - - pub(crate) fn compute( - &mut self, - blocks: &blocks::Vecs, - starting_lengths: &Lengths, - exit: &Exit, - ratio_source: &impl ReadableVec, - ) -> Result<()> { - // Expanding SMA (all history) - self.all.ppm.height.compute_sma_( - starting_lengths.height, - ratio_source, - usize::MAX, - exit, - None, - )?; - - // Rolling SMAs - for (sma, lookback) in [ - (&mut self._1w, &blocks.lookback._1w.inner), - (&mut self._1m, &blocks.lookback._1m.inner), - (&mut self._1y, &blocks.lookback._1y.inner), - (&mut self._2y, &blocks.lookback._2y), - (&mut self._4y, &blocks.lookback._4y), - ] { - sma.ppm.height.compute_rolling_average( - starting_lengths.height, - lookback, - ratio_source, - exit, - )?; - } - - Ok(()) - } -} diff --git a/crates/brk_computer/src/internal/per_block/ratio/std_dev_bands.rs b/crates/brk_computer/src/internal/per_block/ratio/std_dev_bands.rs deleted file mode 100644 index 265ce6ffa..000000000 --- a/crates/brk_computer/src/internal/per_block/ratio/std_dev_bands.rs +++ /dev/null @@ -1,65 +0,0 @@ -use brk_error::Result; -use brk_indexer::Lengths; -use brk_traversable::Traversable; -use brk_types::{Cents, Height, StoredF32, Version}; -use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode}; - -use crate::{blocks, indexes, internal::StdDevPerBlockExtended}; - -use super::RatioSma; - -#[derive(Traversable)] -pub struct RatioPerBlockStdDevBands { - pub all: StdDevPerBlockExtended, - pub _4y: StdDevPerBlockExtended, - pub _2y: StdDevPerBlockExtended, - pub _1y: StdDevPerBlockExtended, -} - -const VERSION: Version = Version::new(4); - -impl RatioPerBlockStdDevBands { - pub(crate) fn forced_import( - db: &Database, - name: &str, - version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let v = version + VERSION; - - macro_rules! import_sd { - ($period:expr, $days:expr) => { - StdDevPerBlockExtended::forced_import(db, name, $period, $days, v, indexes)? - }; - } - - Ok(Self { - all: import_sd!("", usize::MAX), - _1y: import_sd!("1y", 365), - _2y: import_sd!("2y", 2 * 365), - _4y: import_sd!("4y", 4 * 365), - }) - } - - pub(crate) fn compute( - &mut self, - blocks: &blocks::Vecs, - starting_lengths: &Lengths, - exit: &Exit, - ratio_source: &impl ReadableVec, - series_price: &impl ReadableVec, - sma: &RatioSma, - ) -> Result<()> { - for (sd, sma_ratio) in [ - (&mut self.all, &sma.all.ratio.height), - (&mut self._4y, &sma._4y.ratio.height), - (&mut self._2y, &sma._2y.ratio.height), - (&mut self._1y, &sma._1y.ratio.height), - ] { - sd.compute_all(blocks, starting_lengths, exit, ratio_source, sma_ratio)?; - sd.compute_cents_bands(starting_lengths, series_price, sma_ratio, exit)?; - } - - Ok(()) - } -} diff --git a/crates/brk_computer/src/internal/per_block/stddev/extended.rs b/crates/brk_computer/src/internal/per_block/stddev/extended.rs deleted file mode 100644 index 0b3f327da..000000000 --- a/crates/brk_computer/src/internal/per_block/stddev/extended.rs +++ /dev/null @@ -1,237 +0,0 @@ -use brk_error::Result; -use brk_indexer::Lengths; -use brk_traversable::Traversable; -use brk_types::{Cents, Height, StoredF32, Version}; -use vecdb::{ - AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex, - WritableVec, -}; - -use crate::{ - blocks, indexes, - internal::{PerBlock, Price, PriceTimesRatioCents, per_block::stddev::period_suffix}, -}; - -#[derive(Traversable)] -pub struct StdDevBand { - #[traversable(flatten)] - pub ratio: PerBlock, - pub price: Price>, -} - -#[derive(Traversable)] -pub struct StdDevPerBlockExtended { - days: usize, - pub sd: PerBlock, - pub zscore: PerBlock, - - pub _0sd: Price>, - pub p0_5sd: StdDevBand, - pub p1sd: StdDevBand, - pub p1_5sd: StdDevBand, - pub p2sd: StdDevBand, - pub p2_5sd: StdDevBand, - pub p3sd: StdDevBand, - pub m0_5sd: StdDevBand, - pub m1sd: StdDevBand, - pub m1_5sd: StdDevBand, - pub m2sd: StdDevBand, - pub m2_5sd: StdDevBand, - pub m3sd: StdDevBand, -} - -impl StdDevPerBlockExtended { - pub(crate) fn forced_import( - db: &Database, - name: &str, - period: &str, - days: usize, - parent_version: Version, - indexes: &indexes::Vecs, - ) -> Result { - let version = parent_version + Version::TWO; - let p = period_suffix(period); - - macro_rules! import { - ($suffix:expr) => { - PerBlock::forced_import(db, &format!("{name}_{}{p}", $suffix), version, indexes)? - }; - } - - macro_rules! import_price { - ($suffix:expr) => { - Price::forced_import(db, &format!("{name}_{}{p}", $suffix), version, indexes)? - }; - } - - macro_rules! import_band { - ($suffix:expr) => {{ - StdDevBand { - ratio: import!(concat!("ratio_", $suffix)), - price: import_price!($suffix), - } - }}; - } - - Ok(Self { - days, - sd: import!("ratio_sd"), - zscore: import!("ratio_zscore"), - _0sd: import_price!("0sd"), - p0_5sd: import_band!("p0_5sd"), - p1sd: import_band!("p1sd"), - p1_5sd: import_band!("p1_5sd"), - p2sd: import_band!("p2sd"), - p2_5sd: import_band!("p2_5sd"), - p3sd: import_band!("p3sd"), - m0_5sd: import_band!("m0_5sd"), - m1sd: import_band!("m1sd"), - m1_5sd: import_band!("m1_5sd"), - m2sd: import_band!("m2sd"), - m2_5sd: import_band!("m2_5sd"), - m3sd: import_band!("m3sd"), - }) - } - - pub(crate) fn compute_all( - &mut self, - blocks: &blocks::Vecs, - starting_lengths: &Lengths, - exit: &Exit, - source: &impl ReadableVec, - sma: &impl ReadableVec, - ) -> Result<()> { - if self.days == usize::MAX { - self.sd - .height - .compute_expanding_sd(starting_lengths.height, source, sma, exit)?; - } else { - let window_starts = blocks.lookback.start_vec(self.days); - self.sd.height.compute_rolling_sd( - starting_lengths.height, - window_starts, - source, - sma, - exit, - )?; - } - - self.compute_bands(starting_lengths, exit, sma, source) - } - - fn compute_bands( - &mut self, - starting_lengths: &Lengths, - exit: &Exit, - sma: &impl ReadableVec, - source: &impl ReadableVec, - ) -> Result<()> { - let source_version = source.version(); - - self.mut_band_height_vecs() - .try_for_each(|v| -> Result<()> { - v.validate_computed_version_or_reset(source_version)?; - Ok(()) - })?; - - let starting_height = self - .mut_band_height_vecs() - .map(|v| Height::from(v.len())) - .min() - .unwrap() - .min(starting_lengths.height); - - let start = starting_height.to_usize(); - - let source_len = source.len(); - let source_data = source.collect_range_at(start, source_len); - - let sma_data = sma.collect_range_at(start, sma.len()); - let sd_data = self.sd.height.collect_range_at(start, self.sd.height.len()); - - const MULTIPLIERS: [f32; 12] = [ - 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0, - ]; - for (vec, mult) in self.mut_band_height_vecs().zip(MULTIPLIERS) { - vec.truncate_if_needed_at(start)?; - for (offset, _) in source_data.iter().enumerate() { - let average = sma_data[offset]; - let sd = sd_data[offset]; - vec.push(average + StoredF32::from(mult * *sd)); - } - } - - { - let _lock = exit.lock(); - self.mut_band_height_vecs() - .try_for_each(|v| v.write().map(|_| ()))?; - } - - self.zscore.height.compute_zscore( - starting_lengths.height, - source, - sma, - &self.sd.height, - exit, - )?; - - Ok(()) - } - - pub(crate) fn compute_cents_bands( - &mut self, - starting_lengths: &Lengths, - series_price: &impl ReadableVec, - sma: &impl ReadableVec, - exit: &Exit, - ) -> Result<()> { - macro_rules! compute_band_price { - ($price:expr, $band_source:expr) => { - $price - .cents - .compute_binary::( - starting_lengths.height, - series_price, - $band_source, - exit, - )?; - }; - } - - compute_band_price!(&mut self._0sd, sma); - compute_band_price!(&mut self.p0_5sd.price, &self.p0_5sd.ratio.height); - compute_band_price!(&mut self.p1sd.price, &self.p1sd.ratio.height); - compute_band_price!(&mut self.p1_5sd.price, &self.p1_5sd.ratio.height); - compute_band_price!(&mut self.p2sd.price, &self.p2sd.ratio.height); - compute_band_price!(&mut self.p2_5sd.price, &self.p2_5sd.ratio.height); - compute_band_price!(&mut self.p3sd.price, &self.p3sd.ratio.height); - compute_band_price!(&mut self.m0_5sd.price, &self.m0_5sd.ratio.height); - compute_band_price!(&mut self.m1sd.price, &self.m1sd.ratio.height); - compute_band_price!(&mut self.m1_5sd.price, &self.m1_5sd.ratio.height); - compute_band_price!(&mut self.m2sd.price, &self.m2sd.ratio.height); - compute_band_price!(&mut self.m2_5sd.price, &self.m2_5sd.ratio.height); - compute_band_price!(&mut self.m3sd.price, &self.m3sd.ratio.height); - - Ok(()) - } - - fn mut_band_height_vecs( - &mut self, - ) -> impl Iterator>> { - [ - &mut self.p0_5sd.ratio.height, - &mut self.p1sd.ratio.height, - &mut self.p1_5sd.ratio.height, - &mut self.p2sd.ratio.height, - &mut self.p2_5sd.ratio.height, - &mut self.p3sd.ratio.height, - &mut self.m0_5sd.ratio.height, - &mut self.m1sd.ratio.height, - &mut self.m1_5sd.ratio.height, - &mut self.m2sd.ratio.height, - &mut self.m2_5sd.ratio.height, - &mut self.m3sd.ratio.height, - ] - .into_iter() - } -} diff --git a/crates/brk_computer/src/internal/per_block/stddev/mod.rs b/crates/brk_computer/src/internal/per_block/stddev/mod.rs index cc909c00e..4316484eb 100644 --- a/crates/brk_computer/src/internal/per_block/stddev/mod.rs +++ b/crates/brk_computer/src/internal/per_block/stddev/mod.rs @@ -1,8 +1,6 @@ mod base; -mod extended; pub use base::*; -pub use extended::*; fn period_suffix(period: &str) -> String { if period.is_empty() { diff --git a/crates/brk_computer/src/internal/transform/derived.rs b/crates/brk_computer/src/internal/transform/derived.rs index 60eec5854..4c0c90955 100644 --- a/crates/brk_computer/src/internal/transform/derived.rs +++ b/crates/brk_computer/src/internal/transform/derived.rs @@ -47,15 +47,6 @@ impl UnaryTransform for TimesSqrt { } } -pub struct PriceTimesRatioCents; - -impl BinaryTransform for PriceTimesRatioCents { - #[inline(always)] - fn apply(price: Cents, ratio: StoredF32) -> Cents { - Cents::from(f64::from(price) * f64::from(ratio)) - } -} - pub struct PriceTimesRatio(PhantomData); impl BinaryTransform for PriceTimesRatio { diff --git a/crates/brk_computer/src/internal/transform/mod.rs b/crates/brk_computer/src/internal/transform/mod.rs index 07512ba5b..c2199fed0 100644 --- a/crates/brk_computer/src/internal/transform/mod.rs +++ b/crates/brk_computer/src/internal/transform/mod.rs @@ -16,8 +16,7 @@ pub use currency::{ NegCentsUnsignedToDollars, SatsSignedToBitcoin, SatsToBitcoin, SatsToCents, }; pub use derived::{ - Days1, Days7, Days30, Days365, DaysToYears, PriceTimesRatio, PriceTimesRatioCents, - RatioCents64, TimesSqrt, + Days1, Days7, Days30, Days365, DaysToYears, PriceTimesRatio, RatioCents64, TimesSqrt, }; pub use fixed_ratio::{FixedToPercent, FixedToRatio}; pub use ratio::{ diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index 4ca0014d1..c195faf2a 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -436,7 +436,6 @@ impl Computer { &self.inputs, &self.outputs, &self.transactions, - &self.blocks, &self.price, exit, ) @@ -517,7 +516,14 @@ impl Computer { self.indicators .rarity_meter - .compute(indexer, &self.distribution, &self.price, exit)?; + .compute( + indexer, + &self.distribution, + &self.cointime, + &self.coinflow, + &self.price, + exit, + )?; info!("Total compute time: {:?}", compute_start.elapsed()); Ok(()) diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index e5ad02620..ef2a4559a 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -2480,6 +2480,63 @@ function createSeriesPattern35(client, name) { return /** @type {SeriesPattern35 // Reusable structural pattern factories +/** + * @typedef {Object} IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern + * @property {SeriesPattern1} index + * @property {CentsSatsUsdPattern} pct001 + * @property {CentsSatsUsdPattern} pct05 + * @property {CentsSatsUsdPattern} pct1 + * @property {CentsSatsUsdPattern} pct10 + * @property {CentsSatsUsdPattern} pct2 + * @property {CentsSatsUsdPattern} pct20 + * @property {CentsSatsUsdPattern} pct30 + * @property {CentsSatsUsdPattern} pct40 + * @property {CentsSatsUsdPattern} pct5 + * @property {CentsSatsUsdPattern} pct50 + * @property {CentsSatsUsdPattern} pct60 + * @property {CentsSatsUsdPattern} pct70 + * @property {CentsSatsUsdPattern} pct80 + * @property {CentsSatsUsdPattern} pct90 + * @property {CentsSatsUsdPattern} pct95 + * @property {CentsSatsUsdPattern} pct98 + * @property {CentsSatsUsdPattern} pct99 + * @property {CentsSatsUsdPattern} pct995 + * @property {CentsSatsUsdPattern} pct999 + * @property {SeriesPattern1} score + */ + +/** + * Create a IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern pattern node + * @param {BrkClient} client + * @param {string} acc - Accumulated series name + * @returns {IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern} + */ +function createIndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern(client, acc) { + return { + index: createSeriesPattern1(client, _m(acc, 'index')), + pct001: createCentsSatsUsdPattern(client, _m(acc, 'pct0_01')), + pct05: createCentsSatsUsdPattern(client, _m(acc, 'pct0_5')), + pct1: createCentsSatsUsdPattern(client, _m(acc, 'pct01')), + pct10: createCentsSatsUsdPattern(client, _m(acc, 'pct10')), + pct2: createCentsSatsUsdPattern(client, _m(acc, 'pct02')), + pct20: createCentsSatsUsdPattern(client, _m(acc, 'pct20')), + pct30: createCentsSatsUsdPattern(client, _m(acc, 'pct30')), + pct40: createCentsSatsUsdPattern(client, _m(acc, 'pct40')), + pct5: createCentsSatsUsdPattern(client, _m(acc, 'pct05')), + pct50: createCentsSatsUsdPattern(client, _m(acc, 'pct50')), + pct60: createCentsSatsUsdPattern(client, _m(acc, 'pct60')), + pct70: createCentsSatsUsdPattern(client, _m(acc, 'pct70')), + pct80: createCentsSatsUsdPattern(client, _m(acc, 'pct80')), + pct90: createCentsSatsUsdPattern(client, _m(acc, 'pct90')), + pct95: createCentsSatsUsdPattern(client, _m(acc, 'pct95')), + pct98: createCentsSatsUsdPattern(client, _m(acc, 'pct98')), + pct99: createCentsSatsUsdPattern(client, _m(acc, 'pct99')), + pct995: createCentsSatsUsdPattern(client, _m(acc, 'pct99_5')), + pct999: createCentsSatsUsdPattern(client, _m(acc, 'pct99_9')), + score: createSeriesPattern1(client, _m(acc, 'score')), + }; +} + /** * @typedef {Object} Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern * @property {CentsSatsUsdPattern} pct05 @@ -2534,24 +2591,58 @@ function createPct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65 } /** - * @typedef {Object} _0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m3sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p3sd - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore + * @typedef {Object} Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern + * @property {PpmPriceRatioPattern} pct001 + * @property {PpmPriceRatioPattern} pct05 + * @property {PpmPriceRatioPattern} pct1 + * @property {PpmPriceRatioPattern} pct10 + * @property {PpmPriceRatioPattern} pct2 + * @property {PpmPriceRatioPattern} pct20 + * @property {PpmPriceRatioPattern} pct30 + * @property {PpmPriceRatioPattern} pct40 + * @property {PpmPriceRatioPattern} pct5 + * @property {PpmPriceRatioPattern} pct50 + * @property {PpmPriceRatioPattern} pct60 + * @property {PpmPriceRatioPattern} pct70 + * @property {PpmPriceRatioPattern} pct80 + * @property {PpmPriceRatioPattern} pct90 + * @property {PpmPriceRatioPattern} pct95 + * @property {PpmPriceRatioPattern} pct98 + * @property {PpmPriceRatioPattern} pct99 + * @property {PpmPriceRatioPattern} pct995 + * @property {PpmPriceRatioPattern} pct999 */ +/** + * Create a Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern pattern node + * @param {BrkClient} client + * @param {string} acc - Accumulated series name + * @returns {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} + */ +function createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, acc) { + return { + pct001: createPpmPriceRatioPattern(client, acc, 'pct0_01'), + pct05: createPpmPriceRatioPattern(client, acc, 'pct0_5'), + pct1: createPpmPriceRatioPattern(client, acc, 'pct1'), + pct10: createPpmPriceRatioPattern(client, acc, 'pct10'), + pct2: createPpmPriceRatioPattern(client, acc, 'pct2'), + pct20: createPpmPriceRatioPattern(client, acc, 'pct20'), + pct30: createPpmPriceRatioPattern(client, acc, 'pct30'), + pct40: createPpmPriceRatioPattern(client, acc, 'pct40'), + pct5: createPpmPriceRatioPattern(client, acc, 'pct5'), + pct50: createPpmPriceRatioPattern(client, acc, 'pct50'), + pct60: createPpmPriceRatioPattern(client, acc, 'pct60'), + pct70: createPpmPriceRatioPattern(client, acc, 'pct70'), + pct80: createPpmPriceRatioPattern(client, acc, 'pct80'), + pct90: createPpmPriceRatioPattern(client, acc, 'pct90'), + pct95: createPpmPriceRatioPattern(client, acc, 'pct95'), + pct98: createPpmPriceRatioPattern(client, acc, 'pct98'), + pct99: createPpmPriceRatioPattern(client, acc, 'pct99'), + pct995: createPpmPriceRatioPattern(client, acc, 'pct99_5'), + pct999: createPpmPriceRatioPattern(client, acc, 'pct99_9'), + }; +} + /** * @typedef {Object} AllEmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern * @property {AverageBlockCumulativeSumPattern} all @@ -2696,13 +2787,36 @@ function create_10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, acc) { * @property {SeriesPattern1} mvrv * @property {BlockChangeCumulativeDeltaSumPattern} netPnl * @property {BlockCumulativeSumPattern} peakRegret - * @property {CentsPercentilesPpmRatioSatsSmaStdUsdPattern} price + * @property {CentsPpmRatioSatsUsdPattern} price * @property {BlockCumulativeSumPattern} profit * @property {_1m1w1y24hPattern} profitToLossRatio * @property {_1m1w1y24hPattern8} sellSideRiskRatio * @property {AdjustedRatioValuePattern} sopr */ +/** + * Create a CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern pattern node + * @param {BrkClient} client + * @param {string} acc - Accumulated series name + * @returns {CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern} + */ +function createCapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern(client, acc) { + return { + cap: createCentsDeltaToUsdPattern(client, _m(acc, 'realized_cap')), + capitalized: createPricePattern(client, _m(acc, 'capitalized_price')), + grossPnl: createBlockCumulativeSumPattern(client, _m(acc, 'realized_gross_pnl')), + loss: createBlockCumulativeNegativeSumPattern(client, _m(acc, 'realized_loss')), + mvrv: createSeriesPattern1(client, _m(acc, 'mvrv')), + netPnl: createBlockChangeCumulativeDeltaSumPattern(client, _m(acc, 'net')), + peakRegret: createBlockCumulativeSumPattern(client, _m(acc, 'realized_peak_regret')), + price: createCentsPpmRatioSatsUsdPattern(client, _m(acc, 'realized_price')), + profit: createBlockCumulativeSumPattern(client, _m(acc, 'realized_profit')), + profitToLossRatio: create_1m1w1y24hPattern(client, _m(acc, 'realized_profit_to_loss_ratio')), + sellSideRiskRatio: create_1m1w1y24hPattern8(client, _m(acc, 'sell_side_risk_ratio')), + sopr: createAdjustedRatioValuePattern(client, acc), + }; +} + /** * @typedef {Object} CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2 * @property {CentsDeltaToUsdPattern} cap @@ -2712,13 +2826,36 @@ function create_10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, acc) { * @property {SeriesPattern1} mvrv * @property {BlockChangeCumulativeDeltaSumPattern} netPnl * @property {BlockCumulativeSumPattern} peakRegret - * @property {CentsPercentilesPpmRatioSatsSmaStdUsdPattern} price + * @property {CentsPpmRatioSatsUsdPattern} price * @property {BlockCumulativeSumPattern} profit * @property {_1m1w1y24hPattern} profitToLossRatio * @property {_1m1w1y24hPattern8} sellSideRiskRatio * @property {RatioValuePattern2} sopr */ +/** + * Create a CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2 pattern node + * @param {BrkClient} client + * @param {string} acc - Accumulated series name + * @returns {CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2} + */ +function createCapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2(client, acc) { + return { + cap: createCentsDeltaToUsdPattern(client, _m(acc, 'realized_cap')), + capitalized: createPricePattern(client, _m(acc, 'capitalized_price')), + grossPnl: createBlockCumulativeSumPattern(client, _m(acc, 'realized_gross_pnl')), + loss: createBlockCumulativeNegativeSumPattern(client, _m(acc, 'realized_loss')), + mvrv: createSeriesPattern1(client, _m(acc, 'mvrv')), + netPnl: createBlockChangeCumulativeDeltaSumPattern(client, _m(acc, 'net')), + peakRegret: createBlockCumulativeSumPattern(client, _m(acc, 'realized_peak_regret')), + price: createCentsPpmRatioSatsUsdPattern(client, _m(acc, 'realized_price')), + profit: createBlockCumulativeSumPattern(client, _m(acc, 'realized_profit')), + profitToLossRatio: create_1m1w1y24hPattern(client, _m(acc, 'realized_profit_to_loss_ratio')), + sellSideRiskRatio: create_1m1w1y24hPattern8(client, _m(acc, 'sell_side_risk_ratio')), + sopr: createRatioValuePattern2(client, acc), + }; +} + /** * @typedef {Object} EmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2 * @property {_1m1w1y24hPercentPpmRatioPattern} empty @@ -2885,41 +3022,6 @@ function createAverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern(c * @property {AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5} share */ -/** - * @typedef {Object} IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern - * @property {SeriesPattern1} index - * @property {CentsSatsUsdPattern} pct05 - * @property {CentsSatsUsdPattern} pct1 - * @property {CentsSatsUsdPattern} pct2 - * @property {CentsSatsUsdPattern} pct5 - * @property {CentsSatsUsdPattern} pct95 - * @property {CentsSatsUsdPattern} pct98 - * @property {CentsSatsUsdPattern} pct99 - * @property {CentsSatsUsdPattern} pct995 - * @property {SeriesPattern1} score - */ - -/** - * Create a IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern pattern node - * @param {BrkClient} client - * @param {string} acc - Accumulated series name - * @returns {IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern} - */ -function createIndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern(client, acc) { - return { - index: createSeriesPattern1(client, _m(acc, 'index')), - pct05: createCentsSatsUsdPattern(client, _m(acc, 'pct0_5')), - pct1: createCentsSatsUsdPattern(client, _m(acc, 'pct01')), - pct2: createCentsSatsUsdPattern(client, _m(acc, 'pct02')), - pct5: createCentsSatsUsdPattern(client, _m(acc, 'pct05')), - pct95: createCentsSatsUsdPattern(client, _m(acc, 'pct95')), - pct98: createCentsSatsUsdPattern(client, _m(acc, 'pct98')), - pct99: createCentsSatsUsdPattern(client, _m(acc, 'pct99')), - pct995: createCentsSatsUsdPattern(client, _m(acc, 'pct99_5')), - score: createSeriesPattern1(client, _m(acc, 'score')), - }; -} - /** * @typedef {Object} AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6 * @property {AverageBlockCumulativeSumPattern} all @@ -3151,49 +3253,6 @@ function createPct10Pct20Pct30Pct40Pct50Pct60Pct70Pct80Pct90Pattern(client, acc) }; } -/** - * @typedef {Object} CentsPercentilesPpmRatioSatsSmaStdUsdPattern - * @property {SeriesPattern1} cents - * @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles - * @property {SeriesPattern1} ppm - * @property {SeriesPattern1} ratio - * @property {SeriesPattern1} sats - * @property {_1m1w1y2y4yAllPattern} sma - * @property {_1y2y4yAllPattern} stdDev - * @property {SeriesPattern1} usd - */ - -/** - * @typedef {Object} Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern - * @property {PpmPriceRatioPattern} pct05 - * @property {PpmPriceRatioPattern} pct1 - * @property {PpmPriceRatioPattern} pct2 - * @property {PpmPriceRatioPattern} pct5 - * @property {PpmPriceRatioPattern} pct95 - * @property {PpmPriceRatioPattern} pct98 - * @property {PpmPriceRatioPattern} pct99 - * @property {PpmPriceRatioPattern} pct995 - */ - -/** - * Create a Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern pattern node - * @param {BrkClient} client - * @param {string} acc - Accumulated series name - * @returns {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} - */ -function createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc) { - return { - pct05: createPpmPriceRatioPattern(client, acc, 'pct0_5'), - pct1: createPpmPriceRatioPattern(client, acc, 'pct1'), - pct2: createPpmPriceRatioPattern(client, acc, 'pct2'), - pct5: createPpmPriceRatioPattern(client, acc, 'pct5'), - pct95: createPpmPriceRatioPattern(client, acc, 'pct95'), - pct98: createPpmPriceRatioPattern(client, acc, 'pct98'), - pct99: createPpmPriceRatioPattern(client, acc, 'pct99'), - pct995: createPpmPriceRatioPattern(client, acc, 'pct99_5'), - }; -} - /** * @typedef {Object} _10y2y3y4y5y6y8yPattern * @property {PercentPpmRatioPattern} _10y @@ -3274,6 +3333,24 @@ function create_1m1w1y24hPercentPpmRatioPattern(client, acc) { * @property {CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2} unrealized */ +/** + * Create a ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2 pattern node + * @param {BrkClient} client + * @param {string} acc - Accumulated series name + * @returns {ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2} + */ +function createActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2(client, acc) { + return { + activity: createCoindaysCoinyearsDormancyTransferPattern(client, acc), + costBasis: createInMaxMinPerSupplyPattern(client, acc), + investedCapital: createInPattern(client, _m(acc, 'invested_capital_in')), + outputs: createSpentUnspentUtxoPattern(client, acc), + realized: createCapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2(client, acc), + supply: createDeltaDominanceHalfInTotalPattern2(client, _m(acc, 'supply')), + unrealized: createCapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(client, acc), + }; +} + /** * @typedef {Object} CapLossMvrvNetPriceProfitSoprPattern * @property {CentsDeltaUsdPattern} cap @@ -3421,33 +3498,6 @@ function createMaxMedianMinPct10Pct25Pct75Pct90Pattern(client, acc) { }; } -/** - * @typedef {Object} _1m1w1y2y4yAllPattern - * @property {PpmRatioPattern2} _1m - * @property {PpmRatioPattern2} _1w - * @property {PpmRatioPattern2} _1y - * @property {PpmRatioPattern2} _2y - * @property {PpmRatioPattern2} _4y - * @property {PpmRatioPattern2} all - */ - -/** - * Create a _1m1w1y2y4yAllPattern pattern node - * @param {BrkClient} client - * @param {string} acc - Accumulated series name - * @returns {_1m1w1y2y4yAllPattern} - */ -function create_1m1w1y2y4yAllPattern(client, acc) { - return { - _1m: createPpmRatioPattern2(client, _m(acc, '1m')), - _1w: createPpmRatioPattern2(client, _m(acc, '1w')), - _1y: createPpmRatioPattern2(client, _m(acc, '1y')), - _2y: createPpmRatioPattern2(client, _m(acc, '2y')), - _4y: createPpmRatioPattern2(client, _m(acc, '4y')), - all: createPpmRatioPattern2(client, _m(acc, 'all')), - }; -} - /** * @typedef {Object} ActivityAddrOutputsRealizedSupplyUnrealizedPattern * @property {TransferPattern} activity @@ -3529,33 +3579,6 @@ function createCentsNegativeToUsdPattern2(client, acc) { }; } -/** - * @typedef {Object} CentsPercentilesPpmRatioSatsUsdPattern - * @property {SeriesPattern1} cents - * @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles - * @property {SeriesPattern1} ppm - * @property {SeriesPattern1} ratio - * @property {SeriesPattern1} sats - * @property {SeriesPattern1} usd - */ - -/** - * Create a CentsPercentilesPpmRatioSatsUsdPattern pattern node - * @param {BrkClient} client - * @param {string} acc - Accumulated series name - * @returns {CentsPercentilesPpmRatioSatsUsdPattern} - */ -function createCentsPercentilesPpmRatioSatsUsdPattern(client, acc) { - return { - cents: createSeriesPattern1(client, _m(acc, 'cents')), - percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc), - ppm: createSeriesPattern1(client, _m(acc, 'ratio_ppm')), - ratio: createSeriesPattern1(client, _m(acc, 'ratio')), - sats: createSeriesPattern1(client, _m(acc, 'sats')), - usd: createSeriesPattern1(client, acc), - }; -} - /** * @typedef {Object} ChainDataOutputTxPattern * @property {PercentPpmRatioPattern2} chainShare @@ -4191,14 +4214,6 @@ function create_1m1w1y24hPattern8(client, acc) { }; } -/** - * @typedef {Object} _1y2y4yAllPattern - * @property {_0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern} _1y - * @property {_0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern} _2y - * @property {_0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern} _4y - * @property {_0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern} all - */ - /** * @typedef {Object} AverageBlockCumulativeSumPattern2 * @property {_1m1w1y24hPattern} average @@ -5455,26 +5470,6 @@ function createPpmRatioPattern(client, acc) { }; } -/** - * @typedef {Object} PriceRatioPattern - * @property {CentsSatsUsdPattern} price - * @property {SeriesPattern1} ratio - */ - -/** - * Create a PriceRatioPattern pattern node - * @param {BrkClient} client - * @param {string} acc - Accumulated series name - * @param {string} disc - Discriminator suffix - * @returns {PriceRatioPattern} - */ -function createPriceRatioPattern(client, acc, disc) { - return { - price: createCentsSatsUsdPattern(client, _m(acc, disc)), - ratio: createSeriesPattern1(client, _m(_m(acc, 'ratio'), disc)), - }; -} - /** * @typedef {Object} RatioValuePattern2 * @property {_1m1w1y24hPattern} ratio @@ -5591,7 +5586,7 @@ function createNuplPattern(client, acc) { /** * @typedef {Object} PricePattern - * @property {CentsPercentilesPpmRatioSatsUsdPattern} price + * @property {CentsPpmRatioSatsUsdPattern} price */ /** @@ -5602,7 +5597,7 @@ function createNuplPattern(client, acc) { */ function createPricePattern(client, acc) { return { - price: createCentsPercentilesPpmRatioSatsUsdPattern(client, acc), + price: createCentsPpmRatioSatsUsdPattern(client, acc), }; } @@ -6622,10 +6617,10 @@ function createTransferPattern(client, acc) { /** * @typedef {Object} SeriesTree_Cointime_Prices - * @property {CentsPercentilesPpmRatioSatsUsdPattern} vaulted - * @property {CentsPercentilesPpmRatioSatsUsdPattern} active - * @property {CentsPercentilesPpmRatioSatsUsdPattern} trueMarketMean - * @property {CentsPercentilesPpmRatioSatsUsdPattern} cointime + * @property {CentsPpmRatioSatsUsdPattern} vaulted + * @property {CentsPpmRatioSatsUsdPattern} active + * @property {CentsPpmRatioSatsUsdPattern} trueMarketMean + * @property {CentsPpmRatioSatsUsdPattern} cointime */ /** @@ -6996,9 +6991,29 @@ function createTransferPattern(client, acc) { /** * @typedef {Object} SeriesTree_Indicators_RarityMeter - * @property {IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern} full - * @property {IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern} local - * @property {IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern} cycle + * @property {SeriesTree_Indicators_RarityMeter_Components} components + * @property {IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern} full + * @property {IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern} local + * @property {IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern} cycle + */ + +/** + * @typedef {Object} SeriesTree_Indicators_RarityMeter_Components + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} realizedPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} capitalizedPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} sthRealizedPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} sthCapitalizedPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} lthRealizedPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} lthCapitalizedPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} over6mRealizedPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} over4mRealizedPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} under4mRealizedPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} under6mRealizedPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} vaultedPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} activePrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} trueMarketMeanPrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} cointimePrice + * @property {Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern} coinflowPrice */ /** @@ -7544,7 +7559,7 @@ function createTransferPattern(client, acc) { * @typedef {Object} SeriesTree_Cohorts_Utxo * @property {SeriesTree_Cohorts_Utxo_All} all * @property {SeriesTree_Cohorts_Utxo_Sth} sth - * @property {SeriesTree_Cohorts_Utxo_Lth} lth + * @property {ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2} lth * @property {SeriesTree_Cohorts_Utxo_AgeRange} ageRange * @property {SeriesTree_Cohorts_Utxo_UnderAge} underAge * @property {SeriesTree_Cohorts_Utxo_OverAge} overAge @@ -7590,7 +7605,7 @@ function createTransferPattern(client, acc) { * @property {CentsDeltaToUsdPattern} cap * @property {BlockCumulativeSumPattern} profit * @property {BlockCumulativeNegativeSumPattern} loss - * @property {SeriesTree_Cohorts_Utxo_All_Realized_Price} price + * @property {CentsPpmRatioSatsUsdPattern} price * @property {SeriesPattern1} mvrv * @property {BlockChangeCumulativeDeltaSumPattern} netPnl * @property {SeriesTree_Cohorts_Utxo_All_Realized_Sopr} sopr @@ -7601,102 +7616,6 @@ function createTransferPattern(client, acc) { * @property {_1m1w1y24hPattern} profitToLossRatio */ -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_All_Realized_Price - * @property {SeriesPattern1} usd - * @property {SeriesPattern1} cents - * @property {SeriesPattern1} sats - * @property {SeriesPattern1} ppm - * @property {SeriesPattern1} ratio - * @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles - * @property {_1m1w1y2y4yAllPattern} sma - * @property {SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev} stdDev - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev - * @property {SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All} all - * @property {SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y} _4y - * @property {SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y} _2y - * @property {SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_1y} _1y - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_1y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - /** * @typedef {Object} SeriesTree_Cohorts_Utxo_All_Realized_Sopr * @property {AverageBlockCumulativeSumPattern} valueDestroyed @@ -7771,247 +7690,12 @@ function createTransferPattern(client, acc) { * @property {DeltaDominanceHalfInTotalPattern2} supply * @property {SpentUnspentUtxoPattern} outputs * @property {CoindaysCoinyearsDormancyTransferPattern} activity - * @property {SeriesTree_Cohorts_Utxo_Sth_Realized} realized + * @property {CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern} realized * @property {InMaxMinPerSupplyPattern} costBasis * @property {CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2} unrealized * @property {InPattern} investedCapital */ -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Sth_Realized - * @property {CentsDeltaToUsdPattern} cap - * @property {BlockCumulativeSumPattern} profit - * @property {BlockCumulativeNegativeSumPattern} loss - * @property {SeriesTree_Cohorts_Utxo_Sth_Realized_Price} price - * @property {SeriesPattern1} mvrv - * @property {BlockChangeCumulativeDeltaSumPattern} netPnl - * @property {AdjustedRatioValuePattern} sopr - * @property {BlockCumulativeSumPattern} grossPnl - * @property {_1m1w1y24hPattern8} sellSideRiskRatio - * @property {BlockCumulativeSumPattern} peakRegret - * @property {PricePattern} capitalized - * @property {_1m1w1y24hPattern} profitToLossRatio - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Sth_Realized_Price - * @property {SeriesPattern1} usd - * @property {SeriesPattern1} cents - * @property {SeriesPattern1} sats - * @property {SeriesPattern1} ppm - * @property {SeriesPattern1} ratio - * @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles - * @property {_1m1w1y2y4yAllPattern} sma - * @property {SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev} stdDev - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev - * @property {SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All} all - * @property {SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y} _4y - * @property {SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y} _2y - * @property {SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_1y} _1y - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_1y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Lth - * @property {DeltaDominanceHalfInTotalPattern2} supply - * @property {SpentUnspentUtxoPattern} outputs - * @property {CoindaysCoinyearsDormancyTransferPattern} activity - * @property {SeriesTree_Cohorts_Utxo_Lth_Realized} realized - * @property {InMaxMinPerSupplyPattern} costBasis - * @property {CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2} unrealized - * @property {InPattern} investedCapital - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Lth_Realized - * @property {CentsDeltaToUsdPattern} cap - * @property {BlockCumulativeSumPattern} profit - * @property {BlockCumulativeNegativeSumPattern} loss - * @property {SeriesTree_Cohorts_Utxo_Lth_Realized_Price} price - * @property {SeriesPattern1} mvrv - * @property {BlockChangeCumulativeDeltaSumPattern} netPnl - * @property {RatioValuePattern2} sopr - * @property {BlockCumulativeSumPattern} grossPnl - * @property {_1m1w1y24hPattern8} sellSideRiskRatio - * @property {BlockCumulativeSumPattern} peakRegret - * @property {PricePattern} capitalized - * @property {_1m1w1y24hPattern} profitToLossRatio - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Lth_Realized_Price - * @property {SeriesPattern1} usd - * @property {SeriesPattern1} cents - * @property {SeriesPattern1} sats - * @property {SeriesPattern1} ppm - * @property {SeriesPattern1} ratio - * @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles - * @property {_1m1w1y2y4yAllPattern} sma - * @property {SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev} stdDev - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev - * @property {SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All} all - * @property {SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y} _4y - * @property {SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y} _2y - * @property {SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_1y} _1y - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_1y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - /** * @typedef {Object} SeriesTree_Cohorts_Utxo_AgeRange * @property {ActivityOutputsRealizedSupplyUnrealizedPattern} under1h @@ -8114,254 +7798,8 @@ function createTransferPattern(client, acc) { /** * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry - * @property {SeriesTree_Cohorts_Utxo_Entry_Discount} discount - * @property {SeriesTree_Cohorts_Utxo_Entry_Premium} premium - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Discount - * @property {DeltaDominanceHalfInTotalPattern2} supply - * @property {SpentUnspentUtxoPattern} outputs - * @property {CoindaysCoinyearsDormancyTransferPattern} activity - * @property {SeriesTree_Cohorts_Utxo_Entry_Discount_Realized} realized - * @property {InMaxMinPerSupplyPattern} costBasis - * @property {CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2} unrealized - * @property {InPattern} investedCapital - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Discount_Realized - * @property {CentsDeltaToUsdPattern} cap - * @property {BlockCumulativeSumPattern} profit - * @property {BlockCumulativeNegativeSumPattern} loss - * @property {SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price} price - * @property {SeriesPattern1} mvrv - * @property {BlockChangeCumulativeDeltaSumPattern} netPnl - * @property {RatioValuePattern2} sopr - * @property {BlockCumulativeSumPattern} grossPnl - * @property {_1m1w1y24hPattern8} sellSideRiskRatio - * @property {BlockCumulativeSumPattern} peakRegret - * @property {PricePattern} capitalized - * @property {_1m1w1y24hPattern} profitToLossRatio - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price - * @property {SeriesPattern1} usd - * @property {SeriesPattern1} cents - * @property {SeriesPattern1} sats - * @property {SeriesPattern1} ppm - * @property {SeriesPattern1} ratio - * @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles - * @property {_1m1w1y2y4yAllPattern} sma - * @property {SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev} stdDev - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev - * @property {SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_All} all - * @property {SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_4y} _4y - * @property {SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_2y} _2y - * @property {SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_1y} _1y - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_All - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_4y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_2y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_1y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Premium - * @property {DeltaDominanceHalfInTotalPattern2} supply - * @property {SpentUnspentUtxoPattern} outputs - * @property {CoindaysCoinyearsDormancyTransferPattern} activity - * @property {SeriesTree_Cohorts_Utxo_Entry_Premium_Realized} realized - * @property {InMaxMinPerSupplyPattern} costBasis - * @property {CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2} unrealized - * @property {InPattern} investedCapital - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Premium_Realized - * @property {CentsDeltaToUsdPattern} cap - * @property {BlockCumulativeSumPattern} profit - * @property {BlockCumulativeNegativeSumPattern} loss - * @property {SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price} price - * @property {SeriesPattern1} mvrv - * @property {BlockChangeCumulativeDeltaSumPattern} netPnl - * @property {RatioValuePattern2} sopr - * @property {BlockCumulativeSumPattern} grossPnl - * @property {_1m1w1y24hPattern8} sellSideRiskRatio - * @property {BlockCumulativeSumPattern} peakRegret - * @property {PricePattern} capitalized - * @property {_1m1w1y24hPattern} profitToLossRatio - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price - * @property {SeriesPattern1} usd - * @property {SeriesPattern1} cents - * @property {SeriesPattern1} sats - * @property {SeriesPattern1} ppm - * @property {SeriesPattern1} ratio - * @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles - * @property {_1m1w1y2y4yAllPattern} sma - * @property {SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev} stdDev - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev - * @property {SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_All} all - * @property {SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_4y} _4y - * @property {SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_2y} _2y - * @property {SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_1y} _1y - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_All - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_4y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_2y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd - */ - -/** - * @typedef {Object} SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_1y - * @property {SeriesPattern1} sd - * @property {SeriesPattern1} zscore - * @property {CentsSatsUsdPattern} _0sd - * @property {PriceRatioPattern} p05sd - * @property {PriceRatioPattern} p1sd - * @property {PriceRatioPattern} p15sd - * @property {PriceRatioPattern} p2sd - * @property {PriceRatioPattern} p25sd - * @property {PriceRatioPattern} p3sd - * @property {PriceRatioPattern} m05sd - * @property {PriceRatioPattern} m1sd - * @property {PriceRatioPattern} m15sd - * @property {PriceRatioPattern} m2sd - * @property {PriceRatioPattern} m25sd - * @property {PriceRatioPattern} m3sd + * @property {ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2} discount + * @property {ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2} premium */ /** @@ -10476,10 +9914,10 @@ class BrkClient extends BrkClientBase { aviv: createPpmRatioPattern2(this, 'aviv_ratio'), }, prices: { - vaulted: createCentsPercentilesPpmRatioSatsUsdPattern(this, 'vaulted_price'), - active: createCentsPercentilesPpmRatioSatsUsdPattern(this, 'active_price'), - trueMarketMean: createCentsPercentilesPpmRatioSatsUsdPattern(this, 'true_market_mean'), - cointime: createCentsPercentilesPpmRatioSatsUsdPattern(this, 'cointime_price'), + vaulted: createCentsPpmRatioSatsUsdPattern(this, 'vaulted_price'), + active: createCentsPpmRatioSatsUsdPattern(this, 'active_price'), + trueMarketMean: createCentsPpmRatioSatsUsdPattern(this, 'true_market_mean'), + cointime: createCentsPpmRatioSatsUsdPattern(this, 'cointime_price'), }, adjusted: { inflationRate: createPercentPpmRatioPattern(this, 'cointime_adj_inflation_rate'), @@ -10719,9 +10157,26 @@ class BrkClient extends BrkClientBase { stockToFlow: createSeriesPattern1(this, 'stock_to_flow'), sellerExhaustion: createSeriesPattern1(this, 'seller_exhaustion'), rarityMeter: { - full: createIndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern(this, 'rarity_meter'), - local: createIndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern(this, 'local_rarity_meter'), - cycle: createIndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern(this, 'cycle_rarity_meter'), + components: { + realizedPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'realized_price'), + capitalizedPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'capitalized_price'), + sthRealizedPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'sth_realized_price'), + sthCapitalizedPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'sth_capitalized_price'), + lthRealizedPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'lth_realized_price'), + lthCapitalizedPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'lth_capitalized_price'), + over6mRealizedPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'over_6m_realized_price'), + over4mRealizedPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'over_4m_realized_price'), + under4mRealizedPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'under_4m_realized_price'), + under6mRealizedPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'under_6m_realized_price'), + vaultedPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'vaulted_price'), + activePrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'active_price'), + trueMarketMeanPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'true_market_mean_price'), + cointimePrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'cointime_price'), + coinflowPrice: createPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(this, 'coinflow_price'), + }, + full: createIndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern(this, 'rarity_meter'), + local: createIndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern(this, 'local_rarity_meter'), + cycle: createIndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern(this, 'cycle_rarity_meter'), }, }, investing: { @@ -11167,85 +10622,7 @@ class BrkClient extends BrkClientBase { cap: createCentsDeltaToUsdPattern(this, 'realized_cap'), profit: createBlockCumulativeSumPattern(this, 'realized_profit'), loss: createBlockCumulativeNegativeSumPattern(this, 'realized_loss'), - price: { - usd: createSeriesPattern1(this, 'realized_price'), - cents: createSeriesPattern1(this, 'realized_price_cents'), - sats: createSeriesPattern1(this, 'realized_price_sats'), - ppm: createSeriesPattern1(this, 'realized_price_ratio_ppm'), - ratio: createSeriesPattern1(this, 'realized_price_ratio'), - percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'realized_price'), - sma: create_1m1w1y2y4yAllPattern(this, 'realized_price_ratio_sma'), - stdDev: { - all: { - sd: createSeriesPattern1(this, 'realized_price_ratio_sd'), - zscore: createSeriesPattern1(this, 'realized_price_ratio_zscore'), - _0sd: createCentsSatsUsdPattern(this, 'realized_price_0sd'), - p05sd: createPriceRatioPattern(this, 'realized_price', 'p0_5sd'), - p1sd: createPriceRatioPattern(this, 'realized_price', 'p1sd'), - p15sd: createPriceRatioPattern(this, 'realized_price', 'p1_5sd'), - p2sd: createPriceRatioPattern(this, 'realized_price', 'p2sd'), - p25sd: createPriceRatioPattern(this, 'realized_price', 'p2_5sd'), - p3sd: createPriceRatioPattern(this, 'realized_price', 'p3sd'), - m05sd: createPriceRatioPattern(this, 'realized_price', 'm0_5sd'), - m1sd: createPriceRatioPattern(this, 'realized_price', 'm1sd'), - m15sd: createPriceRatioPattern(this, 'realized_price', 'm1_5sd'), - m2sd: createPriceRatioPattern(this, 'realized_price', 'm2sd'), - m25sd: createPriceRatioPattern(this, 'realized_price', 'm2_5sd'), - m3sd: createPriceRatioPattern(this, 'realized_price', 'm3sd'), - }, - _4y: { - sd: createSeriesPattern1(this, 'realized_price_ratio_sd_4y'), - zscore: createSeriesPattern1(this, 'realized_price_ratio_zscore_4y'), - _0sd: createCentsSatsUsdPattern(this, 'realized_price_0sd_4y'), - p05sd: createPriceRatioPattern(this, 'realized_price', 'p0_5sd_4y'), - p1sd: createPriceRatioPattern(this, 'realized_price', 'p1sd_4y'), - p15sd: createPriceRatioPattern(this, 'realized_price', 'p1_5sd_4y'), - p2sd: createPriceRatioPattern(this, 'realized_price', 'p2sd_4y'), - p25sd: createPriceRatioPattern(this, 'realized_price', 'p2_5sd_4y'), - p3sd: createPriceRatioPattern(this, 'realized_price', 'p3sd_4y'), - m05sd: createPriceRatioPattern(this, 'realized_price', 'm0_5sd_4y'), - m1sd: createPriceRatioPattern(this, 'realized_price', 'm1sd_4y'), - m15sd: createPriceRatioPattern(this, 'realized_price', 'm1_5sd_4y'), - m2sd: createPriceRatioPattern(this, 'realized_price', 'm2sd_4y'), - m25sd: createPriceRatioPattern(this, 'realized_price', 'm2_5sd_4y'), - m3sd: createPriceRatioPattern(this, 'realized_price', 'm3sd_4y'), - }, - _2y: { - sd: createSeriesPattern1(this, 'realized_price_ratio_sd_2y'), - zscore: createSeriesPattern1(this, 'realized_price_ratio_zscore_2y'), - _0sd: createCentsSatsUsdPattern(this, 'realized_price_0sd_2y'), - p05sd: createPriceRatioPattern(this, 'realized_price', 'p0_5sd_2y'), - p1sd: createPriceRatioPattern(this, 'realized_price', 'p1sd_2y'), - p15sd: createPriceRatioPattern(this, 'realized_price', 'p1_5sd_2y'), - p2sd: createPriceRatioPattern(this, 'realized_price', 'p2sd_2y'), - p25sd: createPriceRatioPattern(this, 'realized_price', 'p2_5sd_2y'), - p3sd: createPriceRatioPattern(this, 'realized_price', 'p3sd_2y'), - m05sd: createPriceRatioPattern(this, 'realized_price', 'm0_5sd_2y'), - m1sd: createPriceRatioPattern(this, 'realized_price', 'm1sd_2y'), - m15sd: createPriceRatioPattern(this, 'realized_price', 'm1_5sd_2y'), - m2sd: createPriceRatioPattern(this, 'realized_price', 'm2sd_2y'), - m25sd: createPriceRatioPattern(this, 'realized_price', 'm2_5sd_2y'), - m3sd: createPriceRatioPattern(this, 'realized_price', 'm3sd_2y'), - }, - _1y: { - sd: createSeriesPattern1(this, 'realized_price_ratio_sd_1y'), - zscore: createSeriesPattern1(this, 'realized_price_ratio_zscore_1y'), - _0sd: createCentsSatsUsdPattern(this, 'realized_price_0sd_1y'), - p05sd: createPriceRatioPattern(this, 'realized_price', 'p0_5sd_1y'), - p1sd: createPriceRatioPattern(this, 'realized_price', 'p1sd_1y'), - p15sd: createPriceRatioPattern(this, 'realized_price', 'p1_5sd_1y'), - p2sd: createPriceRatioPattern(this, 'realized_price', 'p2sd_1y'), - p25sd: createPriceRatioPattern(this, 'realized_price', 'p2_5sd_1y'), - p3sd: createPriceRatioPattern(this, 'realized_price', 'p3sd_1y'), - m05sd: createPriceRatioPattern(this, 'realized_price', 'm0_5sd_1y'), - m1sd: createPriceRatioPattern(this, 'realized_price', 'm1sd_1y'), - m15sd: createPriceRatioPattern(this, 'realized_price', 'm1_5sd_1y'), - m2sd: createPriceRatioPattern(this, 'realized_price', 'm2sd_1y'), - m25sd: createPriceRatioPattern(this, 'realized_price', 'm2_5sd_1y'), - m3sd: createPriceRatioPattern(this, 'realized_price', 'm3sd_1y'), - }, - }, - }, + price: createCentsPpmRatioSatsUsdPattern(this, 'realized_price'), mvrv: createSeriesPattern1(this, 'mvrv'), netPnl: createBlockChangeCumulativeDeltaSumPattern(this, 'net'), sopr: { @@ -11308,202 +10685,12 @@ class BrkClient extends BrkClientBase { supply: createDeltaDominanceHalfInTotalPattern2(this, 'sth_supply'), outputs: createSpentUnspentUtxoPattern(this, 'sth'), activity: createCoindaysCoinyearsDormancyTransferPattern(this, 'sth'), - realized: { - cap: createCentsDeltaToUsdPattern(this, 'sth_realized_cap'), - profit: createBlockCumulativeSumPattern(this, 'sth_realized_profit'), - loss: createBlockCumulativeNegativeSumPattern(this, 'sth_realized_loss'), - price: { - usd: createSeriesPattern1(this, 'sth_realized_price'), - cents: createSeriesPattern1(this, 'sth_realized_price_cents'), - sats: createSeriesPattern1(this, 'sth_realized_price_sats'), - ppm: createSeriesPattern1(this, 'sth_realized_price_ratio_ppm'), - ratio: createSeriesPattern1(this, 'sth_realized_price_ratio'), - percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'sth_realized_price'), - sma: create_1m1w1y2y4yAllPattern(this, 'sth_realized_price_ratio_sma'), - stdDev: { - all: { - sd: createSeriesPattern1(this, 'sth_realized_price_ratio_sd'), - zscore: createSeriesPattern1(this, 'sth_realized_price_ratio_zscore'), - _0sd: createCentsSatsUsdPattern(this, 'sth_realized_price_0sd'), - p05sd: createPriceRatioPattern(this, 'sth_realized_price', 'p0_5sd'), - p1sd: createPriceRatioPattern(this, 'sth_realized_price', 'p1sd'), - p15sd: createPriceRatioPattern(this, 'sth_realized_price', 'p1_5sd'), - p2sd: createPriceRatioPattern(this, 'sth_realized_price', 'p2sd'), - p25sd: createPriceRatioPattern(this, 'sth_realized_price', 'p2_5sd'), - p3sd: createPriceRatioPattern(this, 'sth_realized_price', 'p3sd'), - m05sd: createPriceRatioPattern(this, 'sth_realized_price', 'm0_5sd'), - m1sd: createPriceRatioPattern(this, 'sth_realized_price', 'm1sd'), - m15sd: createPriceRatioPattern(this, 'sth_realized_price', 'm1_5sd'), - m2sd: createPriceRatioPattern(this, 'sth_realized_price', 'm2sd'), - m25sd: createPriceRatioPattern(this, 'sth_realized_price', 'm2_5sd'), - m3sd: createPriceRatioPattern(this, 'sth_realized_price', 'm3sd'), - }, - _4y: { - sd: createSeriesPattern1(this, 'sth_realized_price_ratio_sd_4y'), - zscore: createSeriesPattern1(this, 'sth_realized_price_ratio_zscore_4y'), - _0sd: createCentsSatsUsdPattern(this, 'sth_realized_price_0sd_4y'), - p05sd: createPriceRatioPattern(this, 'sth_realized_price', 'p0_5sd_4y'), - p1sd: createPriceRatioPattern(this, 'sth_realized_price', 'p1sd_4y'), - p15sd: createPriceRatioPattern(this, 'sth_realized_price', 'p1_5sd_4y'), - p2sd: createPriceRatioPattern(this, 'sth_realized_price', 'p2sd_4y'), - p25sd: createPriceRatioPattern(this, 'sth_realized_price', 'p2_5sd_4y'), - p3sd: createPriceRatioPattern(this, 'sth_realized_price', 'p3sd_4y'), - m05sd: createPriceRatioPattern(this, 'sth_realized_price', 'm0_5sd_4y'), - m1sd: createPriceRatioPattern(this, 'sth_realized_price', 'm1sd_4y'), - m15sd: createPriceRatioPattern(this, 'sth_realized_price', 'm1_5sd_4y'), - m2sd: createPriceRatioPattern(this, 'sth_realized_price', 'm2sd_4y'), - m25sd: createPriceRatioPattern(this, 'sth_realized_price', 'm2_5sd_4y'), - m3sd: createPriceRatioPattern(this, 'sth_realized_price', 'm3sd_4y'), - }, - _2y: { - sd: createSeriesPattern1(this, 'sth_realized_price_ratio_sd_2y'), - zscore: createSeriesPattern1(this, 'sth_realized_price_ratio_zscore_2y'), - _0sd: createCentsSatsUsdPattern(this, 'sth_realized_price_0sd_2y'), - p05sd: createPriceRatioPattern(this, 'sth_realized_price', 'p0_5sd_2y'), - p1sd: createPriceRatioPattern(this, 'sth_realized_price', 'p1sd_2y'), - p15sd: createPriceRatioPattern(this, 'sth_realized_price', 'p1_5sd_2y'), - p2sd: createPriceRatioPattern(this, 'sth_realized_price', 'p2sd_2y'), - p25sd: createPriceRatioPattern(this, 'sth_realized_price', 'p2_5sd_2y'), - p3sd: createPriceRatioPattern(this, 'sth_realized_price', 'p3sd_2y'), - m05sd: createPriceRatioPattern(this, 'sth_realized_price', 'm0_5sd_2y'), - m1sd: createPriceRatioPattern(this, 'sth_realized_price', 'm1sd_2y'), - m15sd: createPriceRatioPattern(this, 'sth_realized_price', 'm1_5sd_2y'), - m2sd: createPriceRatioPattern(this, 'sth_realized_price', 'm2sd_2y'), - m25sd: createPriceRatioPattern(this, 'sth_realized_price', 'm2_5sd_2y'), - m3sd: createPriceRatioPattern(this, 'sth_realized_price', 'm3sd_2y'), - }, - _1y: { - sd: createSeriesPattern1(this, 'sth_realized_price_ratio_sd_1y'), - zscore: createSeriesPattern1(this, 'sth_realized_price_ratio_zscore_1y'), - _0sd: createCentsSatsUsdPattern(this, 'sth_realized_price_0sd_1y'), - p05sd: createPriceRatioPattern(this, 'sth_realized_price', 'p0_5sd_1y'), - p1sd: createPriceRatioPattern(this, 'sth_realized_price', 'p1sd_1y'), - p15sd: createPriceRatioPattern(this, 'sth_realized_price', 'p1_5sd_1y'), - p2sd: createPriceRatioPattern(this, 'sth_realized_price', 'p2sd_1y'), - p25sd: createPriceRatioPattern(this, 'sth_realized_price', 'p2_5sd_1y'), - p3sd: createPriceRatioPattern(this, 'sth_realized_price', 'p3sd_1y'), - m05sd: createPriceRatioPattern(this, 'sth_realized_price', 'm0_5sd_1y'), - m1sd: createPriceRatioPattern(this, 'sth_realized_price', 'm1sd_1y'), - m15sd: createPriceRatioPattern(this, 'sth_realized_price', 'm1_5sd_1y'), - m2sd: createPriceRatioPattern(this, 'sth_realized_price', 'm2sd_1y'), - m25sd: createPriceRatioPattern(this, 'sth_realized_price', 'm2_5sd_1y'), - m3sd: createPriceRatioPattern(this, 'sth_realized_price', 'm3sd_1y'), - }, - }, - }, - mvrv: createSeriesPattern1(this, 'sth_mvrv'), - netPnl: createBlockChangeCumulativeDeltaSumPattern(this, 'sth_net'), - sopr: createAdjustedRatioValuePattern(this, 'sth'), - grossPnl: createBlockCumulativeSumPattern(this, 'sth_realized_gross_pnl'), - sellSideRiskRatio: create_1m1w1y24hPattern8(this, 'sth_sell_side_risk_ratio'), - peakRegret: createBlockCumulativeSumPattern(this, 'sth_realized_peak_regret'), - capitalized: createPricePattern(this, 'sth_capitalized_price'), - profitToLossRatio: create_1m1w1y24hPattern(this, 'sth_realized_profit_to_loss_ratio'), - }, + realized: createCapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern(this, 'sth'), costBasis: createInMaxMinPerSupplyPattern(this, 'sth'), unrealized: createCapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(this, 'sth'), investedCapital: createInPattern(this, 'sth_invested_capital_in'), }, - lth: { - supply: createDeltaDominanceHalfInTotalPattern2(this, 'lth_supply'), - outputs: createSpentUnspentUtxoPattern(this, 'lth'), - activity: createCoindaysCoinyearsDormancyTransferPattern(this, 'lth'), - realized: { - cap: createCentsDeltaToUsdPattern(this, 'lth_realized_cap'), - profit: createBlockCumulativeSumPattern(this, 'lth_realized_profit'), - loss: createBlockCumulativeNegativeSumPattern(this, 'lth_realized_loss'), - price: { - usd: createSeriesPattern1(this, 'lth_realized_price'), - cents: createSeriesPattern1(this, 'lth_realized_price_cents'), - sats: createSeriesPattern1(this, 'lth_realized_price_sats'), - ppm: createSeriesPattern1(this, 'lth_realized_price_ratio_ppm'), - ratio: createSeriesPattern1(this, 'lth_realized_price_ratio'), - percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'lth_realized_price'), - sma: create_1m1w1y2y4yAllPattern(this, 'lth_realized_price_ratio_sma'), - stdDev: { - all: { - sd: createSeriesPattern1(this, 'lth_realized_price_ratio_sd'), - zscore: createSeriesPattern1(this, 'lth_realized_price_ratio_zscore'), - _0sd: createCentsSatsUsdPattern(this, 'lth_realized_price_0sd'), - p05sd: createPriceRatioPattern(this, 'lth_realized_price', 'p0_5sd'), - p1sd: createPriceRatioPattern(this, 'lth_realized_price', 'p1sd'), - p15sd: createPriceRatioPattern(this, 'lth_realized_price', 'p1_5sd'), - p2sd: createPriceRatioPattern(this, 'lth_realized_price', 'p2sd'), - p25sd: createPriceRatioPattern(this, 'lth_realized_price', 'p2_5sd'), - p3sd: createPriceRatioPattern(this, 'lth_realized_price', 'p3sd'), - m05sd: createPriceRatioPattern(this, 'lth_realized_price', 'm0_5sd'), - m1sd: createPriceRatioPattern(this, 'lth_realized_price', 'm1sd'), - m15sd: createPriceRatioPattern(this, 'lth_realized_price', 'm1_5sd'), - m2sd: createPriceRatioPattern(this, 'lth_realized_price', 'm2sd'), - m25sd: createPriceRatioPattern(this, 'lth_realized_price', 'm2_5sd'), - m3sd: createPriceRatioPattern(this, 'lth_realized_price', 'm3sd'), - }, - _4y: { - sd: createSeriesPattern1(this, 'lth_realized_price_ratio_sd_4y'), - zscore: createSeriesPattern1(this, 'lth_realized_price_ratio_zscore_4y'), - _0sd: createCentsSatsUsdPattern(this, 'lth_realized_price_0sd_4y'), - p05sd: createPriceRatioPattern(this, 'lth_realized_price', 'p0_5sd_4y'), - p1sd: createPriceRatioPattern(this, 'lth_realized_price', 'p1sd_4y'), - p15sd: createPriceRatioPattern(this, 'lth_realized_price', 'p1_5sd_4y'), - p2sd: createPriceRatioPattern(this, 'lth_realized_price', 'p2sd_4y'), - p25sd: createPriceRatioPattern(this, 'lth_realized_price', 'p2_5sd_4y'), - p3sd: createPriceRatioPattern(this, 'lth_realized_price', 'p3sd_4y'), - m05sd: createPriceRatioPattern(this, 'lth_realized_price', 'm0_5sd_4y'), - m1sd: createPriceRatioPattern(this, 'lth_realized_price', 'm1sd_4y'), - m15sd: createPriceRatioPattern(this, 'lth_realized_price', 'm1_5sd_4y'), - m2sd: createPriceRatioPattern(this, 'lth_realized_price', 'm2sd_4y'), - m25sd: createPriceRatioPattern(this, 'lth_realized_price', 'm2_5sd_4y'), - m3sd: createPriceRatioPattern(this, 'lth_realized_price', 'm3sd_4y'), - }, - _2y: { - sd: createSeriesPattern1(this, 'lth_realized_price_ratio_sd_2y'), - zscore: createSeriesPattern1(this, 'lth_realized_price_ratio_zscore_2y'), - _0sd: createCentsSatsUsdPattern(this, 'lth_realized_price_0sd_2y'), - p05sd: createPriceRatioPattern(this, 'lth_realized_price', 'p0_5sd_2y'), - p1sd: createPriceRatioPattern(this, 'lth_realized_price', 'p1sd_2y'), - p15sd: createPriceRatioPattern(this, 'lth_realized_price', 'p1_5sd_2y'), - p2sd: createPriceRatioPattern(this, 'lth_realized_price', 'p2sd_2y'), - p25sd: createPriceRatioPattern(this, 'lth_realized_price', 'p2_5sd_2y'), - p3sd: createPriceRatioPattern(this, 'lth_realized_price', 'p3sd_2y'), - m05sd: createPriceRatioPattern(this, 'lth_realized_price', 'm0_5sd_2y'), - m1sd: createPriceRatioPattern(this, 'lth_realized_price', 'm1sd_2y'), - m15sd: createPriceRatioPattern(this, 'lth_realized_price', 'm1_5sd_2y'), - m2sd: createPriceRatioPattern(this, 'lth_realized_price', 'm2sd_2y'), - m25sd: createPriceRatioPattern(this, 'lth_realized_price', 'm2_5sd_2y'), - m3sd: createPriceRatioPattern(this, 'lth_realized_price', 'm3sd_2y'), - }, - _1y: { - sd: createSeriesPattern1(this, 'lth_realized_price_ratio_sd_1y'), - zscore: createSeriesPattern1(this, 'lth_realized_price_ratio_zscore_1y'), - _0sd: createCentsSatsUsdPattern(this, 'lth_realized_price_0sd_1y'), - p05sd: createPriceRatioPattern(this, 'lth_realized_price', 'p0_5sd_1y'), - p1sd: createPriceRatioPattern(this, 'lth_realized_price', 'p1sd_1y'), - p15sd: createPriceRatioPattern(this, 'lth_realized_price', 'p1_5sd_1y'), - p2sd: createPriceRatioPattern(this, 'lth_realized_price', 'p2sd_1y'), - p25sd: createPriceRatioPattern(this, 'lth_realized_price', 'p2_5sd_1y'), - p3sd: createPriceRatioPattern(this, 'lth_realized_price', 'p3sd_1y'), - m05sd: createPriceRatioPattern(this, 'lth_realized_price', 'm0_5sd_1y'), - m1sd: createPriceRatioPattern(this, 'lth_realized_price', 'm1sd_1y'), - m15sd: createPriceRatioPattern(this, 'lth_realized_price', 'm1_5sd_1y'), - m2sd: createPriceRatioPattern(this, 'lth_realized_price', 'm2sd_1y'), - m25sd: createPriceRatioPattern(this, 'lth_realized_price', 'm2_5sd_1y'), - m3sd: createPriceRatioPattern(this, 'lth_realized_price', 'm3sd_1y'), - }, - }, - }, - mvrv: createSeriesPattern1(this, 'lth_mvrv'), - netPnl: createBlockChangeCumulativeDeltaSumPattern(this, 'lth_net'), - sopr: createRatioValuePattern2(this, 'lth'), - grossPnl: createBlockCumulativeSumPattern(this, 'lth_realized_gross_pnl'), - sellSideRiskRatio: create_1m1w1y24hPattern8(this, 'lth_sell_side_risk_ratio'), - peakRegret: createBlockCumulativeSumPattern(this, 'lth_realized_peak_regret'), - capitalized: createPricePattern(this, 'lth_capitalized_price'), - profitToLossRatio: create_1m1w1y24hPattern(this, 'lth_realized_profit_to_loss_ratio'), - }, - costBasis: createInMaxMinPerSupplyPattern(this, 'lth'), - unrealized: createCapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(this, 'lth'), - investedCapital: createInPattern(this, 'lth_invested_capital_in'), - }, + lth: createActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2(this, 'lth'), ageRange: { under1h: createActivityOutputsRealizedSupplyUnrealizedPattern(this, 'utxos_under_1h_old'), _1hTo1d: createActivityOutputsRealizedSupplyUnrealizedPattern(this, 'utxos_1h_to_1d_old'), @@ -11595,206 +10782,8 @@ class BrkClient extends BrkClientBase { _2026: createActivityOutputsRealizedSupplyUnrealizedPattern(this, 'class_2026'), }, entry: { - discount: { - supply: createDeltaDominanceHalfInTotalPattern2(this, 'veteran_supply'), - outputs: createSpentUnspentUtxoPattern(this, 'veteran'), - activity: createCoindaysCoinyearsDormancyTransferPattern(this, 'veteran'), - realized: { - cap: createCentsDeltaToUsdPattern(this, 'veteran_realized_cap'), - profit: createBlockCumulativeSumPattern(this, 'veteran_realized_profit'), - loss: createBlockCumulativeNegativeSumPattern(this, 'veteran_realized_loss'), - price: { - usd: createSeriesPattern1(this, 'veteran_realized_price'), - cents: createSeriesPattern1(this, 'veteran_realized_price_cents'), - sats: createSeriesPattern1(this, 'veteran_realized_price_sats'), - ppm: createSeriesPattern1(this, 'veteran_realized_price_ratio_ppm'), - ratio: createSeriesPattern1(this, 'veteran_realized_price_ratio'), - percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'veteran_realized_price'), - sma: create_1m1w1y2y4yAllPattern(this, 'veteran_realized_price_ratio_sma'), - stdDev: { - all: { - sd: createSeriesPattern1(this, 'veteran_realized_price_ratio_sd'), - zscore: createSeriesPattern1(this, 'veteran_realized_price_ratio_zscore'), - _0sd: createCentsSatsUsdPattern(this, 'veteran_realized_price_0sd'), - p05sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p0_5sd'), - p1sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p1sd'), - p15sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p1_5sd'), - p2sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p2sd'), - p25sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p2_5sd'), - p3sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p3sd'), - m05sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm0_5sd'), - m1sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm1sd'), - m15sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm1_5sd'), - m2sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm2sd'), - m25sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm2_5sd'), - m3sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm3sd'), - }, - _4y: { - sd: createSeriesPattern1(this, 'veteran_realized_price_ratio_sd_4y'), - zscore: createSeriesPattern1(this, 'veteran_realized_price_ratio_zscore_4y'), - _0sd: createCentsSatsUsdPattern(this, 'veteran_realized_price_0sd_4y'), - p05sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p0_5sd_4y'), - p1sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p1sd_4y'), - p15sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p1_5sd_4y'), - p2sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p2sd_4y'), - p25sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p2_5sd_4y'), - p3sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p3sd_4y'), - m05sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm0_5sd_4y'), - m1sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm1sd_4y'), - m15sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm1_5sd_4y'), - m2sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm2sd_4y'), - m25sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm2_5sd_4y'), - m3sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm3sd_4y'), - }, - _2y: { - sd: createSeriesPattern1(this, 'veteran_realized_price_ratio_sd_2y'), - zscore: createSeriesPattern1(this, 'veteran_realized_price_ratio_zscore_2y'), - _0sd: createCentsSatsUsdPattern(this, 'veteran_realized_price_0sd_2y'), - p05sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p0_5sd_2y'), - p1sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p1sd_2y'), - p15sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p1_5sd_2y'), - p2sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p2sd_2y'), - p25sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p2_5sd_2y'), - p3sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p3sd_2y'), - m05sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm0_5sd_2y'), - m1sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm1sd_2y'), - m15sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm1_5sd_2y'), - m2sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm2sd_2y'), - m25sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm2_5sd_2y'), - m3sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm3sd_2y'), - }, - _1y: { - sd: createSeriesPattern1(this, 'veteran_realized_price_ratio_sd_1y'), - zscore: createSeriesPattern1(this, 'veteran_realized_price_ratio_zscore_1y'), - _0sd: createCentsSatsUsdPattern(this, 'veteran_realized_price_0sd_1y'), - p05sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p0_5sd_1y'), - p1sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p1sd_1y'), - p15sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p1_5sd_1y'), - p2sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p2sd_1y'), - p25sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p2_5sd_1y'), - p3sd: createPriceRatioPattern(this, 'veteran_realized_price', 'p3sd_1y'), - m05sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm0_5sd_1y'), - m1sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm1sd_1y'), - m15sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm1_5sd_1y'), - m2sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm2sd_1y'), - m25sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm2_5sd_1y'), - m3sd: createPriceRatioPattern(this, 'veteran_realized_price', 'm3sd_1y'), - }, - }, - }, - mvrv: createSeriesPattern1(this, 'veteran_mvrv'), - netPnl: createBlockChangeCumulativeDeltaSumPattern(this, 'veteran_net'), - sopr: createRatioValuePattern2(this, 'veteran'), - grossPnl: createBlockCumulativeSumPattern(this, 'veteran_realized_gross_pnl'), - sellSideRiskRatio: create_1m1w1y24hPattern8(this, 'veteran_sell_side_risk_ratio'), - peakRegret: createBlockCumulativeSumPattern(this, 'veteran_realized_peak_regret'), - capitalized: createPricePattern(this, 'veteran_capitalized_price'), - profitToLossRatio: create_1m1w1y24hPattern(this, 'veteran_realized_profit_to_loss_ratio'), - }, - costBasis: createInMaxMinPerSupplyPattern(this, 'veteran'), - unrealized: createCapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(this, 'veteran'), - investedCapital: createInPattern(this, 'veteran_invested_capital_in'), - }, - premium: { - supply: createDeltaDominanceHalfInTotalPattern2(this, 'rookie_supply'), - outputs: createSpentUnspentUtxoPattern(this, 'rookie'), - activity: createCoindaysCoinyearsDormancyTransferPattern(this, 'rookie'), - realized: { - cap: createCentsDeltaToUsdPattern(this, 'rookie_realized_cap'), - profit: createBlockCumulativeSumPattern(this, 'rookie_realized_profit'), - loss: createBlockCumulativeNegativeSumPattern(this, 'rookie_realized_loss'), - price: { - usd: createSeriesPattern1(this, 'rookie_realized_price'), - cents: createSeriesPattern1(this, 'rookie_realized_price_cents'), - sats: createSeriesPattern1(this, 'rookie_realized_price_sats'), - ppm: createSeriesPattern1(this, 'rookie_realized_price_ratio_ppm'), - ratio: createSeriesPattern1(this, 'rookie_realized_price_ratio'), - percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'rookie_realized_price'), - sma: create_1m1w1y2y4yAllPattern(this, 'rookie_realized_price_ratio_sma'), - stdDev: { - all: { - sd: createSeriesPattern1(this, 'rookie_realized_price_ratio_sd'), - zscore: createSeriesPattern1(this, 'rookie_realized_price_ratio_zscore'), - _0sd: createCentsSatsUsdPattern(this, 'rookie_realized_price_0sd'), - p05sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p0_5sd'), - p1sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p1sd'), - p15sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p1_5sd'), - p2sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p2sd'), - p25sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p2_5sd'), - p3sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p3sd'), - m05sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm0_5sd'), - m1sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm1sd'), - m15sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm1_5sd'), - m2sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm2sd'), - m25sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm2_5sd'), - m3sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm3sd'), - }, - _4y: { - sd: createSeriesPattern1(this, 'rookie_realized_price_ratio_sd_4y'), - zscore: createSeriesPattern1(this, 'rookie_realized_price_ratio_zscore_4y'), - _0sd: createCentsSatsUsdPattern(this, 'rookie_realized_price_0sd_4y'), - p05sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p0_5sd_4y'), - p1sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p1sd_4y'), - p15sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p1_5sd_4y'), - p2sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p2sd_4y'), - p25sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p2_5sd_4y'), - p3sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p3sd_4y'), - m05sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm0_5sd_4y'), - m1sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm1sd_4y'), - m15sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm1_5sd_4y'), - m2sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm2sd_4y'), - m25sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm2_5sd_4y'), - m3sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm3sd_4y'), - }, - _2y: { - sd: createSeriesPattern1(this, 'rookie_realized_price_ratio_sd_2y'), - zscore: createSeriesPattern1(this, 'rookie_realized_price_ratio_zscore_2y'), - _0sd: createCentsSatsUsdPattern(this, 'rookie_realized_price_0sd_2y'), - p05sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p0_5sd_2y'), - p1sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p1sd_2y'), - p15sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p1_5sd_2y'), - p2sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p2sd_2y'), - p25sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p2_5sd_2y'), - p3sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p3sd_2y'), - m05sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm0_5sd_2y'), - m1sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm1sd_2y'), - m15sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm1_5sd_2y'), - m2sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm2sd_2y'), - m25sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm2_5sd_2y'), - m3sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm3sd_2y'), - }, - _1y: { - sd: createSeriesPattern1(this, 'rookie_realized_price_ratio_sd_1y'), - zscore: createSeriesPattern1(this, 'rookie_realized_price_ratio_zscore_1y'), - _0sd: createCentsSatsUsdPattern(this, 'rookie_realized_price_0sd_1y'), - p05sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p0_5sd_1y'), - p1sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p1sd_1y'), - p15sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p1_5sd_1y'), - p2sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p2sd_1y'), - p25sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p2_5sd_1y'), - p3sd: createPriceRatioPattern(this, 'rookie_realized_price', 'p3sd_1y'), - m05sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm0_5sd_1y'), - m1sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm1sd_1y'), - m15sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm1_5sd_1y'), - m2sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm2sd_1y'), - m25sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm2_5sd_1y'), - m3sd: createPriceRatioPattern(this, 'rookie_realized_price', 'm3sd_1y'), - }, - }, - }, - mvrv: createSeriesPattern1(this, 'rookie_mvrv'), - netPnl: createBlockChangeCumulativeDeltaSumPattern(this, 'rookie_net'), - sopr: createRatioValuePattern2(this, 'rookie'), - grossPnl: createBlockCumulativeSumPattern(this, 'rookie_realized_gross_pnl'), - sellSideRiskRatio: create_1m1w1y24hPattern8(this, 'rookie_sell_side_risk_ratio'), - peakRegret: createBlockCumulativeSumPattern(this, 'rookie_realized_peak_regret'), - capitalized: createPricePattern(this, 'rookie_capitalized_price'), - profitToLossRatio: create_1m1w1y24hPattern(this, 'rookie_realized_profit_to_loss_ratio'), - }, - costBasis: createInMaxMinPerSupplyPattern(this, 'rookie'), - unrealized: createCapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(this, 'rookie'), - investedCapital: createInPattern(this, 'rookie_invested_capital_in'), - }, + discount: createActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2(this, 'veteran'), + premium: createActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2(this, 'rookie'), }, overAmount: { _1sat: createActivityOutputsRealizedSupplyUnrealizedPattern2(this, 'utxos_over_1sat'), diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index ad741e1cc..03d6aaaca 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -2999,6 +2999,33 @@ class SeriesPattern35(Generic[T]): # Reusable structural pattern classes +class IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClient, acc: str): + """Create pattern node with accumulated series name.""" + self.index: SeriesPattern1[StoredI8] = SeriesPattern1(client, _m(acc, 'index')) + self.pct0_01: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct0_01')) + self.pct0_5: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct0_5')) + self.pct1: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct01')) + self.pct10: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct10')) + self.pct2: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct02')) + self.pct20: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct20')) + self.pct30: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct30')) + self.pct40: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct40')) + self.pct5: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct05')) + self.pct50: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct50')) + self.pct60: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct60')) + self.pct70: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct70')) + self.pct80: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct80')) + self.pct90: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct90')) + self.pct95: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct95')) + self.pct98: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct98')) + self.pct99: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct99')) + self.pct99_5: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct99_5')) + self.pct99_9: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct99_9')) + self.score: SeriesPattern1[StoredI8] = SeriesPattern1(client, _m(acc, 'score')) + class Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern: """Pattern struct for repeated tree structure.""" @@ -3024,9 +3051,30 @@ class Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct7 self.pct90: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct90')) self.pct95: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct95')) -class _0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern: +class Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern: """Pattern struct for repeated tree structure.""" - pass + + def __init__(self, client: BrkClient, acc: str): + """Create pattern node with accumulated series name.""" + self.pct0_01: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct0_01') + self.pct0_5: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct0_5') + self.pct1: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct1') + self.pct10: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct10') + self.pct2: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct2') + self.pct20: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct20') + self.pct30: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct30') + self.pct40: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct40') + self.pct5: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct5') + self.pct50: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct50') + self.pct60: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct60') + self.pct70: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct70') + self.pct80: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct80') + self.pct90: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct90') + self.pct95: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct95') + self.pct98: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct98') + self.pct99: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct99') + self.pct99_5: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct99_5') + self.pct99_9: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct99_9') class AllEmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern: """Pattern struct for repeated tree structure.""" @@ -3089,11 +3137,39 @@ class AllEmptyP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern: class CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern: """Pattern struct for repeated tree structure.""" - pass + + def __init__(self, client: BrkClient, acc: str): + """Create pattern node with accumulated series name.""" + self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, _m(acc, 'realized_cap')) + self.capitalized: PricePattern = PricePattern(client, _m(acc, 'capitalized_price')) + self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, _m(acc, 'realized_gross_pnl')) + self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, _m(acc, 'realized_loss')) + self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'mvrv')) + self.net_pnl: BlockChangeCumulativeDeltaSumPattern = BlockChangeCumulativeDeltaSumPattern(client, _m(acc, 'net')) + self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, _m(acc, 'realized_peak_regret')) + self.price: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, _m(acc, 'realized_price')) + self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, _m(acc, 'realized_profit')) + self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, _m(acc, 'realized_profit_to_loss_ratio')) + self.sell_side_risk_ratio: _1m1w1y24hPattern8 = _1m1w1y24hPattern8(client, _m(acc, 'sell_side_risk_ratio')) + self.sopr: AdjustedRatioValuePattern = AdjustedRatioValuePattern(client, acc) class CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2: """Pattern struct for repeated tree structure.""" - pass + + def __init__(self, client: BrkClient, acc: str): + """Create pattern node with accumulated series name.""" + self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, _m(acc, 'realized_cap')) + self.capitalized: PricePattern = PricePattern(client, _m(acc, 'capitalized_price')) + self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, _m(acc, 'realized_gross_pnl')) + self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, _m(acc, 'realized_loss')) + self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'mvrv')) + self.net_pnl: BlockChangeCumulativeDeltaSumPattern = BlockChangeCumulativeDeltaSumPattern(client, _m(acc, 'net')) + self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, _m(acc, 'realized_peak_regret')) + self.price: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, _m(acc, 'realized_price')) + self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, _m(acc, 'realized_profit')) + self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, _m(acc, 'realized_profit_to_loss_ratio')) + self.sell_side_risk_ratio: _1m1w1y24hPattern8 = _1m1w1y24hPattern8(client, _m(acc, 'sell_side_risk_ratio')) + self.sopr: RatioValuePattern2 = RatioValuePattern2(client, acc) class EmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2: """Pattern struct for repeated tree structure.""" @@ -3168,22 +3244,6 @@ class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshSharePattern: """Pattern struct for repeated tree structure.""" pass -class IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClient, acc: str): - """Create pattern node with accumulated series name.""" - self.index: SeriesPattern1[StoredI8] = SeriesPattern1(client, _m(acc, 'index')) - self.pct0_5: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct0_5')) - self.pct1: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct01')) - self.pct2: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct02')) - self.pct5: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct05')) - self.pct95: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct95')) - self.pct98: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct98')) - self.pct99: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct99')) - self.pct99_5: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct99_5')) - self.score: SeriesPattern1[StoredI8] = SeriesPattern1(client, _m(acc, 'score')) - class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6: """Pattern struct for repeated tree structure.""" @@ -3289,24 +3349,6 @@ class Pct10Pct20Pct30Pct40Pct50Pct60Pct70Pct80Pct90Pattern: self.pct80: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, 'pct80')) self.pct90: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, 'pct90')) -class CentsPercentilesPpmRatioSatsSmaStdUsdPattern: - """Pattern struct for repeated tree structure.""" - pass - -class Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClient, acc: str): - """Create pattern node with accumulated series name.""" - self.pct0_5: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct0_5') - self.pct1: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct1') - self.pct2: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct2') - self.pct5: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct5') - self.pct95: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct95') - self.pct98: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct98') - self.pct99: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct99') - self.pct99_5: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct99_5') - class _10y2y3y4y5y6y8yPattern: """Pattern struct for repeated tree structure.""" @@ -3339,7 +3381,16 @@ class ActiveInputOutputSpendablePattern: class ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2: """Pattern struct for repeated tree structure.""" - pass + + def __init__(self, client: BrkClient, acc: str): + """Create pattern node with accumulated series name.""" + self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, acc) + self.cost_basis: InMaxMinPerSupplyPattern = InMaxMinPerSupplyPattern(client, acc) + self.invested_capital: InPattern = InPattern(client, _m(acc, 'invested_capital_in')) + self.outputs: SpentUnspentUtxoPattern = SpentUnspentUtxoPattern(client, acc) + self.realized: CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2 = CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2(client, acc) + self.supply: DeltaDominanceHalfInTotalPattern2 = DeltaDominanceHalfInTotalPattern2(client, _m(acc, 'supply')) + self.unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 = CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(client, acc) class CapLossMvrvNetPriceProfitSoprPattern: """Pattern struct for repeated tree structure.""" @@ -3406,18 +3457,6 @@ class MaxMedianMinPct10Pct25Pct75Pct90Pattern(Generic[T]): self.pct75: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct75')) self.pct90: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct90')) -class _1m1w1y2y4yAllPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClient, acc: str): - """Create pattern node with accumulated series name.""" - self._1m: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, '1m')) - self._1w: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, '1w')) - self._1y: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, '1y')) - self._2y: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, '2y')) - self._4y: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, '4y')) - self.all: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, 'all')) - class ActivityAddrOutputsRealizedSupplyUnrealizedPattern: """Pattern struct for repeated tree structure.""" @@ -3454,18 +3493,6 @@ class CentsNegativeToUsdPattern2: self.to_own_mcap: PercentPpmRatioPattern2 = PercentPpmRatioPattern2(client, _m(acc, 'to_own_mcap')) self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc) -class CentsPercentilesPpmRatioSatsUsdPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClient, acc: str): - """Create pattern node with accumulated series name.""" - self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) - self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc) - self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, _m(acc, 'ratio_ppm')) - self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'ratio')) - self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, _m(acc, 'sats')) - self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc) - class ChainDataOutputTxPattern: """Pattern struct for repeated tree structure.""" @@ -3744,10 +3771,6 @@ class _1m1w1y24hPattern8: self._1y: PercentPpmRatioPattern2 = PercentPpmRatioPattern2(client, _m(acc, '1y')) self._24h: PercentPpmRatioPattern2 = PercentPpmRatioPattern2(client, _m(acc, '24h')) -class _1y2y4yAllPattern: - """Pattern struct for repeated tree structure.""" - pass - class AverageBlockCumulativeSumPattern2: """Pattern struct for repeated tree structure.""" @@ -4282,14 +4305,6 @@ class PpmRatioPattern: self.ppm: SeriesPattern1[PartsPerMillionSigned32] = SeriesPattern1(client, _m(acc, 'ppm')) self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc) -class PriceRatioPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClient, acc: str, disc: str): - """Create pattern node with accumulated series name.""" - self.price: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, disc)) - self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, f'ratio_{disc}')) - class RatioValuePattern2: """Pattern struct for repeated tree structure.""" @@ -4344,7 +4359,7 @@ class PricePattern: def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" - self.price: CentsPercentilesPpmRatioSatsUsdPattern = CentsPercentilesPpmRatioSatsUsdPattern(client, acc) + self.price: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, acc) class SharePattern: """Pattern struct for repeated tree structure.""" @@ -5376,10 +5391,10 @@ class SeriesTree_Cointime_Prices: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): - self.vaulted: CentsPercentilesPpmRatioSatsUsdPattern = CentsPercentilesPpmRatioSatsUsdPattern(client, 'vaulted_price') - self.active: CentsPercentilesPpmRatioSatsUsdPattern = CentsPercentilesPpmRatioSatsUsdPattern(client, 'active_price') - self.true_market_mean: CentsPercentilesPpmRatioSatsUsdPattern = CentsPercentilesPpmRatioSatsUsdPattern(client, 'true_market_mean') - self.cointime: CentsPercentilesPpmRatioSatsUsdPattern = CentsPercentilesPpmRatioSatsUsdPattern(client, 'cointime_price') + self.vaulted: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'vaulted_price') + self.active: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'active_price') + self.true_market_mean: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'true_market_mean') + self.cointime: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'cointime_price') class SeriesTree_Cointime_Adjusted: """Series tree node.""" @@ -5789,13 +5804,34 @@ class SeriesTree_Indicators_Dormancy: self.supply_adj: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'dormancy_supply_adj') self.flow: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'dormancy_flow') +class SeriesTree_Indicators_RarityMeter_Components: + """Series tree node.""" + + def __init__(self, client: BrkClient, base_path: str = ''): + self.realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'realized_price') + self.capitalized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'capitalized_price') + self.sth_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'sth_realized_price') + self.sth_capitalized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'sth_capitalized_price') + self.lth_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'lth_realized_price') + self.lth_capitalized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'lth_capitalized_price') + self.over_6m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'over_6m_realized_price') + self.over_4m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'over_4m_realized_price') + self.under_4m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'under_4m_realized_price') + self.under_6m_realized_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'under_6m_realized_price') + self.vaulted_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'vaulted_price') + self.active_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'active_price') + self.true_market_mean_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'true_market_mean_price') + self.cointime_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'cointime_price') + self.coinflow_price: Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern = Pct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99Pattern(client, 'coinflow_price') + class SeriesTree_Indicators_RarityMeter: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): - self.full: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern = IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern(client, 'rarity_meter') - self.local: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern = IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern(client, 'local_rarity_meter') - self.cycle: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern = IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern(client, 'cycle_rarity_meter') + self.components: SeriesTree_Indicators_RarityMeter_Components = SeriesTree_Indicators_RarityMeter_Components(client) + self.full: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern = IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern(client, 'rarity_meter') + self.local: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern = IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern(client, 'local_rarity_meter') + self.cycle: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern = IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern(client, 'cycle_rarity_meter') class SeriesTree_Indicators: """Series tree node.""" @@ -6401,108 +6437,6 @@ class SeriesTree_Cohorts_Utxo_All_Activity: self.coinyears_destroyed: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'coinyears_destroyed') self.dormancy: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, 'dormancy') -class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_sd') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_zscore') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'realized_price_0sd') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p0_5sd') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p1sd') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p1_5sd') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p2sd') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p2_5sd') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p3sd') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm0_5sd') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm1sd') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm1_5sd') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm2sd') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm2_5sd') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm3sd') - -class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_sd_4y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_zscore_4y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'realized_price_0sd_4y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p0_5sd_4y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p1sd_4y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p1_5sd_4y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p2sd_4y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p2_5sd_4y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p3sd_4y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm0_5sd_4y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm1sd_4y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm1_5sd_4y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm2sd_4y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm2_5sd_4y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm3sd_4y') - -class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_sd_2y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_zscore_2y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'realized_price_0sd_2y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p0_5sd_2y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p1sd_2y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p1_5sd_2y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p2sd_2y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p2_5sd_2y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p3sd_2y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm0_5sd_2y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm1sd_2y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm1_5sd_2y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm2sd_2y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm2_5sd_2y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm3sd_2y') - -class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_1y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_sd_1y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_zscore_1y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'realized_price_0sd_1y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p0_5sd_1y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p1sd_1y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p1_5sd_1y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p2sd_1y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p2_5sd_1y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'p3sd_1y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm0_5sd_1y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm1sd_1y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm1_5sd_1y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm2sd_1y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm2_5sd_1y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'realized_price', 'm3sd_1y') - -class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.all: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All = SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All(client) - self._4y: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y = SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y(client) - self._2y: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y = SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y(client) - self._1y: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_1y = SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_1y(client) - -class SeriesTree_Cohorts_Utxo_All_Realized_Price: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'realized_price') - self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'realized_price_cents') - self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'realized_price_sats') - self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'realized_price_ratio_ppm') - self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio') - self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'realized_price') - self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'realized_price_ratio_sma') - self.std_dev: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev = SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev(client) - class SeriesTree_Cohorts_Utxo_All_Realized_Sopr_Adjusted: """Series tree node.""" @@ -6526,7 +6460,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized: self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, 'realized_cap') self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'realized_profit') self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, 'realized_loss') - self.price: SeriesTree_Cohorts_Utxo_All_Realized_Price = SeriesTree_Cohorts_Utxo_All_Realized_Price(client) + self.price: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'realized_price') self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'mvrv') self.net_pnl: BlockChangeCumulativeDeltaSumPattern = BlockChangeCumulativeDeltaSumPattern(client, 'net') self.sopr: SeriesTree_Cohorts_Utxo_All_Realized_Sopr = SeriesTree_Cohorts_Utxo_All_Realized_Sopr(client) @@ -6609,125 +6543,6 @@ class SeriesTree_Cohorts_Utxo_All: self.unrealized: SeriesTree_Cohorts_Utxo_All_Unrealized = SeriesTree_Cohorts_Utxo_All_Unrealized(client) self.invested_capital: InPattern = InPattern(client, 'invested_capital_in') -class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_sd') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_zscore') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'sth_realized_price_0sd') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p0_5sd') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p1sd') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p1_5sd') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p2sd') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p2_5sd') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p3sd') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm0_5sd') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm1sd') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm1_5sd') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm2sd') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm2_5sd') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm3sd') - -class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_sd_4y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_zscore_4y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'sth_realized_price_0sd_4y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p0_5sd_4y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p1sd_4y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p1_5sd_4y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p2sd_4y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p2_5sd_4y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p3sd_4y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm0_5sd_4y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm1sd_4y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm1_5sd_4y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm2sd_4y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm2_5sd_4y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm3sd_4y') - -class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_sd_2y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_zscore_2y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'sth_realized_price_0sd_2y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p0_5sd_2y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p1sd_2y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p1_5sd_2y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p2sd_2y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p2_5sd_2y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p3sd_2y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm0_5sd_2y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm1sd_2y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm1_5sd_2y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm2sd_2y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm2_5sd_2y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm3sd_2y') - -class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_1y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_sd_1y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_zscore_1y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'sth_realized_price_0sd_1y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p0_5sd_1y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p1sd_1y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p1_5sd_1y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p2sd_1y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p2_5sd_1y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'p3sd_1y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm0_5sd_1y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm1sd_1y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm1_5sd_1y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm2sd_1y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm2_5sd_1y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'sth_realized_price', 'm3sd_1y') - -class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.all: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All = SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All(client) - self._4y: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y = SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y(client) - self._2y: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y = SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y(client) - self._1y: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_1y = SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_1y(client) - -class SeriesTree_Cohorts_Utxo_Sth_Realized_Price: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'sth_realized_price') - self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'sth_realized_price_cents') - self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'sth_realized_price_sats') - self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'sth_realized_price_ratio_ppm') - self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio') - self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'sth_realized_price') - self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'sth_realized_price_ratio_sma') - self.std_dev: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev = SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev(client) - -class SeriesTree_Cohorts_Utxo_Sth_Realized: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, 'sth_realized_cap') - self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'sth_realized_profit') - self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, 'sth_realized_loss') - self.price: SeriesTree_Cohorts_Utxo_Sth_Realized_Price = SeriesTree_Cohorts_Utxo_Sth_Realized_Price(client) - self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_mvrv') - self.net_pnl: BlockChangeCumulativeDeltaSumPattern = BlockChangeCumulativeDeltaSumPattern(client, 'sth_net') - self.sopr: AdjustedRatioValuePattern = AdjustedRatioValuePattern(client, 'sth') - self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'sth_realized_gross_pnl') - self.sell_side_risk_ratio: _1m1w1y24hPattern8 = _1m1w1y24hPattern8(client, 'sth_sell_side_risk_ratio') - self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'sth_realized_peak_regret') - self.capitalized: PricePattern = PricePattern(client, 'sth_capitalized_price') - self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'sth_realized_profit_to_loss_ratio') - class SeriesTree_Cohorts_Utxo_Sth: """Series tree node.""" @@ -6735,142 +6550,11 @@ class SeriesTree_Cohorts_Utxo_Sth: self.supply: DeltaDominanceHalfInTotalPattern2 = DeltaDominanceHalfInTotalPattern2(client, 'sth_supply') self.outputs: SpentUnspentUtxoPattern = SpentUnspentUtxoPattern(client, 'sth') self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'sth') - self.realized: SeriesTree_Cohorts_Utxo_Sth_Realized = SeriesTree_Cohorts_Utxo_Sth_Realized(client) + self.realized: CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern = CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern(client, 'sth') self.cost_basis: InMaxMinPerSupplyPattern = InMaxMinPerSupplyPattern(client, 'sth') self.unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 = CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(client, 'sth') self.invested_capital: InPattern = InPattern(client, 'sth_invested_capital_in') -class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_sd') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_zscore') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'lth_realized_price_0sd') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p0_5sd') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p1sd') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p1_5sd') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p2sd') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p2_5sd') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p3sd') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm0_5sd') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm1sd') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm1_5sd') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm2sd') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm2_5sd') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm3sd') - -class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_sd_4y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_zscore_4y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'lth_realized_price_0sd_4y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p0_5sd_4y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p1sd_4y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p1_5sd_4y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p2sd_4y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p2_5sd_4y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p3sd_4y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm0_5sd_4y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm1sd_4y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm1_5sd_4y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm2sd_4y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm2_5sd_4y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm3sd_4y') - -class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_sd_2y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_zscore_2y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'lth_realized_price_0sd_2y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p0_5sd_2y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p1sd_2y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p1_5sd_2y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p2sd_2y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p2_5sd_2y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p3sd_2y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm0_5sd_2y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm1sd_2y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm1_5sd_2y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm2sd_2y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm2_5sd_2y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm3sd_2y') - -class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_1y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_sd_1y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_zscore_1y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'lth_realized_price_0sd_1y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p0_5sd_1y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p1sd_1y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p1_5sd_1y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p2sd_1y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p2_5sd_1y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'p3sd_1y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm0_5sd_1y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm1sd_1y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm1_5sd_1y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm2sd_1y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm2_5sd_1y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'lth_realized_price', 'm3sd_1y') - -class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.all: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All = SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All(client) - self._4y: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y = SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y(client) - self._2y: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y = SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y(client) - self._1y: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_1y = SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_1y(client) - -class SeriesTree_Cohorts_Utxo_Lth_Realized_Price: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'lth_realized_price') - self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'lth_realized_price_cents') - self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'lth_realized_price_sats') - self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'lth_realized_price_ratio_ppm') - self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio') - self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'lth_realized_price') - self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'lth_realized_price_ratio_sma') - self.std_dev: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev = SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev(client) - -class SeriesTree_Cohorts_Utxo_Lth_Realized: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, 'lth_realized_cap') - self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'lth_realized_profit') - self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, 'lth_realized_loss') - self.price: SeriesTree_Cohorts_Utxo_Lth_Realized_Price = SeriesTree_Cohorts_Utxo_Lth_Realized_Price(client) - self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_mvrv') - self.net_pnl: BlockChangeCumulativeDeltaSumPattern = BlockChangeCumulativeDeltaSumPattern(client, 'lth_net') - self.sopr: RatioValuePattern2 = RatioValuePattern2(client, 'lth') - self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'lth_realized_gross_pnl') - self.sell_side_risk_ratio: _1m1w1y24hPattern8 = _1m1w1y24hPattern8(client, 'lth_sell_side_risk_ratio') - self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'lth_realized_peak_regret') - self.capitalized: PricePattern = PricePattern(client, 'lth_capitalized_price') - self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'lth_realized_profit_to_loss_ratio') - -class SeriesTree_Cohorts_Utxo_Lth: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.supply: DeltaDominanceHalfInTotalPattern2 = DeltaDominanceHalfInTotalPattern2(client, 'lth_supply') - self.outputs: SpentUnspentUtxoPattern = SpentUnspentUtxoPattern(client, 'lth') - self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'lth') - self.realized: SeriesTree_Cohorts_Utxo_Lth_Realized = SeriesTree_Cohorts_Utxo_Lth_Realized(client) - self.cost_basis: InMaxMinPerSupplyPattern = InMaxMinPerSupplyPattern(client, 'lth') - self.unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 = CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(client, 'lth') - self.invested_capital: InPattern = InPattern(client, 'lth_invested_capital_in') - class SeriesTree_Cohorts_Utxo_AgeRange: """Series tree node.""" @@ -6976,274 +6660,12 @@ class SeriesTree_Cohorts_Utxo_Class: self._2025: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'class_2025') self._2026: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'class_2026') -class SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_All: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'veteran_realized_price_ratio_sd') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'veteran_realized_price_ratio_zscore') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'veteran_realized_price_0sd') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p0_5sd') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p1sd') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p1_5sd') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p2sd') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p2_5sd') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p3sd') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm0_5sd') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm1sd') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm1_5sd') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm2sd') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm2_5sd') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm3sd') - -class SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_4y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'veteran_realized_price_ratio_sd_4y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'veteran_realized_price_ratio_zscore_4y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'veteran_realized_price_0sd_4y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p0_5sd_4y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p1sd_4y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p1_5sd_4y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p2sd_4y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p2_5sd_4y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p3sd_4y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm0_5sd_4y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm1sd_4y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm1_5sd_4y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm2sd_4y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm2_5sd_4y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm3sd_4y') - -class SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_2y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'veteran_realized_price_ratio_sd_2y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'veteran_realized_price_ratio_zscore_2y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'veteran_realized_price_0sd_2y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p0_5sd_2y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p1sd_2y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p1_5sd_2y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p2sd_2y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p2_5sd_2y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p3sd_2y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm0_5sd_2y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm1sd_2y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm1_5sd_2y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm2sd_2y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm2_5sd_2y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm3sd_2y') - -class SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_1y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'veteran_realized_price_ratio_sd_1y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'veteran_realized_price_ratio_zscore_1y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'veteran_realized_price_0sd_1y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p0_5sd_1y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p1sd_1y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p1_5sd_1y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p2sd_1y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p2_5sd_1y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'p3sd_1y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm0_5sd_1y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm1sd_1y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm1_5sd_1y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm2sd_1y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm2_5sd_1y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'veteran_realized_price', 'm3sd_1y') - -class SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.all: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_All = SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_All(client) - self._4y: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_4y = SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_4y(client) - self._2y: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_2y = SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_2y(client) - self._1y: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_1y = SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev_1y(client) - -class SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'veteran_realized_price') - self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'veteran_realized_price_cents') - self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'veteran_realized_price_sats') - self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'veteran_realized_price_ratio_ppm') - self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'veteran_realized_price_ratio') - self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'veteran_realized_price') - self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'veteran_realized_price_ratio_sma') - self.std_dev: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev = SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price_StdDev(client) - -class SeriesTree_Cohorts_Utxo_Entry_Discount_Realized: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, 'veteran_realized_cap') - self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'veteran_realized_profit') - self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, 'veteran_realized_loss') - self.price: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price = SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price(client) - self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'veteran_mvrv') - self.net_pnl: BlockChangeCumulativeDeltaSumPattern = BlockChangeCumulativeDeltaSumPattern(client, 'veteran_net') - self.sopr: RatioValuePattern2 = RatioValuePattern2(client, 'veteran') - self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'veteran_realized_gross_pnl') - self.sell_side_risk_ratio: _1m1w1y24hPattern8 = _1m1w1y24hPattern8(client, 'veteran_sell_side_risk_ratio') - self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'veteran_realized_peak_regret') - self.capitalized: PricePattern = PricePattern(client, 'veteran_capitalized_price') - self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'veteran_realized_profit_to_loss_ratio') - -class SeriesTree_Cohorts_Utxo_Entry_Discount: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.supply: DeltaDominanceHalfInTotalPattern2 = DeltaDominanceHalfInTotalPattern2(client, 'veteran_supply') - self.outputs: SpentUnspentUtxoPattern = SpentUnspentUtxoPattern(client, 'veteran') - self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'veteran') - self.realized: SeriesTree_Cohorts_Utxo_Entry_Discount_Realized = SeriesTree_Cohorts_Utxo_Entry_Discount_Realized(client) - self.cost_basis: InMaxMinPerSupplyPattern = InMaxMinPerSupplyPattern(client, 'veteran') - self.unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 = CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(client, 'veteran') - self.invested_capital: InPattern = InPattern(client, 'veteran_invested_capital_in') - -class SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_All: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rookie_realized_price_ratio_sd') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rookie_realized_price_ratio_zscore') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'rookie_realized_price_0sd') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p0_5sd') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p1sd') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p1_5sd') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p2sd') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p2_5sd') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p3sd') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm0_5sd') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm1sd') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm1_5sd') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm2sd') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm2_5sd') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm3sd') - -class SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_4y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rookie_realized_price_ratio_sd_4y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rookie_realized_price_ratio_zscore_4y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'rookie_realized_price_0sd_4y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p0_5sd_4y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p1sd_4y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p1_5sd_4y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p2sd_4y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p2_5sd_4y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p3sd_4y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm0_5sd_4y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm1sd_4y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm1_5sd_4y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm2sd_4y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm2_5sd_4y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm3sd_4y') - -class SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_2y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rookie_realized_price_ratio_sd_2y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rookie_realized_price_ratio_zscore_2y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'rookie_realized_price_0sd_2y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p0_5sd_2y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p1sd_2y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p1_5sd_2y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p2sd_2y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p2_5sd_2y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p3sd_2y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm0_5sd_2y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm1sd_2y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm1_5sd_2y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm2sd_2y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm2_5sd_2y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm3sd_2y') - -class SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_1y: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rookie_realized_price_ratio_sd_1y') - self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rookie_realized_price_ratio_zscore_1y') - self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'rookie_realized_price_0sd_1y') - self.p0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p0_5sd_1y') - self.p1sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p1sd_1y') - self.p1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p1_5sd_1y') - self.p2sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p2sd_1y') - self.p2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p2_5sd_1y') - self.p3sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'p3sd_1y') - self.m0_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm0_5sd_1y') - self.m1sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm1sd_1y') - self.m1_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm1_5sd_1y') - self.m2sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm2sd_1y') - self.m2_5sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm2_5sd_1y') - self.m3sd: PriceRatioPattern = PriceRatioPattern(client, 'rookie_realized_price', 'm3sd_1y') - -class SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.all: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_All = SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_All(client) - self._4y: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_4y = SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_4y(client) - self._2y: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_2y = SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_2y(client) - self._1y: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_1y = SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev_1y(client) - -class SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'rookie_realized_price') - self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'rookie_realized_price_cents') - self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'rookie_realized_price_sats') - self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'rookie_realized_price_ratio_ppm') - self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rookie_realized_price_ratio') - self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'rookie_realized_price') - self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'rookie_realized_price_ratio_sma') - self.std_dev: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev = SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price_StdDev(client) - -class SeriesTree_Cohorts_Utxo_Entry_Premium_Realized: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, 'rookie_realized_cap') - self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'rookie_realized_profit') - self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, 'rookie_realized_loss') - self.price: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price = SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price(client) - self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rookie_mvrv') - self.net_pnl: BlockChangeCumulativeDeltaSumPattern = BlockChangeCumulativeDeltaSumPattern(client, 'rookie_net') - self.sopr: RatioValuePattern2 = RatioValuePattern2(client, 'rookie') - self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'rookie_realized_gross_pnl') - self.sell_side_risk_ratio: _1m1w1y24hPattern8 = _1m1w1y24hPattern8(client, 'rookie_sell_side_risk_ratio') - self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'rookie_realized_peak_regret') - self.capitalized: PricePattern = PricePattern(client, 'rookie_capitalized_price') - self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'rookie_realized_profit_to_loss_ratio') - -class SeriesTree_Cohorts_Utxo_Entry_Premium: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.supply: DeltaDominanceHalfInTotalPattern2 = DeltaDominanceHalfInTotalPattern2(client, 'rookie_supply') - self.outputs: SpentUnspentUtxoPattern = SpentUnspentUtxoPattern(client, 'rookie') - self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'rookie') - self.realized: SeriesTree_Cohorts_Utxo_Entry_Premium_Realized = SeriesTree_Cohorts_Utxo_Entry_Premium_Realized(client) - self.cost_basis: InMaxMinPerSupplyPattern = InMaxMinPerSupplyPattern(client, 'rookie') - self.unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 = CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(client, 'rookie') - self.invested_capital: InPattern = InPattern(client, 'rookie_invested_capital_in') - class SeriesTree_Cohorts_Utxo_Entry: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): - self.discount: SeriesTree_Cohorts_Utxo_Entry_Discount = SeriesTree_Cohorts_Utxo_Entry_Discount(client) - self.premium: SeriesTree_Cohorts_Utxo_Entry_Premium = SeriesTree_Cohorts_Utxo_Entry_Premium(client) + self.discount: ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2 = ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2(client, 'veteran') + self.premium: ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2 = ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2(client, 'rookie') class SeriesTree_Cohorts_Utxo_OverAmount: """Series tree node.""" @@ -7420,7 +6842,7 @@ class SeriesTree_Cohorts_Utxo: def __init__(self, client: BrkClient, base_path: str = ''): self.all: SeriesTree_Cohorts_Utxo_All = SeriesTree_Cohorts_Utxo_All(client) self.sth: SeriesTree_Cohorts_Utxo_Sth = SeriesTree_Cohorts_Utxo_Sth(client) - self.lth: SeriesTree_Cohorts_Utxo_Lth = SeriesTree_Cohorts_Utxo_Lth(client) + self.lth: ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2 = ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2(client, 'lth') self.age_range: SeriesTree_Cohorts_Utxo_AgeRange = SeriesTree_Cohorts_Utxo_AgeRange(client) self.under_age: SeriesTree_Cohorts_Utxo_UnderAge = SeriesTree_Cohorts_Utxo_UnderAge(client) self.over_age: SeriesTree_Cohorts_Utxo_OverAge = SeriesTree_Cohorts_Utxo_OverAge(client) diff --git a/website/llms-full.txt b/website/llms-full.txt index 0339b465e..a5ef8c848 100644 --- a/website/llms-full.txt +++ b/website/llms-full.txt @@ -4,7 +4,7 @@ - Version: `v0.3.6` - Base URL: https://bitview.space -- Metrics: 56973 +- Metrics: 56817 - Operations: 97 For machine-readable tool construction, use [https://bitview.space/openapi.json](https://bitview.space/openapi.json). For the complete source-derived series tree, use [https://bitview.space/api/series](https://bitview.space/api/series). diff --git a/website/llms.txt b/website/llms.txt index 8fb278efb..64c023f89 100644 --- a/website/llms.txt +++ b/website/llms.txt @@ -1,6 +1,6 @@ # Bitcoin Research Kit (BRK) -> Free, open-source Bitcoin analytics API and block explorer. 56973 on-chain time-series and 97 API operations. No authentication required. +> Free, open-source Bitcoin analytics API and block explorer. 56817 on-chain time-series and 97 API operations. No authentication required. ## API diff --git a/website/scripts/_types.js b/website/scripts/_types.js index 7ac67877a..026289334 100644 --- a/website/scripts/_types.js +++ b/website/scripts/_types.js @@ -36,7 +36,7 @@ * @typedef {Brk.SeriesTree_Cohorts_Addr} AddrCohortTree * @typedef {Brk.SeriesTree_Cohorts_Utxo_All} AllUtxoPattern * @typedef {Brk.SeriesTree_Cohorts_Utxo_Sth} ShortTermPattern - * @typedef {Brk.SeriesTree_Cohorts_Utxo_Lth} LongTermPattern + * @typedef {Brk.ActivityCostInvestedOutputsRealizedSupplyUnrealizedPattern2} LongTermPattern * @typedef {Brk.SeriesTree_Cohorts_Utxo_All_Unrealized} AllRelativePattern * @typedef {keyof Brk.BtcCentsSatsUsdPattern} BtcSatsUsdKey * @typedef {Brk.BtcCentsSatsUsdPattern} SupplyPattern @@ -53,7 +53,6 @@ * @typedef {Brk.ActivityOutputsRealizedSupplyUnrealizedPattern} BasicUtxoPattern * @typedef {Brk.ActivityOutputsRealizedSupplyUnrealizedPattern} EpochPattern * @typedef {Brk.ActivityOutputsRealizedSupplyUnrealizedPattern3} EmptyPattern - * @typedef {Brk._0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern} Ratio1ySdPattern * @typedef {Brk.Dollars} Dollars * @typedef {Brk.BlockInfo} BlockInfo * @typedef {Brk.Height} Height @@ -68,12 +67,8 @@ * @typedef {Brk.BlockTemplate} BlockTemplate * @typedef {Brk.MempoolBlock} MempoolBlock * @typedef {Brk.NextBlockHash} NextBlockHash - * ActivePriceRatioPattern: ratio pattern with price (extended) - * @typedef {Brk.PriceRatioPattern} ActivePriceRatioPattern - * PriceRatioPercentilesPattern: price pattern with ratio + percentiles (no SMAs/stdDev) - * @typedef {Brk.CentsPercentilesPpmRatioSatsUsdPattern} PriceRatioPercentilesPattern - * AnyRatioPattern: full ratio pattern with percentiles, SMAs, and std dev bands - * @typedef {Brk.CentsPercentilesPpmRatioSatsSmaStdUsdPattern} AnyRatioPattern + * AnyRatioPattern: price pattern with a ratio + * @typedef {AnyPricePattern & { ratio: AnySeriesPattern }} AnyRatioPattern * FullValuePattern: block + cumulative + sum + average rolling windows (sats/btc/cents/usd) * @typedef {Brk.AverageBlockCumulativeSumPattern3} FullValuePattern * RollingWindowSlot: a single rolling window with stats (pct10, pct25, median, pct75, pct90, max, min) per unit @@ -113,9 +108,6 @@ * PPM + ratio pattern (for NUPL and similar) * @typedef {Brk.PpmRatioPattern} NuplPattern * - * LTH realized tree - * @typedef {Brk.SeriesTree_Cohorts_Utxo_Lth_Realized} LthRealizedPattern - * * Net PnL pattern with change (base + change + cumulative + delta + rel + sum) * @typedef {Brk.BlockChangeCumulativeDeltaSumPattern} NetPnlFullPattern * @@ -256,7 +248,6 @@ * * Capitalized price percentiles (pct1/2/5/95/98/99) * @typedef {Brk.Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} CapitalizedPercentilesPattern - * @typedef {Brk.PriceRatioPattern} CapitalizedPercentileEntry * * Generic tree node type for walking * @typedef {AnySeriesPattern | Record} TreeNode diff --git a/website/scripts/options/distribution/prices.js b/website/scripts/options/distribution/prices.js index b24556a58..b06ca7b08 100644 --- a/website/scripts/options/distribution/prices.js +++ b/website/scripts/options/distribution/prices.js @@ -3,18 +3,18 @@ * * Structure (single cohort): * - Compare: Both prices on one chart - * - Realized: Price + Ratio (MVRV) + Z-Scores (for full cohorts) - * - Capitalized: Price + Ratio + Z-Scores (for full cohorts) + * - Realized: Price + Ratio (MVRV) + * - Capitalized: Price + Ratio * * Structure (grouped cohorts): * - Realized: Price + Ratio comparison across cohorts * - Capitalized: Price + Ratio comparison across cohorts * - * For cohorts WITHOUT full ratio patterns: basic Price/Ratio charts only (no Z-Scores) + * Cohorts without percentile patterns use basic Price/Ratio charts. */ import { colors } from "../../utils/colors.js"; -import { createPriceRatioCharts, mapCohortsWithAll, priceRatioPercentilesTree } from "../shared.js"; +import { mapCohortsWithAll } from "../shared.js"; import { baseline, price } from "../series.js"; import { Unit } from "../../utils/units.js"; @@ -39,25 +39,41 @@ export function createPricesSectionFull({ cohort, title }) { }, { name: "Realized", - tree: createPriceRatioCharts({ - context: cohort.title, - legend: "Realized", - pricePattern: tree.realized.price, - ratio: tree.realized.price, - color, - priceTitle: title("Realized Price"), - titlePrefix: "Realized Price", - }), + title: title("Realized Price"), + top: [ + price({ + series: tree.realized.price, + name: "Realized", + color, + }), + ], + bottom: [ + baseline({ + series: tree.realized.price.ratio, + name: "Ratio", + unit: Unit.ratio, + base: 1, + }), + ], }, { name: "Capitalized", - tree: priceRatioPercentilesTree({ - pattern: tree.realized.capitalized.price, - title: title("Capitalized Price"), - ratioTitle: title("Capitalized Price Ratio"), - legend: "Capitalized", - color, - }), + title: title("Capitalized Price"), + top: [ + price({ + series: tree.realized.capitalized.price, + name: "Capitalized", + color, + }), + ], + bottom: [ + baseline({ + series: tree.realized.capitalized.price.ratio, + name: "Ratio", + unit: Unit.ratio, + base: 1, + }), + ], }, ], }; @@ -76,24 +92,21 @@ export function createPricesSectionBasic({ cohort, title }) { tree: [ { name: "Realized", - tree: [ - { - name: "Price", - title: title("Realized Price"), - top: [price({ series: tree.realized.price, name: "Realized", color })], - }, - { + title: title("Realized Price"), + top: [ + price({ + series: tree.realized.price, + name: "Realized", + color, + }), + ], + bottom: [ + baseline({ + series: tree.realized.price.ratio, name: "Ratio", - title: title("Realized Price Ratio"), - bottom: [ - baseline({ - series: tree.realized.price.ratio, - name: "Ratio", - unit: Unit.ratio, - base: 1, - }), - ], - }, + unit: Unit.ratio, + base: 1, + }), ], }, ], diff --git a/website/scripts/options/frameworks/cointime/index.js b/website/scripts/options/frameworks/cointime/index.js index 0ff7cd635..3943a176b 100644 --- a/website/scripts/options/frameworks/cointime/index.js +++ b/website/scripts/options/frameworks/cointime/index.js @@ -10,7 +10,7 @@ import { sumsAndAveragesCumulative, } from "../../series.js"; import { ageRanges } from "../../age-ranges.js"; -import { satsBtcUsd, priceRatioPercentilesTree } from "../../shared.js"; +import { satsBtcUsd, simplePriceRatioTree } from "../../shared.js"; import { createCointimeAgeRangeSection } from "./age-range.js"; /** @@ -44,7 +44,7 @@ export function createCointimeSection() { }, ]); - /** @type {readonly { pattern: PriceRatioPercentilesPattern, name: string, title: (name: string) => string, color: Color, defaultActive: boolean }[]} */ + /** @type {readonly { pattern: AnyPricePattern & { ratio: AnySeriesPattern }, name: string, title: (name: string) => string, color: Color, defaultActive: boolean }[]} */ const prices = [ { pattern: cointimePrices.trueMarketMean, @@ -198,23 +198,15 @@ export function createCointimeSection() { ), ], }, - ...prices.map(({ pattern, name, title, color }) => ({ - name, - tree: priceRatioPercentilesTree({ + ...prices.map(({ pattern, name, title, color }) => { + const [chart] = simplePriceRatioTree({ pattern, title: title(name), legend: name, color, - priceReferences: [ - price({ - series: all.realized.price, - name: "Realized", - color: colors.realized, - defaultActive: false, - }), - ], - }), - })), + }); + return { ...chart, name }; + }), ], }, diff --git a/website/scripts/options/market.js b/website/scripts/options/market.js index fb1dfca22..367b4e9d5 100644 --- a/website/scripts/options/market.js +++ b/website/scripts/options/market.js @@ -226,7 +226,15 @@ function historicalSubSection(name, periods) { * @returns {PartialOptionsGroup} */ export function createMarketSection() { - const { market, supply, cohorts, price: prices, indicators } = brk.series; + const { + market, + supply, + cohorts, + price: prices, + indicators, + cointime, + coinflow, + } = brk.series; const { movingAverage: ma, ath, @@ -921,6 +929,27 @@ export function createMarketSection() { { name: "Indicators", tree: [ + { + name: "Value Anchors", + title: "Bitcoin Value Anchors", + top: [ + price({ + series: cointime.prices.trueMarketMean, + name: "True Market Mean", + color: colors.trueMarketMean, + }), + price({ + series: coinflow.price, + name: "Coinflow Price", + color: colors.coinflow, + }), + price({ + series: indicators.rarityMeter.cycle.pct50, + name: "Cycle Rarity Midpoint", + color: colors.ratioPct._50, + }), + ], + }, { name: "NVT", title: "NVT Ratio", diff --git a/website/scripts/options/models/rarity-meter.js b/website/scripts/options/models/rarity-meter.js index bf880d89c..db1ae5f3a 100644 --- a/website/scripts/options/models/rarity-meter.js +++ b/website/scripts/options/models/rarity-meter.js @@ -14,7 +14,9 @@ import { */ export function createRarityMeterSection() { const { rarityMeter } = brk.series.indicators; - const { all, sth, lth } = brk.series.cohorts.utxo; + const { all, sth, lth, overAge, underAge } = brk.series.cohorts.utxo; + const { cointime, coinflow } = brk.series; + const components = rarityMeter.components; return { name: "Rarity Meter", @@ -28,7 +30,7 @@ export function createRarityMeterSection() { return { name: variant.name, title: variant.title, - top: priceBands(percentileBands(meter), { defaultActive: true }), + top: priceBands(percentileBands(meter)), bottom: [ histogram({ series: meter.index, @@ -36,6 +38,7 @@ export function createRarityMeterSection() { unit: Unit.count, colorFn: (value) => /** @type {const} */ ([ + colors.ratioPct._0_01, colors.ratioPct._0_5, colors.ratioPct._1, colors.ratioPct._2, @@ -45,13 +48,14 @@ export function createRarityMeterSection() { colors.ratioPct._98, colors.ratioPct._99, colors.ratioPct._99_5, - ])[value + 4], + colors.ratioPct._99_9, + ])[value + 5], }), baseline({ series: meter.score, name: "Score", unit: Unit.count, - color: [colors.ratioPct._99, colors.ratioPct._1], + color: [colors.ratioPct._99_9, colors.ratioPct._0_01], defaultActive: false, }), ], @@ -61,56 +65,134 @@ export function createRarityMeterSection() { name: "Components", tree: [ { - name: "Realized Price", + name: "RP", title: "Realized Price", pattern: all.realized.price, - legend: "Realized", + percentiles: components.realizedPrice, + legend: "RP", color: colors.realized, }, { - name: "Capitalized Price", + name: "CP", title: "Capitalized Price", pattern: all.realized.capitalized.price, - legend: "Capitalized", + percentiles: components.capitalizedPrice, + legend: "CP", color: colors.capitalized, }, { name: "STH RP", title: "STH Realized Price", pattern: sth.realized.price, - legend: "Realized", + percentiles: components.sthRealizedPrice, + legend: "STH RP", color: colors.realized, }, { name: "STH CP", title: "STH Capitalized Price", pattern: sth.realized.capitalized.price, - legend: "Capitalized", + percentiles: components.sthCapitalizedPrice, + legend: "STH CP", color: colors.capitalized, }, { name: "LTH RP", title: "LTH Realized Price", pattern: lth.realized.price, - legend: "Realized", + percentiles: components.lthRealizedPrice, + legend: "LTH RP", color: colors.realized, }, { name: "LTH CP", title: "LTH Capitalized Price", pattern: lth.realized.capitalized.price, - legend: "Capitalized", + percentiles: components.lthCapitalizedPrice, + legend: "LTH CP", color: colors.capitalized, }, + { + name: ">6M RP", + title: ">6M Realized Price", + pattern: overAge._6m.realized.price, + percentiles: components.over6mRealizedPrice, + legend: ">6M RP", + color: colors.realized, + }, + { + name: ">4M RP", + title: ">4M Realized Price", + pattern: overAge._4m.realized.price, + percentiles: components.over4mRealizedPrice, + legend: ">4M RP", + color: colors.realized, + }, + { + name: "<4M RP", + title: "<4M Realized Price", + pattern: underAge._4m.realized.price, + percentiles: components.under4mRealizedPrice, + legend: "<4M RP", + color: colors.realized, + }, + { + name: "<6M RP", + title: "<6M Realized Price", + pattern: underAge._6m.realized.price, + percentiles: components.under6mRealizedPrice, + legend: "<6M RP", + color: colors.realized, + }, + { + name: "Vaulted Price", + title: "Vaulted Price", + pattern: cointime.prices.vaulted, + percentiles: components.vaultedPrice, + legend: "Vaulted", + color: colors.vaulted, + }, + { + name: "Active Price", + title: "Active Price", + pattern: cointime.prices.active, + percentiles: components.activePrice, + legend: "Active", + color: colors.active, + }, + { + name: "True Market Mean", + title: "True Market Mean", + pattern: cointime.prices.trueMarketMean, + percentiles: components.trueMarketMeanPrice, + legend: "True Market Mean", + color: colors.trueMarketMean, + }, + { + name: "Cointime Price", + title: "Cointime Price", + pattern: cointime.prices.cointime, + percentiles: components.cointimePrice, + legend: "Cointime", + color: colors.cointime, + }, + { + name: "Coinflow Price", + title: "Coinflow Price", + pattern: coinflow.price, + percentiles: components.coinflowPrice, + legend: "Coinflow", + color: colors.coinflow, + }, ].map((component) => { - const [, ratioChart] = priceRatioPercentilesTree({ + const [chart] = priceRatioPercentilesTree({ pattern: component.pattern, - title: component.title, + percentiles: component.percentiles, + title: `Bitcoin Rarity Meter: ${component.title}`, legend: component.legend, color: component.color, - defaultActivePercentiles: true, }); - return { ...ratioChart, name: component.name }; + return { ...chart, name: component.name }; }), }, ], diff --git a/website/scripts/options/shared.js b/website/scripts/options/shared.js index 7728e4e6b..e3e82f804 100644 --- a/website/scripts/options/shared.js +++ b/website/scripts/options/shared.js @@ -10,7 +10,6 @@ import { chartsFromPercentCumulativeEntries, sumsAndAveragesCumulativeWith, } from "./series.js"; -import { priceLine, priceLines } from "./constants.js"; import { colors } from "../utils/colors.js"; // ============================================================================ @@ -611,14 +610,9 @@ export function avgHoldingsSubtree(pattern, title) { export function simplePriceRatioTree({ pattern, title, legend, color }) { return [ { - name: "Price", + name: title, title, top: [price({ series: pattern, name: legend, color })], - }, - { - name: "Ratio", - title: `${title} Ratio`, - top: [price({ series: pattern, name: legend, color })], bottom: [ baseline({ series: pattern.ratio, @@ -632,7 +626,7 @@ export function simplePriceRatioTree({ pattern, title, legend, color }) { } /** - * @param {{ pct95: AnyPricePattern, pct5: AnyPricePattern, pct98: AnyPricePattern, pct2: AnyPricePattern, pct99: AnyPricePattern, pct1: AnyPricePattern, pct995: AnyPricePattern, pct05: AnyPricePattern }} p + * @param {{ pct001: AnyPricePattern, pct05: AnyPricePattern, pct1: AnyPricePattern, pct2: AnyPricePattern, pct5: AnyPricePattern, pct10: AnyPricePattern, pct20: AnyPricePattern, pct30: AnyPricePattern, pct40: AnyPricePattern, pct50: AnyPricePattern, pct60: AnyPricePattern, pct70: AnyPricePattern, pct80: AnyPricePattern, pct90: AnyPricePattern, pct95: AnyPricePattern, pct98: AnyPricePattern, pct99: AnyPricePattern, pct995: AnyPricePattern, pct999: AnyPricePattern }} p */ export function percentileBands(p) { return percentileBandsWith(p, (e) => e); @@ -641,51 +635,126 @@ export function percentileBands(p) { /** * @template E * @template T - * @param {{ pct95: E, pct5: E, pct98: E, pct2: E, pct99: E, pct1: E, pct995: E, pct05: E }} p + * @param {{ pct001: E, pct05: E, pct1: E, pct2: E, pct5: E, pct10: E, pct20: E, pct30: E, pct40: E, pct50: E, pct60: E, pct70: E, pct80: E, pct90: E, pct95: E, pct98: E, pct99: E, pct995: E, pct999: E }} p * @param {(entry: E) => T} extract */ export function percentileBandsWith(p, extract) { return [ - { name: "P95", prop: extract(p.pct95), color: colors.ratioPct._95 }, - { name: "P5", prop: extract(p.pct5), color: colors.ratioPct._5 }, - { name: "P98", prop: extract(p.pct98), color: colors.ratioPct._98 }, - { name: "P2", prop: extract(p.pct2), color: colors.ratioPct._2 }, - { name: "P99", prop: extract(p.pct99), color: colors.ratioPct._99 }, - { name: "P1", prop: extract(p.pct1), color: colors.ratioPct._1 }, - { name: "P99.5", prop: extract(p.pct995), color: colors.ratioPct._99_5 }, - { name: "P0.5", prop: extract(p.pct05), color: colors.ratioPct._0_5 }, + { + name: "P95", + prop: extract(p.pct95), + color: colors.ratioPct._95, + defaultActive: true, + lineStyle: 0, + }, + { + name: "P98", + prop: extract(p.pct98), + color: colors.ratioPct._98, + defaultActive: true, + lineStyle: 0, + }, + { + name: "P99", + prop: extract(p.pct99), + color: colors.ratioPct._99, + defaultActive: true, + lineStyle: 0, + }, + { + name: "P99.5", + prop: extract(p.pct995), + color: colors.ratioPct._99_5, + defaultActive: true, + lineStyle: 0, + }, + { + name: "P99.9", + prop: extract(p.pct999), + color: colors.ratioPct._99_9, + defaultActive: true, + lineStyle: 0, + }, + { + name: "P5", + prop: extract(p.pct5), + color: colors.ratioPct._5, + defaultActive: true, + lineStyle: 0, + }, + { + name: "P2", + prop: extract(p.pct2), + color: colors.ratioPct._2, + defaultActive: true, + lineStyle: 0, + }, + { + name: "P1", + prop: extract(p.pct1), + color: colors.ratioPct._1, + defaultActive: true, + lineStyle: 0, + }, + { + name: "P0.5", + prop: extract(p.pct05), + color: colors.ratioPct._0_5, + defaultActive: true, + lineStyle: 0, + }, + { + name: "P0.01", + prop: extract(p.pct001), + color: colors.ratioPct._0_01, + defaultActive: true, + lineStyle: 0, + }, + { + name: "P50", + prop: extract(p.pct50), + color: colors.ratioPct._50, + defaultActive: true, + lineStyle: 0, + }, + { name: "P10", prop: extract(p.pct10), color: colors.ratioPct._10 }, + { name: "P20", prop: extract(p.pct20), color: colors.ratioPct._20 }, + { name: "P30", prop: extract(p.pct30), color: colors.ratioPct._30 }, + { name: "P40", prop: extract(p.pct40), color: colors.ratioPct._40 }, + { name: "P60", prop: extract(p.pct60), color: colors.ratioPct._60 }, + { name: "P70", prop: extract(p.pct70), color: colors.ratioPct._70 }, + { name: "P80", prop: extract(p.pct80), color: colors.ratioPct._80 }, + { name: "P90", prop: extract(p.pct90), color: colors.ratioPct._90 }, ]; } /** - * @param {{ name: string, prop: AnyPricePattern, color: Color }[]} bands - * @param {{ defaultActive?: boolean }} [opts] + * @param {{ name: string, prop: AnyPricePattern, color: Color, defaultActive?: boolean, lineStyle?: number }[]} bands */ -export function priceBands(bands, opts) { - return bands.map(({ name, prop, color }) => +export function priceBands(bands) { + return bands.map(({ name, prop, color, defaultActive, lineStyle }) => price({ series: prop, name, color, - defaultActive: opts?.defaultActive ?? false, - options: { lineStyle: 1 }, + defaultActive: defaultActive ?? false, + options: { lineStyle: lineStyle ?? 1 }, }), ); } /** - * @param {{ name: string, prop: AnySeriesPattern, color: Color }[]} bands - * @param {{ defaultActive?: boolean }} [opts] + * @param {{ name: string, prop: AnySeriesPattern, color: Color, defaultActive?: boolean, lineStyle?: number }[]} bands */ -function ratioBands(bands, opts) { - return bands.map(({ name, prop, color }) => +function ratioBands(bands) { + return bands.map(({ name, prop, color, defaultActive, lineStyle }) => line({ series: prop, name, color, - defaultActive: opts?.defaultActive ?? false, + defaultActive: defaultActive ?? false, unit: Unit.ratio, - options: { lineStyle: 1 }, + options: { lineStyle: lineStyle ?? 1 }, }), ); } @@ -693,43 +762,32 @@ function ratioBands(bands, opts) { /** * Price + Ratio charts with percentile bands * @param {Object} args - * @param {PriceRatioPercentilesPattern} args.pattern + * @param {AnyPricePattern & { ratio: AnySeriesPattern }} args.pattern + * @param {{ pct001: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct05: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct1: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct2: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct5: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct10: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct20: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct30: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct40: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct50: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct60: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct70: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct80: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct90: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct95: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct98: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct99: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct995: { price: AnyPricePattern, ratio: AnySeriesPattern }, pct999: { price: AnyPricePattern, ratio: AnySeriesPattern }} args.percentiles * @param {string} args.title * @param {string} args.legend * @param {Color} [args.color] - * @param {string} [args.ratioTitle] * @param {FetchedPriceSeriesBlueprint[]} [args.priceReferences] - * @param {boolean} [args.defaultActivePercentiles] * @returns {PartialOptionsTree} */ export function priceRatioPercentilesTree({ pattern, + percentiles, title, legend, color, - ratioTitle, priceReferences, - defaultActivePercentiles, }) { - const p = pattern.percentiles; - const pctUsd = percentileBandsWith(p, (e) => e.price); - const pctRatio = percentileBandsWith(p, (e) => e.ratio); + const pctUsd = percentileBandsWith(percentiles, (e) => e.price); + const pctRatio = percentileBandsWith(percentiles, (e) => e.ratio); return [ { - name: "Price", + name: title, title, top: [ price({ series: pattern, name: legend, color }), ...(priceReferences ?? []), - ...priceBands(pctUsd, { defaultActive: defaultActivePercentiles }), - ], - }, - { - name: "Ratio", - title: ratioTitle ?? `${title} Ratio`, - top: [ - price({ series: pattern, name: legend, color }), - ...priceBands(pctUsd, { defaultActive: defaultActivePercentiles }), + ...priceBands(pctUsd), ], bottom: [ baseline({ @@ -738,7 +796,7 @@ export function priceRatioPercentilesTree({ unit: Unit.ratio, base: 1, }), - ...ratioBands(pctRatio, { defaultActive: defaultActivePercentiles }), + ...ratioBands(pctRatio), ], }, ]; @@ -772,115 +830,8 @@ export function revenueRollingBtcSatsUsd({ coinbase, subsidy, fee }) { ]; } -/** @param {AnyRatioPattern} ratio */ -export function percentileUsdMap(ratio) { - return percentileBandsWith(ratio.percentiles, (e) => e.price); -} - -/** @param {AnyRatioPattern} ratio */ -export function percentileMap(ratio) { - return percentileBandsWith(ratio.percentiles, (e) => e.ratio); -} - /** - * Build SD patterns from a ratio pattern - * @param {AnyRatioPattern} ratio - */ -export function sdPatterns(ratio) { - return /** @type {const} */ ([ - { - nameAddon: "All Time", - titleAddon: "All Time", - sd: ratio.stdDev.all, - smaRatio: ratio.sma.all.ratio, - }, - { - nameAddon: "4y", - titleAddon: "4y", - sd: ratio.stdDev._4y, - smaRatio: ratio.sma._4y.ratio, - }, - { - nameAddon: "2y", - titleAddon: "2y", - sd: ratio.stdDev._2y, - smaRatio: ratio.sma._2y.ratio, - }, - { - nameAddon: "1y", - titleAddon: "1y", - sd: ratio.stdDev._1y, - smaRatio: ratio.sma._1y.ratio, - }, - ]); -} - -/** - * Build SD band mappings from an SD pattern - * @param {Ratio1ySdPattern} sd - */ -export function sdBandsUsd(sd) { - return /** @type {const} */ ([ - { name: "0σ", prop: sd._0sd, color: colors.sd._0 }, - { name: "+0.5σ", prop: sd.p05sd.price, color: colors.sd.p05 }, - { name: "−0.5σ", prop: sd.m05sd.price, color: colors.sd.m05 }, - { name: "+1σ", prop: sd.p1sd.price, color: colors.sd.p1 }, - { name: "−1σ", prop: sd.m1sd.price, color: colors.sd.m1 }, - { name: "+1.5σ", prop: sd.p15sd.price, color: colors.sd.p15 }, - { name: "−1.5σ", prop: sd.m15sd.price, color: colors.sd.m15 }, - { name: "+2σ", prop: sd.p2sd.price, color: colors.sd.p2 }, - { name: "−2σ", prop: sd.m2sd.price, color: colors.sd.m2 }, - { name: "+2.5σ", prop: sd.p25sd.price, color: colors.sd.p25 }, - { name: "−2.5σ", prop: sd.m25sd.price, color: colors.sd.m25 }, - { name: "+3σ", prop: sd.p3sd.price, color: colors.sd.p3 }, - { name: "−3σ", prop: sd.m3sd.price, color: colors.sd.m3 }, - ]); -} - -/** - * Build SD band mappings (ratio) from an SD pattern - * @param {Ratio1ySdPattern} sd - * @param {AnySeriesPattern} smaRatio - */ -export function sdBandsRatio(sd, smaRatio) { - return /** @type {const} */ ([ - { name: "0σ", prop: smaRatio, color: colors.sd._0 }, - { name: "+0.5σ", prop: sd.p05sd.ratio, color: colors.sd.p05 }, - { name: "−0.5σ", prop: sd.m05sd.ratio, color: colors.sd.m05 }, - { name: "+1σ", prop: sd.p1sd.ratio, color: colors.sd.p1 }, - { name: "−1σ", prop: sd.m1sd.ratio, color: colors.sd.m1 }, - { name: "+1.5σ", prop: sd.p15sd.ratio, color: colors.sd.p15 }, - { name: "−1.5σ", prop: sd.m15sd.ratio, color: colors.sd.m15 }, - { name: "+2σ", prop: sd.p2sd.ratio, color: colors.sd.p2 }, - { name: "−2σ", prop: sd.m2sd.ratio, color: colors.sd.m2 }, - { name: "+2.5σ", prop: sd.p25sd.ratio, color: colors.sd.p25 }, - { name: "−2.5σ", prop: sd.m25sd.ratio, color: colors.sd.m25 }, - { name: "+3σ", prop: sd.p3sd.ratio, color: colors.sd.p3 }, - { name: "−3σ", prop: sd.m3sd.ratio, color: colors.sd.m3 }, - ]); -} - -/** - * Build ratio SMA series from a ratio pattern - * @param {AnyRatioPattern} ratio - */ -export function ratioSmas(ratio) { - return [ - { name: "1w SMA", series: ratio.sma._1w.ratio }, - { name: "1m SMA", series: ratio.sma._1m.ratio }, - { name: "1y SMA", series: ratio.sma._1y.ratio }, - { name: "2y SMA", series: ratio.sma._2y.ratio }, - { name: "4y SMA", series: ratio.sma._4y.ratio }, - { - name: "All Time SMA", - series: ratio.sma.all.ratio, - color: colors.time.all, - }, - ].map((s, i, arr) => ({ color: colors.at(i, arr.length), ...s })); -} - -/** - * Ratio bottom series: baseline + SMAs + percentiles + * Ratio bottom series * @param {AnyRatioPattern} ratio * @returns {AnyFetchedSeriesBlueprint[]} */ @@ -892,249 +843,6 @@ export function ratioBottomSeries(ratio) { unit: Unit.ratio, base: 1, }), - ...ratioSmas(ratio).map(({ name, series, color }) => - line({ series, name, color, unit: Unit.ratio, defaultActive: false }), - ), - ...percentileMap(ratio).map(({ name, prop, color }) => - line({ - series: prop, - name, - color, - defaultActive: false, - unit: Unit.ratio, - options: { lineStyle: 1 }, - }), - ), - ]; -} - -/** - * @param {Object} args - * @param {(name: string) => string} args.title - * @param {AnyPricePattern} args.pricePattern - * @param {AnyRatioPattern} args.ratio - * @param {Color} args.color - * @param {string} [args.name] - * @param {string} [args.legend] - * @returns {PartialChartOption} - */ -export function createRatioChart({ - title, - pricePattern, - ratio, - color, - name, - legend, -}) { - return { - name: name ?? "Ratio", - title: title(name ?? "Ratio"), - top: [ - price({ series: pricePattern, name: legend ?? "Price", color }), - ...percentileUsdMap(ratio).map(({ name, prop, color }) => - price({ - series: prop, - name, - color, - defaultActive: false, - options: { lineStyle: 1 }, - }), - ), - ], - bottom: ratioBottomSeries(ratio), - }; -} - -/** - * Create ZScores folder from ActivePriceRatioPattern - * @param {Object} args - * @param {(suffix: string) => string} args.formatTitle - Function that takes series suffix and returns full title - * @param {string} args.legend - * @param {AnyPricePattern} args.pricePattern - The price pattern to show in top pane - * @param {AnyRatioPattern} args.ratio - The ratio pattern - * @param {Color} args.color - * @returns {PartialOptionsGroup} - */ -export function createZScoresFolder({ - formatTitle, - legend, - pricePattern, - ratio, - color, -}) { - const sdPats = sdPatterns(ratio); - - const zscorePeriods = [ - { name: "1y", sd: ratio.stdDev._1y }, - { name: "2y", sd: ratio.stdDev._2y }, - { name: "4y", sd: ratio.stdDev._4y }, - { name: "All Time", sd: ratio.stdDev.all, color: colors.time.all }, - ].map((s, i, arr) => ({ color: colors.at(i, arr.length), ...s })); - - return { - name: "Z-Scores", - tree: [ - { - name: "Compare", - title: formatTitle("Z-Scores"), - top: [ - price({ series: pricePattern, name: legend, color }), - ...zscorePeriods.map((p) => - price({ - series: p.sd._0sd, - name: `${p.name} 0σ`, - color: p.color, - defaultActive: false, - }), - ), - ], - bottom: [ - ...zscorePeriods.reverse().map((p) => - line({ - series: p.sd.zscore, - name: p.name, - color: p.color, - unit: Unit.sd, - }), - ), - ...priceLines({ - unit: Unit.sd, - numbers: [0, 1, -1, 2, -2, 3, -3], - defaultActive: false, - }), - ], - }, - ...sdPats.map(({ nameAddon, titleAddon, sd, smaRatio }) => { - const prefix = titleAddon ? `${titleAddon} ` : ""; - const topPrice = price({ series: pricePattern, name: legend, color }); - return { - name: nameAddon, - tree: [ - { - name: "Score", - title: formatTitle(`${prefix}Z-Score`), - top: [ - topPrice, - ...sdBandsUsd(sd).map( - ({ name: bandName, prop, color: bandColor }) => - price({ - series: prop, - name: bandName, - color: bandColor, - defaultActive: false, - }), - ), - ], - bottom: [ - baseline({ - series: sd.zscore, - name: "Z-Score", - unit: Unit.sd, - }), - priceLine({ - unit: Unit.sd, - }), - ...priceLines({ - unit: Unit.sd, - numbers: [1, -1, 2, -2, 3, -3], - defaultActive: false, - }), - ], - }, - { - name: "Ratio", - title: formatTitle(`${prefix}Ratio`), - top: [topPrice], - bottom: [ - baseline({ - series: ratio.ratio, - name: "Ratio", - unit: Unit.ratio, - base: 1, - }), - ...sdBandsRatio(sd, smaRatio).map( - ({ name: bandName, prop, color: bandColor }) => - line({ - series: prop, - name: bandName, - color: bandColor, - unit: Unit.ratio, - defaultActive: false, - }), - ), - ], - }, - { - name: "Volatility", - title: formatTitle(`${prefix}Volatility`), - top: [topPrice], - bottom: [ - line({ - series: sd.sd, - name: "Volatility", - color: colors.gray, - unit: Unit.percentage, - }), - ], - }, - ], - }; - }), - ], - }; -} - -/** - * Create price + ratio + z-scores charts - flat array - * Unified helper for averages, distribution, and other price-based series - * @param {Object} args - * @param {string} args.context - Context string for ratio/z-scores titles (e.g., "1 Week SMA", "STH") - * @param {string} args.legend - Legend name for the price series - * @param {AnyPricePattern} args.pricePattern - The price pattern - * @param {AnyRatioPattern} args.ratio - The ratio pattern - * @param {Color} args.color - * @param {string} [args.priceTitle] - Optional override for price chart title (default: context) - * @param {string} [args.titlePrefix] - Optional prefix for ratio/z-scores titles (e.g., "Realized Price" gives "Realized Price Ratio: STH") - * @param {FetchedPriceSeriesBlueprint[]} [args.priceReferences] - Optional additional price series to show in Price chart - * @returns {PartialOptionsTree} - */ -export function createPriceRatioCharts({ - context, - legend, - pricePattern, - ratio, - color, - priceTitle, - titlePrefix, - priceReferences, -}) { - const titleFn = formatCohortTitle(context); - const pctUsd = percentileBandsWith(ratio.percentiles, (e) => e.price); - return [ - { - name: "Price", - title: priceTitle ?? context, - top: [ - price({ series: pricePattern, name: legend, color }), - ...(priceReferences ?? []), - ...priceBands(pctUsd), - ], - }, - createRatioChart({ - title: (name) => titleFn(titlePrefix ? `${titlePrefix} ${name}` : name), - pricePattern, - ratio, - color, - legend, - }), - createZScoresFolder({ - formatTitle: (name) => - titleFn(titlePrefix ? `${titlePrefix} ${name}` : name), - legend, - pricePattern, - ratio, - color, - }), ]; } diff --git a/website/scripts/utils/colors.js b/website/scripts/utils/colors.js index ce43c74dc..bb1e5aff4 100644 --- a/website/scripts/utils/colors.js +++ b/website/scripts/utils/colors.js @@ -152,7 +152,7 @@ export const colors = { active: palette.rose, activity: palette.purple, cointime: palette.yellow, - coinflow: palette.blue, + coinflow: palette.purple, mobile: palette.rose, immobile: palette.lime, destroyed: palette.red, @@ -220,14 +220,25 @@ export const colors = { // Ratio percentile bands (extreme values) ratioPct: { + _99_9: palette.rose, _99_5: palette.red, _99: palette.orange, _98: palette.amber, _95: palette.yellow, + _90: palette.avocado, + _80: palette.lime, + _70: palette.green, + _60: palette.emerald, + _50: palette.green, + _40: palette.teal, + _30: palette.cyan, + _20: palette.sky, + _10: palette.blue, _5: palette.cyan, _2: palette.sky, _1: palette.blue, _0_5: palette.indigo, + _0_01: palette.purple, }, bedrock: { @@ -252,23 +263,6 @@ export const colors = { ], }, - // Standard deviation bands (warm = positive, cool = negative) - sd: { - _0: palette.lime, - p05: palette.yellow, - m05: palette.teal, - p1: palette.amber, - m1: palette.cyan, - p15: palette.orange, - m15: palette.sky, - p2: palette.red, - m2: palette.blue, - p25: palette.rose, - m25: palette.indigo, - p3: palette.pink, - m3: palette.violet, - }, - time: { _24h: palette.red, _1w: palette.yellow, diff --git a/website/scripts/utils/units.js b/website/scripts/utils/units.js index bd20716c2..5655a94d9 100644 --- a/website/scripts/utils/units.js +++ b/website/scripts/utils/units.js @@ -14,7 +14,6 @@ export const Unit = /** @type {const} */ ({ cagr: { id: "cagr", name: "CAGR (%/year)" }, ratio: { id: "ratio", name: "Ratio" }, index: { id: "index", name: "Index" }, - sd: { id: "sd", name: "Std Dev" }, // Relative percentages pctSupply: { id: "pct-supply", name: "% of circulating" }, diff --git a/website_next/ask/storage.js b/website_next/ask/storage.js index 0fdb5d675..c39cb98bb 100644 --- a/website_next/ask/storage.js +++ b/website_next/ask/storage.js @@ -53,6 +53,7 @@ const CHART_COLORS = new Set([ * @property {string} key * @property {Record} arguments * @property {string[]} [fields] + * @property {(string | number | boolean | Record)[]} [records] * * @typedef {Object} SourceContext * @property {string} revision @@ -64,6 +65,7 @@ const CHART_COLORS = new Set([ * @typedef {Object} KnowledgeContext * @property {string} title * @property {string} description + * @property {string[]} [subjects] * * @typedef {Object} StoredResponseStep * @property {string} label @@ -200,10 +202,38 @@ function readApiContext(value) { .filter((field) => typeof field === "string" && field.length <= 256) .slice(0, 12) : []; + const records = Array.isArray(context.records) + ? context.records + .map((record) => { + if ( + typeof record === "string" || + typeof record === "number" || + typeof record === "boolean" + ) return record; + if (!record || typeof record !== "object" || Array.isArray(record)) { + return undefined; + } + return Object.fromEntries( + Object.entries(record) + .filter(([key, item]) => + key.length <= 64 && + ( + typeof item === "string" || + typeof item === "number" || + typeof item === "boolean" + ) + ) + .slice(0, 16), + ); + }) + .filter((record) => record !== undefined) + .slice(0, 4) + : []; return /** @type {ApiContext} */ ({ key: context.key, arguments: arguments_, ...(fields.length ? { fields } : {}), + ...(records.length ? { records } : {}), }); } @@ -247,6 +277,14 @@ function readKnowledgeContext(value) { return { title: context.title.trim().slice(0, 160), description: context.description.trim().slice(0, 1_500), + ...(Array.isArray(context.subjects) + ? { + subjects: context.subjects + .filter((subject) => typeof subject === "string" && subject.trim()) + .map((subject) => subject.trim().slice(0, 160)) + .slice(0, 12), + } + : {}), }; } diff --git a/website_next/ask/tools/api/answer.js b/website_next/ask/tools/api/answer.js index c377a528e..d3b3bfb17 100644 --- a/website_next/ask/tools/api/answer.js +++ b/website_next/ask/tools/api/answer.js @@ -1,5 +1,5 @@ import { renderApiAnswer } from "../render.js"; -import { relevance } from "../text.js"; +import { normalize, relevance, tokenAffinity } from "../text.js"; import { focusApiData } from "./result.js"; const MAX_FIELDS = 10; @@ -8,6 +8,8 @@ const MAX_FIELDS = 10; function dimension(type) { const value = type.toLowerCase(); if (value.includes("sats")) return "sats"; + if (value.includes("vsize")) return "vB"; + if (value.includes("feerate")) return "sat/vB"; return value; } @@ -15,10 +17,37 @@ function dimension(type) { function displayedUnit(type) { const value = dimension(type); if (value === "sats") return " sats"; + if (value === "vB") return " vB"; + if (value === "sat/vB") return " sat/vB"; + if (value.includes("timestamp")) return ""; if (value === "number" || value === "integer" || value === "float") return ""; return ` ${type}`; } +/** @param {string} question */ +function languageHints(question) { + const words = new Set(question.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)); + return [ + ...(words.has("when") ? ["time timestamp date"] : []), + ...(words.has("many") ? ["count number total"] : []), + ...(words.has("much") ? ["amount value total"] : []), + ]; +} + +/** @param {string} query @param {string} document */ +function lexicalAffinity(query, document) { + const queryWords = normalize(query).split(" ").filter(Boolean); + const documentWords = normalize(document).split(" ").filter(Boolean); + return Math.max( + 0, + ...queryWords.flatMap((queryWord) => + documentWords.map((documentWord) => + tokenAffinity(queryWord, documentWord) + ) + ), + ); +} + /** * @typedef {Object} ApiAnswerField * @property {string} ref @@ -34,6 +63,7 @@ function displayedUnit(type) { * @property {ApiAnswerField} [previous] * @property {ApiAnswerField} [resolved] * @property {ApiAnswerField} [direct] + * @property {ApiAnswerField[]} related * @property {ApiAnswerField[]} ambiguous * @property {any[]} tools */ @@ -50,9 +80,15 @@ function valueAt(value, path) { return current; } -/** @param {unknown} value */ -function formattedValue(value) { +/** @param {unknown} value @param {string} [type] */ +function formattedValue(value, type = "") { if (typeof value === "number") { + if (dimension(type).includes("timestamp")) { + return new Date(value * 1_000).toLocaleString(undefined, { + dateStyle: "medium", + timeStyle: "medium", + }); + } return new Intl.NumberFormat("en-US", { maximumFractionDigits: 8, }).format(value); @@ -67,6 +103,74 @@ export function summarizeApiAnswer(grounding) { const responseFields = /** @type {{ name: string, type: string, description?: string, ownDescription?: string }[]} */ ( grounding.operation.response.fields ?? [] ); + if (Array.isArray(data)) { + const rows = data.slice(0, 4); + const fields = responseFields.slice(0, 4); + const total = grounding.data && + typeof grounding.data === "object" && + !Array.isArray(grounding.data) && + typeof grounding.data.count === "number" + ? grounding.data.count + : data.length; + const primitiveRows = rows.every((row) => + typeof row === "string" || + typeof row === "number" || + typeof row === "boolean" + ); + if (primitiveRows) { + const itemType = grounding.operation.response.type.replace(/\[\]$/, ""); + return { + output: renderApiAnswer( + [ + `${total.toLocaleString()} record${total === 1 ? "" : "s"}${ + total > rows.length ? ` · showing ${rows.length}` : "" + }`, + ...rows.map((row, index) => + `${index + 1}. ${formattedValue(row, itemType)}` + ), + ].join("\n\n"), + grounding.operation, + ), + fields: [], + }; + } + if (!rows.length || !fields.length) { + return { + output: renderApiAnswer( + "The API returned no compact records to display.", + grounding.operation, + ), + fields: [], + }; + } + const output = [ + `${total.toLocaleString()} record${total === 1 ? "" : "s"}${ + total > rows.length ? ` · showing ${rows.length}` : "" + }`, + ...rows.map((row, index) => { + const values = fields + .map((field) => ({ + field, + value: valueAt(row, field.name.split(".")), + })) + .filter(({ value }) => + typeof value === "string" || + typeof value === "number" || + typeof value === "boolean" + ) + .map(({ field, value }) => + `**${field.name.replaceAll(".", " · ").replaceAll("_", " ")}**: ${ + formattedValue(value, field.type) + }${typeof value === "number" ? displayedUnit(field.type) : ""}` + ); + return `${index + 1}. ${values.join(" · ")}`; + }), + ].join("\n\n"); + return { + output: renderApiAnswer(output, grounding.operation), + fields: fields.map(({ name }) => name), + }; + } const fields = responseFields .map((field) => ({ ...field, @@ -90,7 +194,7 @@ export function summarizeApiAnswer(grounding) { const output = fields .map((/** @type {any} */ field) => `- **${field.name.replaceAll(".", " · ").replaceAll("_", " ")}**: ${ - formattedValue(field.value) + formattedValue(field.value, field.type) }${typeof field.value === "number" ? displayedUnit(field.type) : ""}` ) .join("\n"); @@ -112,7 +216,7 @@ export function createApiAnswerTool(grounding) { const previousParents = new Set( (grounding.previousFields ?? []).map((/** @type {string} */ name) => name.split(".").slice(0, -1).join(".") - ), + ).filter(Boolean), ); const previousParent = previousParents.size === 1 ? [...previousParents][0] @@ -140,19 +244,32 @@ export function createApiAnswerTool(grounding) { `${field.name} ${field.ownDescription || field.description || ""}`, ) + relevance(grounding.question, field.name) + + lexicalAffinity(grounding.question, field.name) * 8 + relevance( grounding.question, field.ownDescription || field.description || "", + ) + + Math.max( + 0, + ...languageHints(grounding.question).map((hint) => + relevance( + hint, + `${field.name} ${field.type} ${ + field.ownDescription || field.description || "" + }`, + ) + ), ) - - Math.max(0, field.name.split(".").length - 1) * 2, + Math.max(0, field.name.split(".").length - 1) * 2 + + ( + previousParent && + field.name.split(".").slice(0, -1).join(".") === previousParent + ? 2 + : 0 + ), })) .sort((left, right) => { - const leftParent = left.name.split(".").slice(0, -1).join("."); - const rightParent = right.name.split(".").slice(0, -1).join("."); - const leftAffinity = previousParent && leftParent === previousParent ? 2 : 0; - const rightAffinity = previousParent && rightParent === previousParent ? 2 : 0; - return right.score + rightAffinity - left.score - leftAffinity || - left.index - right.index; + return right.score - left.score || left.index - right.index; }); const answerCandidates = primitive.filter(({ name }) => name !== previousName && @@ -196,6 +313,11 @@ export function createApiAnswerTool(grounding) { value: /** @type {string | number | boolean} */ (field.value), ref: `n${index + 1}`, })); + const related = best + ? fields.filter((field) => + field.score >= 6 && best.score - field.score < 3 + ) + : []; const previousChoices = fields .filter(({ name }) => grounding.previousFields?.includes(name)) .sort((left, right) => right.score - left.score); @@ -258,6 +380,7 @@ export function createApiAnswerTool(grounding) { "answer_api", [ "Choose select for one raw primitive field.", + "Choose select_many when several raw primitive fields were requested.", "Choose calculate to derive the result from component fields, including a narrower concept than an aggregate.", previous ? `Choose continue only to apply arithmetic to preceding ${previous.ref}=${previous.name}.` @@ -269,6 +392,7 @@ export function createApiAnswerTool(grounding) { type: "string", enum: [ ...(fields.length ? ["select"] : []), + ...(fields.length > 1 ? ["select_many"] : []), ...(calculationFields.length >= 2 ? ["calculate"] : []), ...(previous ? ["continue"] : []), "text", @@ -277,6 +401,14 @@ export function createApiAnswerTool(grounding) { ...(fields.length ? { field: reference, + fields: { + type: "array", + minItems: 2, + maxItems: Math.min(10, fields.length), + items: reference, + description: + "Ordered verified field refs when several raw fields were requested.", + }, ...(calculationFields.length === 2 ? { operator, @@ -329,6 +461,7 @@ export function createApiAnswerTool(grounding) { previous, resolved, direct: direct ? fields.find(({ name }) => name === direct.name) : undefined, + related, ambiguous: fields.filter(({ name }) => ambiguousNames.has(name)), tools, }; @@ -337,13 +470,45 @@ export function createApiAnswerTool(grounding) { /** @param {string} name @param {Record} action @param {ApiAnswerField[]} fields @param {any} grounding */ export function finishApiAnswer(name, action, fields, grounding) { const byRef = new Map(fields.map((field) => [field.ref, field])); + if (name === "calculate_api_rate") { + const numerator = byRef.get(String(action.left)); + const denominator = byRef.get(String(action.right)); + if ( + !numerator || + !denominator || + typeof numerator.value !== "number" || + typeof denominator.value !== "number" + ) { + throw new Error("The AI returned invalid rate fields"); + } + const denominatorDimension = dimension(denominator.type); + const divisor = denominatorDimension === "vB" + ? denominator.value + : denominatorDimension === "weight" + ? Math.ceil(denominator.value / 4) + : 0; + if (!divisor) throw new Error("Cannot calculate this rate"); + const value = dimension(numerator.type) === "sats" + ? Math.ceil(numerator.value * 1_000 / divisor) / 1_000 + : numerator.value / divisor; + const label = typeof action.label === "string" && action.label.trim() + ? action.label.trim().replaceAll("_", " ") + : "rate"; + const unit = dimension(numerator.type) === "sats" ? " sat/vB" : ""; + return renderApiAnswer( + `**${label}**: ${ + new Intl.NumberFormat("en-US", { maximumFractionDigits: 8 }).format(value) + }${unit}`, + grounding.operation, + ); + } if (name === "select_api_field") { const field = byRef.get(String(action.field)); if (!field) throw new Error("The AI selected an unknown API field"); const label = typeof action.label === "string" && action.label.trim() ? action.label.trim().replaceAll("_", " ") : field.name.replaceAll(".", " · ").replaceAll("_", " "); - const formatted = formattedValue(field.value); + const formatted = formattedValue(field.value, field.type); return renderApiAnswer( `**${label}**: ${formatted}${ typeof field.value === "number" ? displayedUnit(field.type) : "" @@ -351,6 +516,22 @@ export function finishApiAnswer(name, action, fields, grounding) { grounding.operation, ); } + if (name === "select_api_fields") { + const refs = Array.isArray(action.fields) ? action.fields : []; + const selected = refs.map((ref) => byRef.get(String(ref))).filter(Boolean); + if (selected.length < 2) { + throw new Error("The AI selected too few API fields"); + } + const output = selected.map((field) => { + const formatted = formattedValue(field.value, field.type); + return `- **${ + field.name.replaceAll(".", " · ").replaceAll("_", " ") + }**: ${formatted}${ + typeof field.value === "number" ? displayedUnit(field.type) : "" + }`; + }).join("\n"); + return renderApiAnswer(output, grounding.operation); + } if (name === "answer_api_text") { const text = typeof action.text === "string" ? action.text.trim() : ""; if (!text) throw new Error("The AI returned an empty API answer"); diff --git a/website_next/ask/tools/api/records.js b/website_next/ask/tools/api/records.js new file mode 100644 index 000000000..a57e52c5a --- /dev/null +++ b/website_next/ask/tools/api/records.js @@ -0,0 +1,80 @@ +import { normalize } from "../text.js"; + +const ORDINALS = new Map([ + ["first", 0], + ["second", 1], + ["third", 2], + ["fourth", 3], + ["fifth", 4], + ["sixth", 5], + ["seventh", 6], + ["eighth", 7], + ["ninth", 8], + ["tenth", 9], +]); + +/** @param {unknown} data */ +export function apiRows(data) { + if (Array.isArray(data)) return data; + return data && + typeof data === "object" && + Array.isArray(data.sample) + ? data.sample + : undefined; +} + +/** + * @param {unknown} data + * @param {string} question + * @param {Record} [previousArguments] + */ +export function selectApiRecord(data, question, previousArguments = {}) { + const rows = apiRows(data); + if (!rows?.length) return undefined; + + const previous = Object.entries(previousArguments); + const matching = rows.filter((row) => { + if (!row || typeof row !== "object" || Array.isArray(row)) return false; + const shared = previous.filter(([name]) => Object.hasOwn(row, name)); + return shared.length && + shared.every(([name, value]) => String(row[name]) === String(value)); + }); + if (matching.length === 1) return matching[0]; + + const words = new Set(normalize(question).split(" ")); + if (words.has("last")) return rows.at(-1); + for (const [word, index] of ORDINALS) { + if (words.has(word) && index < rows.length) return rows[index]; + } + const numbered = normalize(question).match( + /\b(\d+)\s*(?:st|nd|rd|th)\b/, + ); + const index = numbered ? Number(numbered[1]) - 1 : -1; + return index >= 0 && index < rows.length ? rows[index] : undefined; +} + +/** @param {unknown} record @param {string} responseType */ +export function recordArguments(record, responseType) { + if ( + typeof record === "string" || + typeof record === "number" || + typeof record === "boolean" + ) { + const name = normalize(responseType.replace(/\[\]$/, "")).replaceAll( + " ", + "_", + ); + return name && !["string", "number", "integer", "boolean"].includes(name) + ? { [name]: record } + : {}; + } + return record && typeof record === "object" && !Array.isArray(record) + ? Object.fromEntries( + Object.entries(record).filter(([, value]) => + typeof value === "string" || + typeof value === "number" || + typeof value === "boolean" + ), + ) + : {}; +} diff --git a/website_next/ask/tools/api/worker.js b/website_next/ask/tools/api/worker.js index 6ab3f1186..f938dda0e 100644 --- a/website_next/ask/tools/api/worker.js +++ b/website_next/ask/tools/api/worker.js @@ -84,7 +84,13 @@ function state(id, url) { if (!statePromise || url !== stateUrl) { stateUrl = url; self.postMessage({ id, status: "progress" }); - statePromise = buildState(url); + const pending = buildState(url); + statePromise = pending; + void pending.catch(() => { + if (statePromise !== pending) return; + statePromise = undefined; + stateUrl = ""; + }); } return statePromise; } @@ -93,7 +99,7 @@ function state(id, url) { function searchOne(index, query, limit) { const normalized = searchable(query) .split(" ") - .filter((word) => word.length < 32) + .filter((word) => word.length >= 3 && word.length < 32) .join(" "); if (!normalized) return []; const words = [...new Set(normalized.split(" ").filter(Boolean))]; diff --git a/website_next/ask/tools/index.js b/website_next/ask/tools/index.js index f5bb56116..400188c0d 100644 --- a/website_next/ask/tools/index.js +++ b/website_next/ask/tools/index.js @@ -4,12 +4,17 @@ import { summarizeApiAnswer, } from "./api/answer.js"; import { prewarmApiIndex, terminateApiIndex } from "./api/index.js"; +import { + apiRows, + recordArguments, + selectApiRecord, +} from "./api/records.js"; import { prewarmMetricIndex, terminateMetricIndex } from "./metrics/index.js"; import { renderEvidence } from "./render.js"; import { AskToolSession } from "./session/index.js"; import { arithmeticAnswer } from "./source/arithmetic.js"; import { AskSource } from "./source/index.js"; -import { normalize } from "./text.js"; +import { normalize, relevance } from "./text.js"; const NUMBER = /\d+(?:[.,]\d+)*/g; @@ -59,6 +64,13 @@ function removeUnsupportedQuantitySentences(answer, messages) { return kept.join(" ").trim(); } +/** @param {string} reference */ +function codeSubject(reference) { + const value = reference.trim(); + if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) return value; + return value.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*\(/)?.[1]; +} + /** * @typedef {Object} ToolOutcome * @property {boolean} done @@ -68,8 +80,8 @@ function removeUnsupportedQuantitySentences(answer, messages) { * @property {import("../storage.js").ApiContext} [apiContext] * @property {import("../storage.js").SourceContext[]} [sourceContext] * @property {import("../storage.js").KnowledgeContext} [knowledgeContext] - * @property {{ question: string, metrics: { name: string, path: string, unit?: string }[], facts: string[], excerpts: import("../storage.js").SourceContext[] }} [grounding] - * @property {{ question: string, previousFields: string[], operation: { key: string, method: string, path: string, summary: string, description: string, parameters: { name: string }[], response: { fields?: { name: string, type: string, description?: string }[] } }, arguments: Record, requestPath: string, data: unknown, truncated: boolean }} [apiGrounding] + * @property {{ question: string, title?: string, metrics: { name: string, path: string, unit?: string }[], facts: string[], contextFacts?: string[], excerpts: import("../storage.js").SourceContext[], subjects?: string[], renderFacts?: boolean, validateNumbers?: boolean }} [grounding] + * @property {{ question: string, previousFields: string[], previousArguments?: Record, previousRecords?: unknown[], operation: { key: string, method: string, path: string, summary: string, description: string, parameters: { name: string }[], response: { type: string, fields?: { name: string, type: string, description?: string }[] } }, arguments: Record, requestPath: string, data: unknown, truncated: boolean }} [apiGrounding] * * @typedef {Object} AskAnswer * @property {string} output @@ -112,8 +124,11 @@ async function answerFromEvidence(model, grounding, onStatus) { `- ${name} | ${path}${unit ? ` | unit: ${unit}` : ""}` ).join("\n")}` : "", - grounding.facts.length - ? `Verified facts:\n${grounding.facts.map((fact) => `- ${fact}`).join("\n")}` + grounding.facts.length || grounding.contextFacts?.length + ? `Verified facts:\n${ + [...grounding.facts, ...(grounding.contextFacts ?? [])] + .map((fact) => `- ${fact}`).join("\n") + }` : "", grounding.excerpts.length ? `Verified source excerpts, strongest first:\n${grounding.excerpts.map( @@ -122,32 +137,101 @@ async function answerFromEvidence(model, grounding, onStatus) { ).join("\n\n")}` : "", ].filter(Boolean).join("\n\n"); - const result = await model.generate( - [ - { - role: "system", - content: "Use only the verified evidence. Answer the exact request in at most 45 words. Metric names and units are exact. Never add a fact absent from the evidence. Do not cite, number, name, or quote source files; the renderer appends source links.", - }, - { - role: "user", - content: `${evidence}\n\nUse only the verified evidence above. Do not explain what code identifiers mean unless the evidence does.`, - }, - ], + const messages = [ + { + role: /** @type {const} */ ("system"), + content: "Use only the verified evidence. Answer the exact request in at most 45 words. When an example is requested, instantiate the evidence as a clearly hypothetical sequence with named actors or objects and concrete actions instead of summarizing it; use symbolic labels or qualitative amounts rather than unsupported quantities. Metric names and units are exact. Never add a fact absent from the evidence. Do not cite, number, name, or quote source files; the renderer appends source links.", + }, + { + role: /** @type {const} */ ("user"), + content: `${evidence}\n\nUse only the verified evidence above. Do not explain what code identifiers mean unless the evidence does.`, + }, + ]; + let result = await model.generate( + messages, () => {}, [], "none", { maxTokens: 72 }, ); - const answer = result.text.trim(); + if ( + grounding.validateNumbers && + unsupportedNumbers(result.text, messages).size + ) { + result = await model.generate( + [ + ...messages, + { + role: "assistant", + content: result.text, + }, + { + role: "user", + content: "Replace the draft with the exact requested answer using no numeric quantities absent from the verified evidence. For a hypothetical example, reconstruct it directly from the evidence: name individual entities with symbolic labels such as A and B, then state the before state, the action, and the after state. Use only entities and actions supported by the evidence, with no numerals or value calculations. Return only the replacement answer.", + }, + ], + () => {}, + [], + "none", + { maxTokens: 72 }, + ); + } + if (!result.text.trim()) { + result = await model.generate( + [ + ...messages, + { + role: "user", + content: "Return one concise direct answer to the request using only the verified evidence. If the request asks for an example, use the example already present in the evidence. Return only the answer.", + }, + ], + () => {}, + [], + "none", + { maxTokens: 72 }, + ); + } + const draft = ( + grounding.validateNumbers + ? removeUnsupportedQuantitySentences(result.text, messages) + : result.text + ).trim(); + const evidenceText = normalize([ + ...grounding.metrics.flatMap(({ name, path, unit }) => [ + name, + path, + unit, + ]), + ...grounding.facts, + ...(grounding.contextFacts ?? []), + ...grounding.excerpts.flatMap(({ path, content }) => [path, content]), + ].filter(Boolean).join(" ")); + const inline = [...draft.matchAll(/`([^`\n]+)`/g)].map((match) => match[1]); + const paths = draft.match(/(?:[A-Za-z0-9_.-]+\/){2,}[A-Za-z0-9_.-]+/g) ?? []; + const unsupportedReference = [...inline, ...paths].some((reference) => { + const normalized = normalize(reference); + return normalized && !evidenceText.includes(normalized); + }); + const answer = unsupportedReference ? "" : draft; + const answerSubjects = [...new Set( + inline.map(codeSubject).filter(Boolean), + )]; + const groundedSubject = grounding.subjects?.[0]; + const referencedSubjects = answerSubjects.length > 1 && groundedSubject + ? answerSubjects.filter((subject) => subject !== groundedSubject) + : answerSubjects; + const subjects = grounding.subjects?.length + ? grounding.subjects + : referencedSubjects; const sources = grounding.excerpts.slice(0, 2); const fallback = !answer && sources[0] - ? `The strongest verified source match is \`${sources[0].path}:${sources[0].startLine}${sources[0].endLine ? `-${sources[0].endLine}` : ""}\`.` + ? "I found related source, but not enough verified evidence for a precise answer." : ""; return { output: renderEvidence({ facts: [ answer || fallback, - ...grounding.facts, + ...(grounding.renderFacts === false ? [] : grounding.facts), ].filter(Boolean), sources, excerpts: [], @@ -155,8 +239,14 @@ async function answerFromEvidence(model, grounding, onStatus) { sourceContext: sources, knowledgeContext: answer ? { - title: grounding.metrics[0]?.name ?? grounding.question.slice(0, 160), + title: grounding.metrics[0]?.name ?? + grounding.title ?? + subjects[0] ?? + grounding.question.slice(0, 160), description: answer, + ...(subjects.length + ? { subjects } + : {}), } : undefined, }; @@ -169,7 +259,7 @@ function requestedArithmetic(question) { { action: "add", words: ["add", "plus"] }, { action: "subtract", words: ["subtract", "minus"] }, { action: "multiply", words: ["multiply", "times"] }, - { action: "divide", words: ["divide"] }, + { action: "divide", words: ["divide", "rate"] }, ].filter(({ words: candidates }) => candidates.some((word) => words.has(word)) ); @@ -190,16 +280,113 @@ function fieldPosition(question, field) { return positions.length ? Math.min(...positions) : -1; } +/** @param {{ name: string, description?: string, ownDescription?: string }} field */ +function apiFieldText(field) { + return `${field.name} ${field.ownDescription || field.description || ""}`; +} + +/** + * Match clauses to independently described schema fields. Returns nothing + * unless every clause has one clear winner. + * + * @param {string[]} clauses + * @param {import("./api/answer.js").ApiAnswerField[]} fields + * @param {string[] | undefined} previousFields + */ +function matchApiClauses(clauses, fields, previousFields) { + const parents = new Set( + (previousFields ?? []) + .map((name) => name.split(".").slice(0, -1).join(".")) + .filter(Boolean), + ); + const preferredParent = parents.size === 1 ? [...parents][0] : undefined; + const resolve = (candidates) => { + const chosen = []; + for (const clause of clauses) { + const ranked = candidates + .filter(({ ref }) => !chosen.some((field) => field.ref === ref)) + .map((field) => ({ + field, + score: relevance(clause, apiFieldText(field)) + + ( + preferredParent && + field.name.split(".").slice(0, -1).join(".") === preferredParent + ? 2 + : 0 + ), + })) + .sort((left, right) => right.score - left.score); + const [best, runnerUp] = ranked; + if ( + !best || + best.score < 2 || + best.score - (runnerUp?.score ?? 0) < 0.4 + ) { + return []; + } + chosen.push(best.field); + } + return chosen; + }; + const previousNames = new Set(previousFields ?? []); + const previous = fields.filter(({ name }) => previousNames.has(name)); + const contextual = previous.length >= clauses.length + ? resolve(previous) + : []; + return contextual.length ? contextual : resolve(fields); +} + +/** + * @param {string} question + * @param {import("./api/answer.js").ApiAnswerField[]} fields + * @param {string[] | undefined} previousFields + */ +function coordinatedApiFields(question, fields, previousFields) { + const clauses = normalize(question) + .split(" and ") + .map((clause) => clause.trim()) + .filter(Boolean); + return clauses.length < 2 + ? [] + : matchApiClauses(clauses, fields, previousFields); +} + +/** + * @param {string} question + * @param {"add" | "subtract" | "multiply" | "divide"} arithmetic + * @param {import("./api/answer.js").ApiAnswerField[]} fields + * @param {string[] | undefined} previousFields + */ +function arithmeticApiFields(question, arithmetic, fields, previousFields) { + const normalized = ` ${normalize(question)} `; + const separator = arithmetic === "subtract" + ? " from " + : arithmetic === "divide" + ? " by " + : " and "; + const parts = normalized + .split(separator) + .map((part) => part.trim()) + .filter(Boolean); + if (parts.length !== 2) return []; + const clauses = arithmetic === "subtract" + ? [parts[1], parts[0]] + : parts; + return matchApiClauses(clauses, fields, previousFields); +} + /** * @param {import("../model.js").AskModel} model * @param {NonNullable} grounding * @param {(status: string) => void} onStatus */ -async function answerFromApi(model, grounding, onStatus) { +async function answerFromApiGrounding(model, grounding, onStatus) { const apiAnswer = createApiAnswerTool(grounding); const normalizedQuestion = normalize(grounding.question); const question = ` ${normalizedQuestion} `; + const questionWords = new Set(normalizedQuestion.split(" ")); const arithmetic = requestedArithmetic(grounding.question); + const asksForSeveral = questionWords.has("and"); const parameterNames = new Set( grounding.operation.parameters.map(({ name }) => normalize(name)), ); @@ -214,9 +401,137 @@ async function answerFromApi(model, grounding, onStatus) { const name = normalize(field.name.split(".").at(-1)); return name && question.includes(` ${name} `); }); - if (arithmetic && apiAnswer.previous && directFields.length === 1) { + const coordinated = coordinatedApiFields( + grounding.question, + apiAnswer.fields, + grounding.previousFields, + ); + const calculated = arithmetic + ? arithmeticApiFields( + grounding.question, + arithmetic, + apiAnswer.fields, + grounding.previousFields, + ) + : []; + if (calculated.length > 1) { + return { + output: finishApiAnswer( + "calculate_api_fields", + { + operator: arithmetic, + operands: calculated.map(({ ref }) => ref), + label: "result", + }, + apiAnswer.fields, + grounding, + ), + fields: calculated.map(({ name }) => name), + }; + } + if (coordinated.length > 1) { + if (arithmetic) { + return { + output: finishApiAnswer( + "calculate_api_fields", + { + operator: arithmetic, + operands: coordinated.map(({ ref }) => ref), + label: "result", + }, + apiAnswer.fields, + grounding, + ), + fields: coordinated.map(({ name }) => name), + }; + } + return { + output: finishApiAnswer( + "select_api_fields", + { fields: coordinated.map(({ ref }) => ref) }, + apiAnswer.fields, + grounding, + ), + fields: coordinated.map(({ name }) => name), + }; + } + if ( + arithmetic === "divide" && + directFields.length === 1 + ) { + const numerator = directFields[0]; + const rateDenominators = apiAnswer.fields.filter((field) => + field.ref !== numerator.ref && + typeof field.value === "number" && + ["vsize", "weight"].some((type) => + normalize(field.type).includes(type) + ) + ); + if (rateDenominators.length) { + const denominator = rateDenominators.find(({ type }) => + normalize(type).includes("vsize") + ) ?? rateDenominators[0]; + return { + output: finishApiAnswer( + "calculate_api_rate", + { + left: numerator.ref, + right: denominator.ref, + label: `${ + numerator.name.split(".").at(-1)?.replaceAll("_", " ") + } rate`, + }, + apiAnswer.fields, + grounding, + ), + fields: [numerator.name, denominator.name], + }; + } + const denominators = apiAnswer.fields.filter((field) => + field.ref !== numerator.ref && + typeof field.value === "number" && + field.type !== numerator.type && + !directFields.some(({ ref }) => ref === field.ref) + ); + if (denominators.length === 1) { + const denominator = denominators[0]; + return { + output: finishApiAnswer( + "calculate_api_fields", + { + operator: "divide", + left: numerator.ref, + right: denominator.ref, + label: `${ + numerator.name.split(".").at(-1)?.replaceAll("_", " ") + } rate`, + }, + apiAnswer.fields, + grounding, + ), + fields: [numerator.name, denominator.name], + }; + } + } + const previousOperand = arithmetic && apiAnswer.previous + ? directFields.find(({ ref }) => ref !== apiAnswer.previous.ref) ?? + (() => { + const compatible = apiAnswer.fields + .filter((field) => + field.ref !== apiAnswer.previous.ref && + typeof field.value === "number" && + field.type === apiAnswer.previous.type + ) + .sort((left, right) => right.score - left.score); + return compatible[0]?.score >= 6 && + compatible[0].score - (compatible[1]?.score ?? 0) >= 0.5 + ? compatible[0] + : undefined; + })() + : undefined; + if (arithmetic && apiAnswer.previous && previousOperand) { const previous = apiAnswer.previous; - const current = directFields[0]; + const current = previousOperand; const previousPosition = fieldPosition(grounding.question, previous.name); const currentPosition = fieldPosition(grounding.question, current.name); const fromPosition = normalizedQuestion.split(" ").indexOf("from"); @@ -258,6 +573,7 @@ async function answerFromApi(model, grounding, onStatus) { } if ( !arithmetic && + !asksForSeveral && apiAnswer.resolved ) { const field = apiAnswer.resolved; @@ -293,12 +609,11 @@ async function answerFromApi(model, grounding, onStatus) { } if ( !grounding.previousFields?.length && - !mentionedResponse && - !apiAnswer.direct + !mentionedResponse ) { return summarizeApiAnswer(grounding); } - if (!arithmetic && apiAnswer.direct) { + if (!arithmetic && !asksForSeveral && apiAnswer.direct) { const field = apiAnswer.direct; return { output: finishApiAnswer( @@ -310,9 +625,20 @@ async function answerFromApi(model, grounding, onStatus) { fields: [field.name], }; } + if (!arithmetic && apiAnswer.related.length > 1) { + return { + output: finishApiAnswer( + "select_api_fields", + { fields: apiAnswer.related.map(({ ref }) => ref) }, + apiAnswer.fields, + grounding, + ), + fields: apiAnswer.related.map(({ name }) => name), + }; + } onStatus("Answering from API…"); const instruction = apiAnswer.fields.length - ? `Answer the exact newest request using only the verified API result. Call exactly one matching tool. Select a raw field only when that field itself was requested${apiAnswer.previous ? "; continue the preceding numeric answer when the request applies arithmetic to it" : ""}. When the requested concept is narrower than an aggregate field, derive it from matching component fields. Never replace requested arithmetic with a convenient field. For subtraction and division, keep operands in the request's arithmetic order: minuend or dividend first. Preserve identifiers and units. Never invent missing values.` + ? `Answer the exact newest request using only the verified API result. Call exactly one matching tool. Select one raw field only when that field itself was requested; select_many when several raw fields were requested${apiAnswer.previous ? "; continue the preceding numeric answer when the request applies arithmetic to it" : ""}. When the requested concept is narrower than an aggregate field, derive it from matching component fields. Never replace requested arithmetic with a convenient field. For subtraction and division, keep operands in the request's arithmetic order: minuend or dividend first. Preserve identifiers and units. Never invent missing values.` : "Answer the exact request using only the verified API result. Call answer_api_text exactly once. Preserve identifiers and units. Never invent missing values."; const prompt = { question: grounding.question, @@ -331,14 +657,13 @@ async function answerFromApi(model, grounding, onStatus) { description, value, })), - ...(!apiAnswer.previous ? { data: grounding.data } : {}), }; - const generateAnswer = (extra = "") => + const generateAnswer = () => model.generate( [ { role: "system", - content: extra ? `${instruction} ${extra}` : instruction, + content: instruction, }, { role: "user", content: JSON.stringify(prompt) }, ], @@ -355,6 +680,8 @@ async function answerFromApi(model, grounding, onStatus) { const actionFor = (/** @type {Record} */ arguments_) => arguments_.action === "select" ? "select_api_field" + : arguments_.action === "select_many" + ? "select_api_fields" : arguments_.action === "continue" ? "continue_api_calculation" : arguments_.action === "calculate" @@ -366,6 +693,8 @@ async function answerFromApi(model, grounding, onStatus) { if (!actionName) return summarizeApiAnswer(grounding); const selectedRefs = actionName === "select_api_field" ? [call.arguments.field] + : actionName === "select_api_fields" + ? Array.isArray(call.arguments.fields) ? call.arguments.fields : [] : actionName === "continue_api_calculation" ? [apiAnswer.previous?.ref, call.arguments.operand] : typeof call.arguments.left === "string" && @@ -392,6 +721,46 @@ async function answerFromApi(model, grounding, onStatus) { } } +/** + * @param {import("../model.js").AskModel} model + * @param {NonNullable} grounding + * @param {(status: string) => void} onStatus + */ +async function answerFromApi(model, grounding, onStatus) { + const previousRows = apiRows(grounding.previousRecords); + const currentRows = apiRows(grounding.data); + const previousRecord = selectApiRecord( + previousRows, + grounding.question, + grounding.previousArguments, + ); + const record = previousRecord ?? selectApiRecord( + currentRows, + grounding.question, + grounding.previousArguments, + ); + const contextRows = previousRecord ? previousRows : currentRows; + const answered = await answerFromApiGrounding( + model, + record ? { ...grounding, data: record } : grounding, + onStatus, + ); + return { + ...answered, + ...(record + ? { + contextArguments: { + ...grounding.arguments, + ...recordArguments(record, grounding.operation.response.type), + }, + } + : {}), + ...(contextRows?.length + ? { contextRecords: contextRows.slice(0, 4) } + : {}), + }; +} + export function createAskTools() { const source = new AskSource(); /** @type {AbortController | undefined} */ @@ -439,6 +808,7 @@ export function createAskTools() { const direct = session.directRoute(); let call = direct?.call; let action = direct?.action ?? ""; + let continueContext = false; if (!action) { onStatus("Choosing capability…"); const routeTools = session.routeTools(); @@ -450,41 +820,21 @@ export function createAskTools() { { maxTokens: 48 }, ); const selected = route.toolCalls[0]; - const sourceQuery = selected?.name === "choose_capability" && - typeof selected.arguments.sourceQuery === "string" - ? selected.arguments.sourceQuery - : ""; const selectedCapability = selected?.name === "choose_capability" && typeof selected.arguments.capability === "string" ? selected.arguments.capability : ""; - action = sourceQuery && - ( - ( - selectedCapability === "answer_general" && - session.hasSourceContext() - ) || - selectedCapability === "search_source" - ) - ? "search_source" - : selectedCapability; + const selectedContinuesContext = selected?.name === + "choose_capability" && + selected.arguments.continuesContext === true; + action = selectedCapability; + if (action === "answer_general" && selectedContinuesContext) { + action = session.contextualGeneralAction(true) ?? action; + } if (!action) { throw new Error("The AI did not choose a valid capability"); } - call = action === "call_api" && - typeof selected?.arguments.apiRef === "string" - ? { - name: action, - arguments: { ref: selected.arguments.apiRef }, - } - : action === "search_source" - ? { - name: action, - arguments: { - query: sourceQuery || question, - }, - } - : session.directCall(action); + call = session.directCall(action); } signal.throwIfAborted(); @@ -497,40 +847,49 @@ export function createAskTools() { onStatus("Understanding request…"); if (action === "answer_general") { const messages = session.actionMessages(action); - let result = await model.generate( + const tool = session.actionTool(action); + const result = await model.generate( messages, () => {}, - [], - "none", - { maxTokens: 96 }, + [tool], + { name: action }, + { maxTokens: 128 }, ); - if (unsupportedNumbers(result.text, messages).size) { - result = await model.generate( - [ - ...messages, - { - role: "assistant", - content: result.text, - }, - { - role: "user", - content: "Inspect the newest request before replacing the draft. If it requests quantities but the verified context identifies no exact metric, resource, object, or timeframe, return one concise clarification question asking what to measure. Otherwise replace the draft with a direct answer containing no unsupported observations; established static Bitcoin facts are allowed only when directly requested. Return only the replacement answer. Never mention the draft, review, evidence, context, or these instructions.", - }, - ], - () => {}, - [], - "none", - { maxTokens: 96 }, + const generalCall = result.toolCalls[0]; + continueContext = session.continuesGeneral( + generalCall?.arguments.explicitSubject, + generalCall?.arguments.topic, + ); + const contextualAction = session.contextualGeneralAction( + continueContext, + ); + if (contextualAction && contextualAction !== action) { + action = contextualAction; + call = session.directCall(action); + } else { + const rawAnswer = + typeof generalCall?.arguments.answer === "string" + ? generalCall.arguments.answer + : ""; + const answer = ( + generalCall?.arguments.quantityUse === "hypothetical" + ? rawAnswer.trim() + : removeUnsupportedQuantitySentences(rawAnswer, messages) + ) || ( + unsupportedNumbers(rawAnswer, messages).size + ? "What would you like numbers for—for example, a metric, transaction, address, or block?" + : "I do not have enough verified context to answer that without guessing." ); + if (generalCall?.name === action) { + call = { + ...generalCall, + arguments: { + ...generalCall.arguments, + answer, + }, + }; + } } - const answer = removeUnsupportedQuantitySentences( - result.text, - messages, - ) || "I do not have enough verified context to answer that without guessing."; - call = { - name: action, - arguments: { answer }, - }; } else { const result = await model.generate( session.actionMessages(action), @@ -551,7 +910,10 @@ export function createAskTools() { : ""; if (!ref) throw new Error("The AI did not select an API operation"); let arguments_ = session.apiArguments(ref); - if (!session.hasApiArguments(ref, arguments_)) { + if ( + !session.hasApiArguments(ref, arguments_) && + Object.keys(arguments_).length + ) { onStatus("Reading API arguments…"); const argumentsResult = await model.generate( session.apiArgumentMessages(ref), @@ -612,6 +974,12 @@ export function createAskTools() { apiContext: outcome.apiContext ? { ...outcome.apiContext, + ...(answered.contextArguments + ? { arguments: answered.contextArguments } + : {}), + ...(answered.contextRecords + ? { records: answered.contextRecords } + : {}), ...(answered.fields.length ? { fields: answered.fields } : {}), @@ -642,7 +1010,9 @@ export function createAskTools() { capability: action, metricPaths: outcome.metricPaths, apiContext: outcome.apiContext, - sourceContext: outcome.sourceContext, + sourceContext: action === "answer_general" && !continueContext + ? undefined + : outcome.sourceContext, knowledgeContext: outcome.knowledgeContext, chat: prepared.chat, }; diff --git a/website_next/ask/tools/learn.js b/website_next/ask/tools/learn.js index 0f9eea3cc..acfbb0e3b 100644 --- a/website_next/ask/tools/learn.js +++ b/website_next/ask/tools/learn.js @@ -22,6 +22,7 @@ function record(node, breadcrumbs) { sectionTitle: node.title, breadcrumbs, description: node.description ?? "", + ...(node.example ? { example: node.example } : {}), unit: node.chart.unit?.id, series, }; diff --git a/website_next/ask/tools/metrics/worker.js b/website_next/ask/tools/metrics/worker.js index 9468acc1b..8f1c01305 100644 --- a/website_next/ask/tools/metrics/worker.js +++ b/website_next/ask/tools/metrics/worker.js @@ -112,7 +112,13 @@ function state(id, url) { if (!statePromise || url !== stateUrl) { stateUrl = url; self.postMessage({ id, status: "progress" }); - statePromise = buildState(url); + const pending = buildState(url); + statePromise = pending; + void pending.catch(() => { + if (statePromise !== pending) return; + statePromise = undefined; + stateUrl = ""; + }); } return statePromise; } diff --git a/website_next/ask/tools/session/capabilities.js b/website_next/ask/tools/session/capabilities.js index 9a5fac3c6..78c878d5d 100644 --- a/website_next/ask/tools/session/capabilities.js +++ b/website_next/ask/tools/session/capabilities.js @@ -1,5 +1,40 @@ import { normalize, tokenAffinity } from "../text.js"; +const CHART_STYLES = ["line", "area", "stacked", "bar", "dots", "linear", "log"]; +const CHART_STYLE_ALIASES = new Map([ + ["bars", "bar"], + ["logarithm", "log"], + ["logarithmic", "log"], +]); +const ACTION_VOCABULARY = new Map([ + [ + "search_source", + [ + "called", + "caller", + "callers", + "code", + "implemented", + "implementation", + "source", + "usage", + "usages", + ], + ], +]); + +/** @param {string} question */ +export function requestedChartStyles(question) { + const requested = new Set(normalize(question).split(" ")); + return [...new Set( + [...requested] + .map((term) => + CHART_STYLES.includes(term) ? term : CHART_STYLE_ALIASES.get(term) + ) + .filter(Boolean), + )]; +} + /** @param {string} name @param {string} description @param {Record} properties @param {string[]} required */ function tool(name, description, properties = {}, required = []) { return { @@ -93,6 +128,13 @@ export function availableActions(evidence) { if (evidence.guideOptions.length || evidence.metricOptions.length) { actions.push("explain_metric_calculation"); } + if ( + evidence.guideOptions.some( + (/** @type {any} */ { guide }) => guide.example, + ) + ) { + actions.push("show_guide_example"); + } if (!evidence.metricOptions.length) actions.push("find_chart_metrics"); actions.push("search_source"); actions.push("describe_capabilities", "answer_general", "clarify"); @@ -112,8 +154,9 @@ const ROUTE_DESCRIPTIONS = { list_metric_cohorts_variants: "Choose only when the requested result is a list of available cohorts, groupings, or series variants.", select_metric_variant: "Choose when the request selects one matched cohort or series variant without requesting a value or chart yet.", explain_metric_calculation: "Choose for a Bitview metric definition grounded in matched metric evidence.", + show_guide_example: "Choose when the requested result is an example supplied by a matched Learn guide.", find_chart_metrics: "Choose when the user asks which chart metrics exist but no exact metric matched yet.", - search_source: "Choose for a question about BRK repository code, implementation, callers, or source structure.", + search_source: "Choose when the requested output explains, locates, or finds usages of BRK repository code.", call_api: "Choose for a concrete blockchain record or resource when a generated operation can accept the supplied or contextual identifier.", describe_capabilities: "Choose only when the user asks what this assistant can do.", answer_general: "Choose for ordinary Bitcoin knowledge, conversation, or writing.", @@ -135,10 +178,37 @@ function terms(value) { * @param {any} evidence * @param {string} question */ -export function directAction(evidence, question) { - const actions = availableActions(evidence).filter( - (action) => action !== "answer_general" && action !== "clarify", +export function directAction(evidence, question, allowContext = false) { + const hasCurrentExplanationEvidence = + evidence.metricOptions.some( + (/** @type {any} */ { origin }) => origin === "mentioned", + ) || + evidence.guideOptions.some( + (/** @type {any} */ { origin }) => origin === "current", + ); + const actions = availableActions(evidence).filter((action) => + action !== "answer_general" && + action !== "clarify" && + ( + action !== "explain_metric_calculation" || + hasCurrentExplanationEvidence + ) ); + if ( + evidence.context.chart && + requestedChartStyles(question).length + ) { + return "set_chart_view_scale"; + } + const queryTerms = terms(question); + const vocabularyMatches = actions.filter((action) => + (ACTION_VOCABULARY.get(action) ?? []).some((candidate) => + [...queryTerms].some((term) => + tokenAffinity(term, candidate) >= 0.68 + ) + ) + ); + if (vocabularyMatches.length === 1) return vocabularyMatches[0]; const owners = new Map(); for (const action of actions) { for (const term of terms(action)) { @@ -158,7 +228,20 @@ export function directAction(evidence, question) { } } } - if (matched.size === 1) return [...matched][0]; + if (matched.size === 1) { + const action = [...matched][0]; + if ( + action === "show_guide_example" && + !allowContext && + !evidence.guideOptions.some( + (/** @type {any} */ { guide }) => + guide.example && guide.origin === "current", + ) + ) { + return undefined; + } + return action; + } const variants = evidence.metricOptions.filter( (/** @type {any} */ { origin }) => origin === "variant", @@ -180,6 +263,19 @@ export function directAction(evidence, question) { ) { return evidence.context.capability; } + if ( + matched.size === 0 && + !evidence.context.knowledge && + evidence.guideOptions.length > 0 && + ( + evidence.guideOptions.length === 1 || + Number(evidence.guideOptions[0].guide.score ?? 0) - + Number(evidence.guideOptions[1].guide.score ?? 0) >= 5 + ) && + mentioned.length === 0 + ) { + return "explain_metric_calculation"; + } return matched.size === 0 && variants.length === 1 ? "select_metric_variant" : undefined; @@ -258,23 +354,12 @@ export function routeTools(evidence) { type: "string", enum: actions, }, - ...(evidence.apiOptions.length - ? { - apiRef: { - type: "string", - enum: evidence.apiOptions.map( - (/** @type {any} */ { ref }) => ref, - ), - description: "When capability is call_api, the one matching generated operation.", - }, - } - : {}), - sourceQuery: { - type: "string", - description: "Provide only when capability is search_source: one compact lexical code-search query using symbols and implementation terms from the request and verified context.", + continuesContext: { + type: "boolean", + description: "True only when the newest request continues the single active conversational subject rather than introducing another subject.", }, }, - ["capability"], + ["capability", "continuesContext"], ), ]; } @@ -307,7 +392,7 @@ export function actionTool(evidence, action) { maxItems: 2, items: { type: "string", - enum: ["line", "area", "stacked", "bar", "dots", "linear", "log"], + enum: CHART_STYLES, }, }, }, @@ -429,10 +514,21 @@ export function actionTool(evidence, action) { ["refs"], ); } + if (action === "show_guide_example") { + const examples = guideOptions.filter( + (/** @type {any} */ { guide }) => guide.example, + ); + return tool( + action, + "Select a matched Learn guide that supplies the requested example.", + { refs: references(examples, 1) }, + ["refs"], + ); + } if (action === "search_source") { return tool( action, - "Search the current BRK source snapshot before answering.", + "Inspect the current BRK source snapshot before answering.", { query: { type: "string", @@ -477,8 +573,21 @@ export function actionTool(evidence, action) { type: "string", description: "A clear concise answer to the exact request.", }, + topic: { + type: "string", + description: "A short standalone subject for future follow-ups. Preserve the verified previous topic when the newest request continues it; replace it when the request clearly introduces another subject.", + }, + explicitSubject: { + type: "string", + description: "A noun phrase explicitly naming a subject in the newest request. Never copy a question, command, pronoun, or request phrase. Use an empty string when the request is indirect.", + }, + quantityUse: { + type: "string", + enum: ["none", "verified", "hypothetical", "unsupported"], + description: "Classify quantities in the answer: none has no quantities; verified copies only request or context quantities; hypothetical uses clearly illustrative quantities that do not claim actual data; unsupported claims an actual quantity absent from verified context.", + }, }, - ["answer"], + ["answer", "topic", "explicitSubject", "quantityUse"], ); } if (action === "describe_capabilities") { @@ -514,18 +623,24 @@ export function apiArgumentTool(operation) { ); } -export const ROUTE_INSTRUCTION = `Choose one capability for the newest request from verified context and matches. Treat context.activeCapability as the active tool mode: continue it for an elliptical follow-up unless the newest request clearly selects a different available output. +export const ROUTE_INSTRUCTION = `Choose one capability for the newest request from verified context and matches. Catalog matches are retrieval hints, not user intent: never turn a broad topic or conversation request into a metric, API, chart, or source action unless the requested output asks for data, a value, a chart, a record, an endpoint, or code. Treat context.activeCapability as the active tool mode: continue it for an elliptical follow-up unless the newest request clearly selects a different available output. A generic action applied to an indirect reference continues the single active subject and must not use clarify. + +Set continuesContext true only when the newest request continues the single active subject, including indirect and ordinal references. Set it false when the request introduces a different subject or no active subject exists. + +Examples: with activeCapability source, "explain it" means search_source and continuesContext true. With activeCapability metric, "what is its latest value?" means read_latest_metric and true. With an active API list, "show the first one" means call_api and true. Without a matched quantitative subject, "give me some numbers" means clarify. A request naming a different subject sets continuesContext false. The requested output wins: edit an active chart with its edit/style capability; otherwise choose the matching chart, latest-value, historical-value, range, variant-list, or variant-selection capability. -Use call_api for a concrete blockchain resource or its contextual follow-up, explain_metric_calculation for a metric definition, find_chart_metrics to discover real chart series when none matched yet, describe_capabilities only for a request about the assistant itself, and answer_general for ordinary Bitcoin knowledge or conversation. -Use search_source only when the request explicitly asks about BRK repository code, source location, implementation, or callers. Never choose it merely because source matches exist. -Use clarify when essential information is missing. In particular, a requested quantitative result without a matched metric, API resource, or quantitative context needs one concise clarification instead of a qualitative answer or guessed dataset. With call_api select apiRef. With search_source provide sourceQuery. +Use call_api when the request asks to show, read, inspect, or retrieve a concrete matched API resource or its contextual follow-up. Choose it even when a required identifier is missing; the endpoint schema will ask for that identifier. Use explain_metric_calculation for a metric definition, find_chart_metrics only when the request asks about a chart, metric, or series and none matched yet, describe_capabilities only for a request about the assistant itself, and answer_general for ordinary Bitcoin knowledge or conversation. +Use show_guide_example when the user asks for an example and a matched Learn guide supplies one. +When context.concept exists, follow-up requests for a reason, explanation, example, or comparison use answer_general unless the newest request explicitly asks for a chart, value, API record, or repository source. +Use search_source only when the requested output explains, locates, or finds usages of BRK repository code. Resolve indirect or ordinal source references from context.sourceSubjects. Never choose it merely because source matches exist. +Use clarify when essential information is missing. In particular, a requested quantitative result without a matched metric, API resource, or quantitative context needs one concise clarification instead of a qualitative answer or guessed dataset. Call choose_capability exactly once.`; /** @param {string} action */ export function actionInstruction(action) { const common = `Call ${action} exactly once. Copy selected refs and explicit values exactly. Never invent evidence, values, or arguments. Put only positively requested subjects in refs and explicitly rejected subjects in excludedRefs when that field is available; never put the same ref in both. Dependent follow-ups prefer context-origin subjects; newly named subjects prefer mentioned-origin matches. Similar candidates are alternatives, not a reason to select all of them.`; if (action === "answer_general") { - return "Answer the newest request naturally in at most 60 words. Resolve elliptical follow-ups from the provided context without repeating that context first. Every claim must apply specifically to Bitcoin, not generic blockchain systems or Bitview's product, datasets, or availability. A request for examples needs at least three distinct named things, not a restatement or a list of benefits. Never invent observations, quantities, live, current, or real-time values. Do not mention routing, candidates, schemas, or internal instructions."; + return "Call answer_general exactly once. A vague request for numbers without an exact metric, resource, or timeframe must answer with one concise clarification question and quantityUse none; never draft sample observations. Otherwise directly answer the newest request in at most 30 words and never repeat or paraphrase the question as the answer. explicitSubject must be only a noun phrase explicitly naming a subject in the newest request: never copy a question, command, pronoun, or request phrase, and use an empty string for an indirect follow-up. Resolve indirect follow-ups from the preceding assistant answer. If the newest request names another subject, ignore previous context and answer that new Bitcoin subject with one high-level, well-established sentence and no implementation details. When the preceding answer is verified and the request continues it, use only its facts and direct logical consequences. Without verified context, do not invent implementation components or make claims about trust, centralization, security, failure modes, or loss of funds; say when the requested detail cannot be answered reliably. Every claim must apply specifically to Bitcoin, not generic blockchain systems or Bitview's product, datasets, or availability. Use hypothetical quantities only when the newest request explicitly asks for a hypothetical, example, or illustration. Never invent observations, live, current, or real-time values. Set topic to the short standalone subject actually answered, preserving the preceding topic for an indirect follow-up, and classify quantityUse accurately. Do not mention routing, candidates, schemas, or internal instructions."; } if (action === "read_metric_at") { return `${common} Copy the requested historical position exactly into at.`; @@ -546,8 +661,11 @@ export function actionInstruction(action) { if (action === "explain_metric_calculation") { return `${common} Select the one excerpt that directly answers the request. For a metric definition, prefer its computation or formula over UI configuration, imports, aggregation, or downstream usage. Select matching metrics when the request is about a metric.`; } + if (action === "show_guide_example") { + return `${common} Select the one matched guide whose canonical example answers the request.`; + } if (action === "search_source") { - return `${common} Produce one compact lexical code-search query, not an answer. Preserve relevant symbols and identifiers from verified source context.`; + return `${common} Produce one compact lexical code-search query, not an answer. Resolve ordinals and indirect references from previousContext.subjects, then preserve the exact relevant symbol.`; } if (action === "find_chart_metrics") { return `${common} Produce one compact catalog query containing only the metric subjects requested or referenced in verified context.`; @@ -555,5 +673,8 @@ export function actionInstruction(action) { if (action === "call_api") { return `${common} Choose one directly matching operation. Copy identifiers exactly and never invent a required argument.`; } + if (action === "clarify") { + return `${common} Ask exactly one direct question for the essential missing value.`; + } return common; } diff --git a/website_next/ask/tools/session/context.js b/website_next/ask/tools/session/context.js index 5ebea140b..72115cbde 100644 --- a/website_next/ask/tools/session/context.js +++ b/website_next/ask/tools/session/context.js @@ -59,6 +59,7 @@ export async function loadSessionContext(history, onProgress) { operation, arguments: message.apiContext?.arguments ?? {}, fields: message.apiContext?.fields ?? [], + records: message.apiContext?.records ?? [], }, } : {}), diff --git a/website_next/ask/tools/session/evidence.js b/website_next/ask/tools/session/evidence.js index e09adae07..d4c58c34b 100644 --- a/website_next/ask/tools/session/evidence.js +++ b/website_next/ask/tools/session/evidence.js @@ -121,6 +121,7 @@ function acceptsMetric(metric) { /** @param {any} operation @param {string} question @param {any} previous */ function acceptsApi(operation, question, previous) { const specificity = Number(operation.specificity ?? 0); + const reusable = Boolean(reusableArguments(operation, previous)); const required = operation.parameters.some( (/** @type {any} */ parameter) => parameter.required, ); @@ -128,11 +129,14 @@ function acceptsApi(operation, question, previous) { return Number(operation.titleMatchedTerms ?? 0) > 0 && specificity >= 2.5; } - return Number(operation.titleMatchedTerms ?? 0) > 0 && - ( - hasRequiredArguments(operation, explicitArguments(operation, question)) || - Boolean(reusableArguments(operation, previous)) - ); + const supplied = hasRequiredArguments( + operation, + explicitArguments(operation, question), + ); + return supplied || reusable + ? Number(operation.titleMatchedTerms ?? 0) > 0 || + reusable && Number(operation.matchedTerms ?? 0) > 0 + : Number(operation.titleMatchedTerms ?? 0) > 0; } /** @param {any} match */ @@ -180,7 +184,7 @@ export async function collectEvidence({ refs, onStatus, }) { - const [foundMetrics, foundApi, searchedGuides, mentionedNames] = + const [foundMetrics, foundApi, currentGuides, contextGuides, mentionedNames] = await Promise.all([ searchMetrics( [question], @@ -194,9 +198,22 @@ export async function collectEvidence({ () => onStatus("Indexing API…"), ), searchLearn(question, MAX_GUIDES), + context.knowledge?.title + ? searchLearn(context.knowledge.title, MAX_GUIDES) + : [], mentionedMetricNames(question), ]); - const foundGuides = searchedGuides.filter(acceptsGuide); + const foundGuides = unique( + [ + ...currentGuides + .filter(acceptsGuide) + .map((guide) => ({ ...guide, origin: "current" })), + ...contextGuides + .filter(acceptsGuide) + .map((guide) => ({ ...guide, origin: "context" })), + ], + (guide) => guide.breadcrumbs.join("/"), + ); const mentionedMetrics = (await Promise.all( mentionedNames.map((name) => metricByName(name)), )).filter(Boolean); @@ -269,6 +286,8 @@ export async function collectEvidence({ .sort((left, right) => Number(right.titleMatchedTerms ?? 0) - Number(left.titleMatchedTerms ?? 0) || + Number(Boolean(reusableArguments(right, context.api))) - + Number(Boolean(reusableArguments(left, context.api))) || right.response.fields.length - left.response.fields.length || Number(right.matchedTerms ?? 0) - Number(left.matchedTerms ?? 0) || @@ -317,6 +336,7 @@ export async function collectEvidence({ ), label: guide.title, guide, + origin: guide.origin, })); return { @@ -348,7 +368,13 @@ export async function collectSourceOptions({ refs, onStatus, }) { - const metric = evidence.metricOptions[0]?.metric; + const currentGuide = evidence.guideOptions.some( + (/** @type {any} */ { origin }) => origin === "current", + ); + const metricOption = evidence.metricOptions.find( + (/** @type {any} */ { origin }) => origin === "mentioned", + ) ?? (currentGuide ? undefined : evidence.metricOptions[0]); + const metric = metricOption?.metric; const metricSubject = metric ? await sourceMetricSubject(metric) : undefined; diff --git a/website_next/ask/tools/session/executor.js b/website_next/ask/tools/session/executor.js index 907bb4a39..b7cbb9c95 100644 --- a/website_next/ask/tools/session/executor.js +++ b/website_next/ask/tools/session/executor.js @@ -4,12 +4,24 @@ import { createChartArtifact } from "../chart.js"; import { resolveChartUnit } from "../chart/units.js"; import { readMetric } from "../data.js"; import { metricVariants, searchMetrics } from "../metrics/index.js"; -import { renderData } from "../render.js"; +import { renderData, renderEvidence } from "../render.js"; import { normalize } from "../text.js"; import { schemaSourceQueries } from "./evidence.js"; const CHART_VIEWS = new Set(["line", "area", "stacked", "bar", "dots"]); const CHART_SCALES = new Set(["linear", "log"]); +const SOURCE_USAGE_TERMS = new Set([ + "called", + "usage", + "usages", +]); +const SOURCE_DEFINITION_TERMS = new Set([ + "declared", + "defined", + "definition", + "implemented", + "implementation", +]); /** @param {unknown} value @param {string} name */ function requiredString(value, name) { @@ -48,19 +60,45 @@ function inlineCode(value) { /** @param {string | undefined} value */ function sourceSubject(value) { - return inlineCode(value).find((term) => + const subject = inlineCode(value).find((term) => !term.includes("/") && !term.includes("\\") ); + if (!subject) return undefined; + return subject.match(/\b([A-Za-z_][A-Za-z0-9_]*)\s*\(/)?.[1] ?? subject; +} + +/** @param {string} value */ +function querySubject(value) { + const query = value.trim(); + if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(query)) return query; + return query.match(/\b([A-Za-z_][A-Za-z0-9_]*)\s*\(/)?.[1] ?? + query.match( + /\b(?:[A-Za-z][A-Za-z0-9]*_[A-Za-z0-9_]+|[a-z][A-Za-z0-9]*[A-Z][A-Za-z0-9]*)\b/, + )?.[0]; +} + +/** @param {string} value */ +function sourceFocus(value) { + const words = new Set(normalize(value).split(" ")); + if ([...words].some((word) => SOURCE_USAGE_TERMS.has(word))) { + return /** @type {const} */ ("usage"); + } + if ([...words].some((word) => SOURCE_DEFINITION_TERMS.has(word))) { + return /** @type {const} */ ("definition"); + } + return undefined; } /** @param {string} content @param {string} field */ function computesField(content, field) { - return content.split("\n").some((line) => { - const normalized = normalize(line); - return normalized.includes(field) && - ["+=", "-=", "*=", "/=", " + ", " - ", " * ", " / "] - .some((operator) => line.includes(operator)); - }); + const code = content + .replace(/\/\*[\s\S]*?\*\//g, "") + .split("\n") + .map((line) => line.split("//")[0]) + .join("\n"); + return normalize(code).includes(field) && + ["+=", "-=", "*=", "/=", " + ", " - ", " * ", " / "] + .some((operator) => code.includes(operator)); } /** @param {any} result @param {string} query */ @@ -69,13 +107,15 @@ function rankedSchemaMatches(result, query) { const field = normalize(parts.at(-1) ?? ""); const owner = normalize(parts.slice(0, -1).join(" ")); const terms = normalize(query).split(" ").filter(Boolean); - return [...result.matches].sort((left, right) => { + return result.matches.filter((match) => + normalize(match.content).includes(field) + ).sort((left, right) => { const leftContent = normalize(left.content); const rightContent = normalize(right.content); const leftOwner = owner && leftContent.includes(owner) ? 1 : 0; const rightOwner = owner && rightContent.includes(owner) ? 1 : 0; - const leftComputes = leftOwner && computesField(left.content, field) ? 1 : 0; - const rightComputes = rightOwner && computesField(right.content, field) ? 1 : 0; + const leftComputes = computesField(left.content, field) ? 1 : 0; + const rightComputes = computesField(right.content, field) ? 1 : 0; const leftPath = terms.filter((term) => normalize(left.path).split(" ").includes(term) ).length; @@ -89,6 +129,80 @@ function rankedSchemaMatches(result, query) { }); } +/** @param {string} value */ +function regexEscape(value) { + return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +/** @param {string} content @param {string} subject */ +function containsSymbol(content, subject) { + return new RegExp(`\\b${regexEscape(subject)}\\b`).test(content); +} + +/** @param {string} content @param {string} subject */ +function declaresSymbol(content, subject) { + return new RegExp( + `\\b(?:fn|function|class|struct|enum|trait|interface)\\s+${ + regexEscape(subject) + }\\b`, + ).test(content); +} + +/** @param {{ content: string, startLine: number }} source @param {string} subject */ +function declarationLine(source, subject) { + const declaration = new RegExp( + `\\b(?:fn|function|class|struct|enum|trait|interface)\\s+${ + regexEscape(subject) + }\\b`, + ); + const index = source.content.split("\n").findIndex((line) => + declaration.test(line) + ); + return index < 0 ? source.startLine : source.startLine + index; +} + +/** @param {any[]} excerpts @param {string} subject */ +function usageCallers(excerpts, subject) { + if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(subject)) return []; + const call = new RegExp(`\\b${regexEscape(subject)}\\s*\\(`); + const declaration = /\b(?:fn|function|def)\s+([A-Za-z_][A-Za-z0-9_]*)/; + const callers = []; + for (const excerpt of excerpts) { + let owner; + let ownerType; + for (const line of excerpt.content.split("\n")) { + const implemented = line.match( + /\bimpl(?:<[^>]*>)?\s+(?:[A-Za-z_][A-Za-z0-9_:<>]*\s+for\s+)?([A-Za-z_][A-Za-z0-9_]*)/, + )?.[1]; + if (implemented) ownerType = implemented; + const declarationMatch = line.match(declaration); + const declared = declarationMatch?.[1]; + if (declared) { + const indented = /^\s/.test(line); + const module = excerpt.path.split("/").at(-1)?.replace(/\.[^.]+$/, ""); + owner = ownerType + ? `${ownerType}::${declared}` + : indented && module + ? `${module}::${declared}` + : declared; + } + if ( + !owner || + owner === subject || + declared || + !call.test(line.split("//")[0]) + ) continue; + callers.push({ name: owner, source: excerpt }); + } + } + return [...new Map( + callers.map((caller) => [ + `${caller.source.path}:${caller.name}`, + caller, + ]), + ).values()]; +} + export class CapabilityExecutor { /** * @param {Object} options @@ -115,16 +229,33 @@ export class CapabilityExecutor { /** @param {Record} arguments_ */ answerGeneral(arguments_) { const answer = requiredString(arguments_.answer, "an answer"); + const topic = requiredString(arguments_.topic, "an answer topic"); return { done: true, output: answer, + sourceContext: this.evidence.context.source, knowledgeContext: { - title: this.question.slice(0, 160), + title: topic.slice(0, 160), description: answer, }, }; } + /** @param {Record} arguments_ */ + guideExample(arguments_) { + const [{ value: guide }] = this.selected(arguments_.refs, "guide"); + const example = requiredString(guide.example, "a guide example"); + return { + done: true, + output: example, + sourceContext: this.evidence.context.source, + knowledgeContext: { + title: guide.title, + description: example, + }, + }; + } + /** @param {Record} arguments_ */ clarify(arguments_) { return { @@ -187,13 +318,30 @@ export class CapabilityExecutor { /** @param {Record} arguments_ @param {(status: string) => void} onStatus */ async searchSource(arguments_, onStatus) { const query = requiredString(arguments_.query, "a source search query"); + const requestedFocus = sourceFocus(this.question); + const structuredSubjects = + this.evidence.context.knowledge?.subjects ?? []; + const candidateSubject = querySubject(query); + const explicitCodeSubject = candidateSubject && + ( + inlineCode(this.question).includes(candidateSubject) || + this.question.includes(`${candidateSubject}(`) + ); + const exactSubject = candidateSubject && + ( + !structuredSubjects.length || + structuredSubjects.includes(candidateSubject) || + explicitCodeSubject + ) + ? candidateSubject + : undefined; const schemaQueries = schemaSourceQueries( this.question, this.evidence.apiCandidates ?? [], ); - const subject = sourceSubject( - this.evidence.context.knowledge?.description, - ); + const subject = exactSubject ?? + structuredSubjects[0] ?? + sourceSubject(this.evidence.context.knowledge?.description); const context = this.evidence.context.source; const subjectContext = subject ? context.filter((/** @type {{ content: string }} */ source) => @@ -208,7 +356,7 @@ export class CapabilityExecutor { ? `${query} ${subject}` : query; const searches = [ - { query, path: undefined, focus: undefined }, + { query, path: undefined, focus: requestedFocus }, ...schemaQueries.map((schemaQuery) => ({ query: schemaQuery, path: undefined, @@ -216,15 +364,30 @@ export class CapabilityExecutor { })), ...(contextualQuery === query ? [] - : [{ query: contextualQuery, path: undefined, focus: undefined }]), + : [{ + query: contextualQuery, + path: undefined, + focus: requestedFocus, + }]), ...(subject - ? [{ query: subject, path: undefined, focus: undefined }] + ? [{ + query: subject, + path: undefined, + focus: requestedFocus ?? /** @type {const} */ ("implementation"), + }] : []), ...paths.map((path) => ({ query: contextualQuery, path, - focus: undefined, + focus: requestedFocus, })), + ...(subject + ? [{ + query: subject, + path: undefined, + focus: /** @type {const} */ ("usage"), + }] + : []), ]; onStatus("Searching source…"); @@ -250,31 +413,73 @@ export class CapabilityExecutor { contextualIndex + (contextualQuery === query ? 0 : 1) ] : undefined; + const usageResult = subject ? results.at(-1) : undefined; const rawResult = results[0]; + const definition = requestedFocus === "definition" && exactSubject + ? subjectResult?.matches.find((/** @type {any} */ match) => + declaresSymbol(match.content, exactSubject) + ) + : undefined; + if (definition) { + const source = { + ...definition, + revision: subjectResult.revision, + }; + const line = declarationLine(source, exactSubject); + const description = `\`${exactSubject}\` is defined in \`${source.path}\` at line ${line}.`; + return { + done: true, + output: renderEvidence({ + facts: [description], + sources: [source], + excerpts: [], + }), + sourceContext: [source], + knowledgeContext: { + title: exactSubject, + description, + subjects: [exactSubject], + }, + }; + } const seeded = this.evidence.sourceOptions.map( (/** @type {{ source: any }} */ { source }) => source, ); - const excerpts = [...new Map([ - ...(paths.length ? [] : seeded), - ...schemaResults.flatMap((result, index) => - rankedSchemaMatches(result, schemaQueries[index]).slice(0, 2) - .map((/** @type {any} */ match) => ({ - ...match, - revision: result.revision, - })) - ), - ...scopedResults.flatMap((result) => - result.matches.slice(0, 1).map((/** @type {any} */ match) => ({ + const schemaMatches = schemaResults.flatMap((result, index) => + rankedSchemaMatches(result, schemaQueries[index]).slice(0, 2) + .map((/** @type {any} */ match) => ({ ...match, revision: result.revision, })) - ), + ); + if (schemaQueries.length && !schemaMatches.length) { + return { + done: true, + output: "I could not find enough verified source evidence to answer that.", + }; + } + const candidates = [...new Map([ + ...subjectContext, + ...(paths.length ? [] : seeded), + ...schemaMatches, ...(subjectResult?.matches.slice(0, 3).map( (/** @type {any} */ match) => ({ ...match, revision: subjectResult.revision, }), ) ?? []), + ...(usageResult?.matches.slice(0, 6).map( + (/** @type {any} */ match) => ({ + ...match, + revision: usageResult.revision, + }), + ) ?? []), + ...scopedResults.flatMap((result) => + result.matches.slice(0, 1).map((/** @type {any} */ match) => ({ + ...match, + revision: result.revision, + })) + ), ...(contextualResult?.matches.slice(0, 3).map( (/** @type {any} */ match) => ({ ...match, @@ -289,7 +494,51 @@ export class CapabilityExecutor { ].map((excerpt) => [ `${excerpt.revision}:${excerpt.path}:${excerpt.startLine}`, excerpt, - ])).values()].slice(0, 3); + ])).values()]; + const contextualExplanation = subjectContext.length && + requestedFocus !== "usage"; + const excerpts = (contextualExplanation + ? subjectContext + : exactSubject + ? candidates.filter(({ content }) => + containsSymbol(content, exactSubject) + ) + : candidates).slice(0, 3); + const callers = subject + ? usageCallers( + (usageResult?.matches ?? []).map((/** @type {any} */ match) => ({ + ...match, + revision: usageResult.revision, + })), + subject, + ) + : []; + if (requestedFocus === "usage" && subject && callers.length) { + const sources = [...new Map( + callers.map(({ source }) => [ + `${source.revision}:${source.path}:${source.startLine}`, + source, + ]), + ).values()].slice(0, 3); + const names = callers.map(({ name }) => name); + const description = `\`${subject}\` is called by ${ + names.map((name) => `\`${name}\``).join(", ") + }.`; + return { + done: true, + output: renderEvidence({ + facts: [description], + sources, + excerpts: [], + }), + sourceContext: sources, + knowledgeContext: { + title: subject, + description, + subjects: names.map((name) => name.split("::").at(-1) ?? name), + }, + }; + } if (!excerpts.length) { return { done: true, @@ -303,7 +552,20 @@ export class CapabilityExecutor { question: this.question, metrics: [], facts: [], + contextFacts: [ + ...(this.evidence.context.knowledge?.description + ? [this.evidence.context.knowledge.description] + : []), + ...(callers.length + ? [ + `Verified functions that call \`${subject}\`: ${ + callers.map(({ name }) => `\`${name}\``).join(", ") + }.`, + ] + : []), + ], excerpts, + ...(subject ? { subjects: [subject] } : {}), }, }; } @@ -535,7 +797,15 @@ export class CapabilityExecutor { }, apiGrounding: { question: this.question, - previousFields: this.evidence.context.api?.fields ?? [], + previousFields: + this.evidence.context.api?.operation.key === operation.key + ? this.evidence.context.api.fields ?? [] + : [], + previousArguments: this.evidence.context.api?.arguments ?? {}, + previousRecords: + this.evidence.context.api?.operation.key === operation.key + ? this.evidence.context.api.records ?? [] + : [], ...result, }, }; @@ -551,6 +821,9 @@ export class CapabilityExecutor { if (call.name === "answer_general") { return this.answerGeneral(call.arguments); } + if (call.name === "show_guide_example") { + return this.guideExample(call.arguments); + } if (call.name === "describe_capabilities") { return this.describeCapabilities(call.arguments); } diff --git a/website_next/ask/tools/session/index.js b/website_next/ask/tools/session/index.js index fefcceddc..e3039e498 100644 --- a/website_next/ask/tools/session/index.js +++ b/website_next/ask/tools/session/index.js @@ -8,12 +8,14 @@ import { capabilityMetrics, directAction, generalCapabilities, + requestedChartStyles, routeTools, } from "./capabilities.js"; import { loadSessionContext } from "./context.js"; import { collectEvidence, collectSourceOptions } from "./evidence.js"; import { CapabilityExecutor } from "./executor.js"; -import { normalize } from "../text.js"; +import { normalize, relevance, tokenAffinity } from "../text.js"; +import { recordArguments, selectApiRecord } from "../api/records.js"; import { explicitArguments, hasRequiredArguments, @@ -21,6 +23,17 @@ import { validatedArguments, } from "../api/routing.js"; +const RETRIEVAL_TERMS = new Set([ + "display", + "fetch", + "get", + "inspect", + "lookup", + "read", + "retrieve", + "show", +]); + /** @param {unknown[]} values */ function schemaTokens(values) { return new Set( @@ -35,6 +48,43 @@ function overlapCount(query, document) { return [...query].filter((token) => document.has(token)).length; } +/** @param {Set} query @param {Set} document */ +function semanticOverlapCount(query, document) { + return [...query].filter((token) => + [...document].some((candidate) => + tokenAffinity(token, candidate) >= 0.75 + ) + ).length; +} + +/** @param {string} question */ +function explicitPosition(question) { + const date = question.match( + /(? !requiresExample || guide.example) + .map((option) => ({ + option, + relevance: Math.max( + relevance(question, option.guide.title), + relevance(evidence.context.knowledge?.title, option.guide.title), + ), + })) + .sort((left, right) => right.relevance - left.relevance)[0]?.option; +} + export class AskToolSession { /** @param {import("../source/index.js").AskSource} source */ constructor(source) { @@ -49,6 +99,15 @@ export class AskToolSession { history, () => onStatus("Indexing context…"), ); + if (context.api) { + const record = selectApiRecord(context.api.records, question); + if (record !== undefined) { + context.api.arguments = { + ...context.api.arguments, + ...recordArguments(record, context.api.operation.response.type), + }; + } + } onStatus("Searching tools…"); const evidence = await collectEvidence({ question, @@ -79,19 +138,43 @@ export class AskToolSession { return routeTools(this.evidence); } - hasSourceContext() { - return Boolean(this.evidence?.context.source.length); + /** @param {unknown} subject */ + continuesKnowledge(subject) { + const title = this.evidence?.context.knowledge?.title; + if (typeof subject !== "string" || !title) return false; + return Math.max( + relevance(subject, title), + relevance(title, subject), + ) >= 10; + } + + /** @param {unknown} explicitSubject @param {unknown} topic */ + continuesGeneral(explicitSubject, topic) { + if (!this.evidence?.context.knowledge) return false; + if (this.continuesKnowledge(topic)) return true; + if (typeof explicitSubject === "string") { + return !explicitSubject.trim() || + this.continuesKnowledge(explicitSubject); + } + return false; } routeMessages() { if (!this.evidence) throw new Error("Tool session is not ready"); - const { context, metricOptions, apiOptions, sourceOptions, guideOptions } = - this.evidence; - return [ + const { context, apiOptions } = this.evidence; + const messages = [ { role: /** @type {const} */ ("system"), content: ROUTE_INSTRUCTION, }, + ]; + if (context.knowledge?.description) { + messages.push({ + role: /** @type {const} */ ("assistant"), + content: context.knowledge.description, + }); + } + messages.push( { role: /** @type {const} */ ("user"), content: JSON.stringify({ @@ -108,7 +191,6 @@ export class AskToolSession { : context.knowledge ? "general" : undefined, - previousCapability: context.capability, ...(context.chart ? { activeChart: { @@ -152,37 +234,28 @@ export class AskToolSession { source: context.source.map( (/** @type {any} */ source) => source.path, ), + sourceSubjects: context.knowledge?.subjects ?? [], + } + : {}), + ...(context.knowledge + ? { + concept: { + title: context.knowledge.title, + subjects: context.knowledge.subjects ?? [], + }, } : {}), - ...(context.knowledge ? { concept: context.knowledge } : {}), - }, - matches: { - metrics: metricOptions.map(({ label, origin }) => ({ - label, - origin, - })), - api: apiOptions.map(({ label, operation }) => ({ - label, - required: operation.parameters - .filter((/** @type {any} */ parameter) => parameter.required) - .map((/** @type {any} */ parameter) => ({ - name: parameter.name, - type: parameter.valueType || parameter.type, - description: parameter.description, - })), - returns: operation.response.fields - .slice(0, 8) - .map((/** @type {any} */ field) => field.name), - })), - source: sourceOptions.map((/** @type {any} */ { ref, source }) => ({ - ref, - path: source.path, - })), - guides: guideOptions.map(({ label }) => label), }, + apiMatches: apiOptions.map(({ label, operation }) => ({ + label, + required: operation.parameters + .filter((/** @type {any} */ parameter) => parameter.required) + .map((/** @type {any} */ parameter) => parameter.description), + })), }), }, - ]; + ); + return messages; } directRoute() { @@ -201,9 +274,29 @@ export class AskToolSession { }, }; } - const action = directAction(this.evidence, this.question); + let action = directAction(this.evidence, this.question); + if ( + action === "explain_metric_calculation" && + !this.evidence.metricOptions.some( + (/** @type {any} */ { origin }) => origin === "mentioned", + ) && + this.evidence.apiOptions.some(({ operation }) => + operation.parameters.some((parameter) => parameter.required) && + hasRequiredArguments( + operation, + explicitArguments(operation, this.question), + ) + ) + ) { + action = undefined; + } if (action === "search_source") { - if (this.evidence.context.source.length) return undefined; + if ( + this.evidence.context.source.length && + !this.evidence.context.knowledge?.subjects?.length + ) { + return undefined; + } return { action, call: { @@ -218,6 +311,60 @@ export class AskToolSession { call: this.directCall(action), }; } + const at = explicitPosition(this.question); + const metricAt = at + ? capabilityMetrics(this.evidence, "read_metric_at") + : []; + if (at && metricAt.length === 1) { + return { + action: "read_metric_at", + call: { + name: "read_metric_at", + arguments: { + refs: [metricAt[0].ref], + at, + }, + }, + }; + } + const supplied = this.evidence.apiOptions + .map(({ ref, operation }) => ({ + ref, + operation, + arguments: explicitArguments(operation, this.question), + })) + .filter(({ operation, arguments: arguments_ }) => + hasRequiredArguments(operation, arguments_) && + ( + operation.parameters.some((parameter) => parameter.required) || + Number(operation.titleMatchedTerms ?? 0) >= 2 + ) + ) + .sort(({ operation: left }, { operation: right }) => + Number(right.titleMatchedTerms ?? 0) - + Number(left.titleMatchedTerms ?? 0) || + right.response.fields.length - left.response.fields.length || + Number(right.specificity ?? 0) - Number(left.specificity ?? 0) || + Number(right.score ?? 0) - Number(left.score ?? 0) + ); + const priorArguments = new Set( + Object.values(this.evidence.context.api?.arguments ?? {}).map(String), + ); + const newResource = !this.evidence.context.api || + supplied.some(({ arguments: arguments_ }) => + Object.values(arguments_).some((value) => + !priorArguments.has(String(value)) + ) + ); + if (newResource && supplied[0]) { + return { + action: "call_api", + call: { + name: "call_api", + arguments: { ref: supplied[0].ref }, + }, + }; + } if ( this.evidence.context.metrics.length || this.evidence.context.chart @@ -225,27 +372,94 @@ export class AskToolSession { return undefined; } - const query = schemaTokens([this.question]); + const questionTokens = schemaTokens([this.question]); + if ( + !this.evidence.context.api && + !this.evidence.metricOptions.some( + (/** @type {any} */ { origin }) => origin === "mentioned", + ) && + [...questionTokens].some((token) => RETRIEVAL_TERMS.has(token)) + ) { + const records = this.evidence.apiOptions + .map(({ ref, operation }) => ({ + ref, + operation, + score: overlapCount( + questionTokens, + schemaTokens([operation.label]), + ), + })) + .filter(({ score }) => score > 0) + .sort((left, right) => + right.score - left.score || + right.operation.response.fields.length - + left.operation.response.fields.length + ); + if ( + records[0] && + records[0].score > (records[1]?.score ?? 0) + ) { + return { + action: "call_api", + call: { + name: "call_api", + arguments: { ref: records[0].ref }, + }, + }; + } + } + + const query = questionTokens; const contextKey = this.evidence.context.api?.operation.key; + const contextOperation = this.evidence.context.api?.operation; + const currentFieldNames = schemaTokens( + contextOperation?.response.fields.map( + (/** @type {any} */ field) => field.name, + ) ?? [], + ); + const detail = contextOperation?.response.type.endsWith("[]") && + semanticOverlapCount(query, currentFieldNames) === 0 + ? this.evidence.apiOptions + .filter(({ operation }) => + operation.key !== contextKey && + !operation.response.type.endsWith("[]") && + operation.response.fields.length > + contextOperation.response.fields.length && + Boolean(reusableArguments(operation, this.evidence.context.api)) + ) + .sort(({ operation: left }, { operation: right }) => + Number(right.titleMatchedTerms ?? 0) - + Number(left.titleMatchedTerms ?? 0) || + Number(right.specificity ?? 0) - Number(left.specificity ?? 0) || + right.response.fields.length - left.response.fields.length || + Number(right.score ?? 0) - Number(left.score ?? 0) + )[0] + : undefined; + if (detail) { + return { + action: "call_api", + call: { + name: "call_api", + arguments: { ref: detail.ref }, + }, + }; + } + const apiMatches = []; for (const { ref, operation } of this.evidence.apiOptions) { - const required = schemaTokens( - operation.parameters - .filter((/** @type {any} */ parameter) => parameter.required) - .flatMap((/** @type {any} */ parameter) => [ - parameter.name, - parameter.description, - ]), - ); const returned = schemaTokens( operation.response.fields.flatMap((/** @type {any} */ field) => [ field.name, field.description, ]), ); - const fieldMatches = overlapCount(query, returned); - const suppliedResource = required.size > 0 && - overlapCount(query, required) > 0; + const fieldMatches = semanticOverlapCount(query, returned); + const suppliedResource = + operation.parameters.some((parameter) => parameter.required) && + hasRequiredArguments( + operation, + explicitArguments(operation, this.question), + ); const inheritedResource = reusableArguments( operation, this.evidence.context.api, @@ -256,6 +470,7 @@ export class AskToolSession { ) { apiMatches.push({ score: fieldMatches, + context: operation.key === contextKey, action: "call_api", call: { name: "call_api", @@ -265,6 +480,8 @@ export class AskToolSession { } } apiMatches.sort((left, right) => right.score - left.score); + const activeMatch = apiMatches.find(({ context }) => context); + if (activeMatch) return activeMatch; if ( apiMatches[0] && apiMatches[0].score > (apiMatches[1]?.score ?? 0) @@ -272,21 +489,28 @@ export class AskToolSession { return apiMatches[0]; } - const supplied = this.evidence.apiOptions.find(({ operation }) => - hasRequiredArguments( - operation, - explicitArguments(operation, this.question), + if (supplied.length) { + return { + action: "call_api", + call: { + name: "call_api", + arguments: { ref: supplied[0].ref }, + }, + }; + } + if ( + this.evidence.context.knowledge && + !this.evidence.context.knowledge.subjects?.length && + ( + this.evidence.context.source.length || + this.evidence.guideOptions.some( + (/** @type {any} */ { origin }) => origin === "context", + ) ) - ); - return supplied - ? { - action: "call_api", - call: { - name: "call_api", - arguments: { ref: supplied.ref }, - }, - } - : undefined; + ) { + return { action: "answer_general" }; + } + return undefined; } /** @param {string} action @param {(status: string) => void} onStatus */ @@ -321,12 +545,27 @@ export class AskToolSession { if (!evidence) return undefined; const metrics = capabilityMetrics(evidence, action); + if (action === "search_source") { + return { + name: action, + arguments: { query: this.question }, + }; + } if (action === "describe_capabilities") { return { name: action, arguments: { capabilities: generalCapabilities() }, }; } + if (action === "show_guide_example") { + const guide = preferredGuide(evidence, this.question, true); + return guide + ? { + name: action, + arguments: { refs: [guide.ref] }, + } + : undefined; + } if ( action === "add_chart_series" || action === "remove_chart_series" || @@ -393,12 +632,7 @@ export class AskToolSession { } if (action === "set_chart_view_scale") { - const request = ` ${normalize(this.question)} `; - const styles = actionTool(evidence, action).function.parameters - .properties.styles.items.enum - .filter((/** @type {string} */ style) => - request.includes(` ${normalize(style)} `) - ); + const styles = requestedChartStyles(this.question); if (styles.length) { return { name: action, @@ -415,20 +649,34 @@ export class AskToolSession { ) : undefined; const mentioned = metricOptions.find(({ origin }) => origin === "mentioned"); - const metric = mentioned ?? contextual; + const currentGuide = guideOptions.some( + ({ origin }) => origin === "current", + ); + const metric = mentioned ?? (currentGuide ? undefined : contextual); const grounding = sourceOptions[0] ?? guideOptions[0]; - if (!metric || !grounding) return undefined; + if (!grounding) return undefined; return { name: action, arguments: { refs: [grounding.ref], - metrics: [metric.ref], + ...(metric ? { metrics: [metric.ref] } : {}), }, }; } - /** @param {string} action */ - actionMessages(action) { + contextualGeneralAction(continueContext) { + if (!continueContext || !this.evidence) return undefined; + if ( + this.evidence.context.source.length && + this.evidence.context.knowledge?.subjects?.length + ) { + return "search_source"; + } + return directAction(this.evidence, this.question, true); + } + + /** @param {string} action @param {boolean} [continueContext] */ + actionMessages(action, continueContext = true) { const scopedEvidence = this.evidence; if (!scopedEvidence) throw new Error("Tool session is not ready"); const { context, apiOptions, sourceOptions, guideOptions } = @@ -438,7 +686,6 @@ export class AskToolSession { const evidence = { request: this.question, }; - if ( action === "set_chart_view_scale" || action === "add_chart_series" || @@ -473,7 +720,10 @@ export class AskToolSession { }), ); } - if (action === "explain_metric_calculation" || action === "search_source") { + if ( + action === "explain_metric_calculation" || + action === "search_source" + ) { evidence.source = sourceOptions.map( (/** @type {any} */ { ref, source }) => ({ ref, @@ -497,7 +747,7 @@ export class AskToolSession { ); } if (context.knowledge) { - evidence.previousAnswer = context.knowledge.description; + evidence.previousContext = context.knowledge; } } if (action === "call_api") { @@ -532,6 +782,54 @@ export class AskToolSession { ) { evidence.context = context.knowledge; } + if (action === "answer_general") { + if (!context.knowledge || !continueContext) { + return [ + { + role: /** @type {const} */ ("system"), + content: actionInstruction(action), + }, + { + role: /** @type {const} */ ("user"), + content: this.question, + }, + ]; + } + const verified = Boolean( + context.source.length || + guideOptions.some(({ origin }) => origin === "context"), + ); + const verifiedGuideFacts = guideOptions + .filter(({ origin, guide }) => origin === "context" && guide.description) + .map(({ guide }) => guide.description); + return [ + { + role: /** @type {const} */ ("system"), + content: `${actionInstruction(action)} +The immediately preceding assistant message is the active conversation context. Its topic is "${ + context.knowledge.title + }" and it is ${verified ? "verified" : "not verified"}. ${ + verified + ? "Use only that answer and its direct logical consequences when the new request continues it." + : "Do not treat it as verified evidence." + }${ + verifiedGuideFacts.length + ? `\nVerified supporting facts:\n${ + verifiedGuideFacts.map((fact) => `- ${fact}`).join("\n") + }` + : "" + }`, + }, + { + role: /** @type {const} */ ("assistant"), + content: context.knowledge.description, + }, + { + role: /** @type {const} */ ("user"), + content: this.question, + }, + ]; + } return [ { role: /** @type {const} */ ("system"), diff --git a/website_next/ask/tools/source/index.js b/website_next/ask/tools/source/index.js index c5dc7893b..93dac68c7 100644 --- a/website_next/ask/tools/source/index.js +++ b/website_next/ask/tools/source/index.js @@ -15,7 +15,7 @@ export class AskSource { /** * @param {string} query * @param {string | undefined} path - * @param {"definition" | "implementation" | "availability" | undefined} focus + * @param {"definition" | "implementation" | "usage" | "availability" | undefined} focus * @param {((progress: { loaded: number, total: number }) => void) | undefined} [onProgress] */ search(query, path, focus, onProgress) { diff --git a/website_next/ask/tools/source/search.js b/website_next/ask/tools/source/search.js index a40d71c17..22e449767 100644 --- a/website_next/ask/tools/source/search.js +++ b/website_next/ask/tools/source/search.js @@ -94,6 +94,27 @@ function computationWeight(content) { return arithmetic ? 2 : code.includes(".compute") ? 1 : 0; } +/** @param {string} value */ +function regexEscape(value) { + return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +/** @param {string} line @param {string} query */ +function callsQuery(line, query) { + if (!/^[a-z_][a-z0-9_]*$/i.test(query)) { + return line.includes("(") && normalize(line).includes(query); + } + return new RegExp(`\\b${regexEscape(query)}\\s*\\(`).test(line); +} + +/** @param {string} content @param {string} query */ +function usageWeight(content, query) { + return codeOnly(content).split("\n").filter((line) => + !DECLARATION.test(line) && + callsQuery(line, query) + ).length; +} + /** @param {string} content @param {string} query */ function computesQueryDirectly(content, query) { return codeOnly(content).split("\n").some((line) => { @@ -139,8 +160,16 @@ function relatedToken(left, right) { return left === right || tokenAffinity(left, right) >= TOKEN_AFFINITY; } -/** @param {string} text @param {string[]} tokens @param {number[]} weights @param {string} phrase @param {boolean} preferComputation */ -function bestExcerptLine(text, tokens, weights, phrase, preferComputation) { +/** @param {string} text @param {string[]} tokens @param {number[]} weights @param {string} phrase @param {boolean} preferComputation @param {boolean} preferUsage @param {string} usageQuery */ +function bestExcerptLine( + text, + tokens, + weights, + phrase, + preferComputation, + preferUsage, + usageQuery, +) { const lines = text.split("\n"); const normalized = lines.map((line) => normalize(line)); const strongest = Math.max(...weights, 1); @@ -169,6 +198,12 @@ function bestExcerptLine(text, tokens, weights, phrase, preferComputation) { lines.slice(scope.start, scope.end + 1).join("\n"), ) * strongest * 4; } + if (preferUsage) { + score += usageWeight( + lines.slice(scope.start, scope.end + 1).join("\n"), + usageQuery, + ) * strongest * 4; + } return { ...scope, score }; }).sort((left, right) => right.score - left.score || left.start - right.start); const scope = rankedScopes[0]; @@ -194,6 +229,11 @@ function bestExcerptLine(text, tokens, weights, phrase, preferComputation) { : sum; }, 0); if (phrase && line.includes(phrase)) score += strongest * 3; + if ( + preferUsage && + !DECLARATION.test(lines[index]) && + callsQuery(lines[index], usageQuery) + ) score += strongest * 4; if (!score || score <= bestScore) continue; bestScore = score; bestLine = index + 1; @@ -225,9 +265,12 @@ function candidateFiles(index, token) { * @param {ReturnType} index * @param {string} rawQuery * @param {string} [pathPrefix] - * @param {"definition" | "implementation" | "availability"} [focus] + * @param {"definition" | "implementation" | "usage" | "availability"} [focus] */ export function searchSource(index, rawQuery, pathPrefix = "", focus = undefined) { + const exactUsageQuery = /^[A-Za-z_][A-Za-z0-9_]*$/.test(rawQuery.trim()) + ? rawQuery.trim() + : normalize(rawQuery); const query = normalize(rawQuery); if (!query) throw new Error("Search query is empty"); @@ -304,6 +347,8 @@ export function searchSource(index, rawQuery, pathPrefix = "", focus = undefined match.weights, query, focus === "implementation", + focus === "usage", + exactUsageQuery, ); const excerpt = excerptAt(file.text, line, declaration); const localPhraseOccurrences = phraseOccurrences( @@ -331,6 +376,9 @@ export function searchSource(index, rawQuery, pathPrefix = "", focus = undefined const formulaScore = containsDirectFormula(excerpt.content, query) ? 80 : 0; + const usageScore = focus === "usage" + ? usageWeight(excerpt.content, exactUsageQuery) * 40 + : 0; return { ...match, score: match.score + @@ -338,7 +386,8 @@ export function searchSource(index, rawQuery, pathPrefix = "", focus = undefined definitionScore + implementationScore + directImplementationScore + - formulaScore, + formulaScore + + usageScore, phraseOccurrences: localPhraseOccurrences, ...excerpt, }; diff --git a/website_next/ask/tools/source/worker.js b/website_next/ask/tools/source/worker.js index 306693b82..a6454bd9c 100644 --- a/website_next/ask/tools/source/worker.js +++ b/website_next/ask/tools/source/worker.js @@ -49,7 +49,7 @@ async function prewarm(reportProgress) { } /** - * @param {{ query: string, path?: string, focus?: "definition" | "implementation" | "availability" }} args + * @param {{ query: string, path?: string, focus?: "definition" | "implementation" | "usage" | "availability" }} args * @param {(loaded: number, total: number) => void} reportProgress */ async function search(args, reportProgress) { diff --git a/website_next/learn/data/sections/utxo-set.js b/website_next/learn/data/sections/utxo-set.js index 062c880a5..e399e8a35 100644 --- a/website_next/learn/data/sections/utxo-set.js +++ b/website_next/learn/data/sections/utxo-set.js @@ -24,7 +24,9 @@ const line = /** @type {const} */ ("line"); export const utxoSetSection = { title: "UTXO Set", description: - "The UTXO set is the collection of all spendable bitcoin outputs that exist right now. Each UTXO is a separate coin fragment created by a transaction and later consumed when it is spent. Counting UTXOs shows how Bitcoin is split into pieces, which is different from counting how much BTC those pieces contain.", + "The UTXO set is the collection of all spendable bitcoin outputs that exist right now. An ordinary transaction consumes existing UTXOs as inputs and creates new outputs; each spendable output remains a UTXO until a later transaction spends it. A wallet balance is the sum of the values in its controlled UTXOs, while UTXO count measures pieces, so the count can rise or fall independently of the BTC amount.", + example: + "Transaction A creates spendable output X, so X enters the UTXO set. Later, transaction B uses X as an input, so X leaves the set and B's new spendable outputs enter it.", chart: { title: "UTXO set", unit: units.utxos, diff --git a/website_next/llms-full.txt b/website_next/llms-full.txt index 0339b465e..a5ef8c848 100644 --- a/website_next/llms-full.txt +++ b/website_next/llms-full.txt @@ -4,7 +4,7 @@ - Version: `v0.3.6` - Base URL: https://bitview.space -- Metrics: 56973 +- Metrics: 56817 - Operations: 97 For machine-readable tool construction, use [https://bitview.space/openapi.json](https://bitview.space/openapi.json). For the complete source-derived series tree, use [https://bitview.space/api/series](https://bitview.space/api/series). diff --git a/website_next/llms.txt b/website_next/llms.txt index 8fb278efb..64c023f89 100644 --- a/website_next/llms.txt +++ b/website_next/llms.txt @@ -1,6 +1,6 @@ # Bitcoin Research Kit (BRK) -> Free, open-source Bitcoin analytics API and block explorer. 56973 on-chain time-series and 97 API operations. No authentication required. +> Free, open-source Bitcoin analytics API and block explorer. 56817 on-chain time-series and 97 API operations. No authentication required. ## API