From 98b2afb265541b68de837dadb796a93702408be7 Mon Sep 17 00:00:00 2001 From: nym21 Date: Tue, 4 Aug 2026 09:45:40 +0200 Subject: [PATCH] global: part X --- Cargo.lock | 1 + crates/brk_client/src/lib.rs | 421 +++++++++----- .../src/distribution/compute/block_loop.rs | 2 +- .../src/distribution/compute/readers.rs | 21 +- crates/brk_computer/src/distribution/vecs.rs | 2 +- crates/brk_computer/src/inputs/compute.rs | 1 - crates/brk_computer/src/inputs/import.rs | 4 +- crates/brk_computer/src/inputs/mod.rs | 3 - .../brk_computer/src/inputs/spent/compute.rs | 125 ---- .../brk_computer/src/inputs/spent/import.rs | 14 - crates/brk_computer/src/inputs/spent/mod.rs | 5 - crates/brk_computer/src/inputs/spent/vecs.rs | 9 - crates/brk_computer/src/lib.rs | 3 +- .../src/models/capital_sentiment/compute.rs | 49 +- .../src/models/capital_sentiment/vecs.rs | 2 +- .../src/models/rarity_meter/extremes.rs | 375 ++++++++++++ .../src/models/rarity_meter/mod.rs | 15 + crates/brk_computer/src/outputs/compute.rs | 2 +- .../brk_computer/src/outputs/spent/compute.rs | 14 +- .../brk_computer/src/transactions/compute.rs | 9 +- .../src/transactions/fees/compute.rs | 5 +- .../src/transactions/patterns/compute.rs | 7 +- crates/brk_indexer/Cargo.toml | 1 + crates/brk_indexer/src/constants.rs | 2 +- crates/brk_indexer/src/processor/txin/mod.rs | 32 +- .../src/processor/txin/resolver.rs | 63 +- .../brk_indexer/src/processor/txin/source.rs | 6 +- crates/brk_indexer/src/readers.rs | 6 +- crates/brk_indexer/src/vecs/inputs.rs | 123 +++- crates/brk_query/src/impl/block/txs.rs | 2 +- modules/brk-client/index.js | 546 +++++++++++------- packages/brk_client/brk_client/__init__.py | 209 ++++--- website/llms-full.txt | 2 +- website/llms.txt | 2 +- website_next/llms-full.txt | 2 +- website_next/llms.txt | 2 +- 36 files changed, 1400 insertions(+), 687 deletions(-) delete mode 100644 crates/brk_computer/src/inputs/spent/compute.rs delete mode 100644 crates/brk_computer/src/inputs/spent/import.rs delete mode 100644 crates/brk_computer/src/inputs/spent/mod.rs delete mode 100644 crates/brk_computer/src/inputs/spent/vecs.rs create mode 100644 crates/brk_computer/src/models/rarity_meter/extremes.rs diff --git a/Cargo.lock b/Cargo.lock index ce36f0441..b42187628 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -593,6 +593,7 @@ dependencies = [ "rustc-hash", "schemars", "serde", + "tempfile", "tracing", "vecdb", ] diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index b62fe41ef..54e6c60f6 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -3226,6 +3226,24 @@ impl PpmPriceRatioPattern { } } +/// Pattern struct for repeated tree structure. +pub struct RankTailThresholdPattern { + pub rank: SeriesPattern1, + pub tail: PercentPpmRatioPattern2, + pub threshold: SeriesPattern1, +} + +impl RankTailThresholdPattern { + /// Create a new pattern node with accumulated series name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + rank: SeriesPattern1::new(client.clone(), _m(&acc, "rank")), + tail: PercentPpmRatioPattern2::new(client.clone(), _m(&acc, "tail")), + threshold: SeriesPattern1::new(client.clone(), _m(&acc, "threshold")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct RatioTransferValuePattern { pub ratio: _1m1w1y24hPattern, @@ -3872,9 +3890,8 @@ pub struct SeriesTree { pub scripts: SeriesTree_Scripts, pub op_return: SeriesTree_OpReturn, pub mining: SeriesTree_Mining, - pub cointime: SeriesTree_Cointime, - pub coinflow: SeriesTree_Coinflow, - pub bedrock: SeriesTree_Bedrock, + pub frameworks: SeriesTree_Frameworks, + pub models: SeriesTree_Models, pub constants: SeriesTree_Constants, pub indexes: SeriesTree_Indexes, pub indicators: SeriesTree_Indicators, @@ -3884,6 +3901,7 @@ pub struct SeriesTree { pub price: SeriesTree_Price, pub supply: SeriesTree_Supply, pub cohorts: SeriesTree_Cohorts, + pub cointime: SeriesTree_Cointime, } impl SeriesTree { @@ -3897,9 +3915,8 @@ impl SeriesTree { scripts: SeriesTree_Scripts::new(client.clone(), format!("{base_path}_scripts")), op_return: SeriesTree_OpReturn::new(client.clone(), format!("{base_path}_op_return")), mining: SeriesTree_Mining::new(client.clone(), format!("{base_path}_mining")), - cointime: SeriesTree_Cointime::new(client.clone(), format!("{base_path}_cointime")), - coinflow: SeriesTree_Coinflow::new(client.clone(), format!("{base_path}_coinflow")), - bedrock: SeriesTree_Bedrock::new(client.clone(), format!("{base_path}_bedrock")), + frameworks: SeriesTree_Frameworks::new(client.clone(), format!("{base_path}_frameworks")), + models: SeriesTree_Models::new(client.clone(), format!("{base_path}_models")), constants: SeriesTree_Constants::new(client.clone(), format!("{base_path}_constants")), indexes: SeriesTree_Indexes::new(client.clone(), format!("{base_path}_indexes")), indicators: SeriesTree_Indicators::new(client.clone(), format!("{base_path}_indicators")), @@ -3909,6 +3926,7 @@ impl SeriesTree { price: SeriesTree_Price::new(client.clone(), format!("{base_path}_price")), supply: SeriesTree_Supply::new(client.clone(), format!("{base_path}_supply")), cohorts: SeriesTree_Cohorts::new(client.clone(), format!("{base_path}_cohorts")), + cointime: SeriesTree_Cointime::new(client.clone(), format!("{base_path}_cointime")), } } } @@ -4562,7 +4580,6 @@ impl SeriesTree_Transactions_Volume { /// Series tree node. pub struct SeriesTree_Inputs { pub raw: SeriesTree_Inputs_Raw, - pub spent: SeriesTree_Inputs_Spent, pub count: CumulativeRollingSumPattern, pub per_sec: _1m1w1y24hPattern, pub by_type: SeriesTree_Inputs_ByType, @@ -4572,7 +4589,6 @@ impl SeriesTree_Inputs { pub fn new(client: Arc, base_path: String) -> Self { Self { raw: SeriesTree_Inputs_Raw::new(client.clone(), format!("{base_path}_raw")), - spent: SeriesTree_Inputs_Spent::new(client.clone(), format!("{base_path}_spent")), count: CumulativeRollingSumPattern::new(client.clone(), "input_count".to_string()), per_sec: _1m1w1y24hPattern::new(client.clone(), "inputs_per_sec".to_string()), by_type: SeriesTree_Inputs_ByType::new(client.clone(), format!("{base_path}_by_type")), @@ -4584,6 +4600,8 @@ impl SeriesTree_Inputs { pub struct SeriesTree_Inputs_Raw { pub first_txin_index: SeriesPattern18, pub outpoint: SeriesPattern20, + pub txout_index: SeriesPattern20, + pub value: SeriesPattern20, pub tx_index: SeriesPattern20, pub output_type: SeriesPattern20, pub type_index: SeriesPattern20, @@ -4594,6 +4612,8 @@ impl SeriesTree_Inputs_Raw { Self { first_txin_index: SeriesPattern18::new(client.clone(), "first_txin_index".to_string()), outpoint: SeriesPattern20::new(client.clone(), "outpoint".to_string()), + txout_index: SeriesPattern20::new(client.clone(), "txout_index".to_string()), + value: SeriesPattern20::new(client.clone(), "value".to_string()), tx_index: SeriesPattern20::new(client.clone(), "tx_index".to_string()), output_type: SeriesPattern20::new(client.clone(), "output_type".to_string()), type_index: SeriesPattern20::new(client.clone(), "type_index".to_string()), @@ -4601,21 +4621,6 @@ impl SeriesTree_Inputs_Raw { } } -/// Series tree node. -pub struct SeriesTree_Inputs_Spent { - pub txout_index: SeriesPattern20, - pub value: SeriesPattern20, -} - -impl SeriesTree_Inputs_Spent { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - txout_index: SeriesPattern20::new(client.clone(), "txout_index".to_string()), - value: SeriesPattern20::new(client.clone(), "value".to_string()), - } - } -} - /// Series tree node. pub struct SeriesTree_Inputs_ByType { pub input_count: SeriesTree_Inputs_ByType_InputCount, @@ -5844,43 +5849,57 @@ impl SeriesTree_Mining_Hashrate_Rate_Sma { } /// Series tree node. -pub struct SeriesTree_Cointime { - pub activity: SeriesTree_Cointime_Activity, - pub age_range: SeriesTree_Cointime_AgeRange, - pub supply: SeriesTree_Cointime_Supply, - pub value: SeriesTree_Cointime_Value, - pub cap: SeriesTree_Cointime_Cap, - pub prices: SeriesTree_Cointime_Prices, - pub adjusted: SeriesTree_Cointime_Adjusted, - pub reserve_risk: SeriesTree_Cointime_ReserveRisk, +pub struct SeriesTree_Frameworks { + pub cointime: SeriesTree_Frameworks_Cointime, + pub coinflow: SeriesTree_Frameworks_Coinflow, } -impl SeriesTree_Cointime { +impl SeriesTree_Frameworks { pub fn new(client: Arc, base_path: String) -> Self { Self { - activity: SeriesTree_Cointime_Activity::new(client.clone(), format!("{base_path}_activity")), - age_range: SeriesTree_Cointime_AgeRange::new(client.clone(), format!("{base_path}_age_range")), - supply: SeriesTree_Cointime_Supply::new(client.clone(), format!("{base_path}_supply")), - value: SeriesTree_Cointime_Value::new(client.clone(), format!("{base_path}_value")), - cap: SeriesTree_Cointime_Cap::new(client.clone(), format!("{base_path}_cap")), - prices: SeriesTree_Cointime_Prices::new(client.clone(), format!("{base_path}_prices")), - adjusted: SeriesTree_Cointime_Adjusted::new(client.clone(), format!("{base_path}_adjusted")), - reserve_risk: SeriesTree_Cointime_ReserveRisk::new(client.clone(), format!("{base_path}_reserve_risk")), + cointime: SeriesTree_Frameworks_Cointime::new(client.clone(), format!("{base_path}_cointime")), + coinflow: SeriesTree_Frameworks_Coinflow::new(client.clone(), format!("{base_path}_coinflow")), } } } /// Series tree node. -pub struct SeriesTree_Cointime_Activity { +pub struct SeriesTree_Frameworks_Cointime { + pub activity: SeriesTree_Frameworks_Cointime_Activity, + pub age_range: SeriesTree_Frameworks_Cointime_AgeRange, + pub supply: SeriesTree_Frameworks_Cointime_Supply, + pub value: SeriesTree_Frameworks_Cointime_Value, + pub cap: SeriesTree_Frameworks_Cointime_Cap, + pub prices: SeriesTree_Frameworks_Cointime_Prices, + pub adjusted: SeriesTree_Frameworks_Cointime_Adjusted, + pub reserve_risk: SeriesTree_Frameworks_Cointime_ReserveRisk, +} + +impl SeriesTree_Frameworks_Cointime { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + activity: SeriesTree_Frameworks_Cointime_Activity::new(client.clone(), format!("{base_path}_activity")), + age_range: SeriesTree_Frameworks_Cointime_AgeRange::new(client.clone(), format!("{base_path}_age_range")), + supply: SeriesTree_Frameworks_Cointime_Supply::new(client.clone(), format!("{base_path}_supply")), + value: SeriesTree_Frameworks_Cointime_Value::new(client.clone(), format!("{base_path}_value")), + cap: SeriesTree_Frameworks_Cointime_Cap::new(client.clone(), format!("{base_path}_cap")), + prices: SeriesTree_Frameworks_Cointime_Prices::new(client.clone(), format!("{base_path}_prices")), + adjusted: SeriesTree_Frameworks_Cointime_Adjusted::new(client.clone(), format!("{base_path}_adjusted")), + reserve_risk: SeriesTree_Frameworks_Cointime_ReserveRisk::new(client.clone(), format!("{base_path}_reserve_risk")), + } + } +} + +/// Series tree node. +pub struct SeriesTree_Frameworks_Cointime_Activity { pub coinblocks_created: AverageBlockCumulativeSumPattern, pub coinblocks_stored: AverageBlockCumulativeSumPattern, pub liveliness: SeriesPattern1, pub vaultedness: SeriesPattern1, pub ratio: SeriesPattern1, - pub coinblocks_destroyed: AverageBlockCumulativeSumPattern, } -impl SeriesTree_Cointime_Activity { +impl SeriesTree_Frameworks_Cointime_Activity { pub fn new(client: Arc, base_path: String) -> Self { Self { coinblocks_created: AverageBlockCumulativeSumPattern::new(client.clone(), "coinblocks_created".to_string()), @@ -5888,13 +5907,12 @@ impl SeriesTree_Cointime_Activity { liveliness: SeriesPattern1::new(client.clone(), "liveliness".to_string()), vaultedness: SeriesPattern1::new(client.clone(), "vaultedness".to_string()), ratio: SeriesPattern1::new(client.clone(), "activity_to_vaultedness".to_string()), - coinblocks_destroyed: AverageBlockCumulativeSumPattern::new(client.clone(), "coinblocks_destroyed".to_string()), } } } /// Series tree node. -pub struct SeriesTree_Cointime_AgeRange { +pub struct SeriesTree_Frameworks_Cointime_AgeRange { pub under_1h: CoindaysLivelinessRatioSupplyVaultednessPattern, pub _1h_to_1d: CoindaysLivelinessRatioSupplyVaultednessPattern, pub _1d_to_1w: CoindaysLivelinessRatioSupplyVaultednessPattern, @@ -5920,7 +5938,7 @@ pub struct SeriesTree_Cointime_AgeRange { pub over_15y: CoindaysLivelinessRatioSupplyVaultednessPattern, } -impl SeriesTree_Cointime_AgeRange { +impl SeriesTree_Frameworks_Cointime_AgeRange { pub fn new(client: Arc, base_path: String) -> Self { Self { under_1h: CoindaysLivelinessRatioSupplyVaultednessPattern::new(client.clone(), "utxos_under_1h_old".to_string()), @@ -5951,22 +5969,22 @@ impl SeriesTree_Cointime_AgeRange { } /// Series tree node. -pub struct SeriesTree_Cointime_Supply { +pub struct SeriesTree_Frameworks_Cointime_Supply { pub vaulted: BtcCentsSatsUsdPattern, - pub active: SeriesTree_Cointime_Supply_Active, + pub active: SeriesTree_Frameworks_Cointime_Supply_Active, } -impl SeriesTree_Cointime_Supply { +impl SeriesTree_Frameworks_Cointime_Supply { pub fn new(client: Arc, base_path: String) -> Self { Self { vaulted: BtcCentsSatsUsdPattern::new(client.clone(), "vaulted_supply".to_string()), - active: SeriesTree_Cointime_Supply_Active::new(client.clone(), format!("{base_path}_active")), + active: SeriesTree_Frameworks_Cointime_Supply_Active::new(client.clone(), format!("{base_path}_active")), } } } /// Series tree node. -pub struct SeriesTree_Cointime_Supply_Active { +pub struct SeriesTree_Frameworks_Cointime_Supply_Active { pub btc: SeriesPattern1, pub sats: SeriesPattern1, pub usd: SeriesPattern1, @@ -5974,7 +5992,7 @@ pub struct SeriesTree_Cointime_Supply_Active { pub in_loss: SharePattern2, } -impl SeriesTree_Cointime_Supply_Active { +impl SeriesTree_Frameworks_Cointime_Supply_Active { pub fn new(client: Arc, base_path: String) -> Self { Self { btc: SeriesPattern1::new(client.clone(), "active_supply".to_string()), @@ -5987,14 +6005,14 @@ impl SeriesTree_Cointime_Supply_Active { } /// Series tree node. -pub struct SeriesTree_Cointime_Value { +pub struct SeriesTree_Frameworks_Cointime_Value { pub destroyed: AverageBlockCumulativeSumPattern, pub created: AverageBlockCumulativeSumPattern, pub stored: AverageBlockCumulativeSumPattern, pub vocdd: AverageBlockCumulativeSumPattern, } -impl SeriesTree_Cointime_Value { +impl SeriesTree_Frameworks_Cointime_Value { pub fn new(client: Arc, base_path: String) -> Self { Self { destroyed: AverageBlockCumulativeSumPattern::new(client.clone(), "cointime_value_destroyed".to_string()), @@ -6006,7 +6024,7 @@ impl SeriesTree_Cointime_Value { } /// Series tree node. -pub struct SeriesTree_Cointime_Cap { +pub struct SeriesTree_Frameworks_Cointime_Cap { pub thermo: CentsUsdPattern3, pub investor: CentsUsdPattern3, pub vaulted: CentsUsdPattern3, @@ -6015,7 +6033,7 @@ pub struct SeriesTree_Cointime_Cap { pub aviv: PpmRatioPattern2, } -impl SeriesTree_Cointime_Cap { +impl SeriesTree_Frameworks_Cointime_Cap { pub fn new(client: Arc, base_path: String) -> Self { Self { thermo: CentsUsdPattern3::new(client.clone(), "thermo_cap".to_string()), @@ -6029,14 +6047,14 @@ impl SeriesTree_Cointime_Cap { } /// Series tree node. -pub struct SeriesTree_Cointime_Prices { +pub struct SeriesTree_Frameworks_Cointime_Prices { pub vaulted: CentsPpmRatioSatsUsdPattern, pub active: CentsPpmRatioSatsUsdPattern, pub true_market_mean: CentsPpmRatioSatsUsdPattern, pub cointime: CentsPpmRatioSatsUsdPattern, } -impl SeriesTree_Cointime_Prices { +impl SeriesTree_Frameworks_Cointime_Prices { pub fn new(client: Arc, base_path: String) -> Self { Self { vaulted: CentsPpmRatioSatsUsdPattern::new(client.clone(), "vaulted_price".to_string()), @@ -6048,13 +6066,13 @@ impl SeriesTree_Cointime_Prices { } /// Series tree node. -pub struct SeriesTree_Cointime_Adjusted { +pub struct SeriesTree_Frameworks_Cointime_Adjusted { pub inflation_rate: PercentPpmRatioPattern, pub tx_velocity_native: SeriesPattern1, pub tx_velocity_fiat: SeriesPattern1, } -impl SeriesTree_Cointime_Adjusted { +impl SeriesTree_Frameworks_Cointime_Adjusted { pub fn new(client: Arc, base_path: String) -> Self { Self { inflation_rate: PercentPpmRatioPattern::new(client.clone(), "cointime_adj_inflation_rate".to_string()), @@ -6065,13 +6083,13 @@ impl SeriesTree_Cointime_Adjusted { } /// Series tree node. -pub struct SeriesTree_Cointime_ReserveRisk { +pub struct SeriesTree_Frameworks_Cointime_ReserveRisk { pub value: SeriesPattern1, pub vocdd_median_1y: SeriesPattern18, pub hodl_bank: SeriesPattern18, } -impl SeriesTree_Cointime_ReserveRisk { +impl SeriesTree_Frameworks_Cointime_ReserveRisk { pub fn new(client: Arc, base_path: String) -> Self { Self { value: SeriesPattern1::new(client.clone(), "reserve_risk".to_string()), @@ -6082,20 +6100,20 @@ impl SeriesTree_Cointime_ReserveRisk { } /// Series tree node. -pub struct SeriesTree_Coinflow { - pub age_range: SeriesTree_Coinflow_AgeRange, - pub supply: SeriesTree_Coinflow_Supply, - pub horizon: SeriesTree_Coinflow_Horizon, +pub struct SeriesTree_Frameworks_Coinflow { + pub age_range: SeriesTree_Frameworks_Coinflow_AgeRange, + pub supply: SeriesTree_Frameworks_Coinflow_Supply, + pub horizon: SeriesTree_Frameworks_Coinflow_Horizon, pub cap: CentsUsdPattern3, pub price: CentsPpmRatioSatsUsdPattern, } -impl SeriesTree_Coinflow { +impl SeriesTree_Frameworks_Coinflow { pub fn new(client: Arc, base_path: String) -> Self { Self { - age_range: SeriesTree_Coinflow_AgeRange::new(client.clone(), format!("{base_path}_age_range")), - supply: SeriesTree_Coinflow_Supply::new(client.clone(), format!("{base_path}_supply")), - horizon: SeriesTree_Coinflow_Horizon::new(client.clone(), format!("{base_path}_horizon")), + age_range: SeriesTree_Frameworks_Coinflow_AgeRange::new(client.clone(), format!("{base_path}_age_range")), + supply: SeriesTree_Frameworks_Coinflow_Supply::new(client.clone(), format!("{base_path}_supply")), + horizon: SeriesTree_Frameworks_Coinflow_Horizon::new(client.clone(), format!("{base_path}_horizon")), cap: CentsUsdPattern3::new(client.clone(), "coinflow_cap".to_string()), price: CentsPpmRatioSatsUsdPattern::new(client.clone(), "coinflow_price".to_string()), } @@ -6103,7 +6121,7 @@ impl SeriesTree_Coinflow { } /// Series tree node. -pub struct SeriesTree_Coinflow_AgeRange { +pub struct SeriesTree_Frameworks_Coinflow_AgeRange { pub under_1h: MobilitySpendingSupplyPattern, pub _1h_to_1d: MobilitySpendingSupplyPattern, pub _1d_to_1w: MobilitySpendingSupplyPattern, @@ -6129,7 +6147,7 @@ pub struct SeriesTree_Coinflow_AgeRange { pub over_15y: MobilitySpendingSupplyPattern, } -impl SeriesTree_Coinflow_AgeRange { +impl SeriesTree_Frameworks_Coinflow_AgeRange { pub fn new(client: Arc, base_path: String) -> Self { Self { under_1h: MobilitySpendingSupplyPattern::new(client.clone(), "utxos_under_1h_old".to_string()), @@ -6160,22 +6178,22 @@ impl SeriesTree_Coinflow_AgeRange { } /// Series tree node. -pub struct SeriesTree_Coinflow_Supply { - pub mobile: SeriesTree_Coinflow_Supply_Mobile, +pub struct SeriesTree_Frameworks_Coinflow_Supply { + pub mobile: SeriesTree_Frameworks_Coinflow_Supply_Mobile, pub immobile: BtcCentsSatsUsdPattern, } -impl SeriesTree_Coinflow_Supply { +impl SeriesTree_Frameworks_Coinflow_Supply { pub fn new(client: Arc, base_path: String) -> Self { Self { - mobile: SeriesTree_Coinflow_Supply_Mobile::new(client.clone(), format!("{base_path}_mobile")), + mobile: SeriesTree_Frameworks_Coinflow_Supply_Mobile::new(client.clone(), format!("{base_path}_mobile")), immobile: BtcCentsSatsUsdPattern::new(client.clone(), "immobile_supply".to_string()), } } } /// Series tree node. -pub struct SeriesTree_Coinflow_Supply_Mobile { +pub struct SeriesTree_Frameworks_Coinflow_Supply_Mobile { pub btc: SeriesPattern1, pub sats: SeriesPattern1, pub usd: SeriesPattern1, @@ -6183,7 +6201,7 @@ pub struct SeriesTree_Coinflow_Supply_Mobile { pub in_loss: SharePattern2, } -impl SeriesTree_Coinflow_Supply_Mobile { +impl SeriesTree_Frameworks_Coinflow_Supply_Mobile { pub fn new(client: Arc, base_path: String) -> Self { Self { btc: SeriesPattern1::new(client.clone(), "mobile_supply".to_string()), @@ -6196,7 +6214,7 @@ impl SeriesTree_Coinflow_Supply_Mobile { } /// Series tree node. -pub struct SeriesTree_Coinflow_Horizon { +pub struct SeriesTree_Frameworks_Coinflow_Horizon { pub _8y: SupplyPattern, pub _4y: SupplyPattern, pub _2y: SupplyPattern, @@ -6206,7 +6224,7 @@ pub struct SeriesTree_Coinflow_Horizon { pub _1m: SupplyPattern, } -impl SeriesTree_Coinflow_Horizon { +impl SeriesTree_Frameworks_Coinflow_Horizon { pub fn new(client: Arc, base_path: String) -> Self { Self { _8y: SupplyPattern::new(client.clone(), "coinflow_8y_supply_in_loss_share".to_string()), @@ -6221,7 +6239,24 @@ impl SeriesTree_Coinflow_Horizon { } /// Series tree node. -pub struct SeriesTree_Bedrock { +pub struct SeriesTree_Models { + pub bedrock: SeriesTree_Models_Bedrock, + pub capital_sentiment: SeriesTree_Models_CapitalSentiment, + pub rarity_meter: SeriesTree_Models_RarityMeter, +} + +impl SeriesTree_Models { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + bedrock: SeriesTree_Models_Bedrock::new(client.clone(), format!("{base_path}_bedrock")), + capital_sentiment: SeriesTree_Models_CapitalSentiment::new(client.clone(), format!("{base_path}_capital_sentiment")), + rarity_meter: SeriesTree_Models_RarityMeter::new(client.clone(), format!("{base_path}_rarity_meter")), + } + } +} + +/// Series tree node. +pub struct SeriesTree_Models_Bedrock { pub raw: FloorLevelLossPattern, pub cointime: FloorLevelLossPattern, pub coinflow: FloorLevelLossPattern, @@ -6234,7 +6269,7 @@ pub struct SeriesTree_Bedrock { pub coinflow_1m: FloorLevelLossPattern, } -impl SeriesTree_Bedrock { +impl SeriesTree_Models_Bedrock { pub fn new(client: Arc, base_path: String) -> Self { Self { raw: FloorLevelLossPattern::new(client.clone(), "bedrock_raw".to_string()), @@ -6251,6 +6286,138 @@ impl SeriesTree_Bedrock { } } +/// Series tree node. +pub struct SeriesTree_Models_CapitalSentiment { + pub phase: SeriesPattern1, + pub score: SeriesPattern1, +} + +impl SeriesTree_Models_CapitalSentiment { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + phase: SeriesPattern1::new(client.clone(), "capital_sentiment_phase".to_string()), + score: SeriesPattern1::new(client.clone(), "capital_sentiment_score".to_string()), + } + } +} + +/// Series tree node. +pub struct SeriesTree_Models_RarityMeter { + pub components: SeriesTree_Models_RarityMeter_Components, + pub extremes: SeriesTree_Models_RarityMeter_Extremes, + pub full: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern, + pub local: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern, + pub cycle: IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern, +} + +impl SeriesTree_Models_RarityMeter { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + components: SeriesTree_Models_RarityMeter_Components::new(client.clone(), format!("{base_path}_components")), + extremes: SeriesTree_Models_RarityMeter_Extremes::new(client.clone(), format!("{base_path}_extremes")), + 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_Models_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_Models_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()), + } + } +} + +/// Series tree node. +pub struct SeriesTree_Models_RarityMeter_Extremes { + pub coins_in_loss: SeriesTree_Models_RarityMeter_Extremes_CoinsInLoss, + pub profit_taking: RankTailThresholdPattern, + pub capitulation: RankTailThresholdPattern, + pub peak_regret: RankTailThresholdPattern, + pub seller_exhaustion: SeriesTree_Models_RarityMeter_Extremes_SellerExhaustion, +} + +impl SeriesTree_Models_RarityMeter_Extremes { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + coins_in_loss: SeriesTree_Models_RarityMeter_Extremes_CoinsInLoss::new(client.clone(), format!("{base_path}_coins_in_loss")), + profit_taking: RankTailThresholdPattern::new(client.clone(), "rarity_meter_profit_taking".to_string()), + capitulation: RankTailThresholdPattern::new(client.clone(), "rarity_meter_capitulation".to_string()), + peak_regret: RankTailThresholdPattern::new(client.clone(), "rarity_meter_peak_regret".to_string()), + seller_exhaustion: SeriesTree_Models_RarityMeter_Extremes_SellerExhaustion::new(client.clone(), format!("{base_path}_seller_exhaustion")), + } + } +} + +/// Series tree node. +pub struct SeriesTree_Models_RarityMeter_Extremes_CoinsInLoss { + pub threshold: SeriesPattern1, + pub tail: PercentPpmRatioPattern2, + pub rank: SeriesPattern1, +} + +impl SeriesTree_Models_RarityMeter_Extremes_CoinsInLoss { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + threshold: SeriesPattern1::new(client.clone(), "rarity_meter_coins_in_loss_threshold".to_string()), + tail: PercentPpmRatioPattern2::new(client.clone(), "rarity_meter_coins_in_loss_tail".to_string()), + rank: SeriesPattern1::new(client.clone(), "rarity_meter_coins_in_loss_rank".to_string()), + } + } +} + +/// Series tree node. +pub struct SeriesTree_Models_RarityMeter_Extremes_SellerExhaustion { + pub threshold: SeriesPattern1, + pub tail: PercentPpmRatioPattern2, + pub rank: SeriesPattern1, +} + +impl SeriesTree_Models_RarityMeter_Extremes_SellerExhaustion { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + threshold: SeriesPattern1::new(client.clone(), "rarity_meter_seller_exhaustion_threshold".to_string()), + tail: PercentPpmRatioPattern2::new(client.clone(), "rarity_meter_seller_exhaustion_tail".to_string()), + rank: SeriesPattern1::new(client.clone(), "rarity_meter_seller_exhaustion_rank".to_string()), + } + } +} + /// Series tree node. pub struct SeriesTree_Constants { pub _0: SeriesPattern1, @@ -6882,7 +7049,6 @@ pub struct SeriesTree_Indicators { pub dormancy: SeriesTree_Indicators_Dormancy, pub stock_to_flow: SeriesPattern1, pub seller_exhaustion: SeriesPattern1, - pub rarity_meter: SeriesTree_Indicators_RarityMeter, } impl SeriesTree_Indicators { @@ -6898,7 +7064,6 @@ impl SeriesTree_Indicators { dormancy: SeriesTree_Indicators_Dormancy::new(client.clone(), format!("{base_path}_dormancy")), stock_to_flow: SeriesPattern1::new(client.clone(), "stock_to_flow".to_string()), seller_exhaustion: SeriesPattern1::new(client.clone(), "seller_exhaustion".to_string()), - rarity_meter: SeriesTree_Indicators_RarityMeter::new(client.clone(), format!("{base_path}_rarity_meter")), } } } @@ -6918,66 +7083,6 @@ impl SeriesTree_Indicators_Dormancy { } } -/// Series tree node. -pub struct SeriesTree_Indicators_RarityMeter { - 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 { - 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()), - } - } -} - /// Series tree node. pub struct SeriesTree_Investing { pub sats_per_day: SeriesPattern18, @@ -9297,6 +9402,32 @@ impl SeriesTree_Cohorts_Addr_UnderAmount { } } +/// Series tree node. +pub struct SeriesTree_Cointime { + pub activity: SeriesTree_Cointime_Activity, +} + +impl SeriesTree_Cointime { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + activity: SeriesTree_Cointime_Activity::new(client.clone(), format!("{base_path}_activity")), + } + } +} + +/// Series tree node. +pub struct SeriesTree_Cointime_Activity { + pub coinblocks_destroyed: AverageBlockCumulativeSumPattern, +} + +impl SeriesTree_Cointime_Activity { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + coinblocks_destroyed: AverageBlockCumulativeSumPattern::new(client.clone(), "coinblocks_destroyed".to_string()), + } + } +} + /// Main BRK client with series tree and API methods. pub struct BrkClient { base: Arc, diff --git a/crates/brk_computer/src/distribution/compute/block_loop.rs b/crates/brk_computer/src/distribution/compute/block_loop.rs index 4f60ccaba..a710d6988 100644 --- a/crates/brk_computer/src/distribution/compute/block_loop.rs +++ b/crates/brk_computer/src/distribution/compute/block_loop.rs @@ -136,7 +136,7 @@ pub(crate) fn process_blocks( // Create reusable iterators and buffers for per-block reads let mut txout_iters = TxOutReaders::new(indexer); - let mut txin_iters = TxInReaders::new(indexer, inputs, tx_index_to_height); + let mut txin_iters = TxInReaders::new(indexer, tx_index_to_height); let mut txout_to_tx_index_buf = IndexToTxIndexBuf::new(); let mut txin_to_tx_index_buf = IndexToTxIndexBuf::new(); diff --git a/crates/brk_computer/src/distribution/compute/readers.rs b/crates/brk_computer/src/distribution/compute/readers.rs index 3ee467dd0..35f95fed6 100644 --- a/crates/brk_computer/src/distribution/compute/readers.rs +++ b/crates/brk_computer/src/distribution/compute/readers.rs @@ -6,12 +6,9 @@ use brk_types::{ }; use vecdb::{BytesVecReader, ReadableVec, VecIndex}; -use crate::{ - distribution::{ - RangeMap, - addr::{AddrsDataVecs, AnyAddrIndexesVecs}, - }, - inputs, +use crate::distribution::{ + RangeMap, + addr::{AddrsDataVecs, AnyAddrIndexesVecs}, }; /// Output data collected from separate vecs. @@ -84,7 +81,6 @@ impl<'a> TxOutReaders<'a> { /// Readers for txin vectors. Reuses all buffers across blocks. pub struct TxInReaders<'a> { indexer: &'a Indexer, - txins: &'a inputs::Vecs, tx_index_to_height: &'a mut RangeMap, outpoints_buf: Vec, values_buf: Vec, @@ -96,12 +92,10 @@ pub struct TxInReaders<'a> { impl<'a> TxInReaders<'a> { pub(crate) fn new( indexer: &'a Indexer, - txins: &'a inputs::Vecs, tx_index_to_height: &'a mut RangeMap, ) -> Self { Self { indexer, - txins, tx_index_to_height, outpoints_buf: Vec::new(), values_buf: Vec::new(), @@ -119,10 +113,11 @@ impl<'a> TxInReaders<'a> { current_height: Height, ) -> (&[Sats], &[Height], &[OutputType], &[TypeIndex]) { let end = first_txin_index + input_count; - self.txins - .spent - .value - .collect_range_into_at(first_txin_index, end, &mut self.values_buf); + self.indexer.vecs.inputs.value.collect_range_into_at( + first_txin_index, + end, + &mut self.values_buf, + ); self.indexer.vecs.inputs.outpoint.collect_range_into_at( first_txin_index, end, diff --git a/crates/brk_computer/src/distribution/vecs.rs b/crates/brk_computer/src/distribution/vecs.rs index 5264636d6..4247efdea 100644 --- a/crates/brk_computer/src/distribution/vecs.rs +++ b/crates/brk_computer/src/distribution/vecs.rs @@ -365,7 +365,7 @@ impl Vecs { indexer.vecs.outputs.value.version(), indexer.vecs.outputs.output_type.version(), indexer.vecs.outputs.type_index.version(), - inputs.spent.value.version(), + indexer.vecs.inputs.value.version(), indexer.vecs.inputs.outpoint.version(), indexer.vecs.inputs.output_type.version(), indexer.vecs.inputs.type_index.version(), diff --git a/crates/brk_computer/src/inputs/compute.rs b/crates/brk_computer/src/inputs/compute.rs index db51c0c48..69f7238bf 100644 --- a/crates/brk_computer/src/inputs/compute.rs +++ b/crates/brk_computer/src/inputs/compute.rs @@ -14,7 +14,6 @@ impl Vecs { ) -> Result<()> { self.db.sync_bg_tasks()?; - self.spent.compute(indexer, exit)?; self.count.compute(indexer, blocks, exit)?; self.by_type.compute(indexer, exit)?; diff --git a/crates/brk_computer/src/inputs/import.rs b/crates/brk_computer/src/inputs/import.rs index 7794d5c8f..d5a357ef5 100644 --- a/crates/brk_computer/src/inputs/import.rs +++ b/crates/brk_computer/src/inputs/import.rs @@ -11,7 +11,7 @@ use crate::{ }, }; -use super::{ByTypeVecs, CountVecs, SpentVecs, Vecs}; +use super::{ByTypeVecs, CountVecs, Vecs}; impl Vecs { pub(crate) fn forced_import( @@ -23,14 +23,12 @@ impl Vecs { let db = open_db(parent_path, super::DB_NAME, 20_000_000)?; let version = parent_version; - let spent = SpentVecs::forced_import(&db, version)?; let count = CountVecs::forced_import(&db, version, indexes, cached_starts)?; let per_sec = LazyPerSecondWindows::new("inputs_per_sec", version, &count.rolling.sum); let by_type = ByTypeVecs::forced_import(&db, version, indexes, cached_starts)?; let this = Self { db, - spent, count, per_sec, by_type, diff --git a/crates/brk_computer/src/inputs/mod.rs b/crates/brk_computer/src/inputs/mod.rs index 7564053e2..cd449b89d 100644 --- a/crates/brk_computer/src/inputs/mod.rs +++ b/crates/brk_computer/src/inputs/mod.rs @@ -1,6 +1,5 @@ pub mod by_type; pub mod count; -pub mod spent; mod compute; mod import; @@ -12,7 +11,6 @@ use crate::internal::LazyPerSecondWindows; pub use by_type::Vecs as ByTypeVecs; pub use count::Vecs as CountVecs; -pub use spent::Vecs as SpentVecs; pub const DB_NAME: &str = "inputs"; @@ -21,7 +19,6 @@ pub struct Vecs { #[traversable(skip)] pub(crate) db: Database, - pub spent: SpentVecs, pub count: CountVecs, pub per_sec: LazyPerSecondWindows, pub by_type: ByTypeVecs, diff --git a/crates/brk_computer/src/inputs/spent/compute.rs b/crates/brk_computer/src/inputs/spent/compute.rs deleted file mode 100644 index 32d566884..000000000 --- a/crates/brk_computer/src/inputs/spent/compute.rs +++ /dev/null @@ -1,125 +0,0 @@ -use brk_error::Result; -use brk_indexer::Indexer; -use brk_types::{Sats, TxIndex, TxOutIndex, Vout}; -use rayon::prelude::*; -use tracing::info; -use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec}; - -use super::Vecs; - -const BATCH_SIZE: usize = 2 * 1024 * 1024 * 1024 / size_of::(); - -impl Vecs { - pub(crate) fn compute(&mut self, indexer: &Indexer, exit: &Exit) -> Result<()> { - let starting_lengths = indexer.safe_lengths(); - // Validate computed versions against dependencies - let dep_version = indexer.vecs.inputs.outpoint.version() - + indexer.vecs.transactions.first_txout_index.version() - + indexer.vecs.outputs.value.version(); - self.txout_index - .validate_computed_version_or_reset(dep_version)?; - self.value.validate_computed_version_or_reset(dep_version)?; - - let target = indexer.vecs.inputs.outpoint.len(); - if target == 0 { - return Ok(()); - } - - let len1 = self.txout_index.len(); - let len2 = self.value.len(); - let starting = starting_lengths.txin_index.to_usize(); - let min = len1.min(len2).min(starting); - - if min >= target { - return Ok(()); - } - - let first_txout_index_reader = indexer.vecs.transactions.first_txout_index.reader(); - let value_reader = indexer.vecs.outputs.value.reader(); - let actual_total = target - min; - let mut entries: Vec = Vec::with_capacity(actual_total.min(BATCH_SIZE)); - // Pre-allocate output buffers for scatter-write pattern - let mut out_txout_index: Vec = Vec::new(); - let mut out_value: Vec = Vec::new(); - - let mut batch_start = min; - while batch_start < target { - let batch_end = (batch_start + BATCH_SIZE).min(target); - let batch_len = batch_end - batch_start; - - entries.clear(); - let mut j = 0usize; - indexer - .vecs - .inputs - .outpoint - .for_each_range_at(batch_start, batch_end, |outpoint| { - entries.push(Entry { - original_idx: j, - tx_index: outpoint.tx_index(), - vout: outpoint.vout(), - txout_index: TxOutIndex::COINBASE, - value: Sats::MAX, - }); - j += 1; - }); - - // Sort 1: by tx_index (group by transaction for sequential first_txout_index reads) - entries.par_sort_unstable_by_key(|e| e.tx_index); - for entry in &mut entries { - if entry.tx_index.is_coinbase() { - break; - } - entry.txout_index = first_txout_index_reader.get(entry.tx_index) + entry.vout; - } - - // Sort 2: by txout_index (sequential value reads) - entries.par_sort_unstable_by_key(|e| e.txout_index); - for entry in &mut entries { - if entry.txout_index.is_coinbase() { - break; - } - entry.value = value_reader.get(entry.txout_index); - } - - // Scatter-write to output buffers using original_idx (avoids Sort 3) - out_txout_index.clear(); - out_txout_index.resize(batch_len, TxOutIndex::COINBASE); - out_value.clear(); - out_value.resize(batch_len, Sats::MAX); - - for entry in &entries { - out_txout_index[entry.original_idx] = entry.txout_index; - out_value[entry.original_idx] = entry.value; - } - - self.txout_index.truncate_if_needed_at(batch_start)?; - self.value.truncate_if_needed_at(batch_start)?; - for i in 0..batch_len { - self.txout_index.push(out_txout_index[i]); - self.value.push(out_value[i]); - } - - let _lock = exit.lock(); - let (r1, r2) = rayon::join(|| self.txout_index.write(), || self.value.write()); - r1?; - r2?; - - if batch_end < target { - info!("TxIns: {:.2}%", batch_end as f64 / target as f64 * 100.0); - } - - batch_start = batch_end; - } - - Ok(()) - } -} - -struct Entry { - original_idx: usize, - tx_index: TxIndex, - vout: Vout, - txout_index: TxOutIndex, - value: Sats, -} diff --git a/crates/brk_computer/src/inputs/spent/import.rs b/crates/brk_computer/src/inputs/spent/import.rs deleted file mode 100644 index ef2c0d061..000000000 --- a/crates/brk_computer/src/inputs/spent/import.rs +++ /dev/null @@ -1,14 +0,0 @@ -use brk_error::Result; -use brk_types::Version; -use vecdb::{Database, ImportableVec, PcoVec}; - -use super::Vecs; - -impl Vecs { - pub(crate) fn forced_import(db: &Database, version: Version) -> Result { - Ok(Self { - txout_index: PcoVec::forced_import(db, "txout_index", version)?, - value: PcoVec::forced_import(db, "value", version)?, - }) - } -} diff --git a/crates/brk_computer/src/inputs/spent/mod.rs b/crates/brk_computer/src/inputs/spent/mod.rs deleted file mode 100644 index 1136f9ebd..000000000 --- a/crates/brk_computer/src/inputs/spent/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod compute; -mod import; -mod vecs; - -pub use vecs::Vecs; diff --git a/crates/brk_computer/src/inputs/spent/vecs.rs b/crates/brk_computer/src/inputs/spent/vecs.rs deleted file mode 100644 index 8a7636bb3..000000000 --- a/crates/brk_computer/src/inputs/spent/vecs.rs +++ /dev/null @@ -1,9 +0,0 @@ -use brk_traversable::Traversable; -use brk_types::{Sats, TxInIndex, TxOutIndex}; -use vecdb::{PcoVec, Rw, StorageMode}; - -#[derive(Traversable)] -pub struct Vecs { - pub txout_index: M::Stored>, - pub value: M::Stored>, -} diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index ddf05f8c9..1d6186eb0 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -50,7 +50,7 @@ pub struct Computer { pub op_return: Box>, } -const VERSION: Version = Version::new(7); +const VERSION: Version = Version::new(8); impl Computer { pub fn forced_import(outputs_path: &Path, indexer: &Indexer) -> Result { @@ -363,7 +363,6 @@ impl Computer { indexer, &self.indexes, &self.blocks, - &self.inputs, &self.price, exit, ) diff --git a/crates/brk_computer/src/models/capital_sentiment/compute.rs b/crates/brk_computer/src/models/capital_sentiment/compute.rs index d79dbf5cf..463ae57aa 100644 --- a/crates/brk_computer/src/models/capital_sentiment/compute.rs +++ b/crates/brk_computer/src/models/capital_sentiment/compute.rs @@ -118,7 +118,7 @@ fn classify_phase( let above_sth = price >= sth; let above_lth = price >= lth; let above_sma = price >= sma; - let bull_structure = sth > lth; + let bull_structure = sth >= lth; let above_slow_refs = above_all && above_lth; let references_above_price = [all, sth, lth, sma] .into_iter() @@ -166,6 +166,8 @@ fn classify_phase( #[cfg(test)] mod tests { + use std::{cmp::Reverse, collections::BTreeSet}; + use super::*; fn cents(value: u64) -> Cents { @@ -177,35 +179,56 @@ mod tests { } #[test] - fn classifies_all_ten_phases() { + fn classifies_all_ten_phases_across_all_eight_reference_orders() { use CapitalSentimentPhase as Phase; let cases = [ - ((100, 70, 80, 60, 50), Phase::RagingBull), - ((100, 50, 70, 40, 60), Phase::Bull), - ((50, 40, 30, 60, 70), Phase::CautiousBull), - ((50, 70, 60, 80, 40), Phase::HopefulBull), - ((40, 20, 30, 50, 10), Phase::EarlyBull), - ((100, 50, 110, 60, 70), Phase::WeakBull), - ((30, 40, 50, 20, 10), Phase::Limbo), - ((10, 20, 30, 40, 50), Phase::DeepBear), - ((10, 40, 30, 20, 50), Phase::Bear), - ((30, 40, 50, 20, 60), Phase::EarlyBear), + ((100, 70, 80, 50, 60), Phase::RagingBull), + ((100, 70, 80, 60, 90), Phase::Bull), + ((90, 70, 60, 80, 100), Phase::CautiousBull), + ((40, 80, 60, 100, 20), Phase::HopefulBull), + ((90, 70, 60, 100, 80), Phase::EarlyBull), + ((90, 70, 100, 60, 80), Phase::WeakBull), + ((70, 80, 100, 60, 40), Phase::Limbo), + ((40, 80, 60, 100, 70), Phase::DeepBear), + ((40, 80, 100, 60, 50), Phase::Bear), + ((60, 80, 100, 50, 70), Phase::EarlyBear), ]; + let mut reference_orders = BTreeSet::new(); + for ((price, all, sth, lth, sma), expected) in cases { + assert!( + (sth > all && all > lth) || (lth > all && all > sth), + "All capitalized price must be between STH and LTH" + ); + + let mut references = [("SMA", sma), ("STH", sth), ("All", all), ("LTH", lth)]; + references.sort_unstable_by_key(|(_, value)| Reverse(*value)); + reference_orders.insert(references.map(|(name, _)| name)); + assert_eq!(classify(price, all, sth, lth, sma), expected); } + + assert_eq!(reference_orders.len(), 8); } #[test] fn sma_confirms_the_capitalized_price_structure() { use CapitalSentimentPhase as Phase; - assert_eq!(classify(100, 70, 80, 60, 80), Phase::Bull); + assert_eq!(classify(100, 70, 80, 60, 90), Phase::Bull); assert_eq!(classify(100, 70, 80, 60, 50), Phase::RagingBull); } + #[test] + fn capitalized_crossover_uses_sth_led_tie_break() { + assert_eq!( + classify(70, 50, 50, 50, 100), + CapitalSentimentPhase::EarlyBear + ); + } + #[test] fn missing_reference_has_no_phase() { assert_eq!( diff --git a/crates/brk_computer/src/models/capital_sentiment/vecs.rs b/crates/brk_computer/src/models/capital_sentiment/vecs.rs index 2a19bcf23..454300178 100644 --- a/crates/brk_computer/src/models/capital_sentiment/vecs.rs +++ b/crates/brk_computer/src/models/capital_sentiment/vecs.rs @@ -8,7 +8,7 @@ use crate::internal::{LazyPerBlock, PerBlock}; pub struct Vecs { /// Compact, per-block source of truth. #[traversable(hidden)] - pub(crate) phase_code: PerBlock, + pub phase_code: PerBlock, pub phase: LazyPerBlock, StoredI8>, pub score: LazyPerBlock, Option>, diff --git a/crates/brk_computer/src/models/rarity_meter/extremes.rs b/crates/brk_computer/src/models/rarity_meter/extremes.rs new file mode 100644 index 000000000..f9b403b13 --- /dev/null +++ b/crates/brk_computer/src/models/rarity_meter/extremes.rs @@ -0,0 +1,375 @@ +use std::collections::VecDeque; + +use brk_error::Result; +use brk_indexer::Indexer; +use brk_traversable::Traversable; +use brk_types::{Bitcoin, Dollars, Height, PartsPerMillion32, StoredF32, StoredI8, Version}; +use schemars::JsonSchema; +use vecdb::{ + AnyStoredVec, AnyVec, Database, Exit, ReadableVec, Rw, StorageMode, VecIndex, WritableVec, +}; + +use crate::{ + indexes, + internal::{ + NumericValue, PerBlock, PercentPerBlock, algo::FenwickTree, + db_utils::validate_any_computed_version_or_reset, + }, +}; + +const VERSION: Version = Version::new(4); +const MIN_HISTORY_BLOCKS: usize = 210_000; +const WRITE_INTERVAL: usize = 10_000; +const BANDS: [(f64, i8); 3] = [(0.00025, 3), (0.0005, 2), (0.001, 1)]; + +#[derive(Clone, Copy)] +struct Config { + upper_tail: bool, + rolling: bool, + positive_only: bool, +} + +const REALIZED: Config = Config { + upper_tail: true, + rolling: false, + positive_only: false, +}; + +const COINS_IN_LOSS: Config = Config { + upper_tail: true, + rolling: false, + positive_only: true, +}; + +const SELLER_EXHAUSTION: Config = Config { + upper_tail: false, + rolling: true, + positive_only: true, +}; + +/// Historical extremeness of one metric. +/// +/// `tail` is the current observation's top- or bottom-tail share, `threshold` +/// is the highlight boundary, and `rank` is 0 through 3. +#[derive(Traversable)] +pub struct Extreme +where + T: NumericValue + JsonSchema, +{ + pub threshold: PerBlock, + pub tail: PercentPerBlock, + pub rank: PerBlock, +} + +impl Extreme +where + T: NumericValue + JsonSchema, +{ + fn forced_import( + db: &Database, + name: &str, + version: Version, + indexes: &indexes::Vecs, + ) -> Result { + Ok(Self { + threshold: PerBlock::forced_import(db, &format!("{name}_threshold"), version, indexes)?, + tail: PercentPerBlock::forced_import(db, &format!("{name}_tail"), version, indexes)?, + rank: PerBlock::forced_import(db, &format!("{name}_rank"), version, indexes)?, + }) + } + + fn compute( + &mut self, + indexer: &Indexer, + source: &impl ReadableVec, + config: Config, + exit: &Exit, + ) -> Result<()> { + let dependency_version = source.version(); + for output in [ + &mut self.threshold.height as &mut dyn AnyStoredVec, + &mut self.tail.ppm.height, + &mut self.rank.height, + ] { + validate_any_computed_version_or_reset(output, dependency_version)?; + } + + let source_end = source.len(); + let start = [ + self.threshold.height.len(), + self.tail.ppm.height.len(), + self.rank.height.len(), + indexer.safe_lengths().height.to_usize(), + source_end, + ] + .into_iter() + .min() + .unwrap_or_default(); + + self.threshold.height.any_truncate_if_needed_at(start)?; + self.tail.ppm.height.any_truncate_if_needed_at(start)?; + self.rank.height.any_truncate_if_needed_at(start)?; + + let values: Vec = source + .collect_range_at(0, source_end) + .into_iter() + .map(Into::into) + .collect(); + let is_valid = |value: f64| value.is_finite() && (!config.positive_only || value > 0.0); + let mut coordinates: Vec = values.iter().copied().filter(|&v| is_valid(v)).collect(); + coordinates.sort_unstable_by(f64::total_cmp); + coordinates.dedup_by(|a, b| a.total_cmp(b).is_eq()); + + let mut history = History::new(coordinates.len().max(1), config.rolling); + for &value in &values[..start] { + if is_valid(value) { + history.add(bucket(&coordinates, value)); + } + } + + for (height_index, &value) in values.iter().enumerate().skip(start) { + let state = if is_valid(value) && history.len >= MIN_HISTORY_BLOCKS { + event_state(value, &coordinates, &history, config) + } else { + EventState::missing() + }; + + self.threshold.height.push(T::from(state.threshold)); + self.tail + .ppm + .height + .push(PartsPerMillion32::from(state.tail)); + self.rank.height.push(StoredI8::new(state.rank)); + + if is_valid(value) { + history.add(bucket(&coordinates, value)); + } + + if (height_index + 1).is_multiple_of(WRITE_INTERVAL) || height_index + 1 == source_end { + let _lock = exit.lock(); + self.threshold.height.write()?; + self.tail.ppm.height.write()?; + self.rank.height.write()?; + } + } + + Ok(()) + } +} + +#[derive(Traversable)] +pub struct Extremes { + pub coins_in_loss: Extreme, + pub profit_taking: Extreme, + pub capitulation: Extreme, + pub peak_regret: Extreme, + pub seller_exhaustion: Extreme, +} + +impl Extremes { + pub(super) fn forced_import( + db: &Database, + parent_version: Version, + indexes: &indexes::Vecs, + ) -> Result { + let version = parent_version + VERSION; + Ok(Self { + coins_in_loss: Extreme::forced_import( + db, + "rarity_meter_coins_in_loss", + version, + indexes, + )?, + profit_taking: Extreme::forced_import( + db, + "rarity_meter_profit_taking", + version, + indexes, + )?, + capitulation: Extreme::forced_import( + db, + "rarity_meter_capitulation", + version, + indexes, + )?, + peak_regret: Extreme::forced_import(db, "rarity_meter_peak_regret", version, indexes)?, + seller_exhaustion: Extreme::forced_import( + db, + "rarity_meter_seller_exhaustion", + version, + indexes, + )?, + }) + } + + #[allow(clippy::too_many_arguments)] + pub(super) fn compute( + &mut self, + indexer: &Indexer, + coins_in_loss: &impl ReadableVec, + realized_profit: &impl ReadableVec, + realized_loss: &impl ReadableVec, + peak_regret: &impl ReadableVec, + seller_exhaustion: &impl ReadableVec, + exit: &Exit, + ) -> Result<()> { + self.coins_in_loss + .compute(indexer, coins_in_loss, COINS_IN_LOSS, exit)?; + self.profit_taking + .compute(indexer, realized_profit, REALIZED, exit)?; + self.capitulation + .compute(indexer, realized_loss, REALIZED, exit)?; + self.peak_regret + .compute(indexer, peak_regret, REALIZED, exit)?; + self.seller_exhaustion + .compute(indexer, seller_exhaustion, SELLER_EXHAUSTION, exit)?; + Ok(()) + } +} + +struct History { + tree: FenwickTree, + len: usize, + rolling: Option>, +} + +impl History { + fn new(size: usize, rolling: bool) -> Self { + Self { + tree: FenwickTree::new(size), + len: 0, + rolling: rolling.then(VecDeque::new), + } + } + + fn add(&mut self, bucket: usize) { + self.tree.add(bucket, &1.0); + self.len += 1; + + if let Some(rolling) = &mut self.rolling { + rolling.push_back(bucket); + if rolling.len() > MIN_HISTORY_BLOCKS { + let expired = rolling.pop_front().unwrap(); + self.tree.add(expired, &-1.0); + self.len -= 1; + } + } + } + + fn quantile(&self, coordinates: &[f64], percentile: f64) -> f64 { + let target = ((self.len - 1) as f64 * percentile).floor(); + let mut index = [0]; + self.tree.kth(&[target], &|count: &f64| *count, &mut index); + coordinates[index[0]] + } + + fn tail(&self, bucket: usize, upper: bool) -> f64 { + let less = if bucket == 0 { + 0.0 + } else { + self.tree.prefix_sum(bucket - 1) + }; + let less_or_equal = self.tree.prefix_sum(bucket); + let count = if upper { + self.len as f64 - less + } else { + less_or_equal + }; + (count + 1.0) / (self.len as f64 + 1.0) + } +} + +struct EventState { + threshold: f64, + tail: f64, + rank: i8, +} + +impl EventState { + fn missing() -> Self { + Self { + threshold: f64::NAN, + tail: f64::NAN, + rank: 0, + } + } +} + +fn event_state(value: f64, coordinates: &[f64], history: &History, config: Config) -> EventState { + let percentile = |tail: f64| { + if config.upper_tail { 1.0 - tail } else { tail } + }; + let threshold = history.quantile(coordinates, percentile(BANDS[0].0)); + let rank = BANDS + .into_iter() + .find_map(|(tail, rank)| { + let boundary = history.quantile(coordinates, percentile(tail)); + let reached = if config.upper_tail { + value >= boundary + } else { + value <= boundary + }; + reached.then_some(rank) + }) + .unwrap_or_default(); + + EventState { + threshold, + tail: history.tail(bucket(coordinates, value), config.upper_tail), + rank, + } +} + +fn bucket(coordinates: &[f64], value: f64) -> usize { + coordinates + .binary_search_by(|candidate| candidate.total_cmp(&value)) + .expect("event value must exist in coordinate set") +} + +#[cfg(test)] +mod tests { + use super::*; + + fn history(values: &[f64]) -> (Vec, History) { + let mut coordinates = values.to_vec(); + coordinates.sort_unstable_by(f64::total_cmp); + coordinates.dedup_by(|a, b| a.total_cmp(b).is_eq()); + let mut history = History::new(coordinates.len(), false); + for &value in values { + history.add(bucket(&coordinates, value)); + } + (coordinates, history) + } + + #[test] + fn upper_tail_includes_current_observation() { + let (mut coordinates, mut history) = history(&(1..=100).map(f64::from).collect::>()); + coordinates.push(101.0); + history.tree = { + let mut tree = FenwickTree::new(coordinates.len()); + for value in 1..=100 { + tree.add(bucket(&coordinates, f64::from(value)), &1.0); + } + tree + }; + + let state = event_state(101.0, &coordinates, &history, REALIZED); + assert!((state.tail - 1.0 / 101.0).abs() < f64::EPSILON); + assert_eq!(state.rank, 3); + assert_eq!(state.threshold, 99.0); + } + + #[test] + fn lower_tail_includes_current_observation() { + let coordinates: Vec<_> = (0..=100).map(f64::from).collect(); + let mut history = History::new(coordinates.len(), false); + for value in 1..=100 { + history.add(bucket(&coordinates, f64::from(value))); + } + let state = event_state(0.0, &coordinates, &history, SELLER_EXHAUSTION); + + assert!((state.tail - 1.0 / 101.0).abs() < f64::EPSILON); + assert_eq!(state.rank, 3); + assert_eq!(state.threshold, 1.0); + } +} diff --git a/crates/brk_computer/src/models/rarity_meter/mod.rs b/crates/brk_computer/src/models/rarity_meter/mod.rs index 12e653143..aa7557b9d 100644 --- a/crates/brk_computer/src/models/rarity_meter/mod.rs +++ b/crates/brk_computer/src/models/rarity_meter/mod.rs @@ -1,4 +1,5 @@ mod components; +mod extremes; mod inner; mod percentiles; @@ -15,11 +16,13 @@ use crate::{ }; pub use components::{Component, Components}; +pub use extremes::Extremes; pub use inner::RarityMeterInner; #[derive(Traversable)] pub struct RarityMeter { pub components: Components, + pub extremes: Extremes, pub full: RarityMeterInner, pub local: RarityMeterInner, pub cycle: RarityMeterInner, @@ -36,6 +39,7 @@ impl RarityMeter { let v = version + VERSION; Ok(Self { components: Components::forced_import(db, v, indexes)?, + extremes: Extremes::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)?, @@ -52,9 +56,20 @@ impl RarityMeter { exit: &Exit, ) -> Result<()> { let spot = &prices.spot.cents.height; + let all = &distribution.utxo_cohorts.all.metrics; + let realized = &all.realized; self.components .compute(indexer, distribution, cointime, coinflow, exit)?; + self.extremes.compute( + indexer, + &all.supply.in_loss.btc.height, + &realized.profit.sum._24h.usd.height, + &realized.loss.sum._24h.usd.height, + &realized.peak_regret.value.sum._24h.usd.height, + &realized.sell_side_risk_ratio._24h.percent.height, + exit, + )?; // Full: all Rainbow components, 10 models self.full.compute( diff --git a/crates/brk_computer/src/outputs/compute.rs b/crates/brk_computer/src/outputs/compute.rs index 5c4d7d89c..422ee6b9c 100644 --- a/crates/brk_computer/src/outputs/compute.rs +++ b/crates/brk_computer/src/outputs/compute.rs @@ -29,7 +29,7 @@ impl Vecs { &starting_lengths, exit, )?; - let lock = self.spent.compute(indexer, inputs, exit)?; + let lock = self.spent.compute(indexer, exit)?; self.db.run_bg(move |db| { let _lock = lock; db.compact_deferred_default() diff --git a/crates/brk_computer/src/outputs/spent/compute.rs b/crates/brk_computer/src/outputs/spent/compute.rs index a56ef0df2..cefd2d183 100644 --- a/crates/brk_computer/src/outputs/spent/compute.rs +++ b/crates/brk_computer/src/outputs/spent/compute.rs @@ -5,20 +5,14 @@ use tracing::info; use vecdb::{AnyStoredVec, AnyVec, Exit, ExitGuard, ReadableVec, Stamp, VecIndex, WritableVec}; use super::Vecs; -use crate::inputs; const HEIGHT_BATCH: u32 = 10_000; impl Vecs { - pub(crate) fn compute( - &mut self, - indexer: &Indexer, - inputs: &inputs::Vecs, - exit: &Exit, - ) -> Result { + pub(crate) fn compute(&mut self, indexer: &Indexer, exit: &Exit) -> Result { let starting_lengths = indexer.safe_lengths(); - let dep_version = inputs.spent.txout_index.version() + let dep_version = indexer.vecs.inputs.txout_index.version() + indexer.vecs.outputs.first_txout_index.version() + indexer.vecs.inputs.first_txin_index.version() + indexer.vecs.outputs.value.version(); @@ -41,7 +35,7 @@ impl Vecs { self.txin_index .truncate_if_needed(TxOutIndex::from(min_txout_index))?; - let txin_index_to_txout_index = &inputs.spent.txout_index; + let txin_index_to_txout_index = &indexer.vecs.inputs.txout_index; // Find min_height via binary search (first_txout_index is monotonically non-decreasing) let first_txout_index_vec = &indexer.vecs.outputs.first_txout_index; @@ -105,7 +99,7 @@ impl Vecs { let txin_start = first_txin_index_data[batch_start_height.to_usize() - offset].to_usize(); let txin_end = if batch_end_height >= target_height { - inputs.spent.txout_index.len() + indexer.vecs.inputs.txout_index.len() } else { first_txin_index_data[batch_end_height.to_usize() + 1 - offset].to_usize() }; diff --git a/crates/brk_computer/src/transactions/compute.rs b/crates/brk_computer/src/transactions/compute.rs index 1fe8e47d4..2c59a1e4d 100644 --- a/crates/brk_computer/src/transactions/compute.rs +++ b/crates/brk_computer/src/transactions/compute.rs @@ -3,7 +3,7 @@ use brk_indexer::Indexer; use vecdb::Exit; use super::Vecs; -use crate::{blocks, indexes, inputs, price}; +use crate::{blocks, indexes, price}; impl Vecs { #[allow(clippy::too_many_arguments)] @@ -12,7 +12,6 @@ impl Vecs { indexer: &Indexer, indexes: &indexes::Vecs, blocks: &blocks::Vecs, - inputs: &inputs::Vecs, prices: &price::Vecs, exit: &Exit, ) -> Result<()> { @@ -31,11 +30,9 @@ impl Vecs { r2?; r3?; - self.fees - .compute(indexer, indexes, &inputs.spent, &self.size, exit)?; + self.fees.compute(indexer, indexes, &self.size, exit)?; - self.patterns - .compute(indexer, indexes, &inputs.spent, exit)?; + self.patterns.compute(indexer, indexes, exit)?; self.policy.compute(indexer, indexes, &self.fees, exit)?; diff --git a/crates/brk_computer/src/transactions/fees/compute.rs b/crates/brk_computer/src/transactions/fees/compute.rs index 0277bf952..c2a3be880 100644 --- a/crates/brk_computer/src/transactions/fees/compute.rs +++ b/crates/brk_computer/src/transactions/fees/compute.rs @@ -9,7 +9,7 @@ use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec, unli use super::super::size; use super::Vecs; -use crate::{indexes, inputs}; +use crate::indexes; impl Vecs { #[allow(clippy::too_many_arguments)] @@ -17,7 +17,6 @@ impl Vecs { &mut self, indexer: &Indexer, indexes: &indexes::Vecs, - spent: &inputs::SpentVecs, size_vecs: &size::Vecs, exit: &Exit, ) -> Result<()> { @@ -27,7 +26,7 @@ impl Vecs { starting_lengths.tx_index, &indexer.vecs.transactions.first_txin_index, &indexes.tx_index.input_count, - &spent.value, + &indexer.vecs.inputs.value, exit, )?; self.output_value.compute_sum_from_indexes( diff --git a/crates/brk_computer/src/transactions/patterns/compute.rs b/crates/brk_computer/src/transactions/patterns/compute.rs index 5f23fae96..7396a515e 100644 --- a/crates/brk_computer/src/transactions/patterns/compute.rs +++ b/crates/brk_computer/src/transactions/patterns/compute.rs @@ -4,7 +4,7 @@ use brk_types::{StoredBool, StoredU64, TxIndex}; use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec}; use super::{Vecs, coinjoin::Candidate}; -use crate::{indexes, inputs}; +use crate::indexes; const WRITE_INTERVAL: usize = 10_000; @@ -13,7 +13,6 @@ impl Vecs { &mut self, indexer: &Indexer, indexes: &indexes::Vecs, - spent: &inputs::SpentVecs, exit: &Exit, ) -> Result<()> { let features = &indexer.vecs.transaction_features; @@ -22,7 +21,7 @@ impl Vecs { + indexer.vecs.transactions.first_tx_index.version() + indexer.vecs.transactions.first_txin_index.version() + indexer.vecs.transactions.first_txout_index.version() - + spent.value.version() + + indexer.vecs.inputs.value.version() + indexer.vecs.inputs.output_type.version() + indexer.vecs.inputs.type_index.version() + indexer.vecs.outputs.value.version() @@ -98,7 +97,7 @@ impl Vecs { let mut input_count = indexes.tx_index.input_count.cursor(); let mut output_count = indexes.tx_index.output_count.cursor(); - let mut input_value = spent.value.cursor(); + let mut input_value = indexer.vecs.inputs.value.cursor(); let mut input_type = indexer.vecs.inputs.output_type.cursor(); let mut input_type_index = indexer.vecs.inputs.type_index.cursor(); let mut output_value = indexer.vecs.outputs.value.reader().cursor(); diff --git a/crates/brk_indexer/Cargo.toml b/crates/brk_indexer/Cargo.toml index 87d5646aa..932503c13 100644 --- a/crates/brk_indexer/Cargo.toml +++ b/crates/brk_indexer/Cargo.toml @@ -31,3 +31,4 @@ vecdb = { workspace = true } brk_alloc = { workspace = true } brk_bencher = { workspace = true } color-eyre = { workspace = true } +tempfile = { workspace = true } diff --git a/crates/brk_indexer/src/constants.rs b/crates/brk_indexer/src/constants.rs index a77f64c20..c73855990 100644 --- a/crates/brk_indexer/src/constants.rs +++ b/crates/brk_indexer/src/constants.rs @@ -4,7 +4,7 @@ use brk_types::{TxIndex, Txid, TxidPrefix, Version}; // One version for all data sources // Increment on **change _OR_ addition** -pub const VERSION: Version = Version::new(27); +pub const VERSION: Version = Version::new(28); pub const SNAPSHOT_BLOCK_RANGE: usize = 1_000; /// Known duplicate Bitcoin transactions (BIP30) diff --git a/crates/brk_indexer/src/processor/txin/mod.rs b/crates/brk_indexer/src/processor/txin/mod.rs index 8abe367ea..b05262b5e 100644 --- a/crates/brk_indexer/src/processor/txin/mod.rs +++ b/crates/brk_indexer/src/processor/txin/mod.rs @@ -9,8 +9,8 @@ use brk_cohort::ByAddrType; use brk_error::Result; use brk_store::Store; use brk_types::{ - AddrIndexOutPoint, AddrIndexTxIndex, OutPoint, OutputType, TxInIndex, TxIndex, TypeIndex, Unit, - Vin, + AddrIndexOutPoint, AddrIndexTxIndex, OutPoint, OutputType, Sats, TxInIndex, TxIndex, + TxOutIndex, TypeIndex, Unit, Vin, }; use vecdb::{PcoVec, WritableVec, unlikely}; @@ -48,22 +48,36 @@ pub(super) fn finalize_inputs( let block_txin_index = input_offset + vin; let txin_index = base_txin_index + TxInIndex::from(block_txin_index); let vin = Vin::from(vin); - let (outpoint, output_type, type_index) = match input_source { + let (outpoint, txout_index, value, output_type, type_index) = match input_source { InputSource::PreviousBlock { outpoint, + txout_index, + value, output_type, legacy_sigops: _, type_index, - } => (*outpoint, *output_type, *type_index), - InputSource::Coinbase => { - (OutPoint::COINBASE, OutputType::Unknown, TypeIndex::COINBASE) - } + } => (*outpoint, *txout_index, *value, *output_type, *type_index), + InputSource::Coinbase => ( + OutPoint::COINBASE, + TxOutIndex::COINBASE, + Sats::MAX, + OutputType::Unknown, + TypeIndex::COINBASE, + ), InputSource::SameBlock { outpoint, txout_offset, + txout_index, + value, } => { let output = &txouts[*txout_offset]; - (*outpoint, output.output_type, output.resolved_type_index()) + ( + *outpoint, + *txout_index, + *value, + output.output_type, + output.resolved_type_index(), + ) } }; @@ -73,6 +87,8 @@ pub(super) fn finalize_inputs( inputs.tx_index.checked_push(txin_index, tx_index)?; inputs.outpoint.checked_push(txin_index, outpoint)?; + inputs.txout_index.checked_push(txin_index, txout_index)?; + inputs.value.checked_push(txin_index, value)?; inputs.output_type.checked_push(txin_index, output_type)?; inputs.type_index.checked_push(txin_index, type_index)?; diff --git a/crates/brk_indexer/src/processor/txin/resolver.rs b/crates/brk_indexer/src/processor/txin/resolver.rs index f272b87b4..4cf743e0f 100644 --- a/crates/brk_indexer/src/processor/txin/resolver.rs +++ b/crates/brk_indexer/src/processor/txin/resolver.rs @@ -2,7 +2,7 @@ use std::collections::hash_map::Entry; use brk_error::{Error, Result}; use brk_types::{ - OutPoint, OutputType, SigOps, TxIndex, TxOutIndex, Txid, TxidPrefix, TypeIndex, Vout, + OutPoint, OutputType, Sats, SigOps, TxIndex, TxOutIndex, Txid, TxidPrefix, TypeIndex, Vout, }; use rayon::prelude::*; use rustc_hash::FxHashMap; @@ -29,7 +29,11 @@ impl InputResolver { processor: &BlockProcessor<'_>, txs: &[ComputedTx<'_>], ) -> Result<&[InputSource]> { - self.prepare(txs, processor.lengths.tx_index); + self.prepare( + txs, + processor.lengths.tx_index, + processor.lengths.txout_index, + ); self.reads.resolve( processor, &self.previous_parent_prefixes, @@ -53,17 +57,22 @@ impl InputResolver { UnresolvedInput::SameBlock { outpoint, txout_offset, + txout_index, + value, } => { *resolved = InputSource::SameBlock { outpoint, txout_offset, + txout_index, + value, }; Ok(()) } UnresolvedInput::PreviousBlock { parent_index, vout } => { let parent = reads.parent(parent_index); let outpoint = OutPoint::new(parent.tx_index, vout); - let (output_type, type_index) = reads.output(input_index); + let txout_index = parent.first_txout_index + vout; + let (value, output_type, type_index) = reads.output(input_index); let legacy_sigops = if tracks_executed_legacy_sigops { processor @@ -77,6 +86,8 @@ impl InputResolver { *resolved = InputSource::PreviousBlock { outpoint, + txout_index, + value, output_type, legacy_sigops, type_index, @@ -90,7 +101,12 @@ impl InputResolver { Ok(&self.resolved) } - fn prepare(&mut self, txs: &[ComputedTx<'_>], block_first_tx_index: TxIndex) { + fn prepare( + &mut self, + txs: &[ComputedTx<'_>], + block_first_tx_index: TxIndex, + block_first_txout_index: TxOutIndex, + ) { self.parent_locations.clear(); self.previous_parent_prefixes.clear(); self.inputs.clear(); @@ -121,9 +137,15 @@ impl InputResolver { ParentLocation::SameBlock(tx_index) => { let block_tx_index = usize::from(tx_index) - usize::from(block_first_tx_index); + let tx = &txs[block_tx_index]; + let txout_offset = tx.txout_offset(vout); + let value = Sats::from(tx.tx.output[usize::from(vout)].value); self.inputs.push(UnresolvedInput::SameBlock { outpoint: OutPoint::new(tx_index, vout), - txout_offset: txs[block_tx_index].txout_offset(vout), + txout_offset, + txout_index: block_first_txout_index + + TxOutIndex::from(txout_offset), + value, }); continue; } @@ -189,6 +211,7 @@ struct ReadBatch { outputs: Vec, output_types: Vec, type_indices: Vec, + values: Vec, } impl ReadBatch { @@ -290,6 +313,8 @@ impl ReadBatch { self.output_types.resize(inputs.len(), OutputType::Unknown); self.type_indices.clear(); self.type_indices.resize(inputs.len(), TypeIndex::default()); + self.values.clear(); + self.values.resize(inputs.len(), Sats::MAX); } fn read_outputs(&mut self, processor: &BlockProcessor<'_>) -> Result<()> { @@ -300,8 +325,20 @@ impl ReadBatch { let output_types = &mut self.output_types; let type_indices = &mut self.type_indices; + let values = &mut self.values; - let (output_types_result, type_indices_result) = rayon::join( + let (values_result, metadata_result) = rayon::join( + || -> Result<()> { + for read in outputs { + values[read.input_index] = processor + .vecs + .outputs + .value + .get_append_only(read.txout_index, &processor.readers.txout_index_to_value) + .ok_or(Error::Internal("Missing output value"))?; + } + Ok(()) + }, || -> Result<()> { for read in outputs { output_types[read.input_index] = processor @@ -313,11 +350,6 @@ impl ReadBatch { &processor.readers.txout_index_to_output_type, ) .ok_or(Error::Internal("Missing output_type"))?; - } - Ok(()) - }, - || -> Result<()> { - for read in outputs { type_indices[read.input_index] = processor .vecs .outputs @@ -332,16 +364,17 @@ impl ReadBatch { }, ); - output_types_result?; - type_indices_result + values_result?; + metadata_result } fn parent(&self, original_index: usize) -> ParentRead { self.parents[original_index] } - fn output(&self, input_index: usize) -> (OutputType, TypeIndex) { + fn output(&self, input_index: usize) -> (Sats, OutputType, TypeIndex) { ( + self.values[input_index], self.output_types[input_index], self.type_indices[input_index], ) @@ -358,5 +391,7 @@ enum UnresolvedInput { SameBlock { outpoint: OutPoint, txout_offset: usize, + txout_index: TxOutIndex, + value: Sats, }, } diff --git a/crates/brk_indexer/src/processor/txin/source.rs b/crates/brk_indexer/src/processor/txin/source.rs index 833d376f8..96e404dec 100644 --- a/crates/brk_indexer/src/processor/txin/source.rs +++ b/crates/brk_indexer/src/processor/txin/source.rs @@ -1,10 +1,12 @@ -use brk_types::{OutPoint, OutputType, SigOps, TypeIndex}; +use brk_types::{OutPoint, OutputType, Sats, SigOps, TxOutIndex, TypeIndex}; #[derive(Debug, Clone, Copy)] pub(crate) enum InputSource { Coinbase, PreviousBlock { outpoint: OutPoint, + txout_index: TxOutIndex, + value: Sats, output_type: OutputType, legacy_sigops: SigOps, type_index: TypeIndex, @@ -12,5 +14,7 @@ pub(crate) enum InputSource { SameBlock { outpoint: OutPoint, txout_offset: usize, + txout_index: TxOutIndex, + value: Sats, }, } diff --git a/crates/brk_indexer/src/readers.rs b/crates/brk_indexer/src/readers.rs index 1c035e4ee..97327028c 100644 --- a/crates/brk_indexer/src/readers.rs +++ b/crates/brk_indexer/src/readers.rs @@ -2,8 +2,8 @@ use bitcoin::ScriptBuf; use brk_types::{ AddrBytes, OutputType, P2AAddrIndex, P2ABytes, P2MSOutputIndex, P2PK33AddrIndex, P2PK33Bytes, P2PK65AddrIndex, P2PK65Bytes, P2PKHAddrIndex, P2PKHBytes, P2SHAddrIndex, P2SHBytes, - P2TRAddrIndex, P2TRBytes, P2WPKHAddrIndex, P2WPKHBytes, P2WSHAddrIndex, P2WSHBytes, SigOps, - TxIndex, TxOutIndex, Txid, TypeIndex, UnknownOutputIndex, + P2TRAddrIndex, P2TRBytes, P2WPKHAddrIndex, P2WPKHBytes, P2WSHAddrIndex, P2WSHBytes, Sats, + SigOps, TxIndex, TxOutIndex, Txid, TypeIndex, UnknownOutputIndex, }; use vecdb::{BytesStrategy, VecReader}; @@ -45,6 +45,7 @@ impl AddrReaders { pub struct Readers { pub txid: VecReader>, pub tx_index_to_first_txout_index: VecReader>, + pub txout_index_to_value: VecReader>, pub txout_index_to_output_type: VecReader>, pub txout_index_to_type_index: VecReader>, pub scripts: ScriptReaders, @@ -56,6 +57,7 @@ impl Readers { Self { txid: vecs.transactions.txid.reader(), tx_index_to_first_txout_index: vecs.transactions.first_txout_index.reader(), + txout_index_to_value: vecs.outputs.value.reader(), txout_index_to_output_type: vecs.outputs.output_type.reader(), txout_index_to_type_index: vecs.outputs.type_index.reader(), scripts: ScriptReaders { diff --git a/crates/brk_indexer/src/vecs/inputs.rs b/crates/brk_indexer/src/vecs/inputs.rs index c07048c25..c9a5d5574 100644 --- a/crates/brk_indexer/src/vecs/inputs.rs +++ b/crates/brk_indexer/src/vecs/inputs.rs @@ -1,6 +1,8 @@ use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Height, OutPoint, OutputType, TxInIndex, TxIndex, TypeIndex, Version}; +use brk_types::{ + Height, OutPoint, OutputType, Sats, TxInIndex, TxIndex, TxOutIndex, TypeIndex, Version, +}; use rayon::prelude::*; use vecdb::{AnyStoredVec, Database, ImportableVec, PcoVec, Rw, Stamp, StorageMode, WritableVec}; @@ -10,6 +12,8 @@ use crate::parallel_import; pub struct InputsVecs { pub first_txin_index: M::Stored>, pub outpoint: M::Stored>, + pub txout_index: M::Stored>, + pub value: M::Stored>, pub tx_index: M::Stored>, pub output_type: M::Stored>, pub type_index: M::Stored>, @@ -17,9 +21,11 @@ pub struct InputsVecs { impl InputsVecs { pub fn forced_import(db: &Database, version: Version) -> Result { - let (first_txin_index, outpoint, tx_index, output_type, type_index) = parallel_import! { + let (first_txin_index, outpoint, txout_index, value, tx_index, output_type, type_index) = parallel_import! { first_txin_index = PcoVec::forced_import(db, "first_txin_index", version), outpoint = PcoVec::forced_import(db, "outpoint", version), + txout_index = PcoVec::forced_import(db, "txout_index", version), + value = PcoVec::forced_import(db, "value", version), tx_index = PcoVec::forced_import(db, "tx_index", version), output_type = PcoVec::forced_import(db, "output_type", version), type_index = PcoVec::forced_import(db, "type_index", version), @@ -27,6 +33,8 @@ impl InputsVecs { Ok(Self { first_txin_index, outpoint, + txout_index, + value, tx_index, output_type, type_index, @@ -38,6 +46,10 @@ impl InputsVecs { .truncate_if_needed_with_stamp(height, stamp)?; self.outpoint .truncate_if_needed_with_stamp(txin_index, stamp)?; + self.txout_index + .truncate_if_needed_with_stamp(txin_index, stamp)?; + self.value + .truncate_if_needed_with_stamp(txin_index, stamp)?; self.tx_index .truncate_if_needed_with_stamp(txin_index, stamp)?; self.output_type @@ -51,6 +63,8 @@ impl InputsVecs { [ &mut self.first_txin_index as &mut dyn AnyStoredVec, &mut self.outpoint, + &mut self.txout_index, + &mut self.value, &mut self.tx_index, &mut self.output_type, &mut self.type_index, @@ -62,6 +76,8 @@ impl InputsVecs { [ &self.first_txin_index as &dyn AnyStoredVec, &self.outpoint, + &self.txout_index, + &self.value, &self.tx_index, &self.output_type, &self.type_index, @@ -69,3 +85,106 @@ impl InputsVecs { .into_iter() } } + +#[cfg(test)] +mod tests { + use brk_types::{Version, Vout}; + use rayon::prelude::*; + use vecdb::{AnyVec, ReadableVec}; + + use super::*; + + #[test] + fn rollback_keeps_all_input_facts_aligned() { + let dir = tempfile::tempdir().unwrap(); + let db = Database::open(dir.path()).unwrap(); + let mut inputs = InputsVecs::forced_import(&db, Version::ONE).unwrap(); + + for (height, txin_index) in [(0_usize, 0_usize), (1, 2), (2, 4)] { + inputs + .first_txin_index + .checked_push(Height::from(height), TxInIndex::from(txin_index)) + .unwrap(); + } + + let facts = [ + ( + OutPoint::COINBASE, + TxOutIndex::COINBASE, + Sats::MAX, + TxIndex::ZERO, + OutputType::Unknown, + TypeIndex::COINBASE, + ), + ( + OutPoint::new(TxIndex::from(1_usize), Vout::ZERO), + TxOutIndex::from(2_usize), + Sats::from(21_usize), + TxIndex::from(2_usize), + OutputType::P2PKH, + TypeIndex::from(3_usize), + ), + ( + OutPoint::new(TxIndex::from(2_usize), Vout::ZERO), + TxOutIndex::from(4_usize), + Sats::from(34_usize), + TxIndex::from(3_usize), + OutputType::P2TR, + TypeIndex::from(5_usize), + ), + ( + OutPoint::new(TxIndex::from(3_usize), Vout::ZERO), + TxOutIndex::from(6_usize), + Sats::from(55_usize), + TxIndex::from(4_usize), + OutputType::P2WPKH, + TypeIndex::from(8_usize), + ), + ]; + + for (index, &(outpoint, txout_index, value, tx_index, output_type, type_index)) in + facts.iter().enumerate() + { + let index = TxInIndex::from(index); + inputs.outpoint.checked_push(index, outpoint).unwrap(); + inputs.txout_index.checked_push(index, txout_index).unwrap(); + inputs.value.checked_push(index, value).unwrap(); + inputs.tx_index.checked_push(index, tx_index).unwrap(); + inputs.output_type.checked_push(index, output_type).unwrap(); + inputs.type_index.checked_push(index, type_index).unwrap(); + } + + inputs + .par_iter_mut_any() + .try_for_each(|vec| vec.stamped_write(Stamp::from(2_u64))) + .unwrap(); + + inputs + .truncate( + Height::from(1_usize), + TxInIndex::from(2_usize), + Stamp::from(0_u64), + ) + .unwrap(); + inputs + .par_iter_mut_any() + .try_for_each(|vec| vec.stamped_write(Stamp::from(0_u64))) + .unwrap(); + + drop(inputs); + drop(db); + + let db = Database::open(dir.path()).unwrap(); + let inputs = InputsVecs::forced_import(&db, Version::ONE).unwrap(); + assert_eq!(inputs.first_txin_index.len(), 1); + assert!(inputs.iter_any().skip(1).all(|vec| vec.len() == 2)); + assert_eq!( + inputs.txout_index.collect_range_at(0, 2), + [TxOutIndex::COINBASE, TxOutIndex::from(2_usize)] + ); + assert_eq!( + inputs.value.collect_range_at(0, 2), + [Sats::MAX, Sats::from(21_usize)] + ); + } +} diff --git a/crates/brk_query/src/impl/block/txs.rs b/crates/brk_query/src/impl/block/txs.rs index f7be3fb85..8cede6a97 100644 --- a/crates/brk_query/src/impl/block/txs.rs +++ b/crates/brk_query/src/impl/block/txs.rs @@ -194,7 +194,7 @@ impl Query { let mut outpoint_cursor = indexer.vecs.inputs.outpoint.cursor(); let mut input_output_type_cursor = indexer.vecs.inputs.output_type.cursor(); let mut input_type_index_cursor = indexer.vecs.inputs.type_index.cursor(); - let mut input_value_cursor = self.computer().inputs.spent.value.cursor(); + let mut input_value_cursor = indexer.vecs.inputs.value.cursor(); let mut prevout_input_data: FxHashMap = FxHashMap::with_capacity_and_hasher(total_inputs, Default::default()); diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index 1ecae1a6f..050cc418f 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -335,6 +335,14 @@ prior template's transactions or a full transaction body. * @property {Timestamp} timestamp - Unix timestamp at the window midpoint * @property {Weight} avgWeight - Rolling 24h median block weight (weight units) */ +/** + * Investor phase from the Capital Sentiment model. + * + * Codes are explicit because phase values are persisted. Code `0` represents + * unavailable model inputs and is therefore not a phase. + * + * @typedef {("raging_bull"|"bull"|"cautious_bull"|"hopeful_bull"|"early_bull"|"weak_bull"|"limbo"|"deep_bear"|"bear"|"early_bear")} CapitalSentimentPhase + */ /** * Unsigned cents (u64) - for values that should never be negative. * Used for invested capital, realized cap, etc. @@ -4906,6 +4914,27 @@ function createPpmPriceRatioPattern(client, acc, disc) { }; } +/** + * @typedef {Object} RankTailThresholdPattern + * @property {SeriesPattern1} rank + * @property {PercentPpmRatioPattern2} tail + * @property {SeriesPattern1} threshold + */ + +/** + * Create a RankTailThresholdPattern pattern node + * @param {BrkClient} client + * @param {string} acc - Accumulated series name + * @returns {RankTailThresholdPattern} + */ +function createRankTailThresholdPattern(client, acc) { + return { + rank: createSeriesPattern1(client, _m(acc, 'rank')), + tail: createPercentPpmRatioPattern2(client, _m(acc, 'tail')), + threshold: createSeriesPattern1(client, _m(acc, 'threshold')), + }; +} + /** * @typedef {Object} RatioTransferValuePattern * @property {_1m1w1y24hPattern} ratio @@ -5676,9 +5705,8 @@ function createUnspentPattern(client, acc) { * @property {SeriesTree_Scripts} scripts * @property {SeriesTree_OpReturn} opReturn * @property {SeriesTree_Mining} mining - * @property {SeriesTree_Cointime} cointime - * @property {SeriesTree_Coinflow} coinflow - * @property {SeriesTree_Bedrock} bedrock + * @property {SeriesTree_Frameworks} frameworks + * @property {SeriesTree_Models} models * @property {SeriesTree_Constants} constants * @property {SeriesTree_Indexes} indexes * @property {SeriesTree_Indicators} indicators @@ -5688,6 +5716,7 @@ function createUnspentPattern(client, acc) { * @property {SeriesTree_Price} price * @property {SeriesTree_Supply} supply * @property {SeriesTree_Cohorts} cohorts + * @property {SeriesTree_Cointime} cointime */ /** @@ -5980,7 +6009,6 @@ function createUnspentPattern(client, acc) { /** * @typedef {Object} SeriesTree_Inputs * @property {SeriesTree_Inputs_Raw} raw - * @property {SeriesTree_Inputs_Spent} spent * @property {CumulativeRollingSumPattern} count * @property {_1m1w1y24hPattern} perSec * @property {SeriesTree_Inputs_ByType} byType @@ -5990,17 +6018,13 @@ function createUnspentPattern(client, acc) { * @typedef {Object} SeriesTree_Inputs_Raw * @property {SeriesPattern18} firstTxinIndex * @property {SeriesPattern20} outpoint + * @property {SeriesPattern20} txoutIndex + * @property {SeriesPattern20} value * @property {SeriesPattern20} txIndex * @property {SeriesPattern20} outputType * @property {SeriesPattern20} typeIndex */ -/** - * @typedef {Object} SeriesTree_Inputs_Spent - * @property {SeriesPattern20} txoutIndex - * @property {SeriesPattern20} value - */ - /** * @typedef {Object} SeriesTree_Inputs_ByType * @property {SeriesTree_Inputs_ByType_InputCount} inputCount @@ -6533,29 +6557,34 @@ function createUnspentPattern(client, acc) { */ /** - * @typedef {Object} SeriesTree_Cointime - * @property {SeriesTree_Cointime_Activity} activity - * @property {SeriesTree_Cointime_AgeRange} ageRange - * @property {SeriesTree_Cointime_Supply} supply - * @property {SeriesTree_Cointime_Value} value - * @property {SeriesTree_Cointime_Cap} cap - * @property {SeriesTree_Cointime_Prices} prices - * @property {SeriesTree_Cointime_Adjusted} adjusted - * @property {SeriesTree_Cointime_ReserveRisk} reserveRisk + * @typedef {Object} SeriesTree_Frameworks + * @property {SeriesTree_Frameworks_Cointime} cointime + * @property {SeriesTree_Frameworks_Coinflow} coinflow */ /** - * @typedef {Object} SeriesTree_Cointime_Activity + * @typedef {Object} SeriesTree_Frameworks_Cointime + * @property {SeriesTree_Frameworks_Cointime_Activity} activity + * @property {SeriesTree_Frameworks_Cointime_AgeRange} ageRange + * @property {SeriesTree_Frameworks_Cointime_Supply} supply + * @property {SeriesTree_Frameworks_Cointime_Value} value + * @property {SeriesTree_Frameworks_Cointime_Cap} cap + * @property {SeriesTree_Frameworks_Cointime_Prices} prices + * @property {SeriesTree_Frameworks_Cointime_Adjusted} adjusted + * @property {SeriesTree_Frameworks_Cointime_ReserveRisk} reserveRisk + */ + +/** + * @typedef {Object} SeriesTree_Frameworks_Cointime_Activity * @property {AverageBlockCumulativeSumPattern} coinblocksCreated * @property {AverageBlockCumulativeSumPattern} coinblocksStored * @property {SeriesPattern1} liveliness * @property {SeriesPattern1} vaultedness * @property {SeriesPattern1} ratio - * @property {AverageBlockCumulativeSumPattern} coinblocksDestroyed */ /** - * @typedef {Object} SeriesTree_Cointime_AgeRange + * @typedef {Object} SeriesTree_Frameworks_Cointime_AgeRange * @property {CoindaysLivelinessRatioSupplyVaultednessPattern} under1h * @property {CoindaysLivelinessRatioSupplyVaultednessPattern} _1hTo1d * @property {CoindaysLivelinessRatioSupplyVaultednessPattern} _1dTo1w @@ -6582,13 +6611,13 @@ function createUnspentPattern(client, acc) { */ /** - * @typedef {Object} SeriesTree_Cointime_Supply + * @typedef {Object} SeriesTree_Frameworks_Cointime_Supply * @property {BtcCentsSatsUsdPattern} vaulted - * @property {SeriesTree_Cointime_Supply_Active} active + * @property {SeriesTree_Frameworks_Cointime_Supply_Active} active */ /** - * @typedef {Object} SeriesTree_Cointime_Supply_Active + * @typedef {Object} SeriesTree_Frameworks_Cointime_Supply_Active * @property {SeriesPattern1} btc * @property {SeriesPattern1} sats * @property {SeriesPattern1} usd @@ -6597,7 +6626,7 @@ function createUnspentPattern(client, acc) { */ /** - * @typedef {Object} SeriesTree_Cointime_Value + * @typedef {Object} SeriesTree_Frameworks_Cointime_Value * @property {AverageBlockCumulativeSumPattern} destroyed * @property {AverageBlockCumulativeSumPattern} created * @property {AverageBlockCumulativeSumPattern} stored @@ -6605,7 +6634,7 @@ function createUnspentPattern(client, acc) { */ /** - * @typedef {Object} SeriesTree_Cointime_Cap + * @typedef {Object} SeriesTree_Frameworks_Cointime_Cap * @property {CentsUsdPattern3} thermo * @property {CentsUsdPattern3} investor * @property {CentsUsdPattern3} vaulted @@ -6615,7 +6644,7 @@ function createUnspentPattern(client, acc) { */ /** - * @typedef {Object} SeriesTree_Cointime_Prices + * @typedef {Object} SeriesTree_Frameworks_Cointime_Prices * @property {CentsPpmRatioSatsUsdPattern} vaulted * @property {CentsPpmRatioSatsUsdPattern} active * @property {CentsPpmRatioSatsUsdPattern} trueMarketMean @@ -6623,30 +6652,30 @@ function createUnspentPattern(client, acc) { */ /** - * @typedef {Object} SeriesTree_Cointime_Adjusted + * @typedef {Object} SeriesTree_Frameworks_Cointime_Adjusted * @property {PercentPpmRatioPattern} inflationRate * @property {SeriesPattern1} txVelocityNative * @property {SeriesPattern1} txVelocityFiat */ /** - * @typedef {Object} SeriesTree_Cointime_ReserveRisk + * @typedef {Object} SeriesTree_Frameworks_Cointime_ReserveRisk * @property {SeriesPattern1} value * @property {SeriesPattern18} vocddMedian1y * @property {SeriesPattern18} hodlBank */ /** - * @typedef {Object} SeriesTree_Coinflow - * @property {SeriesTree_Coinflow_AgeRange} ageRange - * @property {SeriesTree_Coinflow_Supply} supply - * @property {SeriesTree_Coinflow_Horizon} horizon + * @typedef {Object} SeriesTree_Frameworks_Coinflow + * @property {SeriesTree_Frameworks_Coinflow_AgeRange} ageRange + * @property {SeriesTree_Frameworks_Coinflow_Supply} supply + * @property {SeriesTree_Frameworks_Coinflow_Horizon} horizon * @property {CentsUsdPattern3} cap * @property {CentsPpmRatioSatsUsdPattern} price */ /** - * @typedef {Object} SeriesTree_Coinflow_AgeRange + * @typedef {Object} SeriesTree_Frameworks_Coinflow_AgeRange * @property {MobilitySpendingSupplyPattern} under1h * @property {MobilitySpendingSupplyPattern} _1hTo1d * @property {MobilitySpendingSupplyPattern} _1dTo1w @@ -6673,13 +6702,13 @@ function createUnspentPattern(client, acc) { */ /** - * @typedef {Object} SeriesTree_Coinflow_Supply - * @property {SeriesTree_Coinflow_Supply_Mobile} mobile + * @typedef {Object} SeriesTree_Frameworks_Coinflow_Supply + * @property {SeriesTree_Frameworks_Coinflow_Supply_Mobile} mobile * @property {BtcCentsSatsUsdPattern} immobile */ /** - * @typedef {Object} SeriesTree_Coinflow_Supply_Mobile + * @typedef {Object} SeriesTree_Frameworks_Coinflow_Supply_Mobile * @property {SeriesPattern1} btc * @property {SeriesPattern1} sats * @property {SeriesPattern1} usd @@ -6688,7 +6717,7 @@ function createUnspentPattern(client, acc) { */ /** - * @typedef {Object} SeriesTree_Coinflow_Horizon + * @typedef {Object} SeriesTree_Frameworks_Coinflow_Horizon * @property {SupplyPattern} _8y * @property {SupplyPattern} _4y * @property {SupplyPattern} _2y @@ -6699,7 +6728,14 @@ function createUnspentPattern(client, acc) { */ /** - * @typedef {Object} SeriesTree_Bedrock + * @typedef {Object} SeriesTree_Models + * @property {SeriesTree_Models_Bedrock} bedrock + * @property {SeriesTree_Models_CapitalSentiment} capitalSentiment + * @property {SeriesTree_Models_RarityMeter} rarityMeter + */ + +/** + * @typedef {Object} SeriesTree_Models_Bedrock * @property {FloorLevelLossPattern} raw * @property {FloorLevelLossPattern} cointime * @property {FloorLevelLossPattern} coinflow @@ -6712,6 +6748,63 @@ function createUnspentPattern(client, acc) { * @property {FloorLevelLossPattern} coinflow1m */ +/** + * @typedef {Object} SeriesTree_Models_CapitalSentiment + * @property {SeriesPattern1} phase + * @property {SeriesPattern1} score + */ + +/** + * @typedef {Object} SeriesTree_Models_RarityMeter + * @property {SeriesTree_Models_RarityMeter_Components} components + * @property {SeriesTree_Models_RarityMeter_Extremes} extremes + * @property {IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern} full + * @property {IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern} local + * @property {IndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern} cycle + */ + +/** + * @typedef {Object} SeriesTree_Models_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 + */ + +/** + * @typedef {Object} SeriesTree_Models_RarityMeter_Extremes + * @property {SeriesTree_Models_RarityMeter_Extremes_CoinsInLoss} coinsInLoss + * @property {RankTailThresholdPattern} profitTaking + * @property {RankTailThresholdPattern} capitulation + * @property {RankTailThresholdPattern} peakRegret + * @property {SeriesTree_Models_RarityMeter_Extremes_SellerExhaustion} sellerExhaustion + */ + +/** + * @typedef {Object} SeriesTree_Models_RarityMeter_Extremes_CoinsInLoss + * @property {SeriesPattern1} threshold + * @property {PercentPpmRatioPattern2} tail + * @property {SeriesPattern1} rank + */ + +/** + * @typedef {Object} SeriesTree_Models_RarityMeter_Extremes_SellerExhaustion + * @property {SeriesPattern1} threshold + * @property {PercentPpmRatioPattern2} tail + * @property {SeriesPattern1} rank + */ + /** * @typedef {Object} SeriesTree_Constants * @property {SeriesPattern1} _0 @@ -6981,7 +7074,6 @@ function createUnspentPattern(client, acc) { * @property {SeriesTree_Indicators_Dormancy} dormancy * @property {SeriesPattern1} stockToFlow * @property {SeriesPattern1} sellerExhaustion - * @property {SeriesTree_Indicators_RarityMeter} rarityMeter */ /** @@ -6990,33 +7082,6 @@ function createUnspentPattern(client, acc) { * @property {SeriesPattern1} flow */ -/** - * @typedef {Object} SeriesTree_Indicators_RarityMeter - * @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 - */ - /** * @typedef {Object} SeriesTree_Investing * @property {SeriesPattern18} satsPerDay @@ -8064,6 +8129,16 @@ function createUnspentPattern(client, acc) { * @property {ActivityAddrOutputsRealizedSupplyPattern} _100kBtc */ +/** + * @typedef {Object} SeriesTree_Cointime + * @property {SeriesTree_Cointime_Activity} activity + */ + +/** + * @typedef {Object} SeriesTree_Cointime_Activity + * @property {AverageBlockCumulativeSumPattern} coinblocksDestroyed + */ + /** * Main BRK client with series tree and API methods * @extends BrkClientBase @@ -9549,14 +9624,12 @@ class BrkClient extends BrkClientBase { raw: { firstTxinIndex: createSeriesPattern18(this, 'first_txin_index'), outpoint: createSeriesPattern20(this, 'outpoint'), + txoutIndex: createSeriesPattern20(this, 'txout_index'), + value: createSeriesPattern20(this, 'value'), txIndex: createSeriesPattern20(this, 'tx_index'), outputType: createSeriesPattern20(this, 'output_type'), typeIndex: createSeriesPattern20(this, 'type_index'), }, - spent: { - txoutIndex: createSeriesPattern20(this, 'txout_index'), - value: createSeriesPattern20(this, 'value'), - }, count: createCumulativeRollingSumPattern(this, 'input_count'), perSec: create_1m1w1y24hPattern(this, 'inputs_per_sec'), byType: { @@ -9932,140 +10005,184 @@ class BrkClient extends BrkClientBase { value: createPhsReboundThsPattern(this, 'hash_value'), }, }, - cointime: { - activity: { - coinblocksCreated: createAverageBlockCumulativeSumPattern(this, 'coinblocks_created'), - coinblocksStored: createAverageBlockCumulativeSumPattern(this, 'coinblocks_stored'), - liveliness: createSeriesPattern1(this, 'liveliness'), - vaultedness: createSeriesPattern1(this, 'vaultedness'), - ratio: createSeriesPattern1(this, 'activity_to_vaultedness'), - coinblocksDestroyed: createAverageBlockCumulativeSumPattern(this, 'coinblocks_destroyed'), - }, - ageRange: { - under1h: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_under_1h_old'), - _1hTo1d: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_1h_to_1d_old'), - _1dTo1w: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_1d_to_1w_old'), - _1wTo1m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_1w_to_1m_old'), - _1mTo2m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_1m_to_2m_old'), - _2mTo3m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_2m_to_3m_old'), - _3mTo4m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_3m_to_4m_old'), - _4mTo5m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_4m_to_5m_old'), - _5mTo6m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_5m_to_6m_old'), - _6mTo9m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_6m_to_9m_old'), - _9mTo1y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_9m_to_1y_old'), - _1yTo18m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_1y_to_18m_old'), - _18mTo2y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_18m_to_2y_old'), - _2yTo3y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_2y_to_3y_old'), - _3yTo4y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_3y_to_4y_old'), - _4yTo5y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_4y_to_5y_old'), - _5yTo6y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_5y_to_6y_old'), - _6yTo7y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_6y_to_7y_old'), - _7yTo8y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_7y_to_8y_old'), - _8yTo10y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_8y_to_10y_old'), - _10yTo12y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_10y_to_12y_old'), - _12yTo15y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_12y_to_15y_old'), - over15y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_over_15y_old'), - }, - supply: { - vaulted: createBtcCentsSatsUsdPattern(this, 'vaulted_supply'), - active: { - btc: createSeriesPattern1(this, 'active_supply'), - sats: createSeriesPattern1(this, 'active_supply_sats'), - usd: createSeriesPattern1(this, 'active_supply_usd'), - cents: createSeriesPattern1(this, 'active_supply_cents'), - inLoss: createSharePattern2(this, 'cointime_supply_in_loss_share'), + frameworks: { + cointime: { + activity: { + coinblocksCreated: createAverageBlockCumulativeSumPattern(this, 'coinblocks_created'), + coinblocksStored: createAverageBlockCumulativeSumPattern(this, 'coinblocks_stored'), + liveliness: createSeriesPattern1(this, 'liveliness'), + vaultedness: createSeriesPattern1(this, 'vaultedness'), + ratio: createSeriesPattern1(this, 'activity_to_vaultedness'), + }, + ageRange: { + under1h: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_under_1h_old'), + _1hTo1d: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_1h_to_1d_old'), + _1dTo1w: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_1d_to_1w_old'), + _1wTo1m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_1w_to_1m_old'), + _1mTo2m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_1m_to_2m_old'), + _2mTo3m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_2m_to_3m_old'), + _3mTo4m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_3m_to_4m_old'), + _4mTo5m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_4m_to_5m_old'), + _5mTo6m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_5m_to_6m_old'), + _6mTo9m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_6m_to_9m_old'), + _9mTo1y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_9m_to_1y_old'), + _1yTo18m: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_1y_to_18m_old'), + _18mTo2y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_18m_to_2y_old'), + _2yTo3y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_2y_to_3y_old'), + _3yTo4y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_3y_to_4y_old'), + _4yTo5y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_4y_to_5y_old'), + _5yTo6y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_5y_to_6y_old'), + _6yTo7y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_6y_to_7y_old'), + _7yTo8y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_7y_to_8y_old'), + _8yTo10y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_8y_to_10y_old'), + _10yTo12y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_10y_to_12y_old'), + _12yTo15y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_12y_to_15y_old'), + over15y: createCoindaysLivelinessRatioSupplyVaultednessPattern(this, 'utxos_over_15y_old'), + }, + supply: { + vaulted: createBtcCentsSatsUsdPattern(this, 'vaulted_supply'), + active: { + btc: createSeriesPattern1(this, 'active_supply'), + sats: createSeriesPattern1(this, 'active_supply_sats'), + usd: createSeriesPattern1(this, 'active_supply_usd'), + cents: createSeriesPattern1(this, 'active_supply_cents'), + inLoss: createSharePattern2(this, 'cointime_supply_in_loss_share'), + }, + }, + value: { + destroyed: createAverageBlockCumulativeSumPattern(this, 'cointime_value_destroyed'), + created: createAverageBlockCumulativeSumPattern(this, 'cointime_value_created'), + stored: createAverageBlockCumulativeSumPattern(this, 'cointime_value_stored'), + vocdd: createAverageBlockCumulativeSumPattern(this, 'vocdd'), + }, + cap: { + thermo: createCentsUsdPattern3(this, 'thermo_cap'), + investor: createCentsUsdPattern3(this, 'investor_cap'), + vaulted: createCentsUsdPattern3(this, 'vaulted_cap'), + active: createCentsUsdPattern3(this, 'active_cap'), + cointime: createCentsUsdPattern3(this, 'cointime_cap'), + aviv: createPpmRatioPattern2(this, 'aviv_ratio'), + }, + prices: { + 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'), + txVelocityNative: createSeriesPattern1(this, 'cointime_adj_tx_velocity_btc'), + txVelocityFiat: createSeriesPattern1(this, 'cointime_adj_tx_velocity_usd'), + }, + reserveRisk: { + value: createSeriesPattern1(this, 'reserve_risk'), + vocddMedian1y: createSeriesPattern18(this, 'vocdd_median_1y'), + hodlBank: createSeriesPattern18(this, 'hodl_bank'), }, }, - value: { - destroyed: createAverageBlockCumulativeSumPattern(this, 'cointime_value_destroyed'), - created: createAverageBlockCumulativeSumPattern(this, 'cointime_value_created'), - stored: createAverageBlockCumulativeSumPattern(this, 'cointime_value_stored'), - vocdd: createAverageBlockCumulativeSumPattern(this, 'vocdd'), - }, - cap: { - thermo: createCentsUsdPattern3(this, 'thermo_cap'), - investor: createCentsUsdPattern3(this, 'investor_cap'), - vaulted: createCentsUsdPattern3(this, 'vaulted_cap'), - active: createCentsUsdPattern3(this, 'active_cap'), - cointime: createCentsUsdPattern3(this, 'cointime_cap'), - aviv: createPpmRatioPattern2(this, 'aviv_ratio'), - }, - prices: { - 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'), - txVelocityNative: createSeriesPattern1(this, 'cointime_adj_tx_velocity_btc'), - txVelocityFiat: createSeriesPattern1(this, 'cointime_adj_tx_velocity_usd'), - }, - reserveRisk: { - value: createSeriesPattern1(this, 'reserve_risk'), - vocddMedian1y: createSeriesPattern18(this, 'vocdd_median_1y'), - hodlBank: createSeriesPattern18(this, 'hodl_bank'), - }, - }, - coinflow: { - ageRange: { - under1h: createMobilitySpendingSupplyPattern(this, 'utxos_under_1h_old'), - _1hTo1d: createMobilitySpendingSupplyPattern(this, 'utxos_1h_to_1d_old'), - _1dTo1w: createMobilitySpendingSupplyPattern(this, 'utxos_1d_to_1w_old'), - _1wTo1m: createMobilitySpendingSupplyPattern(this, 'utxos_1w_to_1m_old'), - _1mTo2m: createMobilitySpendingSupplyPattern(this, 'utxos_1m_to_2m_old'), - _2mTo3m: createMobilitySpendingSupplyPattern(this, 'utxos_2m_to_3m_old'), - _3mTo4m: createMobilitySpendingSupplyPattern(this, 'utxos_3m_to_4m_old'), - _4mTo5m: createMobilitySpendingSupplyPattern(this, 'utxos_4m_to_5m_old'), - _5mTo6m: createMobilitySpendingSupplyPattern(this, 'utxos_5m_to_6m_old'), - _6mTo9m: createMobilitySpendingSupplyPattern(this, 'utxos_6m_to_9m_old'), - _9mTo1y: createMobilitySpendingSupplyPattern(this, 'utxos_9m_to_1y_old'), - _1yTo18m: createMobilitySpendingSupplyPattern(this, 'utxos_1y_to_18m_old'), - _18mTo2y: createMobilitySpendingSupplyPattern(this, 'utxos_18m_to_2y_old'), - _2yTo3y: createMobilitySpendingSupplyPattern(this, 'utxos_2y_to_3y_old'), - _3yTo4y: createMobilitySpendingSupplyPattern(this, 'utxos_3y_to_4y_old'), - _4yTo5y: createMobilitySpendingSupplyPattern(this, 'utxos_4y_to_5y_old'), - _5yTo6y: createMobilitySpendingSupplyPattern(this, 'utxos_5y_to_6y_old'), - _6yTo7y: createMobilitySpendingSupplyPattern(this, 'utxos_6y_to_7y_old'), - _7yTo8y: createMobilitySpendingSupplyPattern(this, 'utxos_7y_to_8y_old'), - _8yTo10y: createMobilitySpendingSupplyPattern(this, 'utxos_8y_to_10y_old'), - _10yTo12y: createMobilitySpendingSupplyPattern(this, 'utxos_10y_to_12y_old'), - _12yTo15y: createMobilitySpendingSupplyPattern(this, 'utxos_12y_to_15y_old'), - over15y: createMobilitySpendingSupplyPattern(this, 'utxos_over_15y_old'), - }, - supply: { - mobile: { - btc: createSeriesPattern1(this, 'mobile_supply'), - sats: createSeriesPattern1(this, 'mobile_supply_sats'), - usd: createSeriesPattern1(this, 'mobile_supply_usd'), - cents: createSeriesPattern1(this, 'mobile_supply_cents'), - inLoss: createSharePattern2(this, 'coinflow_supply_in_loss_share'), + coinflow: { + ageRange: { + under1h: createMobilitySpendingSupplyPattern(this, 'utxos_under_1h_old'), + _1hTo1d: createMobilitySpendingSupplyPattern(this, 'utxos_1h_to_1d_old'), + _1dTo1w: createMobilitySpendingSupplyPattern(this, 'utxos_1d_to_1w_old'), + _1wTo1m: createMobilitySpendingSupplyPattern(this, 'utxos_1w_to_1m_old'), + _1mTo2m: createMobilitySpendingSupplyPattern(this, 'utxos_1m_to_2m_old'), + _2mTo3m: createMobilitySpendingSupplyPattern(this, 'utxos_2m_to_3m_old'), + _3mTo4m: createMobilitySpendingSupplyPattern(this, 'utxos_3m_to_4m_old'), + _4mTo5m: createMobilitySpendingSupplyPattern(this, 'utxos_4m_to_5m_old'), + _5mTo6m: createMobilitySpendingSupplyPattern(this, 'utxos_5m_to_6m_old'), + _6mTo9m: createMobilitySpendingSupplyPattern(this, 'utxos_6m_to_9m_old'), + _9mTo1y: createMobilitySpendingSupplyPattern(this, 'utxos_9m_to_1y_old'), + _1yTo18m: createMobilitySpendingSupplyPattern(this, 'utxos_1y_to_18m_old'), + _18mTo2y: createMobilitySpendingSupplyPattern(this, 'utxos_18m_to_2y_old'), + _2yTo3y: createMobilitySpendingSupplyPattern(this, 'utxos_2y_to_3y_old'), + _3yTo4y: createMobilitySpendingSupplyPattern(this, 'utxos_3y_to_4y_old'), + _4yTo5y: createMobilitySpendingSupplyPattern(this, 'utxos_4y_to_5y_old'), + _5yTo6y: createMobilitySpendingSupplyPattern(this, 'utxos_5y_to_6y_old'), + _6yTo7y: createMobilitySpendingSupplyPattern(this, 'utxos_6y_to_7y_old'), + _7yTo8y: createMobilitySpendingSupplyPattern(this, 'utxos_7y_to_8y_old'), + _8yTo10y: createMobilitySpendingSupplyPattern(this, 'utxos_8y_to_10y_old'), + _10yTo12y: createMobilitySpendingSupplyPattern(this, 'utxos_10y_to_12y_old'), + _12yTo15y: createMobilitySpendingSupplyPattern(this, 'utxos_12y_to_15y_old'), + over15y: createMobilitySpendingSupplyPattern(this, 'utxos_over_15y_old'), }, - immobile: createBtcCentsSatsUsdPattern(this, 'immobile_supply'), + supply: { + mobile: { + btc: createSeriesPattern1(this, 'mobile_supply'), + sats: createSeriesPattern1(this, 'mobile_supply_sats'), + usd: createSeriesPattern1(this, 'mobile_supply_usd'), + cents: createSeriesPattern1(this, 'mobile_supply_cents'), + inLoss: createSharePattern2(this, 'coinflow_supply_in_loss_share'), + }, + immobile: createBtcCentsSatsUsdPattern(this, 'immobile_supply'), + }, + horizon: { + _8y: createSupplyPattern(this, 'coinflow_8y_supply_in_loss_share'), + _4y: createSupplyPattern(this, 'coinflow_4y_supply_in_loss_share'), + _2y: createSupplyPattern(this, 'coinflow_2y_supply_in_loss_share'), + _1y: createSupplyPattern(this, 'coinflow_1y_supply_in_loss_share'), + _6m: createSupplyPattern(this, 'coinflow_6m_supply_in_loss_share'), + _3m: createSupplyPattern(this, 'coinflow_3m_supply_in_loss_share'), + _1m: createSupplyPattern(this, 'coinflow_1m_supply_in_loss_share'), + }, + cap: createCentsUsdPattern3(this, 'coinflow_cap'), + price: createCentsPpmRatioSatsUsdPattern(this, 'coinflow_price'), }, - horizon: { - _8y: createSupplyPattern(this, 'coinflow_8y_supply_in_loss_share'), - _4y: createSupplyPattern(this, 'coinflow_4y_supply_in_loss_share'), - _2y: createSupplyPattern(this, 'coinflow_2y_supply_in_loss_share'), - _1y: createSupplyPattern(this, 'coinflow_1y_supply_in_loss_share'), - _6m: createSupplyPattern(this, 'coinflow_6m_supply_in_loss_share'), - _3m: createSupplyPattern(this, 'coinflow_3m_supply_in_loss_share'), - _1m: createSupplyPattern(this, 'coinflow_1m_supply_in_loss_share'), - }, - cap: createCentsUsdPattern3(this, 'coinflow_cap'), - price: createCentsPpmRatioSatsUsdPattern(this, 'coinflow_price'), }, - bedrock: { - raw: createFloorLevelLossPattern(this, 'bedrock_raw'), - cointime: createFloorLevelLossPattern(this, 'bedrock_cointime'), - coinflow: createFloorLevelLossPattern(this, 'bedrock_coinflow'), - coinflow8y: createFloorLevelLossPattern(this, 'bedrock_coinflow_8y'), - coinflow4y: createFloorLevelLossPattern(this, 'bedrock_coinflow_4y'), - coinflow2y: createFloorLevelLossPattern(this, 'bedrock_coinflow_2y'), - coinflow1y: createFloorLevelLossPattern(this, 'bedrock_coinflow_1y'), - coinflow6m: createFloorLevelLossPattern(this, 'bedrock_coinflow_6m'), - coinflow3m: createFloorLevelLossPattern(this, 'bedrock_coinflow_3m'), - coinflow1m: createFloorLevelLossPattern(this, 'bedrock_coinflow_1m'), + models: { + bedrock: { + raw: createFloorLevelLossPattern(this, 'bedrock_raw'), + cointime: createFloorLevelLossPattern(this, 'bedrock_cointime'), + coinflow: createFloorLevelLossPattern(this, 'bedrock_coinflow'), + coinflow8y: createFloorLevelLossPattern(this, 'bedrock_coinflow_8y'), + coinflow4y: createFloorLevelLossPattern(this, 'bedrock_coinflow_4y'), + coinflow2y: createFloorLevelLossPattern(this, 'bedrock_coinflow_2y'), + coinflow1y: createFloorLevelLossPattern(this, 'bedrock_coinflow_1y'), + coinflow6m: createFloorLevelLossPattern(this, 'bedrock_coinflow_6m'), + coinflow3m: createFloorLevelLossPattern(this, 'bedrock_coinflow_3m'), + coinflow1m: createFloorLevelLossPattern(this, 'bedrock_coinflow_1m'), + }, + capitalSentiment: { + phase: createSeriesPattern1(this, 'capital_sentiment_phase'), + score: createSeriesPattern1(this, 'capital_sentiment_score'), + }, + rarityMeter: { + 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'), + }, + extremes: { + coinsInLoss: { + threshold: createSeriesPattern1(this, 'rarity_meter_coins_in_loss_threshold'), + tail: createPercentPpmRatioPattern2(this, 'rarity_meter_coins_in_loss_tail'), + rank: createSeriesPattern1(this, 'rarity_meter_coins_in_loss_rank'), + }, + profitTaking: createRankTailThresholdPattern(this, 'rarity_meter_profit_taking'), + capitulation: createRankTailThresholdPattern(this, 'rarity_meter_capitulation'), + peakRegret: createRankTailThresholdPattern(this, 'rarity_meter_peak_regret'), + sellerExhaustion: { + threshold: createSeriesPattern1(this, 'rarity_meter_seller_exhaustion_threshold'), + tail: createPercentPpmRatioPattern2(this, 'rarity_meter_seller_exhaustion_tail'), + rank: createSeriesPattern1(this, 'rarity_meter_seller_exhaustion_rank'), + }, + }, + full: createIndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern(this, 'rarity_meter'), + local: createIndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern(this, 'local_rarity_meter'), + cycle: createIndexPct0Pct1Pct10Pct2Pct20Pct30Pct40Pct5Pct50Pct60Pct70Pct80Pct90Pct95Pct98Pct99ScorePattern(this, 'cycle_rarity_meter'), + }, }, constants: { _0: createSeriesPattern1(this, 'constant_0'), @@ -10235,28 +10352,6 @@ class BrkClient extends BrkClientBase { }, stockToFlow: createSeriesPattern1(this, 'stock_to_flow'), sellerExhaustion: createSeriesPattern1(this, 'seller_exhaustion'), - rarityMeter: { - 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: { satsPerDay: createSeriesPattern18(this, 'dca_sats_per_day'), @@ -11086,6 +11181,11 @@ class BrkClient extends BrkClientBase { }, }, }, + cointime: { + activity: { + coinblocksDestroyed: createAverageBlockCumulativeSumPattern(this, 'coinblocks_destroyed'), + }, + }, }; } diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index 727ff634c..c8910955c 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -94,6 +94,11 @@ TxVersionRaw = int # Reconstruction is a single pass: for each entry, either copy # `prior[idx]` or append the inline body. BlockTemplateDiffEntry = Union[int, "Transaction"] +# Investor phase from the Capital Sentiment model. +# +# Codes are explicit because phase values are persisted. Code `0` represents +# unavailable model inputs and is therefore not a phase. +CapitalSentimentPhase = Literal["raging_bull", "bull", "cautious_bull", "hopeful_bull", "early_bull", "weak_bull", "limbo", "deep_bear", "bear", "early_bear"] # Unsigned cents (u64) - for values that should never be negative. # Used for invested capital, realized cap, etc. # `u64::MAX` is reserved as a NaN sentinel. @@ -4068,6 +4073,15 @@ class PpmPriceRatioPattern: self.price: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, disc)) self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, f'ratio_{disc}')) +class RankTailThresholdPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClient, acc: str): + """Create pattern node with accumulated series name.""" + self.rank: SeriesPattern1[StoredI8] = SeriesPattern1(client, _m(acc, 'rank')) + self.tail: PercentPpmRatioPattern2 = PercentPpmRatioPattern2(client, _m(acc, 'tail')) + self.threshold: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, 'threshold')) + class RatioTransferValuePattern: """Pattern struct for repeated tree structure.""" @@ -4705,17 +4719,12 @@ class SeriesTree_Inputs_Raw: def __init__(self, client: BrkClient, base_path: str = ''): self.first_txin_index: SeriesPattern18[TxInIndex] = SeriesPattern18(client, 'first_txin_index') self.outpoint: SeriesPattern20[OutPoint] = SeriesPattern20(client, 'outpoint') + self.txout_index: SeriesPattern20[TxOutIndex] = SeriesPattern20(client, 'txout_index') + self.value: SeriesPattern20[Sats] = SeriesPattern20(client, 'value') self.tx_index: SeriesPattern20[TxIndex] = SeriesPattern20(client, 'tx_index') self.output_type: SeriesPattern20[OutputType] = SeriesPattern20(client, 'output_type') self.type_index: SeriesPattern20[TypeIndex] = SeriesPattern20(client, 'type_index') -class SeriesTree_Inputs_Spent: - """Series tree node.""" - - def __init__(self, client: BrkClient, base_path: str = ''): - self.txout_index: SeriesPattern20[TxOutIndex] = SeriesPattern20(client, 'txout_index') - self.value: SeriesPattern20[Sats] = SeriesPattern20(client, 'value') - class SeriesTree_Inputs_ByType_InputCount: """Series tree node.""" @@ -4780,7 +4789,6 @@ class SeriesTree_Inputs: def __init__(self, client: BrkClient, base_path: str = ''): self.raw: SeriesTree_Inputs_Raw = SeriesTree_Inputs_Raw(client) - self.spent: SeriesTree_Inputs_Spent = SeriesTree_Inputs_Spent(client) self.count: CumulativeRollingSumPattern = CumulativeRollingSumPattern(client, 'input_count') self.per_sec: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, 'inputs_per_sec') self.by_type: SeriesTree_Inputs_ByType = SeriesTree_Inputs_ByType(client) @@ -5312,7 +5320,7 @@ class SeriesTree_Mining: self.rewards: SeriesTree_Mining_Rewards = SeriesTree_Mining_Rewards(client) self.hashrate: SeriesTree_Mining_Hashrate = SeriesTree_Mining_Hashrate(client) -class SeriesTree_Cointime_Activity: +class SeriesTree_Frameworks_Cointime_Activity: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5321,9 +5329,8 @@ class SeriesTree_Cointime_Activity: self.liveliness: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'liveliness') self.vaultedness: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'vaultedness') self.ratio: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'activity_to_vaultedness') - self.coinblocks_destroyed: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, 'coinblocks_destroyed') -class SeriesTree_Cointime_AgeRange: +class SeriesTree_Frameworks_Cointime_AgeRange: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5351,7 +5358,7 @@ class SeriesTree_Cointime_AgeRange: self._12y_to_15y: CoindaysLivelinessRatioSupplyVaultednessPattern = CoindaysLivelinessRatioSupplyVaultednessPattern(client, 'utxos_12y_to_15y_old') self.over_15y: CoindaysLivelinessRatioSupplyVaultednessPattern = CoindaysLivelinessRatioSupplyVaultednessPattern(client, 'utxos_over_15y_old') -class SeriesTree_Cointime_Supply_Active: +class SeriesTree_Frameworks_Cointime_Supply_Active: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5361,14 +5368,14 @@ class SeriesTree_Cointime_Supply_Active: self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'active_supply_cents') self.in_loss: SharePattern2 = SharePattern2(client, 'cointime_supply_in_loss_share') -class SeriesTree_Cointime_Supply: +class SeriesTree_Frameworks_Cointime_Supply: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): self.vaulted: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'vaulted_supply') - self.active: SeriesTree_Cointime_Supply_Active = SeriesTree_Cointime_Supply_Active(client) + self.active: SeriesTree_Frameworks_Cointime_Supply_Active = SeriesTree_Frameworks_Cointime_Supply_Active(client) -class SeriesTree_Cointime_Value: +class SeriesTree_Frameworks_Cointime_Value: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5377,7 +5384,7 @@ class SeriesTree_Cointime_Value: self.stored: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, 'cointime_value_stored') self.vocdd: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, 'vocdd') -class SeriesTree_Cointime_Cap: +class SeriesTree_Frameworks_Cointime_Cap: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5388,7 +5395,7 @@ class SeriesTree_Cointime_Cap: self.cointime: CentsUsdPattern3 = CentsUsdPattern3(client, 'cointime_cap') self.aviv: PpmRatioPattern2 = PpmRatioPattern2(client, 'aviv_ratio') -class SeriesTree_Cointime_Prices: +class SeriesTree_Frameworks_Cointime_Prices: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5397,7 +5404,7 @@ class SeriesTree_Cointime_Prices: self.true_market_mean: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'true_market_mean') self.cointime: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'cointime_price') -class SeriesTree_Cointime_Adjusted: +class SeriesTree_Frameworks_Cointime_Adjusted: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5405,7 +5412,7 @@ class SeriesTree_Cointime_Adjusted: self.tx_velocity_native: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'cointime_adj_tx_velocity_btc') self.tx_velocity_fiat: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'cointime_adj_tx_velocity_usd') -class SeriesTree_Cointime_ReserveRisk: +class SeriesTree_Frameworks_Cointime_ReserveRisk: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5413,20 +5420,20 @@ class SeriesTree_Cointime_ReserveRisk: self.vocdd_median_1y: SeriesPattern18[StoredF64] = SeriesPattern18(client, 'vocdd_median_1y') self.hodl_bank: SeriesPattern18[StoredF64] = SeriesPattern18(client, 'hodl_bank') -class SeriesTree_Cointime: +class SeriesTree_Frameworks_Cointime: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): - self.activity: SeriesTree_Cointime_Activity = SeriesTree_Cointime_Activity(client) - self.age_range: SeriesTree_Cointime_AgeRange = SeriesTree_Cointime_AgeRange(client) - self.supply: SeriesTree_Cointime_Supply = SeriesTree_Cointime_Supply(client) - self.value: SeriesTree_Cointime_Value = SeriesTree_Cointime_Value(client) - self.cap: SeriesTree_Cointime_Cap = SeriesTree_Cointime_Cap(client) - self.prices: SeriesTree_Cointime_Prices = SeriesTree_Cointime_Prices(client) - self.adjusted: SeriesTree_Cointime_Adjusted = SeriesTree_Cointime_Adjusted(client) - self.reserve_risk: SeriesTree_Cointime_ReserveRisk = SeriesTree_Cointime_ReserveRisk(client) + self.activity: SeriesTree_Frameworks_Cointime_Activity = SeriesTree_Frameworks_Cointime_Activity(client) + self.age_range: SeriesTree_Frameworks_Cointime_AgeRange = SeriesTree_Frameworks_Cointime_AgeRange(client) + self.supply: SeriesTree_Frameworks_Cointime_Supply = SeriesTree_Frameworks_Cointime_Supply(client) + self.value: SeriesTree_Frameworks_Cointime_Value = SeriesTree_Frameworks_Cointime_Value(client) + self.cap: SeriesTree_Frameworks_Cointime_Cap = SeriesTree_Frameworks_Cointime_Cap(client) + self.prices: SeriesTree_Frameworks_Cointime_Prices = SeriesTree_Frameworks_Cointime_Prices(client) + self.adjusted: SeriesTree_Frameworks_Cointime_Adjusted = SeriesTree_Frameworks_Cointime_Adjusted(client) + self.reserve_risk: SeriesTree_Frameworks_Cointime_ReserveRisk = SeriesTree_Frameworks_Cointime_ReserveRisk(client) -class SeriesTree_Coinflow_AgeRange: +class SeriesTree_Frameworks_Coinflow_AgeRange: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5454,7 +5461,7 @@ class SeriesTree_Coinflow_AgeRange: self._12y_to_15y: MobilitySpendingSupplyPattern = MobilitySpendingSupplyPattern(client, 'utxos_12y_to_15y_old') self.over_15y: MobilitySpendingSupplyPattern = MobilitySpendingSupplyPattern(client, 'utxos_over_15y_old') -class SeriesTree_Coinflow_Supply_Mobile: +class SeriesTree_Frameworks_Coinflow_Supply_Mobile: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5464,14 +5471,14 @@ class SeriesTree_Coinflow_Supply_Mobile: self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'mobile_supply_cents') self.in_loss: SharePattern2 = SharePattern2(client, 'coinflow_supply_in_loss_share') -class SeriesTree_Coinflow_Supply: +class SeriesTree_Frameworks_Coinflow_Supply: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): - self.mobile: SeriesTree_Coinflow_Supply_Mobile = SeriesTree_Coinflow_Supply_Mobile(client) + self.mobile: SeriesTree_Frameworks_Coinflow_Supply_Mobile = SeriesTree_Frameworks_Coinflow_Supply_Mobile(client) self.immobile: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'immobile_supply') -class SeriesTree_Coinflow_Horizon: +class SeriesTree_Frameworks_Coinflow_Horizon: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5483,17 +5490,24 @@ class SeriesTree_Coinflow_Horizon: self._3m: SupplyPattern = SupplyPattern(client, 'coinflow_3m_supply_in_loss_share') self._1m: SupplyPattern = SupplyPattern(client, 'coinflow_1m_supply_in_loss_share') -class SeriesTree_Coinflow: +class SeriesTree_Frameworks_Coinflow: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): - self.age_range: SeriesTree_Coinflow_AgeRange = SeriesTree_Coinflow_AgeRange(client) - self.supply: SeriesTree_Coinflow_Supply = SeriesTree_Coinflow_Supply(client) - self.horizon: SeriesTree_Coinflow_Horizon = SeriesTree_Coinflow_Horizon(client) + self.age_range: SeriesTree_Frameworks_Coinflow_AgeRange = SeriesTree_Frameworks_Coinflow_AgeRange(client) + self.supply: SeriesTree_Frameworks_Coinflow_Supply = SeriesTree_Frameworks_Coinflow_Supply(client) + self.horizon: SeriesTree_Frameworks_Coinflow_Horizon = SeriesTree_Frameworks_Coinflow_Horizon(client) self.cap: CentsUsdPattern3 = CentsUsdPattern3(client, 'coinflow_cap') self.price: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'coinflow_price') -class SeriesTree_Bedrock: +class SeriesTree_Frameworks: + """Series tree node.""" + + def __init__(self, client: BrkClient, base_path: str = ''): + self.cointime: SeriesTree_Frameworks_Cointime = SeriesTree_Frameworks_Cointime(client) + self.coinflow: SeriesTree_Frameworks_Coinflow = SeriesTree_Frameworks_Coinflow(client) + +class SeriesTree_Models_Bedrock: """Series tree node.""" def __init__(self, client: BrkClient, base_path: str = ''): @@ -5508,6 +5522,77 @@ class SeriesTree_Bedrock: self.coinflow_3m: FloorLevelLossPattern = FloorLevelLossPattern(client, 'bedrock_coinflow_3m') self.coinflow_1m: FloorLevelLossPattern = FloorLevelLossPattern(client, 'bedrock_coinflow_1m') +class SeriesTree_Models_CapitalSentiment: + """Series tree node.""" + + def __init__(self, client: BrkClient, base_path: str = ''): + self.phase: SeriesPattern1[CapitalSentimentPhase] = SeriesPattern1(client, 'capital_sentiment_phase') + self.score: SeriesPattern1[StoredI8] = SeriesPattern1(client, 'capital_sentiment_score') + +class SeriesTree_Models_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_Models_RarityMeter_Extremes_CoinsInLoss: + """Series tree node.""" + + def __init__(self, client: BrkClient, base_path: str = ''): + self.threshold: SeriesPattern1[Bitcoin] = SeriesPattern1(client, 'rarity_meter_coins_in_loss_threshold') + self.tail: PercentPpmRatioPattern2 = PercentPpmRatioPattern2(client, 'rarity_meter_coins_in_loss_tail') + self.rank: SeriesPattern1[StoredI8] = SeriesPattern1(client, 'rarity_meter_coins_in_loss_rank') + +class SeriesTree_Models_RarityMeter_Extremes_SellerExhaustion: + """Series tree node.""" + + def __init__(self, client: BrkClient, base_path: str = ''): + self.threshold: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rarity_meter_seller_exhaustion_threshold') + self.tail: PercentPpmRatioPattern2 = PercentPpmRatioPattern2(client, 'rarity_meter_seller_exhaustion_tail') + self.rank: SeriesPattern1[StoredI8] = SeriesPattern1(client, 'rarity_meter_seller_exhaustion_rank') + +class SeriesTree_Models_RarityMeter_Extremes: + """Series tree node.""" + + def __init__(self, client: BrkClient, base_path: str = ''): + self.coins_in_loss: SeriesTree_Models_RarityMeter_Extremes_CoinsInLoss = SeriesTree_Models_RarityMeter_Extremes_CoinsInLoss(client) + self.profit_taking: RankTailThresholdPattern = RankTailThresholdPattern(client, 'rarity_meter_profit_taking') + self.capitulation: RankTailThresholdPattern = RankTailThresholdPattern(client, 'rarity_meter_capitulation') + self.peak_regret: RankTailThresholdPattern = RankTailThresholdPattern(client, 'rarity_meter_peak_regret') + self.seller_exhaustion: SeriesTree_Models_RarityMeter_Extremes_SellerExhaustion = SeriesTree_Models_RarityMeter_Extremes_SellerExhaustion(client) + +class SeriesTree_Models_RarityMeter: + """Series tree node.""" + + def __init__(self, client: BrkClient, base_path: str = ''): + self.components: SeriesTree_Models_RarityMeter_Components = SeriesTree_Models_RarityMeter_Components(client) + self.extremes: SeriesTree_Models_RarityMeter_Extremes = SeriesTree_Models_RarityMeter_Extremes(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_Models: + """Series tree node.""" + + def __init__(self, client: BrkClient, base_path: str = ''): + self.bedrock: SeriesTree_Models_Bedrock = SeriesTree_Models_Bedrock(client) + self.capital_sentiment: SeriesTree_Models_CapitalSentiment = SeriesTree_Models_CapitalSentiment(client) + self.rarity_meter: SeriesTree_Models_RarityMeter = SeriesTree_Models_RarityMeter(client) + class SeriesTree_Constants: """Series tree node.""" @@ -5807,35 +5892,6 @@ 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.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.""" @@ -5850,7 +5906,6 @@ class SeriesTree_Indicators: self.dormancy: SeriesTree_Indicators_Dormancy = SeriesTree_Indicators_Dormancy(client) self.stock_to_flow: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'stock_to_flow') self.seller_exhaustion: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'seller_exhaustion') - self.rarity_meter: SeriesTree_Indicators_RarityMeter = SeriesTree_Indicators_RarityMeter(client) class SeriesTree_Investing_Period_DcaCostBasis: """Series tree node.""" @@ -6974,6 +7029,18 @@ class SeriesTree_Cohorts: self.utxo: SeriesTree_Cohorts_Utxo = SeriesTree_Cohorts_Utxo(client) self.addr: SeriesTree_Cohorts_Addr = SeriesTree_Cohorts_Addr(client) +class SeriesTree_Cointime_Activity: + """Series tree node.""" + + def __init__(self, client: BrkClient, base_path: str = ''): + self.coinblocks_destroyed: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, 'coinblocks_destroyed') + +class SeriesTree_Cointime: + """Series tree node.""" + + def __init__(self, client: BrkClient, base_path: str = ''): + self.activity: SeriesTree_Cointime_Activity = SeriesTree_Cointime_Activity(client) + class SeriesTree: """Series tree node.""" @@ -6986,9 +7053,8 @@ class SeriesTree: self.scripts: SeriesTree_Scripts = SeriesTree_Scripts(client) self.op_return: SeriesTree_OpReturn = SeriesTree_OpReturn(client) self.mining: SeriesTree_Mining = SeriesTree_Mining(client) - self.cointime: SeriesTree_Cointime = SeriesTree_Cointime(client) - self.coinflow: SeriesTree_Coinflow = SeriesTree_Coinflow(client) - self.bedrock: SeriesTree_Bedrock = SeriesTree_Bedrock(client) + self.frameworks: SeriesTree_Frameworks = SeriesTree_Frameworks(client) + self.models: SeriesTree_Models = SeriesTree_Models(client) self.constants: SeriesTree_Constants = SeriesTree_Constants(client) self.indexes: SeriesTree_Indexes = SeriesTree_Indexes(client) self.indicators: SeriesTree_Indicators = SeriesTree_Indicators(client) @@ -6998,6 +7064,7 @@ class SeriesTree: self.price: SeriesTree_Price = SeriesTree_Price(client) self.supply: SeriesTree_Supply = SeriesTree_Supply(client) self.cohorts: SeriesTree_Cohorts = SeriesTree_Cohorts(client) + self.cointime: SeriesTree_Cointime = SeriesTree_Cointime(client) class BrkClient(BrkClientBase): """Main BRK client with series tree and API methods.""" diff --git a/website/llms-full.txt b/website/llms-full.txt index 25f209d61..f6262d2b8 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: 57754 +- Metrics: 57781 - 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 07e742733..e1fa5b001 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. 57754 on-chain time-series and 97 API operations. No authentication required. +> Free, open-source Bitcoin analytics API and block explorer. 57781 on-chain time-series and 97 API operations. No authentication required. ## API diff --git a/website_next/llms-full.txt b/website_next/llms-full.txt index 25f209d61..f6262d2b8 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: 57754 +- Metrics: 57781 - 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 07e742733..e1fa5b001 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. 57754 on-chain time-series and 97 API operations. No authentication required. +> Free, open-source Bitcoin analytics API and block explorer. 57781 on-chain time-series and 97 API operations. No authentication required. ## API