From e89a67b9a77616ffb8bfc70a2c94b68071f7ed3c Mon Sep 17 00:00:00 2001 From: nym21 Date: Mon, 29 Dec 2025 17:02:17 +0100 Subject: [PATCH] global: snapshot --- Cargo.lock | 4 + crates/brk_binder/Cargo.toml | 2 + crates/brk_binder/src/javascript.rs | 34 + crates/brk_binder/src/python.rs | 28 + crates/brk_binder/src/rust.rs | 7 +- crates/brk_binder/src/types/case.rs | 3 +- crates/brk_client/Cargo.toml | 1 + crates/brk_client/src/lib.rs | 994 +++++----- crates/brk_computer/src/grouped/transforms.rs | 17 +- .../src/stateful/cohorts/address.rs | 5 +- .../src/stateful/cohorts/address_cohorts.rs | 96 +- .../brk_computer/src/stateful/cohorts/utxo.rs | 4 +- .../src/stateful/cohorts/utxo_cohorts/mod.rs | 249 +-- .../src/stateful/metrics/config.rs | 6 +- crates/brk_grouper/Cargo.toml | 1 + crates/brk_grouper/README.md | 5 +- crates/brk_grouper/src/address.rs | 13 +- crates/brk_grouper/src/amount_filter.rs | 36 - crates/brk_grouper/src/by_age_range.rs | 159 +- crates/brk_grouper/src/by_amount_range.rs | 214 +- crates/brk_grouper/src/by_epoch.rs | 67 +- crates/brk_grouper/src/by_ge_amount.rs | 117 +- crates/brk_grouper/src/by_lt_amount.rs | 115 +- crates/brk_grouper/src/by_max_age.rs | 148 +- crates/brk_grouper/src/by_min_age.rs | 148 +- crates/brk_grouper/src/by_spendable_type.rs | 103 +- crates/brk_grouper/src/by_term.rs | 49 +- crates/brk_grouper/src/by_year.rs | 145 +- crates/brk_grouper/src/cohort_context.rs | 20 + crates/brk_grouper/src/cohort_name.rs | 15 + crates/brk_grouper/src/filter.rs | 41 - crates/brk_grouper/src/filtered.rs | 10 +- crates/brk_grouper/src/lib.rs | 2 + crates/brk_grouper/src/term.rs | 7 - crates/brk_grouper/src/time_filter.rs | 71 - crates/brk_grouper/src/utxo.rs | 4 +- crates/brk_types/src/halvingepoch.rs | 2 +- crates/brk_types/src/year.rs | 2 +- modules/brk-client/index.js | 1749 +++++++++++++---- packages/brk_client/__init__.py | 1193 ++++++++--- 40 files changed, 4057 insertions(+), 1829 deletions(-) create mode 100644 crates/brk_grouper/src/cohort_name.rs diff --git a/Cargo.lock b/Cargo.lock index 6844ec5b2..730e96d2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,10 +576,12 @@ dependencies = [ name = "brk_binder" version = "0.1.0-alpha.1" dependencies = [ + "brk_grouper", "brk_query", "brk_types", "oas3", "schemars", + "serde", "serde_json", "vecdb", ] @@ -628,6 +630,7 @@ dependencies = [ name = "brk_client" version = "0.1.0-alpha.1" dependencies = [ + "brk_grouper", "brk_types", "minreq", "serde", @@ -698,6 +701,7 @@ dependencies = [ "brk_traversable", "brk_types", "rayon", + "serde", "vecdb", ] diff --git a/crates/brk_binder/Cargo.toml b/crates/brk_binder/Cargo.toml index 168cc5160..2d753d8c7 100644 --- a/crates/brk_binder/Cargo.toml +++ b/crates/brk_binder/Cargo.toml @@ -9,9 +9,11 @@ repository.workspace = true build = "build.rs" [dependencies] +brk_grouper = { workspace = true } brk_query = { workspace = true } brk_types = { workspace = true } oas3 = "0.20" schemars = { workspace = true } +serde = { workspace = true } serde_json = { workspace = true } vecdb = { workspace = true } diff --git a/crates/brk_binder/src/javascript.rs b/crates/brk_binder/src/javascript.rs index 78fad5205..18ae59bce 100644 --- a/crates/brk_binder/src/javascript.rs +++ b/crates/brk_binder/src/javascript.rs @@ -1,5 +1,9 @@ use std::{collections::HashSet, fmt::Write as FmtWrite, fs, io, path::Path}; +use brk_grouper::{ + AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, EPOCH_NAMES, GE_AMOUNT_NAMES, LT_AMOUNT_NAMES, + MAX_AGE_NAMES, MIN_AGE_NAMES, SPENDABLE_TYPE_NAMES, TERM_NAMES, YEAR_NAMES, +}; use brk_types::{pools, Index, TreeNode}; use serde_json::Value; @@ -60,6 +64,36 @@ fn generate_constants(output: &mut String) { writeln!(output, " {}: \"{}\",", pool.slug(), pool.name).unwrap(); } writeln!(output, "}});\n").unwrap(); + + // Cohort names - serialize from brk_grouper using serde_json + generate_cohort_names(output); +} + +fn generate_cohort_names(output: &mut String) { + use serde::Serialize; + + fn export_const(output: &mut String, name: &str, value: &T) { + let json = serde_json::to_string_pretty(value).unwrap(); + writeln!( + output, + "export const {} = /** @type {{const}} */ ({});\n", + name, json + ) + .unwrap(); + } + + writeln!(output, "// Cohort names\n").unwrap(); + + export_const(output, "TERM_NAMES", &TERM_NAMES); + export_const(output, "EPOCH_NAMES", &EPOCH_NAMES); + export_const(output, "YEAR_NAMES", &YEAR_NAMES); + export_const(output, "SPENDABLE_TYPE_NAMES", &SPENDABLE_TYPE_NAMES); + export_const(output, "AGE_RANGE_NAMES", &AGE_RANGE_NAMES); + export_const(output, "MAX_AGE_NAMES", &MAX_AGE_NAMES); + export_const(output, "MIN_AGE_NAMES", &MIN_AGE_NAMES); + export_const(output, "AMOUNT_RANGE_NAMES", &AMOUNT_RANGE_NAMES); + export_const(output, "GE_AMOUNT_NAMES", &GE_AMOUNT_NAMES); + export_const(output, "LT_AMOUNT_NAMES", <_AMOUNT_NAMES); } fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) { diff --git a/crates/brk_binder/src/python.rs b/crates/brk_binder/src/python.rs index e19e69fb0..29982a9fd 100644 --- a/crates/brk_binder/src/python.rs +++ b/crates/brk_binder/src/python.rs @@ -1,6 +1,11 @@ use std::{collections::HashSet, fmt::Write as FmtWrite, fs, io, path::Path}; +use brk_grouper::{ + AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, EPOCH_NAMES, GE_AMOUNT_NAMES, LT_AMOUNT_NAMES, + MAX_AGE_NAMES, MIN_AGE_NAMES, SPENDABLE_TYPE_NAMES, TERM_NAMES, YEAR_NAMES, +}; use brk_types::{pools, Index, TreeNode}; +use serde::Serialize; use serde_json::Value; use crate::{ @@ -69,6 +74,29 @@ fn generate_constants(output: &mut String) { writeln!(output, " \"{}\": \"{}\",", pool.slug(), pool.name).unwrap(); } writeln!(output, "}}\n").unwrap(); + + // Cohort names + generate_cohort_names(output); +} + +fn generate_cohort_names(output: &mut String) { + fn export_const(output: &mut String, name: &str, value: &T) { + let json = serde_json::to_string_pretty(value).unwrap(); + writeln!(output, "{}: Final = {}\n", name, json).unwrap(); + } + + writeln!(output, "# Cohort names\n").unwrap(); + + export_const(output, "TERM_NAMES", &TERM_NAMES); + export_const(output, "EPOCH_NAMES", &EPOCH_NAMES); + export_const(output, "YEAR_NAMES", &YEAR_NAMES); + export_const(output, "SPENDABLE_TYPE_NAMES", &SPENDABLE_TYPE_NAMES); + export_const(output, "AGE_RANGE_NAMES", &AGE_RANGE_NAMES); + export_const(output, "MAX_AGE_NAMES", &MAX_AGE_NAMES); + export_const(output, "MIN_AGE_NAMES", &MIN_AGE_NAMES); + export_const(output, "AMOUNT_RANGE_NAMES", &AMOUNT_RANGE_NAMES); + export_const(output, "GE_AMOUNT_NAMES", &GE_AMOUNT_NAMES); + export_const(output, "LT_AMOUNT_NAMES", <_AMOUNT_NAMES); } fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) { diff --git a/crates/brk_binder/src/rust.rs b/crates/brk_binder/src/rust.rs index 42174dbdb..2987910b6 100644 --- a/crates/brk_binder/src/rust.rs +++ b/crates/brk_binder/src/rust.rs @@ -21,7 +21,9 @@ pub fn generate_rust_client( writeln!(output, "// Auto-generated BRK Rust client").unwrap(); writeln!(output, "// Do not edit manually\n").unwrap(); writeln!(output, "#![allow(non_camel_case_types)]").unwrap(); - writeln!(output, "#![allow(dead_code)]\n").unwrap(); + writeln!(output, "#![allow(dead_code)]").unwrap(); + writeln!(output, "#![allow(unused_variables)]").unwrap(); + writeln!(output, "#![allow(clippy::useless_format)]\n").unwrap(); generate_imports(&mut output); generate_base_client(&mut output); @@ -41,7 +43,8 @@ fn generate_imports(output: &mut String) { output, r#"use std::sync::Arc; use serde::de::DeserializeOwned; -use brk_types::*; +pub use brk_grouper::*; +pub use brk_types::*; "# ) diff --git a/crates/brk_binder/src/types/case.rs b/crates/brk_binder/src/types/case.rs index 76d887a86..3bce6fcc4 100644 --- a/crates/brk_binder/src/types/case.rs +++ b/crates/brk_binder/src/types/case.rs @@ -14,7 +14,8 @@ pub fn to_pascal_case(s: &str) -> String { /// Convert a string to snake_case, handling Rust keywords. pub fn to_snake_case(s: &str) -> String { - let sanitized = s.replace('-', "_"); + // Convert to lowercase and replace dashes with underscores + let sanitized = s.to_lowercase().replace('-', "_"); // Prefix with _ if starts with digit let sanitized = if sanitized.chars().next().is_some_and(|c| c.is_ascii_digit()) { diff --git a/crates/brk_client/Cargo.toml b/crates/brk_client/Cargo.toml index f63a54b17..fc8fbc2eb 100644 --- a/crates/brk_client/Cargo.toml +++ b/crates/brk_client/Cargo.toml @@ -9,6 +9,7 @@ repository.workspace = true build = "build.rs" [dependencies] +brk_grouper = { workspace = true } brk_types = { workspace = true } minreq = { workspace = true } serde = { workspace = true } diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index 2b1d47af4..d3f7059d5 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -3,10 +3,13 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] +#![allow(unused_variables)] +#![allow(clippy::useless_format)] use std::sync::Arc; use serde::de::DeserializeOwned; -use brk_types::*; +pub use brk_grouper::*; +pub use brk_types::*; /// Error type for BRK client operations. @@ -109,8 +112,15 @@ impl MetricNode { } /// Fetch data points within a range. - pub fn get_range(&self, from: &str, to: &str) -> Result> { - let path = format!("{}?from={}&to={}", self.path, from, to); + pub fn get_range(&self, from: Option<&str>, to: Option<&str>) -> Result> { + let mut params = Vec::new(); + if let Some(f) = from { params.push(format!("from={}", f)); } + if let Some(t) = to { params.push(format!("to={}", t)); } + let path = if params.is_empty() { + self.path.clone() + } else { + format!("{}?{}", self.path, params.join("&")) + }; self.client.get(&path) } } @@ -1045,6 +1055,43 @@ impl Ratio1ySdPattern { } } +/// Pattern struct for repeated tree structure. +pub struct AXbtPattern { + pub _1d_dominance: BlockCountPattern, + pub _1m_blocks_mined: Indexes, + pub _1m_dominance: Indexes, + pub _1w_blocks_mined: Indexes, + pub _1w_dominance: Indexes, + pub _1y_blocks_mined: Indexes, + pub _1y_dominance: Indexes, + pub blocks_mined: BlockCountPattern, + pub coinbase: UnclaimedRewardsPattern, + pub days_since_block: Indexes, + pub dominance: BlockCountPattern, + pub fee: FeePattern2, + pub subsidy: FeePattern2, +} + +impl AXbtPattern { + pub fn new(client: Arc, base_path: &str) -> Self { + Self { + _1d_dominance: BlockCountPattern::new(client.clone(), &format!("{base_path}/1d_dominance")), + _1m_blocks_mined: Indexes::new(client.clone(), &format!("{base_path}/1m_blocks_mined")), + _1m_dominance: Indexes::new(client.clone(), &format!("{base_path}/1m_dominance")), + _1w_blocks_mined: Indexes::new(client.clone(), &format!("{base_path}/1w_blocks_mined")), + _1w_dominance: Indexes::new(client.clone(), &format!("{base_path}/1w_dominance")), + _1y_blocks_mined: Indexes::new(client.clone(), &format!("{base_path}/1y_blocks_mined")), + _1y_dominance: Indexes::new(client.clone(), &format!("{base_path}/1y_dominance")), + blocks_mined: BlockCountPattern::new(client.clone(), &format!("{base_path}/blocks_mined")), + coinbase: UnclaimedRewardsPattern::new(client.clone(), &format!("{base_path}/coinbase")), + days_since_block: Indexes::new(client.clone(), &format!("{base_path}/days_since_block")), + dominance: BlockCountPattern::new(client.clone(), &format!("{base_path}/dominance")), + fee: FeePattern2::new(client.clone(), &format!("{base_path}/fee")), + subsidy: FeePattern2::new(client.clone(), &format!("{base_path}/subsidy")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct ActivePriceRatioPattern { pub ratio: Indexes, @@ -1082,43 +1129,6 @@ impl ActivePriceRatioPattern { } } -/// Pattern struct for repeated tree structure. -pub struct AXbtPattern { - pub _1d_dominance: BlockCountPattern, - pub _1m_blocks_mined: Indexes, - pub _1m_dominance: Indexes, - pub _1w_blocks_mined: Indexes, - pub _1w_dominance: Indexes, - pub _1y_blocks_mined: Indexes, - pub _1y_dominance: Indexes, - pub blocks_mined: BlockCountPattern, - pub coinbase: UnclaimedRewardsPattern, - pub days_since_block: Indexes, - pub dominance: BlockCountPattern, - pub fee: UnclaimedRewardsPattern, - pub subsidy: UnclaimedRewardsPattern, -} - -impl AXbtPattern { - pub fn new(client: Arc, base_path: &str) -> Self { - Self { - _1d_dominance: BlockCountPattern::new(client.clone(), &format!("{base_path}/1d_dominance")), - _1m_blocks_mined: Indexes::new(client.clone(), &format!("{base_path}/1m_blocks_mined")), - _1m_dominance: Indexes::new(client.clone(), &format!("{base_path}/1m_dominance")), - _1w_blocks_mined: Indexes::new(client.clone(), &format!("{base_path}/1w_blocks_mined")), - _1w_dominance: Indexes::new(client.clone(), &format!("{base_path}/1w_dominance")), - _1y_blocks_mined: Indexes::new(client.clone(), &format!("{base_path}/1y_blocks_mined")), - _1y_dominance: Indexes::new(client.clone(), &format!("{base_path}/1y_dominance")), - blocks_mined: BlockCountPattern::new(client.clone(), &format!("{base_path}/blocks_mined")), - coinbase: UnclaimedRewardsPattern::new(client.clone(), &format!("{base_path}/coinbase")), - days_since_block: Indexes::new(client.clone(), &format!("{base_path}/days_since_block")), - dominance: BlockCountPattern::new(client.clone(), &format!("{base_path}/dominance")), - fee: UnclaimedRewardsPattern::new(client.clone(), &format!("{base_path}/fee")), - subsidy: UnclaimedRewardsPattern::new(client.clone(), &format!("{base_path}/subsidy")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct BitcoinPattern { pub average: Indexes4, @@ -1183,6 +1193,35 @@ impl BlockSizePattern { } } +/// Pattern struct for repeated tree structure. +pub struct RelativePattern { + pub neg_unrealized_loss_rel_to_market_cap: Indexes27, + pub net_unrealized_pnl_rel_to_market_cap: Indexes26, + pub supply_in_loss_rel_to_circulating_supply: Indexes27, + pub supply_in_loss_rel_to_own_supply: Indexes27, + pub supply_in_profit_rel_to_circulating_supply: Indexes27, + pub supply_in_profit_rel_to_own_supply: Indexes27, + pub supply_rel_to_circulating_supply: Indexes, + pub unrealized_loss_rel_to_market_cap: Indexes27, + pub unrealized_profit_rel_to_market_cap: Indexes27, +} + +impl RelativePattern { + pub fn new(client: Arc, base_path: &str) -> Self { + Self { + neg_unrealized_loss_rel_to_market_cap: Indexes27::new(client.clone(), &format!("{base_path}/neg_unrealized_loss_rel_to_market_cap")), + net_unrealized_pnl_rel_to_market_cap: Indexes26::new(client.clone(), &format!("{base_path}/net_unrealized_pnl_rel_to_market_cap")), + supply_in_loss_rel_to_circulating_supply: Indexes27::new(client.clone(), &format!("{base_path}/supply_in_loss_rel_to_circulating_supply")), + supply_in_loss_rel_to_own_supply: Indexes27::new(client.clone(), &format!("{base_path}/supply_in_loss_rel_to_own_supply")), + supply_in_profit_rel_to_circulating_supply: Indexes27::new(client.clone(), &format!("{base_path}/supply_in_profit_rel_to_circulating_supply")), + supply_in_profit_rel_to_own_supply: Indexes27::new(client.clone(), &format!("{base_path}/supply_in_profit_rel_to_own_supply")), + supply_rel_to_circulating_supply: Indexes::new(client.clone(), &format!("{base_path}/supply_rel_to_circulating_supply")), + unrealized_loss_rel_to_market_cap: Indexes27::new(client.clone(), &format!("{base_path}/unrealized_loss_rel_to_market_cap")), + unrealized_profit_rel_to_market_cap: Indexes27::new(client.clone(), &format!("{base_path}/unrealized_profit_rel_to_market_cap")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct UnrealizedPattern { pub neg_unrealized_loss: Indexes26, @@ -1213,30 +1252,29 @@ impl UnrealizedPattern { } /// Pattern struct for repeated tree structure. -pub struct RelativePattern { - pub neg_unrealized_loss_rel_to_market_cap: Indexes27, - pub net_unrealized_pnl_rel_to_market_cap: Indexes26, - pub supply_in_loss_rel_to_circulating_supply: Indexes27, - pub supply_in_loss_rel_to_own_supply: Indexes27, - pub supply_in_profit_rel_to_circulating_supply: Indexes27, - pub supply_in_profit_rel_to_own_supply: Indexes27, - pub supply_rel_to_circulating_supply: Indexes, - pub unrealized_loss_rel_to_market_cap: Indexes27, - pub unrealized_profit_rel_to_market_cap: Indexes27, +pub struct Constant0Pattern { + pub dateindex: Indexes5, + pub decadeindex: Indexes7, + pub height: Indexes2, + pub monthindex: Indexes8, + pub quarterindex: Indexes9, + pub semesterindex: Indexes10, + pub weekindex: Indexes11, + pub yearindex: Indexes12, } -impl RelativePattern { - pub fn new(client: Arc, base_path: &str) -> Self { +impl Constant0Pattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: &str) -> Self { Self { - neg_unrealized_loss_rel_to_market_cap: Indexes27::new(client.clone(), &format!("{base_path}/neg_unrealized_loss_rel_to_market_cap")), - net_unrealized_pnl_rel_to_market_cap: Indexes26::new(client.clone(), &format!("{base_path}/net_unrealized_pnl_rel_to_market_cap")), - supply_in_loss_rel_to_circulating_supply: Indexes27::new(client.clone(), &format!("{base_path}/supply_in_loss_rel_to_circulating_supply")), - supply_in_loss_rel_to_own_supply: Indexes27::new(client.clone(), &format!("{base_path}/supply_in_loss_rel_to_own_supply")), - supply_in_profit_rel_to_circulating_supply: Indexes27::new(client.clone(), &format!("{base_path}/supply_in_profit_rel_to_circulating_supply")), - supply_in_profit_rel_to_own_supply: Indexes27::new(client.clone(), &format!("{base_path}/supply_in_profit_rel_to_own_supply")), - supply_rel_to_circulating_supply: Indexes::new(client.clone(), &format!("{base_path}/supply_rel_to_circulating_supply")), - unrealized_loss_rel_to_market_cap: Indexes27::new(client.clone(), &format!("{base_path}/unrealized_loss_rel_to_market_cap")), - unrealized_profit_rel_to_market_cap: Indexes27::new(client.clone(), &format!("{base_path}/unrealized_profit_rel_to_market_cap")), + dateindex: Indexes5::new(client.clone(), &format!("/{acc}")), + decadeindex: Indexes7::new(client.clone(), &format!("/{acc}")), + height: Indexes2::new(client.clone(), &format!("/{acc}")), + monthindex: Indexes8::new(client.clone(), &format!("/{acc}")), + quarterindex: Indexes9::new(client.clone(), &format!("/{acc}")), + semesterindex: Indexes10::new(client.clone(), &format!("/{acc}")), + weekindex: Indexes11::new(client.clone(), &format!("/{acc}")), + yearindex: Indexes12::new(client.clone(), &format!("/{acc}")), } } } @@ -1296,34 +1334,6 @@ impl BlockIntervalPattern { } } -/// Pattern struct for repeated tree structure. -pub struct Constant0Pattern { - pub dateindex: Indexes5, - pub decadeindex: Indexes7, - pub height: Indexes2, - pub monthindex: Indexes8, - pub quarterindex: Indexes9, - pub semesterindex: Indexes10, - pub weekindex: Indexes11, - pub yearindex: Indexes12, -} - -impl Constant0Pattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: &str) -> Self { - Self { - dateindex: Indexes5::new(client.clone(), &format!("/{acc}")), - decadeindex: Indexes7::new(client.clone(), &format!("/{acc}")), - height: Indexes2::new(client.clone(), &format!("/{acc}")), - monthindex: Indexes8::new(client.clone(), &format!("/{acc}")), - quarterindex: Indexes9::new(client.clone(), &format!("/{acc}")), - semesterindex: Indexes10::new(client.clone(), &format!("/{acc}")), - weekindex: Indexes11::new(client.clone(), &format!("/{acc}")), - yearindex: Indexes12::new(client.clone(), &format!("/{acc}")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct _0satsPattern { pub activity: ActivityPattern, @@ -1418,6 +1428,27 @@ impl _10yTo12yPattern { } } +/// Pattern struct for repeated tree structure. +pub struct ActivityPattern { + pub coinblocks_destroyed: BlockCountPattern, + pub coindays_destroyed: BlockCountPattern, + pub satblocks_destroyed: Indexes2, + pub satdays_destroyed: Indexes2, + pub sent: FeePattern2, +} + +impl ActivityPattern { + pub fn new(client: Arc, base_path: &str) -> Self { + Self { + coinblocks_destroyed: BlockCountPattern::new(client.clone(), &format!("{base_path}/coinblocks_destroyed")), + coindays_destroyed: BlockCountPattern::new(client.clone(), &format!("{base_path}/coindays_destroyed")), + satblocks_destroyed: Indexes2::new(client.clone(), &format!("{base_path}/satblocks_destroyed")), + satdays_destroyed: Indexes2::new(client.clone(), &format!("{base_path}/satdays_destroyed")), + sent: FeePattern2::new(client.clone(), &format!("{base_path}/sent")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct SupplyPattern2 { pub supply: SupplyPattern, @@ -1440,35 +1471,14 @@ impl SupplyPattern2 { } /// Pattern struct for repeated tree structure. -pub struct ActivityPattern { - pub coinblocks_destroyed: BlockCountPattern, - pub coindays_destroyed: BlockCountPattern, - pub satblocks_destroyed: Indexes2, - pub satdays_destroyed: Indexes2, - pub sent: SentPattern, -} - -impl ActivityPattern { - pub fn new(client: Arc, base_path: &str) -> Self { - Self { - coinblocks_destroyed: BlockCountPattern::new(client.clone(), &format!("{base_path}/coinblocks_destroyed")), - coindays_destroyed: BlockCountPattern::new(client.clone(), &format!("{base_path}/coindays_destroyed")), - satblocks_destroyed: Indexes2::new(client.clone(), &format!("{base_path}/satblocks_destroyed")), - satdays_destroyed: Indexes2::new(client.clone(), &format!("{base_path}/satdays_destroyed")), - sent: SentPattern::new(client.clone(), &format!("{base_path}/sent")), - } - } -} - -/// Pattern struct for repeated tree structure. -pub struct SentPattern { +pub struct FeePattern2 { pub base: Indexes2, pub bitcoin: BlockCountPattern, pub dollars: BlockCountPattern, pub sats: SatsPattern, } -impl SentPattern { +impl FeePattern2 { pub fn new(client: Arc, base_path: &str) -> Self { Self { base: Indexes2::new(client.clone(), &format!("{base_path}/base")), @@ -1498,40 +1508,6 @@ impl SupplyPattern { } } -/// Pattern struct for repeated tree structure. -pub struct CoinbasePattern { - pub bitcoin: BitcoinPattern, - pub dollars: BitcoinPattern, - pub sats: BitcoinPattern, -} - -impl CoinbasePattern { - pub fn new(client: Arc, base_path: &str) -> Self { - Self { - bitcoin: BitcoinPattern::new(client.clone(), &format!("{base_path}/bitcoin")), - dollars: BitcoinPattern::new(client.clone(), &format!("{base_path}/dollars")), - sats: BitcoinPattern::new(client.clone(), &format!("{base_path}/sats")), - } - } -} - -/// Pattern struct for repeated tree structure. -pub struct ActiveSupplyPattern { - pub bitcoin: Indexes3, - pub dollars: Indexes3, - pub sats: Indexes3, -} - -impl ActiveSupplyPattern { - pub fn new(client: Arc, base_path: &str) -> Self { - Self { - bitcoin: Indexes3::new(client.clone(), &format!("{base_path}/bitcoin")), - dollars: Indexes3::new(client.clone(), &format!("{base_path}/dollars")), - sats: Indexes3::new(client.clone(), &format!("{base_path}/sats")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct UnclaimedRewardsPattern { pub bitcoin: BlockCountPattern, @@ -1566,6 +1542,40 @@ impl PricePaidPattern2 { } } +/// Pattern struct for repeated tree structure. +pub struct CoinbasePattern { + pub bitcoin: BitcoinPattern, + pub dollars: BitcoinPattern, + pub sats: BitcoinPattern, +} + +impl CoinbasePattern { + pub fn new(client: Arc, base_path: &str) -> Self { + Self { + bitcoin: BitcoinPattern::new(client.clone(), &format!("{base_path}/bitcoin")), + dollars: BitcoinPattern::new(client.clone(), &format!("{base_path}/dollars")), + sats: BitcoinPattern::new(client.clone(), &format!("{base_path}/sats")), + } + } +} + +/// Pattern struct for repeated tree structure. +pub struct ActiveSupplyPattern { + pub bitcoin: Indexes3, + pub dollars: Indexes3, + pub sats: Indexes3, +} + +impl ActiveSupplyPattern { + pub fn new(client: Arc, base_path: &str) -> Self { + Self { + bitcoin: Indexes3::new(client.clone(), &format!("{base_path}/bitcoin")), + dollars: Indexes3::new(client.clone(), &format!("{base_path}/dollars")), + sats: Indexes3::new(client.clone(), &format!("{base_path}/sats")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct BlockCountPattern { pub base: Indexes2, @@ -1583,21 +1593,6 @@ impl BlockCountPattern { } } -/// Pattern struct for repeated tree structure. -pub struct SupplyValuePattern { - pub bitcoin: Indexes2, - pub dollars: Indexes2, -} - -impl SupplyValuePattern { - pub fn new(client: Arc, base_path: &str) -> Self { - Self { - bitcoin: Indexes2::new(client.clone(), &format!("{base_path}/bitcoin")), - dollars: Indexes2::new(client.clone(), &format!("{base_path}/dollars")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct PricePaidPattern { pub max_price_paid: Indexes3, @@ -1613,21 +1608,6 @@ impl PricePaidPattern { } } -/// Pattern struct for repeated tree structure. -pub struct SatsPattern { - pub cumulative: Indexes3, - pub sum: Indexes4, -} - -impl SatsPattern { - pub fn new(client: Arc, base_path: &str) -> Self { - Self { - cumulative: Indexes3::new(client.clone(), &format!("{base_path}/cumulative")), - sum: Indexes4::new(client.clone(), &format!("{base_path}/sum")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct _1dReturns1mSdPattern { pub sd: Indexes, @@ -1644,6 +1624,36 @@ impl _1dReturns1mSdPattern { } } +/// Pattern struct for repeated tree structure. +pub struct SupplyValuePattern { + pub bitcoin: Indexes2, + pub dollars: Indexes2, +} + +impl SupplyValuePattern { + pub fn new(client: Arc, base_path: &str) -> Self { + Self { + bitcoin: Indexes2::new(client.clone(), &format!("{base_path}/bitcoin")), + dollars: Indexes2::new(client.clone(), &format!("{base_path}/dollars")), + } + } +} + +/// Pattern struct for repeated tree structure. +pub struct SatsPattern { + pub cumulative: Indexes3, + pub sum: Indexes4, +} + +impl SatsPattern { + pub fn new(client: Arc, base_path: &str) -> Self { + Self { + cumulative: Indexes3::new(client.clone(), &format!("{base_path}/cumulative")), + sum: Indexes4::new(client.clone(), &format!("{base_path}/sum")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct BitcoinPattern2 { pub base: Indexes2, @@ -2539,327 +2549,327 @@ impl CatalogTree_Computed_Pools { /// Catalog tree node. pub struct CatalogTree_Computed_Pools_Vecs { - pub AXbt: AXbtPattern, - pub AaoPool: AXbtPattern, - pub AntPool: AXbtPattern, - pub ArkPool: AXbtPattern, - pub AsicMiner: AXbtPattern, - pub BatPool: AXbtPattern, - pub BcMonster: AXbtPattern, - pub BcpoolIo: AXbtPattern, - pub BinancePool: AXbtPattern, - pub BitClub: AXbtPattern, - pub BitFuFuPool: AXbtPattern, - pub BitFury: AXbtPattern, - pub BitMinter: AXbtPattern, - pub Bitalo: AXbtPattern, - pub BitcoinAffiliateNetwork: AXbtPattern, - pub BitcoinCom: AXbtPattern, - pub BitcoinIndia: AXbtPattern, - pub BitcoinRussia: AXbtPattern, - pub BitcoinUkraine: AXbtPattern, - pub Bitfarms: AXbtPattern, - pub Bitparking: AXbtPattern, - pub Bitsolo: AXbtPattern, - pub Bixin: AXbtPattern, - pub BlockFills: AXbtPattern, - pub BraiinsPool: AXbtPattern, - pub BravoMining: AXbtPattern, - pub BtPool: AXbtPattern, - pub BtcCom: AXbtPattern, - pub BtcDig: AXbtPattern, - pub BtcGuild: AXbtPattern, - pub BtcLab: AXbtPattern, - pub BtcMp: AXbtPattern, - pub BtcNuggets: AXbtPattern, - pub BtcPoolParty: AXbtPattern, - pub BtcServ: AXbtPattern, - pub BtcTop: AXbtPattern, - pub Btcc: AXbtPattern, - pub BwPool: AXbtPattern, - pub BytePool: AXbtPattern, - pub Canoe: AXbtPattern, - pub CanoePool: AXbtPattern, - pub CarbonNegative: AXbtPattern, - pub CkPool: AXbtPattern, - pub CloudHashing: AXbtPattern, - pub CoinLab: AXbtPattern, - pub Cointerra: AXbtPattern, - pub ConnectBtc: AXbtPattern, - pub DPool: AXbtPattern, - pub DcExploration: AXbtPattern, - pub Dcex: AXbtPattern, - pub DigitalBtc: AXbtPattern, - pub DigitalXMintsy: AXbtPattern, - pub EclipseMc: AXbtPattern, - pub EightBaochi: AXbtPattern, - pub EkanemBtc: AXbtPattern, - pub Eligius: AXbtPattern, - pub EmcdPool: AXbtPattern, - pub EntrustCharityPool: AXbtPattern, - pub Eobot: AXbtPattern, - pub ExxBw: AXbtPattern, - pub F2Pool: AXbtPattern, - pub FiftyEightCoin: AXbtPattern, - pub FoundryUsa: AXbtPattern, - pub FutureBitApolloSolo: AXbtPattern, - pub GbMiners: AXbtPattern, - pub GhashIo: AXbtPattern, - pub GiveMeCoins: AXbtPattern, - pub GoGreenLight: AXbtPattern, - pub HaoZhuZhu: AXbtPattern, - pub Haominer: AXbtPattern, - pub HashBx: AXbtPattern, - pub HashPool: AXbtPattern, - pub Helix: AXbtPattern, - pub Hhtt: AXbtPattern, - pub HotPool: AXbtPattern, - pub Hummerpool: AXbtPattern, - pub HuobiPool: AXbtPattern, - pub InnopolisTech: AXbtPattern, - pub KanoPool: AXbtPattern, - pub KncMiner: AXbtPattern, - pub KuCoinPool: AXbtPattern, - pub LubianCom: AXbtPattern, - pub LuckyPool: AXbtPattern, - pub Luxor: AXbtPattern, - pub MaraPool: AXbtPattern, - pub MaxBtc: AXbtPattern, - pub MaxiPool: AXbtPattern, - pub MegaBigPower: AXbtPattern, - pub Minerium: AXbtPattern, - pub MiningCity: AXbtPattern, - pub MiningDutch: AXbtPattern, - pub MiningKings: AXbtPattern, - pub MiningSquared: AXbtPattern, - pub Mmpool: AXbtPattern, - pub MtRed: AXbtPattern, - pub MultiCoinCo: AXbtPattern, - pub Multipool: AXbtPattern, - pub MyBtcCoinPool: AXbtPattern, - pub Neopool: AXbtPattern, - pub Nexious: AXbtPattern, - pub NiceHash: AXbtPattern, - pub NmcBit: AXbtPattern, - pub NovaBlock: AXbtPattern, - pub Ocean: AXbtPattern, - pub OkExPool: AXbtPattern, - pub OkMiner: AXbtPattern, - pub Okkong: AXbtPattern, - pub OkpoolTop: AXbtPattern, - pub OneHash: AXbtPattern, - pub OneM1x: AXbtPattern, - pub OneThash: AXbtPattern, - pub OzCoin: AXbtPattern, - pub PHashIo: AXbtPattern, - pub Parasite: AXbtPattern, - pub Patels: AXbtPattern, - pub PegaPool: AXbtPattern, - pub Phoenix: AXbtPattern, - pub Polmine: AXbtPattern, - pub Pool175btc: AXbtPattern, - pub Pool50btc: AXbtPattern, - pub Poolin: AXbtPattern, - pub PortlandHodl: AXbtPattern, - pub PublicPool: AXbtPattern, - pub PureBtcCom: AXbtPattern, - pub Rawpool: AXbtPattern, - pub RigPool: AXbtPattern, - pub SbiCrypto: AXbtPattern, - pub SecPool: AXbtPattern, - pub SecretSuperstar: AXbtPattern, - pub SevenPool: AXbtPattern, - pub ShawnP0wers: AXbtPattern, - pub SigmapoolCom: AXbtPattern, - pub SimplecoinUs: AXbtPattern, - pub SoloCk: AXbtPattern, - pub SpiderPool: AXbtPattern, - pub StMiningCorp: AXbtPattern, - pub Tangpool: AXbtPattern, - pub TatmasPool: AXbtPattern, - pub TbDice: AXbtPattern, - pub Telco214: AXbtPattern, - pub TerraPool: AXbtPattern, - pub Tiger: AXbtPattern, - pub TigerpoolNet: AXbtPattern, - pub Titan: AXbtPattern, - pub TransactionCoinMining: AXbtPattern, - pub TrickysBtcPool: AXbtPattern, - pub TripleMining: AXbtPattern, - pub TwentyOneInc: AXbtPattern, - pub UltimusPool: AXbtPattern, - pub Unknown: AXbtPattern, - pub Unomp: AXbtPattern, - pub ViaBtc: AXbtPattern, - pub Waterhole: AXbtPattern, - pub WayiCn: AXbtPattern, - pub WhitePool: AXbtPattern, - pub Wk057: AXbtPattern, - pub YourbtcNet: AXbtPattern, - pub Zulupool: AXbtPattern, + pub axbt: AXbtPattern, + pub aaopool: AXbtPattern, + pub antpool: AXbtPattern, + pub arkpool: AXbtPattern, + pub asicminer: AXbtPattern, + pub batpool: AXbtPattern, + pub bcmonster: AXbtPattern, + pub bcpoolio: AXbtPattern, + pub binancepool: AXbtPattern, + pub bitclub: AXbtPattern, + pub bitfufupool: AXbtPattern, + pub bitfury: AXbtPattern, + pub bitminter: AXbtPattern, + pub bitalo: AXbtPattern, + pub bitcoinaffiliatenetwork: AXbtPattern, + pub bitcoincom: AXbtPattern, + pub bitcoinindia: AXbtPattern, + pub bitcoinrussia: AXbtPattern, + pub bitcoinukraine: AXbtPattern, + pub bitfarms: AXbtPattern, + pub bitparking: AXbtPattern, + pub bitsolo: AXbtPattern, + pub bixin: AXbtPattern, + pub blockfills: AXbtPattern, + pub braiinspool: AXbtPattern, + pub bravomining: AXbtPattern, + pub btpool: AXbtPattern, + pub btccom: AXbtPattern, + pub btcdig: AXbtPattern, + pub btcguild: AXbtPattern, + pub btclab: AXbtPattern, + pub btcmp: AXbtPattern, + pub btcnuggets: AXbtPattern, + pub btcpoolparty: AXbtPattern, + pub btcserv: AXbtPattern, + pub btctop: AXbtPattern, + pub btcc: AXbtPattern, + pub bwpool: AXbtPattern, + pub bytepool: AXbtPattern, + pub canoe: AXbtPattern, + pub canoepool: AXbtPattern, + pub carbonnegative: AXbtPattern, + pub ckpool: AXbtPattern, + pub cloudhashing: AXbtPattern, + pub coinlab: AXbtPattern, + pub cointerra: AXbtPattern, + pub connectbtc: AXbtPattern, + pub dpool: AXbtPattern, + pub dcexploration: AXbtPattern, + pub dcex: AXbtPattern, + pub digitalbtc: AXbtPattern, + pub digitalxmintsy: AXbtPattern, + pub eclipsemc: AXbtPattern, + pub eightbaochi: AXbtPattern, + pub ekanembtc: AXbtPattern, + pub eligius: AXbtPattern, + pub emcdpool: AXbtPattern, + pub entrustcharitypool: AXbtPattern, + pub eobot: AXbtPattern, + pub exxbw: AXbtPattern, + pub f2pool: AXbtPattern, + pub fiftyeightcoin: AXbtPattern, + pub foundryusa: AXbtPattern, + pub futurebitapollosolo: AXbtPattern, + pub gbminers: AXbtPattern, + pub ghashio: AXbtPattern, + pub givemecoins: AXbtPattern, + pub gogreenlight: AXbtPattern, + pub haozhuzhu: AXbtPattern, + pub haominer: AXbtPattern, + pub hashbx: AXbtPattern, + pub hashpool: AXbtPattern, + pub helix: AXbtPattern, + pub hhtt: AXbtPattern, + pub hotpool: AXbtPattern, + pub hummerpool: AXbtPattern, + pub huobipool: AXbtPattern, + pub innopolistech: AXbtPattern, + pub kanopool: AXbtPattern, + pub kncminer: AXbtPattern, + pub kucoinpool: AXbtPattern, + pub lubiancom: AXbtPattern, + pub luckypool: AXbtPattern, + pub luxor: AXbtPattern, + pub marapool: AXbtPattern, + pub maxbtc: AXbtPattern, + pub maxipool: AXbtPattern, + pub megabigpower: AXbtPattern, + pub minerium: AXbtPattern, + pub miningcity: AXbtPattern, + pub miningdutch: AXbtPattern, + pub miningkings: AXbtPattern, + pub miningsquared: AXbtPattern, + pub mmpool: AXbtPattern, + pub mtred: AXbtPattern, + pub multicoinco: AXbtPattern, + pub multipool: AXbtPattern, + pub mybtccoinpool: AXbtPattern, + pub neopool: AXbtPattern, + pub nexious: AXbtPattern, + pub nicehash: AXbtPattern, + pub nmcbit: AXbtPattern, + pub novablock: AXbtPattern, + pub ocean: AXbtPattern, + pub okexpool: AXbtPattern, + pub okminer: AXbtPattern, + pub okkong: AXbtPattern, + pub okpooltop: AXbtPattern, + pub onehash: AXbtPattern, + pub onem1x: AXbtPattern, + pub onethash: AXbtPattern, + pub ozcoin: AXbtPattern, + pub phashio: AXbtPattern, + pub parasite: AXbtPattern, + pub patels: AXbtPattern, + pub pegapool: AXbtPattern, + pub phoenix: AXbtPattern, + pub polmine: AXbtPattern, + pub pool175btc: AXbtPattern, + pub pool50btc: AXbtPattern, + pub poolin: AXbtPattern, + pub portlandhodl: AXbtPattern, + pub publicpool: AXbtPattern, + pub purebtccom: AXbtPattern, + pub rawpool: AXbtPattern, + pub rigpool: AXbtPattern, + pub sbicrypto: AXbtPattern, + pub secpool: AXbtPattern, + pub secretsuperstar: AXbtPattern, + pub sevenpool: AXbtPattern, + pub shawnp0wers: AXbtPattern, + pub sigmapoolcom: AXbtPattern, + pub simplecoinus: AXbtPattern, + pub solock: AXbtPattern, + pub spiderpool: AXbtPattern, + pub stminingcorp: AXbtPattern, + pub tangpool: AXbtPattern, + pub tatmaspool: AXbtPattern, + pub tbdice: AXbtPattern, + pub telco214: AXbtPattern, + pub terrapool: AXbtPattern, + pub tiger: AXbtPattern, + pub tigerpoolnet: AXbtPattern, + pub titan: AXbtPattern, + pub transactioncoinmining: AXbtPattern, + pub trickysbtcpool: AXbtPattern, + pub triplemining: AXbtPattern, + pub twentyoneinc: AXbtPattern, + pub ultimuspool: AXbtPattern, + pub unknown: AXbtPattern, + pub unomp: AXbtPattern, + pub viabtc: AXbtPattern, + pub waterhole: AXbtPattern, + pub wayicn: AXbtPattern, + pub whitepool: AXbtPattern, + pub wk057: AXbtPattern, + pub yourbtcnet: AXbtPattern, + pub zulupool: AXbtPattern, } impl CatalogTree_Computed_Pools_Vecs { pub fn new(client: Arc, base_path: &str) -> Self { Self { - AXbt: AXbtPattern::new(client.clone(), &format!("{base_path}/AXbt")), - AaoPool: AXbtPattern::new(client.clone(), &format!("{base_path}/AaoPool")), - AntPool: AXbtPattern::new(client.clone(), &format!("{base_path}/AntPool")), - ArkPool: AXbtPattern::new(client.clone(), &format!("{base_path}/ArkPool")), - AsicMiner: AXbtPattern::new(client.clone(), &format!("{base_path}/AsicMiner")), - BatPool: AXbtPattern::new(client.clone(), &format!("{base_path}/BatPool")), - BcMonster: AXbtPattern::new(client.clone(), &format!("{base_path}/BcMonster")), - BcpoolIo: AXbtPattern::new(client.clone(), &format!("{base_path}/BcpoolIo")), - BinancePool: AXbtPattern::new(client.clone(), &format!("{base_path}/BinancePool")), - BitClub: AXbtPattern::new(client.clone(), &format!("{base_path}/BitClub")), - BitFuFuPool: AXbtPattern::new(client.clone(), &format!("{base_path}/BitFuFuPool")), - BitFury: AXbtPattern::new(client.clone(), &format!("{base_path}/BitFury")), - BitMinter: AXbtPattern::new(client.clone(), &format!("{base_path}/BitMinter")), - Bitalo: AXbtPattern::new(client.clone(), &format!("{base_path}/Bitalo")), - BitcoinAffiliateNetwork: AXbtPattern::new(client.clone(), &format!("{base_path}/BitcoinAffiliateNetwork")), - BitcoinCom: AXbtPattern::new(client.clone(), &format!("{base_path}/BitcoinCom")), - BitcoinIndia: AXbtPattern::new(client.clone(), &format!("{base_path}/BitcoinIndia")), - BitcoinRussia: AXbtPattern::new(client.clone(), &format!("{base_path}/BitcoinRussia")), - BitcoinUkraine: AXbtPattern::new(client.clone(), &format!("{base_path}/BitcoinUkraine")), - Bitfarms: AXbtPattern::new(client.clone(), &format!("{base_path}/Bitfarms")), - Bitparking: AXbtPattern::new(client.clone(), &format!("{base_path}/Bitparking")), - Bitsolo: AXbtPattern::new(client.clone(), &format!("{base_path}/Bitsolo")), - Bixin: AXbtPattern::new(client.clone(), &format!("{base_path}/Bixin")), - BlockFills: AXbtPattern::new(client.clone(), &format!("{base_path}/BlockFills")), - BraiinsPool: AXbtPattern::new(client.clone(), &format!("{base_path}/BraiinsPool")), - BravoMining: AXbtPattern::new(client.clone(), &format!("{base_path}/BravoMining")), - BtPool: AXbtPattern::new(client.clone(), &format!("{base_path}/BtPool")), - BtcCom: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcCom")), - BtcDig: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcDig")), - BtcGuild: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcGuild")), - BtcLab: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcLab")), - BtcMp: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcMp")), - BtcNuggets: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcNuggets")), - BtcPoolParty: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcPoolParty")), - BtcServ: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcServ")), - BtcTop: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcTop")), - Btcc: AXbtPattern::new(client.clone(), &format!("{base_path}/Btcc")), - BwPool: AXbtPattern::new(client.clone(), &format!("{base_path}/BwPool")), - BytePool: AXbtPattern::new(client.clone(), &format!("{base_path}/BytePool")), - Canoe: AXbtPattern::new(client.clone(), &format!("{base_path}/Canoe")), - CanoePool: AXbtPattern::new(client.clone(), &format!("{base_path}/CanoePool")), - CarbonNegative: AXbtPattern::new(client.clone(), &format!("{base_path}/CarbonNegative")), - CkPool: AXbtPattern::new(client.clone(), &format!("{base_path}/CkPool")), - CloudHashing: AXbtPattern::new(client.clone(), &format!("{base_path}/CloudHashing")), - CoinLab: AXbtPattern::new(client.clone(), &format!("{base_path}/CoinLab")), - Cointerra: AXbtPattern::new(client.clone(), &format!("{base_path}/Cointerra")), - ConnectBtc: AXbtPattern::new(client.clone(), &format!("{base_path}/ConnectBtc")), - DPool: AXbtPattern::new(client.clone(), &format!("{base_path}/DPool")), - DcExploration: AXbtPattern::new(client.clone(), &format!("{base_path}/DcExploration")), - Dcex: AXbtPattern::new(client.clone(), &format!("{base_path}/Dcex")), - DigitalBtc: AXbtPattern::new(client.clone(), &format!("{base_path}/DigitalBtc")), - DigitalXMintsy: AXbtPattern::new(client.clone(), &format!("{base_path}/DigitalXMintsy")), - EclipseMc: AXbtPattern::new(client.clone(), &format!("{base_path}/EclipseMc")), - EightBaochi: AXbtPattern::new(client.clone(), &format!("{base_path}/EightBaochi")), - EkanemBtc: AXbtPattern::new(client.clone(), &format!("{base_path}/EkanemBtc")), - Eligius: AXbtPattern::new(client.clone(), &format!("{base_path}/Eligius")), - EmcdPool: AXbtPattern::new(client.clone(), &format!("{base_path}/EmcdPool")), - EntrustCharityPool: AXbtPattern::new(client.clone(), &format!("{base_path}/EntrustCharityPool")), - Eobot: AXbtPattern::new(client.clone(), &format!("{base_path}/Eobot")), - ExxBw: AXbtPattern::new(client.clone(), &format!("{base_path}/ExxBw")), - F2Pool: AXbtPattern::new(client.clone(), &format!("{base_path}/F2Pool")), - FiftyEightCoin: AXbtPattern::new(client.clone(), &format!("{base_path}/FiftyEightCoin")), - FoundryUsa: AXbtPattern::new(client.clone(), &format!("{base_path}/FoundryUsa")), - FutureBitApolloSolo: AXbtPattern::new(client.clone(), &format!("{base_path}/FutureBitApolloSolo")), - GbMiners: AXbtPattern::new(client.clone(), &format!("{base_path}/GbMiners")), - GhashIo: AXbtPattern::new(client.clone(), &format!("{base_path}/GhashIo")), - GiveMeCoins: AXbtPattern::new(client.clone(), &format!("{base_path}/GiveMeCoins")), - GoGreenLight: AXbtPattern::new(client.clone(), &format!("{base_path}/GoGreenLight")), - HaoZhuZhu: AXbtPattern::new(client.clone(), &format!("{base_path}/HaoZhuZhu")), - Haominer: AXbtPattern::new(client.clone(), &format!("{base_path}/Haominer")), - HashBx: AXbtPattern::new(client.clone(), &format!("{base_path}/HashBx")), - HashPool: AXbtPattern::new(client.clone(), &format!("{base_path}/HashPool")), - Helix: AXbtPattern::new(client.clone(), &format!("{base_path}/Helix")), - Hhtt: AXbtPattern::new(client.clone(), &format!("{base_path}/Hhtt")), - HotPool: AXbtPattern::new(client.clone(), &format!("{base_path}/HotPool")), - Hummerpool: AXbtPattern::new(client.clone(), &format!("{base_path}/Hummerpool")), - HuobiPool: AXbtPattern::new(client.clone(), &format!("{base_path}/HuobiPool")), - InnopolisTech: AXbtPattern::new(client.clone(), &format!("{base_path}/InnopolisTech")), - KanoPool: AXbtPattern::new(client.clone(), &format!("{base_path}/KanoPool")), - KncMiner: AXbtPattern::new(client.clone(), &format!("{base_path}/KncMiner")), - KuCoinPool: AXbtPattern::new(client.clone(), &format!("{base_path}/KuCoinPool")), - LubianCom: AXbtPattern::new(client.clone(), &format!("{base_path}/LubianCom")), - LuckyPool: AXbtPattern::new(client.clone(), &format!("{base_path}/LuckyPool")), - Luxor: AXbtPattern::new(client.clone(), &format!("{base_path}/Luxor")), - MaraPool: AXbtPattern::new(client.clone(), &format!("{base_path}/MaraPool")), - MaxBtc: AXbtPattern::new(client.clone(), &format!("{base_path}/MaxBtc")), - MaxiPool: AXbtPattern::new(client.clone(), &format!("{base_path}/MaxiPool")), - MegaBigPower: AXbtPattern::new(client.clone(), &format!("{base_path}/MegaBigPower")), - Minerium: AXbtPattern::new(client.clone(), &format!("{base_path}/Minerium")), - MiningCity: AXbtPattern::new(client.clone(), &format!("{base_path}/MiningCity")), - MiningDutch: AXbtPattern::new(client.clone(), &format!("{base_path}/MiningDutch")), - MiningKings: AXbtPattern::new(client.clone(), &format!("{base_path}/MiningKings")), - MiningSquared: AXbtPattern::new(client.clone(), &format!("{base_path}/MiningSquared")), - Mmpool: AXbtPattern::new(client.clone(), &format!("{base_path}/Mmpool")), - MtRed: AXbtPattern::new(client.clone(), &format!("{base_path}/MtRed")), - MultiCoinCo: AXbtPattern::new(client.clone(), &format!("{base_path}/MultiCoinCo")), - Multipool: AXbtPattern::new(client.clone(), &format!("{base_path}/Multipool")), - MyBtcCoinPool: AXbtPattern::new(client.clone(), &format!("{base_path}/MyBtcCoinPool")), - Neopool: AXbtPattern::new(client.clone(), &format!("{base_path}/Neopool")), - Nexious: AXbtPattern::new(client.clone(), &format!("{base_path}/Nexious")), - NiceHash: AXbtPattern::new(client.clone(), &format!("{base_path}/NiceHash")), - NmcBit: AXbtPattern::new(client.clone(), &format!("{base_path}/NmcBit")), - NovaBlock: AXbtPattern::new(client.clone(), &format!("{base_path}/NovaBlock")), - Ocean: AXbtPattern::new(client.clone(), &format!("{base_path}/Ocean")), - OkExPool: AXbtPattern::new(client.clone(), &format!("{base_path}/OkExPool")), - OkMiner: AXbtPattern::new(client.clone(), &format!("{base_path}/OkMiner")), - Okkong: AXbtPattern::new(client.clone(), &format!("{base_path}/Okkong")), - OkpoolTop: AXbtPattern::new(client.clone(), &format!("{base_path}/OkpoolTop")), - OneHash: AXbtPattern::new(client.clone(), &format!("{base_path}/OneHash")), - OneM1x: AXbtPattern::new(client.clone(), &format!("{base_path}/OneM1x")), - OneThash: AXbtPattern::new(client.clone(), &format!("{base_path}/OneThash")), - OzCoin: AXbtPattern::new(client.clone(), &format!("{base_path}/OzCoin")), - PHashIo: AXbtPattern::new(client.clone(), &format!("{base_path}/PHashIo")), - Parasite: AXbtPattern::new(client.clone(), &format!("{base_path}/Parasite")), - Patels: AXbtPattern::new(client.clone(), &format!("{base_path}/Patels")), - PegaPool: AXbtPattern::new(client.clone(), &format!("{base_path}/PegaPool")), - Phoenix: AXbtPattern::new(client.clone(), &format!("{base_path}/Phoenix")), - Polmine: AXbtPattern::new(client.clone(), &format!("{base_path}/Polmine")), - Pool175btc: AXbtPattern::new(client.clone(), &format!("{base_path}/Pool175btc")), - Pool50btc: AXbtPattern::new(client.clone(), &format!("{base_path}/Pool50btc")), - Poolin: AXbtPattern::new(client.clone(), &format!("{base_path}/Poolin")), - PortlandHodl: AXbtPattern::new(client.clone(), &format!("{base_path}/PortlandHodl")), - PublicPool: AXbtPattern::new(client.clone(), &format!("{base_path}/PublicPool")), - PureBtcCom: AXbtPattern::new(client.clone(), &format!("{base_path}/PureBtcCom")), - Rawpool: AXbtPattern::new(client.clone(), &format!("{base_path}/Rawpool")), - RigPool: AXbtPattern::new(client.clone(), &format!("{base_path}/RigPool")), - SbiCrypto: AXbtPattern::new(client.clone(), &format!("{base_path}/SbiCrypto")), - SecPool: AXbtPattern::new(client.clone(), &format!("{base_path}/SecPool")), - SecretSuperstar: AXbtPattern::new(client.clone(), &format!("{base_path}/SecretSuperstar")), - SevenPool: AXbtPattern::new(client.clone(), &format!("{base_path}/SevenPool")), - ShawnP0wers: AXbtPattern::new(client.clone(), &format!("{base_path}/ShawnP0wers")), - SigmapoolCom: AXbtPattern::new(client.clone(), &format!("{base_path}/SigmapoolCom")), - SimplecoinUs: AXbtPattern::new(client.clone(), &format!("{base_path}/SimplecoinUs")), - SoloCk: AXbtPattern::new(client.clone(), &format!("{base_path}/SoloCk")), - SpiderPool: AXbtPattern::new(client.clone(), &format!("{base_path}/SpiderPool")), - StMiningCorp: AXbtPattern::new(client.clone(), &format!("{base_path}/StMiningCorp")), - Tangpool: AXbtPattern::new(client.clone(), &format!("{base_path}/Tangpool")), - TatmasPool: AXbtPattern::new(client.clone(), &format!("{base_path}/TatmasPool")), - TbDice: AXbtPattern::new(client.clone(), &format!("{base_path}/TbDice")), - Telco214: AXbtPattern::new(client.clone(), &format!("{base_path}/Telco214")), - TerraPool: AXbtPattern::new(client.clone(), &format!("{base_path}/TerraPool")), - Tiger: AXbtPattern::new(client.clone(), &format!("{base_path}/Tiger")), - TigerpoolNet: AXbtPattern::new(client.clone(), &format!("{base_path}/TigerpoolNet")), - Titan: AXbtPattern::new(client.clone(), &format!("{base_path}/Titan")), - TransactionCoinMining: AXbtPattern::new(client.clone(), &format!("{base_path}/TransactionCoinMining")), - TrickysBtcPool: AXbtPattern::new(client.clone(), &format!("{base_path}/TrickysBtcPool")), - TripleMining: AXbtPattern::new(client.clone(), &format!("{base_path}/TripleMining")), - TwentyOneInc: AXbtPattern::new(client.clone(), &format!("{base_path}/TwentyOneInc")), - UltimusPool: AXbtPattern::new(client.clone(), &format!("{base_path}/UltimusPool")), - Unknown: AXbtPattern::new(client.clone(), &format!("{base_path}/Unknown")), - Unomp: AXbtPattern::new(client.clone(), &format!("{base_path}/Unomp")), - ViaBtc: AXbtPattern::new(client.clone(), &format!("{base_path}/ViaBtc")), - Waterhole: AXbtPattern::new(client.clone(), &format!("{base_path}/Waterhole")), - WayiCn: AXbtPattern::new(client.clone(), &format!("{base_path}/WayiCn")), - WhitePool: AXbtPattern::new(client.clone(), &format!("{base_path}/WhitePool")), - Wk057: AXbtPattern::new(client.clone(), &format!("{base_path}/Wk057")), - YourbtcNet: AXbtPattern::new(client.clone(), &format!("{base_path}/YourbtcNet")), - Zulupool: AXbtPattern::new(client.clone(), &format!("{base_path}/Zulupool")), + axbt: AXbtPattern::new(client.clone(), &format!("{base_path}/AXbt")), + aaopool: AXbtPattern::new(client.clone(), &format!("{base_path}/AaoPool")), + antpool: AXbtPattern::new(client.clone(), &format!("{base_path}/AntPool")), + arkpool: AXbtPattern::new(client.clone(), &format!("{base_path}/ArkPool")), + asicminer: AXbtPattern::new(client.clone(), &format!("{base_path}/AsicMiner")), + batpool: AXbtPattern::new(client.clone(), &format!("{base_path}/BatPool")), + bcmonster: AXbtPattern::new(client.clone(), &format!("{base_path}/BcMonster")), + bcpoolio: AXbtPattern::new(client.clone(), &format!("{base_path}/BcpoolIo")), + binancepool: AXbtPattern::new(client.clone(), &format!("{base_path}/BinancePool")), + bitclub: AXbtPattern::new(client.clone(), &format!("{base_path}/BitClub")), + bitfufupool: AXbtPattern::new(client.clone(), &format!("{base_path}/BitFuFuPool")), + bitfury: AXbtPattern::new(client.clone(), &format!("{base_path}/BitFury")), + bitminter: AXbtPattern::new(client.clone(), &format!("{base_path}/BitMinter")), + bitalo: AXbtPattern::new(client.clone(), &format!("{base_path}/Bitalo")), + bitcoinaffiliatenetwork: AXbtPattern::new(client.clone(), &format!("{base_path}/BitcoinAffiliateNetwork")), + bitcoincom: AXbtPattern::new(client.clone(), &format!("{base_path}/BitcoinCom")), + bitcoinindia: AXbtPattern::new(client.clone(), &format!("{base_path}/BitcoinIndia")), + bitcoinrussia: AXbtPattern::new(client.clone(), &format!("{base_path}/BitcoinRussia")), + bitcoinukraine: AXbtPattern::new(client.clone(), &format!("{base_path}/BitcoinUkraine")), + bitfarms: AXbtPattern::new(client.clone(), &format!("{base_path}/Bitfarms")), + bitparking: AXbtPattern::new(client.clone(), &format!("{base_path}/Bitparking")), + bitsolo: AXbtPattern::new(client.clone(), &format!("{base_path}/Bitsolo")), + bixin: AXbtPattern::new(client.clone(), &format!("{base_path}/Bixin")), + blockfills: AXbtPattern::new(client.clone(), &format!("{base_path}/BlockFills")), + braiinspool: AXbtPattern::new(client.clone(), &format!("{base_path}/BraiinsPool")), + bravomining: AXbtPattern::new(client.clone(), &format!("{base_path}/BravoMining")), + btpool: AXbtPattern::new(client.clone(), &format!("{base_path}/BtPool")), + btccom: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcCom")), + btcdig: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcDig")), + btcguild: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcGuild")), + btclab: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcLab")), + btcmp: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcMp")), + btcnuggets: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcNuggets")), + btcpoolparty: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcPoolParty")), + btcserv: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcServ")), + btctop: AXbtPattern::new(client.clone(), &format!("{base_path}/BtcTop")), + btcc: AXbtPattern::new(client.clone(), &format!("{base_path}/Btcc")), + bwpool: AXbtPattern::new(client.clone(), &format!("{base_path}/BwPool")), + bytepool: AXbtPattern::new(client.clone(), &format!("{base_path}/BytePool")), + canoe: AXbtPattern::new(client.clone(), &format!("{base_path}/Canoe")), + canoepool: AXbtPattern::new(client.clone(), &format!("{base_path}/CanoePool")), + carbonnegative: AXbtPattern::new(client.clone(), &format!("{base_path}/CarbonNegative")), + ckpool: AXbtPattern::new(client.clone(), &format!("{base_path}/CkPool")), + cloudhashing: AXbtPattern::new(client.clone(), &format!("{base_path}/CloudHashing")), + coinlab: AXbtPattern::new(client.clone(), &format!("{base_path}/CoinLab")), + cointerra: AXbtPattern::new(client.clone(), &format!("{base_path}/Cointerra")), + connectbtc: AXbtPattern::new(client.clone(), &format!("{base_path}/ConnectBtc")), + dpool: AXbtPattern::new(client.clone(), &format!("{base_path}/DPool")), + dcexploration: AXbtPattern::new(client.clone(), &format!("{base_path}/DcExploration")), + dcex: AXbtPattern::new(client.clone(), &format!("{base_path}/Dcex")), + digitalbtc: AXbtPattern::new(client.clone(), &format!("{base_path}/DigitalBtc")), + digitalxmintsy: AXbtPattern::new(client.clone(), &format!("{base_path}/DigitalXMintsy")), + eclipsemc: AXbtPattern::new(client.clone(), &format!("{base_path}/EclipseMc")), + eightbaochi: AXbtPattern::new(client.clone(), &format!("{base_path}/EightBaochi")), + ekanembtc: AXbtPattern::new(client.clone(), &format!("{base_path}/EkanemBtc")), + eligius: AXbtPattern::new(client.clone(), &format!("{base_path}/Eligius")), + emcdpool: AXbtPattern::new(client.clone(), &format!("{base_path}/EmcdPool")), + entrustcharitypool: AXbtPattern::new(client.clone(), &format!("{base_path}/EntrustCharityPool")), + eobot: AXbtPattern::new(client.clone(), &format!("{base_path}/Eobot")), + exxbw: AXbtPattern::new(client.clone(), &format!("{base_path}/ExxBw")), + f2pool: AXbtPattern::new(client.clone(), &format!("{base_path}/F2Pool")), + fiftyeightcoin: AXbtPattern::new(client.clone(), &format!("{base_path}/FiftyEightCoin")), + foundryusa: AXbtPattern::new(client.clone(), &format!("{base_path}/FoundryUsa")), + futurebitapollosolo: AXbtPattern::new(client.clone(), &format!("{base_path}/FutureBitApolloSolo")), + gbminers: AXbtPattern::new(client.clone(), &format!("{base_path}/GbMiners")), + ghashio: AXbtPattern::new(client.clone(), &format!("{base_path}/GhashIo")), + givemecoins: AXbtPattern::new(client.clone(), &format!("{base_path}/GiveMeCoins")), + gogreenlight: AXbtPattern::new(client.clone(), &format!("{base_path}/GoGreenLight")), + haozhuzhu: AXbtPattern::new(client.clone(), &format!("{base_path}/HaoZhuZhu")), + haominer: AXbtPattern::new(client.clone(), &format!("{base_path}/Haominer")), + hashbx: AXbtPattern::new(client.clone(), &format!("{base_path}/HashBx")), + hashpool: AXbtPattern::new(client.clone(), &format!("{base_path}/HashPool")), + helix: AXbtPattern::new(client.clone(), &format!("{base_path}/Helix")), + hhtt: AXbtPattern::new(client.clone(), &format!("{base_path}/Hhtt")), + hotpool: AXbtPattern::new(client.clone(), &format!("{base_path}/HotPool")), + hummerpool: AXbtPattern::new(client.clone(), &format!("{base_path}/Hummerpool")), + huobipool: AXbtPattern::new(client.clone(), &format!("{base_path}/HuobiPool")), + innopolistech: AXbtPattern::new(client.clone(), &format!("{base_path}/InnopolisTech")), + kanopool: AXbtPattern::new(client.clone(), &format!("{base_path}/KanoPool")), + kncminer: AXbtPattern::new(client.clone(), &format!("{base_path}/KncMiner")), + kucoinpool: AXbtPattern::new(client.clone(), &format!("{base_path}/KuCoinPool")), + lubiancom: AXbtPattern::new(client.clone(), &format!("{base_path}/LubianCom")), + luckypool: AXbtPattern::new(client.clone(), &format!("{base_path}/LuckyPool")), + luxor: AXbtPattern::new(client.clone(), &format!("{base_path}/Luxor")), + marapool: AXbtPattern::new(client.clone(), &format!("{base_path}/MaraPool")), + maxbtc: AXbtPattern::new(client.clone(), &format!("{base_path}/MaxBtc")), + maxipool: AXbtPattern::new(client.clone(), &format!("{base_path}/MaxiPool")), + megabigpower: AXbtPattern::new(client.clone(), &format!("{base_path}/MegaBigPower")), + minerium: AXbtPattern::new(client.clone(), &format!("{base_path}/Minerium")), + miningcity: AXbtPattern::new(client.clone(), &format!("{base_path}/MiningCity")), + miningdutch: AXbtPattern::new(client.clone(), &format!("{base_path}/MiningDutch")), + miningkings: AXbtPattern::new(client.clone(), &format!("{base_path}/MiningKings")), + miningsquared: AXbtPattern::new(client.clone(), &format!("{base_path}/MiningSquared")), + mmpool: AXbtPattern::new(client.clone(), &format!("{base_path}/Mmpool")), + mtred: AXbtPattern::new(client.clone(), &format!("{base_path}/MtRed")), + multicoinco: AXbtPattern::new(client.clone(), &format!("{base_path}/MultiCoinCo")), + multipool: AXbtPattern::new(client.clone(), &format!("{base_path}/Multipool")), + mybtccoinpool: AXbtPattern::new(client.clone(), &format!("{base_path}/MyBtcCoinPool")), + neopool: AXbtPattern::new(client.clone(), &format!("{base_path}/Neopool")), + nexious: AXbtPattern::new(client.clone(), &format!("{base_path}/Nexious")), + nicehash: AXbtPattern::new(client.clone(), &format!("{base_path}/NiceHash")), + nmcbit: AXbtPattern::new(client.clone(), &format!("{base_path}/NmcBit")), + novablock: AXbtPattern::new(client.clone(), &format!("{base_path}/NovaBlock")), + ocean: AXbtPattern::new(client.clone(), &format!("{base_path}/Ocean")), + okexpool: AXbtPattern::new(client.clone(), &format!("{base_path}/OkExPool")), + okminer: AXbtPattern::new(client.clone(), &format!("{base_path}/OkMiner")), + okkong: AXbtPattern::new(client.clone(), &format!("{base_path}/Okkong")), + okpooltop: AXbtPattern::new(client.clone(), &format!("{base_path}/OkpoolTop")), + onehash: AXbtPattern::new(client.clone(), &format!("{base_path}/OneHash")), + onem1x: AXbtPattern::new(client.clone(), &format!("{base_path}/OneM1x")), + onethash: AXbtPattern::new(client.clone(), &format!("{base_path}/OneThash")), + ozcoin: AXbtPattern::new(client.clone(), &format!("{base_path}/OzCoin")), + phashio: AXbtPattern::new(client.clone(), &format!("{base_path}/PHashIo")), + parasite: AXbtPattern::new(client.clone(), &format!("{base_path}/Parasite")), + patels: AXbtPattern::new(client.clone(), &format!("{base_path}/Patels")), + pegapool: AXbtPattern::new(client.clone(), &format!("{base_path}/PegaPool")), + phoenix: AXbtPattern::new(client.clone(), &format!("{base_path}/Phoenix")), + polmine: AXbtPattern::new(client.clone(), &format!("{base_path}/Polmine")), + pool175btc: AXbtPattern::new(client.clone(), &format!("{base_path}/Pool175btc")), + pool50btc: AXbtPattern::new(client.clone(), &format!("{base_path}/Pool50btc")), + poolin: AXbtPattern::new(client.clone(), &format!("{base_path}/Poolin")), + portlandhodl: AXbtPattern::new(client.clone(), &format!("{base_path}/PortlandHodl")), + publicpool: AXbtPattern::new(client.clone(), &format!("{base_path}/PublicPool")), + purebtccom: AXbtPattern::new(client.clone(), &format!("{base_path}/PureBtcCom")), + rawpool: AXbtPattern::new(client.clone(), &format!("{base_path}/Rawpool")), + rigpool: AXbtPattern::new(client.clone(), &format!("{base_path}/RigPool")), + sbicrypto: AXbtPattern::new(client.clone(), &format!("{base_path}/SbiCrypto")), + secpool: AXbtPattern::new(client.clone(), &format!("{base_path}/SecPool")), + secretsuperstar: AXbtPattern::new(client.clone(), &format!("{base_path}/SecretSuperstar")), + sevenpool: AXbtPattern::new(client.clone(), &format!("{base_path}/SevenPool")), + shawnp0wers: AXbtPattern::new(client.clone(), &format!("{base_path}/ShawnP0wers")), + sigmapoolcom: AXbtPattern::new(client.clone(), &format!("{base_path}/SigmapoolCom")), + simplecoinus: AXbtPattern::new(client.clone(), &format!("{base_path}/SimplecoinUs")), + solock: AXbtPattern::new(client.clone(), &format!("{base_path}/SoloCk")), + spiderpool: AXbtPattern::new(client.clone(), &format!("{base_path}/SpiderPool")), + stminingcorp: AXbtPattern::new(client.clone(), &format!("{base_path}/StMiningCorp")), + tangpool: AXbtPattern::new(client.clone(), &format!("{base_path}/Tangpool")), + tatmaspool: AXbtPattern::new(client.clone(), &format!("{base_path}/TatmasPool")), + tbdice: AXbtPattern::new(client.clone(), &format!("{base_path}/TbDice")), + telco214: AXbtPattern::new(client.clone(), &format!("{base_path}/Telco214")), + terrapool: AXbtPattern::new(client.clone(), &format!("{base_path}/TerraPool")), + tiger: AXbtPattern::new(client.clone(), &format!("{base_path}/Tiger")), + tigerpoolnet: AXbtPattern::new(client.clone(), &format!("{base_path}/TigerpoolNet")), + titan: AXbtPattern::new(client.clone(), &format!("{base_path}/Titan")), + transactioncoinmining: AXbtPattern::new(client.clone(), &format!("{base_path}/TransactionCoinMining")), + trickysbtcpool: AXbtPattern::new(client.clone(), &format!("{base_path}/TrickysBtcPool")), + triplemining: AXbtPattern::new(client.clone(), &format!("{base_path}/TripleMining")), + twentyoneinc: AXbtPattern::new(client.clone(), &format!("{base_path}/TwentyOneInc")), + ultimuspool: AXbtPattern::new(client.clone(), &format!("{base_path}/UltimusPool")), + unknown: AXbtPattern::new(client.clone(), &format!("{base_path}/Unknown")), + unomp: AXbtPattern::new(client.clone(), &format!("{base_path}/Unomp")), + viabtc: AXbtPattern::new(client.clone(), &format!("{base_path}/ViaBtc")), + waterhole: AXbtPattern::new(client.clone(), &format!("{base_path}/Waterhole")), + wayicn: AXbtPattern::new(client.clone(), &format!("{base_path}/WayiCn")), + whitepool: AXbtPattern::new(client.clone(), &format!("{base_path}/WhitePool")), + wk057: AXbtPattern::new(client.clone(), &format!("{base_path}/Wk057")), + yourbtcnet: AXbtPattern::new(client.clone(), &format!("{base_path}/YourbtcNet")), + zulupool: AXbtPattern::new(client.clone(), &format!("{base_path}/Zulupool")), } } } diff --git a/crates/brk_computer/src/grouped/transforms.rs b/crates/brk_computer/src/grouped/transforms.rs index c2a2ddf27..7835ceef1 100644 --- a/crates/brk_computer/src/grouped/transforms.rs +++ b/crates/brk_computer/src/grouped/transforms.rs @@ -1,4 +1,4 @@ -use brk_types::{Bitcoin, Close, Dollars, High, Sats, StoredF32, StoredF64, StoredU32}; +use brk_types::{Bitcoin, Close, Dollars, Sats, StoredF32, StoredF64, StoredU32}; use vecdb::{BinaryTransform, UnaryTransform}; /// (Dollars, Dollars) -> Dollars addition @@ -320,18 +320,3 @@ impl BinaryTransform, Dollars, StoredF32> for PercentageDiffClose } } } - -/// (High, Dollars) -> StoredF32 percentage difference ((a/b - 1) × 100) -/// Used for drawdown calculation from high prices -pub struct PercentageDiffHighDollars; - -impl BinaryTransform, Dollars, StoredF32> for PercentageDiffHighDollars { - #[inline(always)] - fn apply(high: High, base: Dollars) -> StoredF32 { - if base == Dollars::ZERO { - StoredF32::default() - } else { - StoredF32::from((**high / *base - 1.0) * 100.0) - } - } -} diff --git a/crates/brk_computer/src/stateful/cohorts/address.rs b/crates/brk_computer/src/stateful/cohorts/address.rs index becb707b1..ccacb6842 100644 --- a/crates/brk_computer/src/stateful/cohorts/address.rs +++ b/crates/brk_computer/src/stateful/cohorts/address.rs @@ -50,9 +50,11 @@ impl AddressCohortVecs { /// /// `all_supply` is the supply metrics from the "all" cohort, used as global /// sources for `*_rel_to_market_cap` ratios. Pass `None` if not available. + #[allow(clippy::too_many_arguments)] pub fn forced_import( db: &Database, filter: Filter, + name: &str, version: Version, indexes: &indexes::Vecs, price: Option<&price::Vecs>, @@ -60,11 +62,12 @@ impl AddressCohortVecs { all_supply: Option<&SupplyMetrics>, ) -> Result { let compute_dollars = price.is_some(); - let full_name = filter.to_full_name(CohortContext::Address); + let full_name = CohortContext::Address.full_name(&filter, name); let cfg = ImportConfig { db, filter, + full_name: &full_name, context: CohortContext::Address, version, indexes, diff --git a/crates/brk_computer/src/stateful/cohorts/address_cohorts.rs b/crates/brk_computer/src/stateful/cohorts/address_cohorts.rs index f2600b870..0a3c9e6f5 100644 --- a/crates/brk_computer/src/stateful/cohorts/address_cohorts.rs +++ b/crates/brk_computer/src/stateful/cohorts/address_cohorts.rs @@ -2,11 +2,10 @@ use std::path::Path; use brk_error::Result; use brk_grouper::{ - AddressGroups, AmountFilter, ByAmountRange, ByGreatEqualAmount, ByLowerThanAmount, Filter, - Filtered, + AddressGroups, ByAmountRange, ByGreatEqualAmount, ByLowerThanAmount, Filter, Filtered, }; use brk_traversable::Traversable; -use brk_types::{Bitcoin, DateIndex, Dollars, Height, Sats, Version}; +use brk_types::{Bitcoin, DateIndex, Dollars, Height, Version}; use derive_deref::{Deref, DerefMut}; use rayon::prelude::*; use vecdb::{AnyStoredVec, Database, Exit, IterableVec}; @@ -37,90 +36,19 @@ impl AddressCohorts { let v = version + VERSION + Version::ZERO; // Helper to create a cohort - only amount_range cohorts have state - let create = |filter: Filter, has_state: bool| -> Result { - let states_path = if has_state { Some(states_path) } else { None }; - AddressCohortVecs::forced_import(db, filter, v, indexes, price, states_path, all_supply) - }; + let create = + |filter: Filter, name: &'static str, has_state: bool| -> Result { + let sp = if has_state { Some(states_path) } else { None }; + AddressCohortVecs::forced_import(db, filter, name, v, indexes, price, sp, all_supply) + }; - let full = |f: Filter| create(f, true); - let none = |f: Filter| create(f, false); + let full = |f: Filter, name: &'static str| create(f, name, true); + let none = |f: Filter, name: &'static str| create(f, name, false); Ok(Self(AddressGroups { - amount_range: ByAmountRange { - _0sats: full(Filter::Amount(AmountFilter::LowerThan(Sats::_1)))?, - _1sat_to_10sats: full(Filter::Amount(AmountFilter::Range(Sats::_1..Sats::_10)))?, - _10sats_to_100sats: full(Filter::Amount(AmountFilter::Range( - Sats::_10..Sats::_100, - )))?, - _100sats_to_1k_sats: full(Filter::Amount(AmountFilter::Range( - Sats::_100..Sats::_1K, - )))?, - _1k_sats_to_10k_sats: full(Filter::Amount(AmountFilter::Range( - Sats::_1K..Sats::_10K, - )))?, - _10k_sats_to_100k_sats: full(Filter::Amount(AmountFilter::Range( - Sats::_10K..Sats::_100K, - )))?, - _100k_sats_to_1m_sats: full(Filter::Amount(AmountFilter::Range( - Sats::_100K..Sats::_1M, - )))?, - _1m_sats_to_10m_sats: full(Filter::Amount(AmountFilter::Range( - Sats::_1M..Sats::_10M, - )))?, - _10m_sats_to_1btc: full(Filter::Amount(AmountFilter::Range( - Sats::_10M..Sats::_1BTC, - )))?, - _1btc_to_10btc: full(Filter::Amount(AmountFilter::Range( - Sats::_1BTC..Sats::_10BTC, - )))?, - _10btc_to_100btc: full(Filter::Amount(AmountFilter::Range( - Sats::_10BTC..Sats::_100BTC, - )))?, - _100btc_to_1k_btc: full(Filter::Amount(AmountFilter::Range( - Sats::_100BTC..Sats::_1K_BTC, - )))?, - _1k_btc_to_10k_btc: full(Filter::Amount(AmountFilter::Range( - Sats::_1K_BTC..Sats::_10K_BTC, - )))?, - _10k_btc_to_100k_btc: full(Filter::Amount(AmountFilter::Range( - Sats::_10K_BTC..Sats::_100K_BTC, - )))?, - _100k_btc_or_more: full(Filter::Amount(AmountFilter::GreaterOrEqual( - Sats::_100K_BTC, - )))?, - }, - - lt_amount: ByLowerThanAmount { - _10sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10)))?, - _100sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100)))?, - _1k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K)))?, - _10k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K)))?, - _100k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K)))?, - _1m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1M)))?, - _10m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10M)))?, - _1btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1BTC)))?, - _10btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10BTC)))?, - _100btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100BTC)))?, - _1k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K_BTC)))?, - _10k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K_BTC)))?, - _100k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K_BTC)))?, - }, - - ge_amount: ByGreatEqualAmount { - _1sat: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1)))?, - _10sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10)))?, - _100sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100)))?, - _1k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K)))?, - _10k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K)))?, - _100k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100K)))?, - _1m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1M)))?, - _10m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10M)))?, - _1btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1BTC)))?, - _10btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10BTC)))?, - _100btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100BTC)))?, - _1k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K_BTC)))?, - _10k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K_BTC)))?, - }, + amount_range: ByAmountRange::try_new(&full)?, + lt_amount: ByLowerThanAmount::try_new(&none)?, + ge_amount: ByGreatEqualAmount::try_new(&none)?, })) } diff --git a/crates/brk_computer/src/stateful/cohorts/utxo.rs b/crates/brk_computer/src/stateful/cohorts/utxo.rs index de94a6c41..f87e951fb 100644 --- a/crates/brk_computer/src/stateful/cohorts/utxo.rs +++ b/crates/brk_computer/src/stateful/cohorts/utxo.rs @@ -38,6 +38,7 @@ impl UTXOCohortVecs { pub fn forced_import( db: &Database, filter: Filter, + name: &str, version: Version, indexes: &indexes::Vecs, price: Option<&price::Vecs>, @@ -46,11 +47,12 @@ impl UTXOCohortVecs { all_supply: Option<&SupplyMetrics>, ) -> Result { let compute_dollars = price.is_some(); - let full_name = filter.to_full_name(CohortContext::Utxo); + let full_name = CohortContext::Utxo.full_name(&filter, name); let cfg = ImportConfig { db, filter, + full_name: &full_name, context: CohortContext::Utxo, version, indexes, diff --git a/crates/brk_computer/src/stateful/cohorts/utxo_cohorts/mod.rs b/crates/brk_computer/src/stateful/cohorts/utxo_cohorts/mod.rs index c88d65729..0a61a39a6 100644 --- a/crates/brk_computer/src/stateful/cohorts/utxo_cohorts/mod.rs +++ b/crates/brk_computer/src/stateful/cohorts/utxo_cohorts/mod.rs @@ -6,16 +6,11 @@ use std::path::Path; use brk_error::Result; use brk_grouper::{ - AmountFilter, ByAgeRange, ByAmountRange, ByEpoch, ByGreatEqualAmount, ByLowerThanAmount, - ByMaxAge, ByMinAge, BySpendableType, ByTerm, ByYear, DAYS_1D, DAYS_1M, DAYS_1W, DAYS_1Y, - DAYS_2M, DAYS_2Y, DAYS_3M, DAYS_3Y, DAYS_4M, DAYS_4Y, DAYS_5M, DAYS_5Y, DAYS_6M, DAYS_6Y, - DAYS_7Y, DAYS_8Y, DAYS_10Y, DAYS_12Y, DAYS_15Y, Filter, Filtered, StateLevel, Term, TimeFilter, - UTXOGroups, + ByAgeRange, ByAmountRange, ByEpoch, ByGreatEqualAmount, ByLowerThanAmount, ByMaxAge, ByMinAge, + BySpendableType, ByTerm, ByYear, Filter, Filtered, StateLevel, UTXOGroups, }; use brk_traversable::Traversable; -use brk_types::{ - Bitcoin, DateIndex, Dollars, HalvingEpoch, Height, OutputType, Sats, Version, Year, -}; +use brk_types::{Bitcoin, DateIndex, Dollars, Height, Sats, Version}; use derive_deref::{Deref, DerefMut}; use rayon::prelude::*; use vecdb::{AnyStoredVec, Database, Exit, IterableVec}; @@ -50,6 +45,7 @@ impl UTXOCohorts { let all = UTXOCohortVecs::forced_import( db, Filter::All, + "", version + VERSION + Version::ONE, indexes, price, @@ -62,227 +58,34 @@ impl UTXOCohorts { let all_supply = Some(&all.metrics.supply); // Create all cohorts first (while borrowing all_supply), then assemble struct - let term = ByTerm { - short: UTXOCohortVecs::forced_import( - db, - Filter::Term(Term::Sth), - v, - indexes, - price, - states_path, - StateLevel::PriceOnly, - all_supply, - )?, - long: UTXOCohortVecs::forced_import( - db, - Filter::Term(Term::Lth), - v, - indexes, - price, - states_path, - StateLevel::PriceOnly, - all_supply, - )?, - }; - - let full = |f: Filter| { + let price_only = |f: Filter, name: &'static str| { UTXOCohortVecs::forced_import( - db, - f, - v, - indexes, - price, - states_path, - StateLevel::Full, - all_supply, - ) - }; - let none = |f: Filter| { - UTXOCohortVecs::forced_import( - db, - f, - v, - indexes, - price, - states_path, - StateLevel::None, - all_supply, + db, f, name, v, indexes, price, states_path, StateLevel::PriceOnly, all_supply, ) }; - let epoch = ByEpoch { - _0: full(Filter::Epoch(HalvingEpoch::new(0)))?, - _1: full(Filter::Epoch(HalvingEpoch::new(1)))?, - _2: full(Filter::Epoch(HalvingEpoch::new(2)))?, - _3: full(Filter::Epoch(HalvingEpoch::new(3)))?, - _4: full(Filter::Epoch(HalvingEpoch::new(4)))?, + let term = ByTerm::try_new(&price_only)?; + + let full = |f: Filter, name: &'static str| { + UTXOCohortVecs::forced_import( + db, f, name, v, indexes, price, states_path, StateLevel::Full, all_supply, + ) + }; + let none = |f: Filter, name: &'static str| { + UTXOCohortVecs::forced_import( + db, f, name, v, indexes, price, states_path, StateLevel::None, all_supply, + ) }; - let year = ByYear { - _2009: full(Filter::Year(Year::new(2009)))?, - _2010: full(Filter::Year(Year::new(2010)))?, - _2011: full(Filter::Year(Year::new(2011)))?, - _2012: full(Filter::Year(Year::new(2012)))?, - _2013: full(Filter::Year(Year::new(2013)))?, - _2014: full(Filter::Year(Year::new(2014)))?, - _2015: full(Filter::Year(Year::new(2015)))?, - _2016: full(Filter::Year(Year::new(2016)))?, - _2017: full(Filter::Year(Year::new(2017)))?, - _2018: full(Filter::Year(Year::new(2018)))?, - _2019: full(Filter::Year(Year::new(2019)))?, - _2020: full(Filter::Year(Year::new(2020)))?, - _2021: full(Filter::Year(Year::new(2021)))?, - _2022: full(Filter::Year(Year::new(2022)))?, - _2023: full(Filter::Year(Year::new(2023)))?, - _2024: full(Filter::Year(Year::new(2024)))?, - _2025: full(Filter::Year(Year::new(2025)))?, - _2026: full(Filter::Year(Year::new(2026)))?, - }; - - let type_ = BySpendableType { - p2pk65: full(Filter::Type(OutputType::P2PK65))?, - p2pk33: full(Filter::Type(OutputType::P2PK33))?, - p2pkh: full(Filter::Type(OutputType::P2PKH))?, - p2sh: full(Filter::Type(OutputType::P2SH))?, - p2wpkh: full(Filter::Type(OutputType::P2WPKH))?, - p2wsh: full(Filter::Type(OutputType::P2WSH))?, - p2tr: full(Filter::Type(OutputType::P2TR))?, - p2a: full(Filter::Type(OutputType::P2A))?, - p2ms: full(Filter::Type(OutputType::P2MS))?, - empty: full(Filter::Type(OutputType::Empty))?, - unknown: full(Filter::Type(OutputType::Unknown))?, - }; - - let max_age = ByMaxAge { - _1w: none(Filter::Time(TimeFilter::LowerThan(DAYS_1W)))?, - _1m: none(Filter::Time(TimeFilter::LowerThan(DAYS_1M)))?, - _2m: none(Filter::Time(TimeFilter::LowerThan(DAYS_2M)))?, - _3m: none(Filter::Time(TimeFilter::LowerThan(DAYS_3M)))?, - _4m: none(Filter::Time(TimeFilter::LowerThan(DAYS_4M)))?, - _5m: none(Filter::Time(TimeFilter::LowerThan(DAYS_5M)))?, - _6m: none(Filter::Time(TimeFilter::LowerThan(DAYS_6M)))?, - _1y: none(Filter::Time(TimeFilter::LowerThan(DAYS_1Y)))?, - _2y: none(Filter::Time(TimeFilter::LowerThan(DAYS_2Y)))?, - _3y: none(Filter::Time(TimeFilter::LowerThan(DAYS_3Y)))?, - _4y: none(Filter::Time(TimeFilter::LowerThan(DAYS_4Y)))?, - _5y: none(Filter::Time(TimeFilter::LowerThan(DAYS_5Y)))?, - _6y: none(Filter::Time(TimeFilter::LowerThan(DAYS_6Y)))?, - _7y: none(Filter::Time(TimeFilter::LowerThan(DAYS_7Y)))?, - _8y: none(Filter::Time(TimeFilter::LowerThan(DAYS_8Y)))?, - _10y: none(Filter::Time(TimeFilter::LowerThan(DAYS_10Y)))?, - _12y: none(Filter::Time(TimeFilter::LowerThan(DAYS_12Y)))?, - _15y: none(Filter::Time(TimeFilter::LowerThan(DAYS_15Y)))?, - }; - - let min_age = ByMinAge { - _1d: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1D)))?, - _1w: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1W)))?, - _1m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1M)))?, - _2m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2M)))?, - _3m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3M)))?, - _4m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4M)))?, - _5m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5M)))?, - _6m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6M)))?, - _1y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1Y)))?, - _2y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2Y)))?, - _3y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3Y)))?, - _4y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4Y)))?, - _5y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5Y)))?, - _6y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6Y)))?, - _7y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_7Y)))?, - _8y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_8Y)))?, - _10y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_10Y)))?, - _12y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_12Y)))?, - }; - - let age_range = ByAgeRange { - up_to_1d: full(Filter::Time(TimeFilter::Range(0..DAYS_1D)))?, - _1d_to_1w: full(Filter::Time(TimeFilter::Range(DAYS_1D..DAYS_1W)))?, - _1w_to_1m: full(Filter::Time(TimeFilter::Range(DAYS_1W..DAYS_1M)))?, - _1m_to_2m: full(Filter::Time(TimeFilter::Range(DAYS_1M..DAYS_2M)))?, - _2m_to_3m: full(Filter::Time(TimeFilter::Range(DAYS_2M..DAYS_3M)))?, - _3m_to_4m: full(Filter::Time(TimeFilter::Range(DAYS_3M..DAYS_4M)))?, - _4m_to_5m: full(Filter::Time(TimeFilter::Range(DAYS_4M..DAYS_5M)))?, - _5m_to_6m: full(Filter::Time(TimeFilter::Range(DAYS_5M..DAYS_6M)))?, - _6m_to_1y: full(Filter::Time(TimeFilter::Range(DAYS_6M..DAYS_1Y)))?, - _1y_to_2y: full(Filter::Time(TimeFilter::Range(DAYS_1Y..DAYS_2Y)))?, - _2y_to_3y: full(Filter::Time(TimeFilter::Range(DAYS_2Y..DAYS_3Y)))?, - _3y_to_4y: full(Filter::Time(TimeFilter::Range(DAYS_3Y..DAYS_4Y)))?, - _4y_to_5y: full(Filter::Time(TimeFilter::Range(DAYS_4Y..DAYS_5Y)))?, - _5y_to_6y: full(Filter::Time(TimeFilter::Range(DAYS_5Y..DAYS_6Y)))?, - _6y_to_7y: full(Filter::Time(TimeFilter::Range(DAYS_6Y..DAYS_7Y)))?, - _7y_to_8y: full(Filter::Time(TimeFilter::Range(DAYS_7Y..DAYS_8Y)))?, - _8y_to_10y: full(Filter::Time(TimeFilter::Range(DAYS_8Y..DAYS_10Y)))?, - _10y_to_12y: full(Filter::Time(TimeFilter::Range(DAYS_10Y..DAYS_12Y)))?, - _12y_to_15y: full(Filter::Time(TimeFilter::Range(DAYS_12Y..DAYS_15Y)))?, - from_15y: full(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_15Y)))?, - }; - - let amount_range = ByAmountRange { - _0sats: full(Filter::Amount(AmountFilter::LowerThan(Sats::_1)))?, - _1sat_to_10sats: full(Filter::Amount(AmountFilter::Range(Sats::_1..Sats::_10)))?, - _10sats_to_100sats: full(Filter::Amount(AmountFilter::Range(Sats::_10..Sats::_100)))?, - _100sats_to_1k_sats: full(Filter::Amount(AmountFilter::Range(Sats::_100..Sats::_1K)))?, - _1k_sats_to_10k_sats: full(Filter::Amount(AmountFilter::Range(Sats::_1K..Sats::_10K)))?, - _10k_sats_to_100k_sats: full(Filter::Amount(AmountFilter::Range( - Sats::_10K..Sats::_100K, - )))?, - _100k_sats_to_1m_sats: full(Filter::Amount(AmountFilter::Range( - Sats::_100K..Sats::_1M, - )))?, - _1m_sats_to_10m_sats: full(Filter::Amount(AmountFilter::Range(Sats::_1M..Sats::_10M)))?, - _10m_sats_to_1btc: full(Filter::Amount(AmountFilter::Range(Sats::_10M..Sats::_1BTC)))?, - _1btc_to_10btc: full(Filter::Amount(AmountFilter::Range( - Sats::_1BTC..Sats::_10BTC, - )))?, - _10btc_to_100btc: full(Filter::Amount(AmountFilter::Range( - Sats::_10BTC..Sats::_100BTC, - )))?, - _100btc_to_1k_btc: full(Filter::Amount(AmountFilter::Range( - Sats::_100BTC..Sats::_1K_BTC, - )))?, - _1k_btc_to_10k_btc: full(Filter::Amount(AmountFilter::Range( - Sats::_1K_BTC..Sats::_10K_BTC, - )))?, - _10k_btc_to_100k_btc: full(Filter::Amount(AmountFilter::Range( - Sats::_10K_BTC..Sats::_100K_BTC, - )))?, - _100k_btc_or_more: full(Filter::Amount(AmountFilter::GreaterOrEqual( - Sats::_100K_BTC, - )))?, - }; - - let lt_amount = ByLowerThanAmount { - _10sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10)))?, - _100sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100)))?, - _1k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K)))?, - _10k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K)))?, - _100k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K)))?, - _1m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1M)))?, - _10m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10M)))?, - _1btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1BTC)))?, - _10btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10BTC)))?, - _100btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100BTC)))?, - _1k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K_BTC)))?, - _10k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K_BTC)))?, - _100k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K_BTC)))?, - }; - - let ge_amount = ByGreatEqualAmount { - _1sat: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1)))?, - _10sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10)))?, - _100sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100)))?, - _1k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K)))?, - _10k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K)))?, - _100k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100K)))?, - _1m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1M)))?, - _10m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10M)))?, - _1btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1BTC)))?, - _10btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10BTC)))?, - _100btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100BTC)))?, - _1k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K_BTC)))?, - _10k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K_BTC)))?, - }; + let epoch = ByEpoch::try_new(&full)?; + let year = ByYear::try_new(&full)?; + let type_ = BySpendableType::try_new(&full)?; + let max_age = ByMaxAge::try_new(&none)?; + let min_age = ByMinAge::try_new(&none)?; + let age_range = ByAgeRange::try_new(&full)?; + let amount_range = ByAmountRange::try_new(&full)?; + let lt_amount = ByLowerThanAmount::try_new(&none)?; + let ge_amount = ByGreatEqualAmount::try_new(&none)?; Ok(Self(UTXOGroups { all, diff --git a/crates/brk_computer/src/stateful/metrics/config.rs b/crates/brk_computer/src/stateful/metrics/config.rs index e05f11336..9c5179cd2 100644 --- a/crates/brk_computer/src/stateful/metrics/config.rs +++ b/crates/brk_computer/src/stateful/metrics/config.rs @@ -8,6 +8,7 @@ use crate::{indexes, price}; pub struct ImportConfig<'a> { pub db: &'a Database, pub filter: Filter, + pub full_name: &'a str, pub context: CohortContext, pub version: Version, pub indexes: &'a indexes::Vecs, @@ -37,11 +38,10 @@ impl<'a> ImportConfig<'a> { /// Get full metric name with filter prefix. pub fn name(&self, suffix: &str) -> String { - let prefix = self.filter.to_full_name(self.context); - if prefix.is_empty() { + if self.full_name.is_empty() { suffix.to_string() } else { - format!("{prefix}_{suffix}") + format!("{}_{suffix}", self.full_name) } } } diff --git a/crates/brk_grouper/Cargo.toml b/crates/brk_grouper/Cargo.toml index ba5491a81..2b0a93b0c 100644 --- a/crates/brk_grouper/Cargo.toml +++ b/crates/brk_grouper/Cargo.toml @@ -14,3 +14,4 @@ brk_types = { workspace = true } brk_traversable = { workspace = true } vecdb = { workspace = true } rayon = { workspace = true } +serde = { workspace = true } diff --git a/crates/brk_grouper/README.md b/crates/brk_grouper/README.md index eff8810fa..689b434a5 100644 --- a/crates/brk_grouper/README.md +++ b/crates/brk_grouper/README.md @@ -38,8 +38,9 @@ let filter = Filter::Time(TimeFilter::GreaterOrEqual(155)); filter.contains_time(200); // true filter.contains_amount(sats); -// Generate metric names -filter.to_full_name(CohortContext::Utxo); // "utxos_min_age_155d" +// Generate metric names (via CohortContext) +let ctx = CohortContext::Utxo; +ctx.full_name(&filter, "min_age_155d"); // "utxos_min_age_155d" ``` ## Built On diff --git a/crates/brk_grouper/src/address.rs b/crates/brk_grouper/src/address.rs index e3162aca6..f93cd4a01 100644 --- a/crates/brk_grouper/src/address.rs +++ b/crates/brk_grouper/src/address.rs @@ -16,7 +16,7 @@ pub struct AddressGroups { impl AddressGroups { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { Self { ge_amount: ByGreatEqualAmount::new(&mut create), @@ -25,6 +25,17 @@ impl AddressGroups { } } + pub fn try_new(create: &F) -> Result + where + F: Fn(Filter, &'static str) -> Result, + { + Ok(Self { + ge_amount: ByGreatEqualAmount::try_new(create)?, + amount_range: ByAmountRange::try_new(create)?, + lt_amount: ByLowerThanAmount::try_new(create)?, + }) + } + pub fn iter(&self) -> impl Iterator { self.ge_amount .iter() diff --git a/crates/brk_grouper/src/amount_filter.rs b/crates/brk_grouper/src/amount_filter.rs index 1ca1ab34a..0afb5c959 100644 --- a/crates/brk_grouper/src/amount_filter.rs +++ b/crates/brk_grouper/src/amount_filter.rs @@ -33,40 +33,4 @@ impl AmountFilter { AmountFilter::Range(_) => false, } } - - pub fn to_name_suffix(&self) -> String { - match self { - AmountFilter::LowerThan(s) if *s == Sats::_1 => "with_0sats".to_string(), - AmountFilter::LowerThan(s) => format!("under_{}", format_sats(*s)), - AmountFilter::GreaterOrEqual(s) => format!("above_{}", format_sats(*s)), - AmountFilter::Range(r) => { - format!( - "above_{}_under_{}", - format_sats(r.start), - format_sats(r.end) - ) - } - } - } -} - -fn format_sats(sats: Sats) -> String { - match sats { - s if s == Sats::ZERO => "0sats".to_string(), - s if s == Sats::_1 => "1sat".to_string(), - s if s == Sats::_10 => "10sats".to_string(), - s if s == Sats::_100 => "100sats".to_string(), - s if s == Sats::_1K => "1k_sats".to_string(), - s if s == Sats::_10K => "10k_sats".to_string(), - s if s == Sats::_100K => "100k_sats".to_string(), - s if s == Sats::_1M => "1m_sats".to_string(), - s if s == Sats::_10M => "10m_sats".to_string(), - s if s == Sats::_1BTC => "1btc".to_string(), - s if s == Sats::_10BTC => "10btc".to_string(), - s if s == Sats::_100BTC => "100btc".to_string(), - s if s == Sats::_1K_BTC => "1k_btc".to_string(), - s if s == Sats::_10K_BTC => "10k_btc".to_string(), - s if s == Sats::_100K_BTC => "100k_btc".to_string(), - _ => format!("{}sats", u64::from(sats)), - } } diff --git a/crates/brk_grouper/src/by_age_range.rs b/crates/brk_grouper/src/by_age_range.rs index f1c523a81..0f4548900 100644 --- a/crates/brk_grouper/src/by_age_range.rs +++ b/crates/brk_grouper/src/by_age_range.rs @@ -1,7 +1,10 @@ +use std::ops::Range; + use brk_traversable::Traversable; use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use serde::Serialize; -use super::{Filter, TimeFilter}; +use super::{CohortName, Filter, TimeFilter}; // Age boundary constants in days pub const DAYS_1D: usize = 1; @@ -31,7 +34,85 @@ pub const AGE_BOUNDARIES: [usize; 19] = [ DAYS_3Y, DAYS_4Y, DAYS_5Y, DAYS_6Y, DAYS_7Y, DAYS_8Y, DAYS_10Y, DAYS_12Y, DAYS_15Y, ]; -#[derive(Default, Clone, Traversable)] +/// Age range bounds (end = usize::MAX means unbounded) +pub const AGE_RANGE_BOUNDS: ByAgeRange> = ByAgeRange { + up_to_1d: 0..DAYS_1D, + _1d_to_1w: DAYS_1D..DAYS_1W, + _1w_to_1m: DAYS_1W..DAYS_1M, + _1m_to_2m: DAYS_1M..DAYS_2M, + _2m_to_3m: DAYS_2M..DAYS_3M, + _3m_to_4m: DAYS_3M..DAYS_4M, + _4m_to_5m: DAYS_4M..DAYS_5M, + _5m_to_6m: DAYS_5M..DAYS_6M, + _6m_to_1y: DAYS_6M..DAYS_1Y, + _1y_to_2y: DAYS_1Y..DAYS_2Y, + _2y_to_3y: DAYS_2Y..DAYS_3Y, + _3y_to_4y: DAYS_3Y..DAYS_4Y, + _4y_to_5y: DAYS_4Y..DAYS_5Y, + _5y_to_6y: DAYS_5Y..DAYS_6Y, + _6y_to_7y: DAYS_6Y..DAYS_7Y, + _7y_to_8y: DAYS_7Y..DAYS_8Y, + _8y_to_10y: DAYS_8Y..DAYS_10Y, + _10y_to_12y: DAYS_10Y..DAYS_12Y, + _12y_to_15y: DAYS_12Y..DAYS_15Y, + from_15y: DAYS_15Y..usize::MAX, +}; + +/// Age range filters +pub const AGE_RANGE_FILTERS: ByAgeRange = ByAgeRange { + up_to_1d: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS.up_to_1d)), + _1d_to_1w: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._1d_to_1w)), + _1w_to_1m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._1w_to_1m)), + _1m_to_2m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._1m_to_2m)), + _2m_to_3m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._2m_to_3m)), + _3m_to_4m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._3m_to_4m)), + _4m_to_5m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._4m_to_5m)), + _5m_to_6m: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._5m_to_6m)), + _6m_to_1y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._6m_to_1y)), + _1y_to_2y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._1y_to_2y)), + _2y_to_3y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._2y_to_3y)), + _3y_to_4y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._3y_to_4y)), + _4y_to_5y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._4y_to_5y)), + _5y_to_6y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._5y_to_6y)), + _6y_to_7y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._6y_to_7y)), + _7y_to_8y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._7y_to_8y)), + _8y_to_10y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._8y_to_10y)), + _10y_to_12y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._10y_to_12y)), + _12y_to_15y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS._12y_to_15y)), + from_15y: Filter::Time(TimeFilter::Range(AGE_RANGE_BOUNDS.from_15y)), +}; + +/// Age range names +pub const AGE_RANGE_NAMES: ByAgeRange = ByAgeRange { + up_to_1d: CohortName::new("up_to_1d_old", "<1d", "Up to 1 Day Old"), + _1d_to_1w: CohortName::new("at_least_1d_up_to_1w_old", "1d-1w", "1 Day to 1 Week Old"), + _1w_to_1m: CohortName::new("at_least_1w_up_to_1m_old", "1w-1m", "1 Week to 1 Month Old"), + _1m_to_2m: CohortName::new("at_least_1m_up_to_2m_old", "1m-2m", "1 to 2 Months Old"), + _2m_to_3m: CohortName::new("at_least_2m_up_to_3m_old", "2m-3m", "2 to 3 Months Old"), + _3m_to_4m: CohortName::new("at_least_3m_up_to_4m_old", "3m-4m", "3 to 4 Months Old"), + _4m_to_5m: CohortName::new("at_least_4m_up_to_5m_old", "4m-5m", "4 to 5 Months Old"), + _5m_to_6m: CohortName::new("at_least_5m_up_to_6m_old", "5m-6m", "5 to 6 Months Old"), + _6m_to_1y: CohortName::new("at_least_6m_up_to_1y_old", "6m-1y", "6 Months to 1 Year Old"), + _1y_to_2y: CohortName::new("at_least_1y_up_to_2y_old", "1y-2y", "1 to 2 Years Old"), + _2y_to_3y: CohortName::new("at_least_2y_up_to_3y_old", "2y-3y", "2 to 3 Years Old"), + _3y_to_4y: CohortName::new("at_least_3y_up_to_4y_old", "3y-4y", "3 to 4 Years Old"), + _4y_to_5y: CohortName::new("at_least_4y_up_to_5y_old", "4y-5y", "4 to 5 Years Old"), + _5y_to_6y: CohortName::new("at_least_5y_up_to_6y_old", "5y-6y", "5 to 6 Years Old"), + _6y_to_7y: CohortName::new("at_least_6y_up_to_7y_old", "6y-7y", "6 to 7 Years Old"), + _7y_to_8y: CohortName::new("at_least_7y_up_to_8y_old", "7y-8y", "7 to 8 Years Old"), + _8y_to_10y: CohortName::new("at_least_8y_up_to_10y_old", "8y-10y", "8 to 10 Years Old"), + _10y_to_12y: CohortName::new("at_least_10y_up_to_12y_old", "10y-12y", "10 to 12 Years Old"), + _12y_to_15y: CohortName::new("at_least_12y_up_to_15y_old", "12y-15y", "12 to 15 Years Old"), + from_15y: CohortName::new("at_least_15y_old", "15y+", "15+ Years Old"), +}; + +impl ByAgeRange { + pub const fn names() -> &'static Self { + &AGE_RANGE_NAMES + } +} + +#[derive(Default, Clone, Traversable, Serialize)] pub struct ByAgeRange { pub up_to_1d: T, pub _1d_to_1w: T, @@ -85,32 +166,64 @@ impl ByAgeRange { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { + let f = AGE_RANGE_FILTERS; + let n = AGE_RANGE_NAMES; Self { - up_to_1d: create(Filter::Time(TimeFilter::Range(0..DAYS_1D))), - _1d_to_1w: create(Filter::Time(TimeFilter::Range(DAYS_1D..DAYS_1W))), - _1w_to_1m: create(Filter::Time(TimeFilter::Range(DAYS_1W..DAYS_1M))), - _1m_to_2m: create(Filter::Time(TimeFilter::Range(DAYS_1M..DAYS_2M))), - _2m_to_3m: create(Filter::Time(TimeFilter::Range(DAYS_2M..DAYS_3M))), - _3m_to_4m: create(Filter::Time(TimeFilter::Range(DAYS_3M..DAYS_4M))), - _4m_to_5m: create(Filter::Time(TimeFilter::Range(DAYS_4M..DAYS_5M))), - _5m_to_6m: create(Filter::Time(TimeFilter::Range(DAYS_5M..DAYS_6M))), - _6m_to_1y: create(Filter::Time(TimeFilter::Range(DAYS_6M..DAYS_1Y))), - _1y_to_2y: create(Filter::Time(TimeFilter::Range(DAYS_1Y..DAYS_2Y))), - _2y_to_3y: create(Filter::Time(TimeFilter::Range(DAYS_2Y..DAYS_3Y))), - _3y_to_4y: create(Filter::Time(TimeFilter::Range(DAYS_3Y..DAYS_4Y))), - _4y_to_5y: create(Filter::Time(TimeFilter::Range(DAYS_4Y..DAYS_5Y))), - _5y_to_6y: create(Filter::Time(TimeFilter::Range(DAYS_5Y..DAYS_6Y))), - _6y_to_7y: create(Filter::Time(TimeFilter::Range(DAYS_6Y..DAYS_7Y))), - _7y_to_8y: create(Filter::Time(TimeFilter::Range(DAYS_7Y..DAYS_8Y))), - _8y_to_10y: create(Filter::Time(TimeFilter::Range(DAYS_8Y..DAYS_10Y))), - _10y_to_12y: create(Filter::Time(TimeFilter::Range(DAYS_10Y..DAYS_12Y))), - _12y_to_15y: create(Filter::Time(TimeFilter::Range(DAYS_12Y..DAYS_15Y))), - from_15y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_15Y))), + up_to_1d: create(f.up_to_1d.clone(), n.up_to_1d.id), + _1d_to_1w: create(f._1d_to_1w.clone(), n._1d_to_1w.id), + _1w_to_1m: create(f._1w_to_1m.clone(), n._1w_to_1m.id), + _1m_to_2m: create(f._1m_to_2m.clone(), n._1m_to_2m.id), + _2m_to_3m: create(f._2m_to_3m.clone(), n._2m_to_3m.id), + _3m_to_4m: create(f._3m_to_4m.clone(), n._3m_to_4m.id), + _4m_to_5m: create(f._4m_to_5m.clone(), n._4m_to_5m.id), + _5m_to_6m: create(f._5m_to_6m.clone(), n._5m_to_6m.id), + _6m_to_1y: create(f._6m_to_1y.clone(), n._6m_to_1y.id), + _1y_to_2y: create(f._1y_to_2y.clone(), n._1y_to_2y.id), + _2y_to_3y: create(f._2y_to_3y.clone(), n._2y_to_3y.id), + _3y_to_4y: create(f._3y_to_4y.clone(), n._3y_to_4y.id), + _4y_to_5y: create(f._4y_to_5y.clone(), n._4y_to_5y.id), + _5y_to_6y: create(f._5y_to_6y.clone(), n._5y_to_6y.id), + _6y_to_7y: create(f._6y_to_7y.clone(), n._6y_to_7y.id), + _7y_to_8y: create(f._7y_to_8y.clone(), n._7y_to_8y.id), + _8y_to_10y: create(f._8y_to_10y.clone(), n._8y_to_10y.id), + _10y_to_12y: create(f._10y_to_12y.clone(), n._10y_to_12y.id), + _12y_to_15y: create(f._12y_to_15y.clone(), n._12y_to_15y.id), + from_15y: create(f.from_15y.clone(), n.from_15y.id), } } + pub fn try_new(mut create: F) -> Result + where + F: FnMut(Filter, &'static str) -> Result, + { + let f = AGE_RANGE_FILTERS; + let n = AGE_RANGE_NAMES; + Ok(Self { + up_to_1d: create(f.up_to_1d.clone(), n.up_to_1d.id)?, + _1d_to_1w: create(f._1d_to_1w.clone(), n._1d_to_1w.id)?, + _1w_to_1m: create(f._1w_to_1m.clone(), n._1w_to_1m.id)?, + _1m_to_2m: create(f._1m_to_2m.clone(), n._1m_to_2m.id)?, + _2m_to_3m: create(f._2m_to_3m.clone(), n._2m_to_3m.id)?, + _3m_to_4m: create(f._3m_to_4m.clone(), n._3m_to_4m.id)?, + _4m_to_5m: create(f._4m_to_5m.clone(), n._4m_to_5m.id)?, + _5m_to_6m: create(f._5m_to_6m.clone(), n._5m_to_6m.id)?, + _6m_to_1y: create(f._6m_to_1y.clone(), n._6m_to_1y.id)?, + _1y_to_2y: create(f._1y_to_2y.clone(), n._1y_to_2y.id)?, + _2y_to_3y: create(f._2y_to_3y.clone(), n._2y_to_3y.id)?, + _3y_to_4y: create(f._3y_to_4y.clone(), n._3y_to_4y.id)?, + _4y_to_5y: create(f._4y_to_5y.clone(), n._4y_to_5y.id)?, + _5y_to_6y: create(f._5y_to_6y.clone(), n._5y_to_6y.id)?, + _6y_to_7y: create(f._6y_to_7y.clone(), n._6y_to_7y.id)?, + _7y_to_8y: create(f._7y_to_8y.clone(), n._7y_to_8y.id)?, + _8y_to_10y: create(f._8y_to_10y.clone(), n._8y_to_10y.id)?, + _10y_to_12y: create(f._10y_to_12y.clone(), n._10y_to_12y.id)?, + _12y_to_15y: create(f._12y_to_15y.clone(), n._12y_to_15y.id)?, + from_15y: create(f.from_15y.clone(), n.from_15y.id)?, + }) + } + pub fn iter(&self) -> impl Iterator { [ &self.up_to_1d, diff --git a/crates/brk_grouper/src/by_amount_range.rs b/crates/brk_grouper/src/by_amount_range.rs index edc5c4e04..61da5e5a3 100644 --- a/crates/brk_grouper/src/by_amount_range.rs +++ b/crates/brk_grouper/src/by_amount_range.rs @@ -1,10 +1,11 @@ -use std::ops::{Add, AddAssign}; +use std::ops::{Add, AddAssign, Range}; use brk_traversable::Traversable; use brk_types::Sats; use rayon::prelude::*; +use serde::Serialize; -use super::{AmountFilter, Filter}; +use super::{AmountFilter, CohortName, Filter}; /// Bucket index for amount ranges. Use for cheap comparisons and direct lookups. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -57,7 +58,108 @@ pub fn amounts_in_different_buckets(a: Sats, b: Sats) -> bool { AmountBucket::from(a) != AmountBucket::from(b) } -#[derive(Debug, Default, Clone, Traversable)] +/// Amount range bounds +pub const AMOUNT_RANGE_BOUNDS: ByAmountRange> = ByAmountRange { + _0sats: Sats::ZERO..Sats::_1, + _1sat_to_10sats: Sats::_1..Sats::_10, + _10sats_to_100sats: Sats::_10..Sats::_100, + _100sats_to_1k_sats: Sats::_100..Sats::_1K, + _1k_sats_to_10k_sats: Sats::_1K..Sats::_10K, + _10k_sats_to_100k_sats: Sats::_10K..Sats::_100K, + _100k_sats_to_1m_sats: Sats::_100K..Sats::_1M, + _1m_sats_to_10m_sats: Sats::_1M..Sats::_10M, + _10m_sats_to_1btc: Sats::_10M..Sats::_1BTC, + _1btc_to_10btc: Sats::_1BTC..Sats::_10BTC, + _10btc_to_100btc: Sats::_10BTC..Sats::_100BTC, + _100btc_to_1k_btc: Sats::_100BTC..Sats::_1K_BTC, + _1k_btc_to_10k_btc: Sats::_1K_BTC..Sats::_10K_BTC, + _10k_btc_to_100k_btc: Sats::_10K_BTC..Sats::_100K_BTC, + _100k_btc_or_more: Sats::_100K_BTC..Sats::MAX, +}; + +/// Amount range names +pub const AMOUNT_RANGE_NAMES: ByAmountRange = ByAmountRange { + _0sats: CohortName::new("with_0sats", "0 sats", "0 Sats"), + _1sat_to_10sats: CohortName::new("above_1sat_under_10sats", "1-10 sats", "1 to 10 Sats"), + _10sats_to_100sats: CohortName::new( + "above_10sats_under_100sats", + "10-100 sats", + "10 to 100 Sats", + ), + _100sats_to_1k_sats: CohortName::new( + "above_100sats_under_1k_sats", + "100-1k sats", + "100 to 1K Sats", + ), + _1k_sats_to_10k_sats: CohortName::new( + "above_1k_sats_under_10k_sats", + "1k-10k sats", + "1K to 10K Sats", + ), + _10k_sats_to_100k_sats: CohortName::new( + "above_10k_sats_under_100k_sats", + "10k-100k sats", + "10K to 100K Sats", + ), + _100k_sats_to_1m_sats: CohortName::new( + "above_100k_sats_under_1m_sats", + "100k-1M sats", + "100K to 1M Sats", + ), + _1m_sats_to_10m_sats: CohortName::new( + "above_1m_sats_under_10m_sats", + "1M-10M sats", + "1M to 10M Sats", + ), + _10m_sats_to_1btc: CohortName::new("above_10m_sats_under_1btc", "0.1-1 BTC", "0.1 to 1 BTC"), + _1btc_to_10btc: CohortName::new("above_1btc_under_10btc", "1-10 BTC", "1 to 10 BTC"), + _10btc_to_100btc: CohortName::new("above_10btc_under_100btc", "10-100 BTC", "10 to 100 BTC"), + _100btc_to_1k_btc: CohortName::new("above_100btc_under_1k_btc", "100-1k BTC", "100 to 1K BTC"), + _1k_btc_to_10k_btc: CohortName::new( + "above_1k_btc_under_10k_btc", + "1k-10k BTC", + "1K to 10K BTC", + ), + _10k_btc_to_100k_btc: CohortName::new( + "above_10k_btc_under_100k_btc", + "10k-100k BTC", + "10K to 100K BTC", + ), + _100k_btc_or_more: CohortName::new("above_100k_btc", "100k+ BTC", "100K+ BTC"), +}; + +/// Amount range filters +pub const AMOUNT_RANGE_FILTERS: ByAmountRange = ByAmountRange { + _0sats: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._0sats)), + _1sat_to_10sats: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._1sat_to_10sats)), + _10sats_to_100sats: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._10sats_to_100sats)), + _100sats_to_1k_sats: Filter::Amount(AmountFilter::Range( + AMOUNT_RANGE_BOUNDS._100sats_to_1k_sats, + )), + _1k_sats_to_10k_sats: Filter::Amount(AmountFilter::Range( + AMOUNT_RANGE_BOUNDS._1k_sats_to_10k_sats, + )), + _10k_sats_to_100k_sats: Filter::Amount(AmountFilter::Range( + AMOUNT_RANGE_BOUNDS._10k_sats_to_100k_sats, + )), + _100k_sats_to_1m_sats: Filter::Amount(AmountFilter::Range( + AMOUNT_RANGE_BOUNDS._100k_sats_to_1m_sats, + )), + _1m_sats_to_10m_sats: Filter::Amount(AmountFilter::Range( + AMOUNT_RANGE_BOUNDS._1m_sats_to_10m_sats, + )), + _10m_sats_to_1btc: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._10m_sats_to_1btc)), + _1btc_to_10btc: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._1btc_to_10btc)), + _10btc_to_100btc: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._10btc_to_100btc)), + _100btc_to_1k_btc: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._100btc_to_1k_btc)), + _1k_btc_to_10k_btc: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._1k_btc_to_10k_btc)), + _10k_btc_to_100k_btc: Filter::Amount(AmountFilter::Range( + AMOUNT_RANGE_BOUNDS._10k_btc_to_100k_btc, + )), + _100k_btc_or_more: Filter::Amount(AmountFilter::Range(AMOUNT_RANGE_BOUNDS._100k_btc_or_more)), +}; + +#[derive(Debug, Default, Clone, Traversable, Serialize)] pub struct ByAmountRange { pub _0sats: T, pub _1sat_to_10sats: T, @@ -76,50 +178,84 @@ pub struct ByAmountRange { pub _100k_btc_or_more: T, } +impl ByAmountRange { + pub const fn names() -> &'static Self { + &AMOUNT_RANGE_NAMES + } +} + impl ByAmountRange { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { + let f = AMOUNT_RANGE_FILTERS; + let n = AMOUNT_RANGE_NAMES; Self { - _0sats: create(Filter::Amount(AmountFilter::Range(Sats::ZERO..Sats::_1))), - _1sat_to_10sats: create(Filter::Amount(AmountFilter::Range(Sats::_1..Sats::_10))), - _10sats_to_100sats: create(Filter::Amount(AmountFilter::Range(Sats::_10..Sats::_100))), - _100sats_to_1k_sats: create(Filter::Amount(AmountFilter::Range(Sats::_100..Sats::_1K))), - _1k_sats_to_10k_sats: create(Filter::Amount(AmountFilter::Range( - Sats::_1K..Sats::_10K, - ))), - _10k_sats_to_100k_sats: create(Filter::Amount(AmountFilter::Range( - Sats::_10K..Sats::_100K, - ))), - _100k_sats_to_1m_sats: create(Filter::Amount(AmountFilter::Range( - Sats::_100K..Sats::_1M, - ))), - _1m_sats_to_10m_sats: create(Filter::Amount(AmountFilter::Range( - Sats::_1M..Sats::_10M, - ))), - _10m_sats_to_1btc: create(Filter::Amount(AmountFilter::Range(Sats::_10M..Sats::_1BTC))), - _1btc_to_10btc: create(Filter::Amount(AmountFilter::Range( - Sats::_1BTC..Sats::_10BTC, - ))), - _10btc_to_100btc: create(Filter::Amount(AmountFilter::Range( - Sats::_10BTC..Sats::_100BTC, - ))), - _100btc_to_1k_btc: create(Filter::Amount(AmountFilter::Range( - Sats::_100BTC..Sats::_1K_BTC, - ))), - _1k_btc_to_10k_btc: create(Filter::Amount(AmountFilter::Range( - Sats::_1K_BTC..Sats::_10K_BTC, - ))), - _10k_btc_to_100k_btc: create(Filter::Amount(AmountFilter::Range( - Sats::_10K_BTC..Sats::_100K_BTC, - ))), - _100k_btc_or_more: create(Filter::Amount(AmountFilter::Range( - Sats::_100K_BTC..Sats::MAX, - ))), + _0sats: create(f._0sats.clone(), n._0sats.id), + _1sat_to_10sats: create(f._1sat_to_10sats.clone(), n._1sat_to_10sats.id), + _10sats_to_100sats: create(f._10sats_to_100sats.clone(), n._10sats_to_100sats.id), + _100sats_to_1k_sats: create(f._100sats_to_1k_sats.clone(), n._100sats_to_1k_sats.id), + _1k_sats_to_10k_sats: create(f._1k_sats_to_10k_sats.clone(), n._1k_sats_to_10k_sats.id), + _10k_sats_to_100k_sats: create( + f._10k_sats_to_100k_sats.clone(), + n._10k_sats_to_100k_sats.id, + ), + _100k_sats_to_1m_sats: create( + f._100k_sats_to_1m_sats.clone(), + n._100k_sats_to_1m_sats.id, + ), + _1m_sats_to_10m_sats: create(f._1m_sats_to_10m_sats.clone(), n._1m_sats_to_10m_sats.id), + _10m_sats_to_1btc: create(f._10m_sats_to_1btc.clone(), n._10m_sats_to_1btc.id), + _1btc_to_10btc: create(f._1btc_to_10btc.clone(), n._1btc_to_10btc.id), + _10btc_to_100btc: create(f._10btc_to_100btc.clone(), n._10btc_to_100btc.id), + _100btc_to_1k_btc: create(f._100btc_to_1k_btc.clone(), n._100btc_to_1k_btc.id), + _1k_btc_to_10k_btc: create(f._1k_btc_to_10k_btc.clone(), n._1k_btc_to_10k_btc.id), + _10k_btc_to_100k_btc: create(f._10k_btc_to_100k_btc.clone(), n._10k_btc_to_100k_btc.id), + _100k_btc_or_more: create(f._100k_btc_or_more.clone(), n._100k_btc_or_more.id), } } + pub fn try_new(mut create: F) -> Result + where + F: FnMut(Filter, &'static str) -> Result, + { + let f = AMOUNT_RANGE_FILTERS; + let n = AMOUNT_RANGE_NAMES; + Ok(Self { + _0sats: create(f._0sats.clone(), n._0sats.id)?, + _1sat_to_10sats: create(f._1sat_to_10sats.clone(), n._1sat_to_10sats.id)?, + _10sats_to_100sats: create(f._10sats_to_100sats.clone(), n._10sats_to_100sats.id)?, + _100sats_to_1k_sats: create(f._100sats_to_1k_sats.clone(), n._100sats_to_1k_sats.id)?, + _1k_sats_to_10k_sats: create( + f._1k_sats_to_10k_sats.clone(), + n._1k_sats_to_10k_sats.id, + )?, + _10k_sats_to_100k_sats: create( + f._10k_sats_to_100k_sats.clone(), + n._10k_sats_to_100k_sats.id, + )?, + _100k_sats_to_1m_sats: create( + f._100k_sats_to_1m_sats.clone(), + n._100k_sats_to_1m_sats.id, + )?, + _1m_sats_to_10m_sats: create( + f._1m_sats_to_10m_sats.clone(), + n._1m_sats_to_10m_sats.id, + )?, + _10m_sats_to_1btc: create(f._10m_sats_to_1btc.clone(), n._10m_sats_to_1btc.id)?, + _1btc_to_10btc: create(f._1btc_to_10btc.clone(), n._1btc_to_10btc.id)?, + _10btc_to_100btc: create(f._10btc_to_100btc.clone(), n._10btc_to_100btc.id)?, + _100btc_to_1k_btc: create(f._100btc_to_1k_btc.clone(), n._100btc_to_1k_btc.id)?, + _1k_btc_to_10k_btc: create(f._1k_btc_to_10k_btc.clone(), n._1k_btc_to_10k_btc.id)?, + _10k_btc_to_100k_btc: create( + f._10k_btc_to_100k_btc.clone(), + n._10k_btc_to_100k_btc.id, + )?, + _100k_btc_or_more: create(f._100k_btc_or_more.clone(), n._100k_btc_or_more.id)?, + }) + } + #[inline(always)] pub fn get(&self, value: Sats) -> &T { match AmountBucket::from(value).0 { diff --git a/crates/brk_grouper/src/by_epoch.rs b/crates/brk_grouper/src/by_epoch.rs index be50e7735..5bf52e939 100644 --- a/crates/brk_grouper/src/by_epoch.rs +++ b/crates/brk_grouper/src/by_epoch.rs @@ -1,10 +1,38 @@ use brk_traversable::Traversable; use brk_types::{HalvingEpoch, Height}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use serde::Serialize; -use super::Filter; +use super::{CohortName, Filter}; -#[derive(Default, Clone, Traversable)] +/// Epoch values +pub const EPOCH_VALUES: ByEpoch = ByEpoch { + _0: HalvingEpoch::new(0), + _1: HalvingEpoch::new(1), + _2: HalvingEpoch::new(2), + _3: HalvingEpoch::new(3), + _4: HalvingEpoch::new(4), +}; + +/// Epoch filters +pub const EPOCH_FILTERS: ByEpoch = ByEpoch { + _0: Filter::Epoch(EPOCH_VALUES._0), + _1: Filter::Epoch(EPOCH_VALUES._1), + _2: Filter::Epoch(EPOCH_VALUES._2), + _3: Filter::Epoch(EPOCH_VALUES._3), + _4: Filter::Epoch(EPOCH_VALUES._4), +}; + +/// Epoch names +pub const EPOCH_NAMES: ByEpoch = ByEpoch { + _0: CohortName::new("epoch_0", "Epoch 0", "Epoch 0"), + _1: CohortName::new("epoch_1", "Epoch 1", "Epoch 1"), + _2: CohortName::new("epoch_2", "Epoch 2", "Epoch 2"), + _3: CohortName::new("epoch_3", "Epoch 3", "Epoch 3"), + _4: CohortName::new("epoch_4", "Epoch 4", "Epoch 4"), +}; + +#[derive(Default, Clone, Traversable, Serialize)] pub struct ByEpoch { pub _0: T, pub _1: T, @@ -13,20 +41,43 @@ pub struct ByEpoch { pub _4: T, } +impl ByEpoch { + pub const fn names() -> &'static Self { + &EPOCH_NAMES + } +} + impl ByEpoch { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { + let f = EPOCH_FILTERS; + let n = EPOCH_NAMES; Self { - _0: create(Filter::Epoch(HalvingEpoch::new(0))), - _1: create(Filter::Epoch(HalvingEpoch::new(1))), - _2: create(Filter::Epoch(HalvingEpoch::new(2))), - _3: create(Filter::Epoch(HalvingEpoch::new(3))), - _4: create(Filter::Epoch(HalvingEpoch::new(4))), + _0: create(f._0, n._0.id), + _1: create(f._1, n._1.id), + _2: create(f._2, n._2.id), + _3: create(f._3, n._3.id), + _4: create(f._4, n._4.id), } } + pub fn try_new(mut create: F) -> Result + where + F: FnMut(Filter, &'static str) -> Result, + { + let f = EPOCH_FILTERS; + let n = EPOCH_NAMES; + Ok(Self { + _0: create(f._0, n._0.id)?, + _1: create(f._1, n._1.id)?, + _2: create(f._2, n._2.id)?, + _3: create(f._3, n._3.id)?, + _4: create(f._4, n._4.id)?, + }) + } + pub fn iter(&self) -> impl Iterator { [&self._0, &self._1, &self._2, &self._3, &self._4].into_iter() } diff --git a/crates/brk_grouper/src/by_ge_amount.rs b/crates/brk_grouper/src/by_ge_amount.rs index 49fa78cc9..57c0af53c 100644 --- a/crates/brk_grouper/src/by_ge_amount.rs +++ b/crates/brk_grouper/src/by_ge_amount.rs @@ -1,10 +1,64 @@ use brk_traversable::Traversable; use brk_types::Sats; use rayon::prelude::*; +use serde::Serialize; -use super::{AmountFilter, Filter}; +use super::{AmountFilter, CohortName, Filter}; -#[derive(Default, Clone, Traversable)] +/// Greater-or-equal amount thresholds +pub const GE_AMOUNT_THRESHOLDS: ByGreatEqualAmount = ByGreatEqualAmount { + _1sat: Sats::_1, + _10sats: Sats::_10, + _100sats: Sats::_100, + _1k_sats: Sats::_1K, + _10k_sats: Sats::_10K, + _100k_sats: Sats::_100K, + _1m_sats: Sats::_1M, + _10m_sats: Sats::_10M, + _1btc: Sats::_1BTC, + _10btc: Sats::_10BTC, + _100btc: Sats::_100BTC, + _1k_btc: Sats::_1K_BTC, + _10k_btc: Sats::_10K_BTC, +}; + +/// Greater-or-equal amount names +pub const GE_AMOUNT_NAMES: ByGreatEqualAmount = ByGreatEqualAmount { + _1sat: CohortName::new("above_1sat", "1+ sats", "Above 1 Sat"), + _10sats: CohortName::new("above_10sats", "10+ sats", "Above 10 Sats"), + _100sats: CohortName::new("above_100sats", "100+ sats", "Above 100 Sats"), + _1k_sats: CohortName::new("above_1k_sats", "1k+ sats", "Above 1K Sats"), + _10k_sats: CohortName::new("above_10k_sats", "10k+ sats", "Above 10K Sats"), + _100k_sats: CohortName::new("above_100k_sats", "100k+ sats", "Above 100K Sats"), + _1m_sats: CohortName::new("above_1m_sats", "1M+ sats", "Above 1M Sats"), + _10m_sats: CohortName::new("above_10m_sats", "0.1+ BTC", "Above 0.1 BTC"), + _1btc: CohortName::new("above_1btc", "1+ BTC", "Above 1 BTC"), + _10btc: CohortName::new("above_10btc", "10+ BTC", "Above 10 BTC"), + _100btc: CohortName::new("above_100btc", "100+ BTC", "Above 100 BTC"), + _1k_btc: CohortName::new("above_1k_btc", "1k+ BTC", "Above 1K BTC"), + _10k_btc: CohortName::new("above_10k_btc", "10k+ BTC", "Above 10K BTC"), +}; + +/// Greater-or-equal amount filters +pub const GE_AMOUNT_FILTERS: ByGreatEqualAmount = ByGreatEqualAmount { + _1sat: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._1sat)), + _10sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._10sats)), + _100sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._100sats)), + _1k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._1k_sats)), + _10k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._10k_sats)), + _100k_sats: Filter::Amount(AmountFilter::GreaterOrEqual( + GE_AMOUNT_THRESHOLDS._100k_sats, + )), + _1m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._1m_sats)), + _10m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._10m_sats)), + _1btc: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._1btc)), + _10btc: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._10btc)), + _100btc: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._100btc)), + _1k_btc: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._1k_btc)), + _10k_btc: Filter::Amount(AmountFilter::GreaterOrEqual(GE_AMOUNT_THRESHOLDS._10k_btc)), +}; + +#[derive(Default, Clone, Traversable, Serialize)] pub struct ByGreatEqualAmount { pub _1sat: T, pub _10sats: T, @@ -21,28 +75,59 @@ pub struct ByGreatEqualAmount { pub _10k_btc: T, } +impl ByGreatEqualAmount { + pub const fn names() -> &'static Self { + &GE_AMOUNT_NAMES + } +} + impl ByGreatEqualAmount { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { + let f = GE_AMOUNT_FILTERS; + let n = GE_AMOUNT_NAMES; Self { - _1sat: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1))), - _10sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10))), - _100sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100))), - _1k_sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K))), - _10k_sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K))), - _100k_sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100K))), - _1m_sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1M))), - _10m_sats: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10M))), - _1btc: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1BTC))), - _10btc: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10BTC))), - _100btc: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100BTC))), - _1k_btc: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K_BTC))), - _10k_btc: create(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K_BTC))), + _1sat: create(f._1sat.clone(), n._1sat.id), + _10sats: create(f._10sats.clone(), n._10sats.id), + _100sats: create(f._100sats.clone(), n._100sats.id), + _1k_sats: create(f._1k_sats.clone(), n._1k_sats.id), + _10k_sats: create(f._10k_sats.clone(), n._10k_sats.id), + _100k_sats: create(f._100k_sats.clone(), n._100k_sats.id), + _1m_sats: create(f._1m_sats.clone(), n._1m_sats.id), + _10m_sats: create(f._10m_sats.clone(), n._10m_sats.id), + _1btc: create(f._1btc.clone(), n._1btc.id), + _10btc: create(f._10btc.clone(), n._10btc.id), + _100btc: create(f._100btc.clone(), n._100btc.id), + _1k_btc: create(f._1k_btc.clone(), n._1k_btc.id), + _10k_btc: create(f._10k_btc.clone(), n._10k_btc.id), } } + pub fn try_new(mut create: F) -> Result + where + F: FnMut(Filter, &'static str) -> Result, + { + let f = GE_AMOUNT_FILTERS; + let n = GE_AMOUNT_NAMES; + Ok(Self { + _1sat: create(f._1sat.clone(), n._1sat.id)?, + _10sats: create(f._10sats.clone(), n._10sats.id)?, + _100sats: create(f._100sats.clone(), n._100sats.id)?, + _1k_sats: create(f._1k_sats.clone(), n._1k_sats.id)?, + _10k_sats: create(f._10k_sats.clone(), n._10k_sats.id)?, + _100k_sats: create(f._100k_sats.clone(), n._100k_sats.id)?, + _1m_sats: create(f._1m_sats.clone(), n._1m_sats.id)?, + _10m_sats: create(f._10m_sats.clone(), n._10m_sats.id)?, + _1btc: create(f._1btc.clone(), n._1btc.id)?, + _10btc: create(f._10btc.clone(), n._10btc.id)?, + _100btc: create(f._100btc.clone(), n._100btc.id)?, + _1k_btc: create(f._1k_btc.clone(), n._1k_btc.id)?, + _10k_btc: create(f._10k_btc.clone(), n._10k_btc.id)?, + }) + } + pub fn iter(&self) -> impl Iterator { [ &self._1sat, diff --git a/crates/brk_grouper/src/by_lt_amount.rs b/crates/brk_grouper/src/by_lt_amount.rs index e8551e802..ec93a71da 100644 --- a/crates/brk_grouper/src/by_lt_amount.rs +++ b/crates/brk_grouper/src/by_lt_amount.rs @@ -1,10 +1,62 @@ use brk_traversable::Traversable; use brk_types::Sats; use rayon::prelude::*; +use serde::Serialize; -use super::{AmountFilter, Filter}; +use super::{AmountFilter, CohortName, Filter}; -#[derive(Default, Clone, Traversable)] +/// Lower-than amount thresholds +pub const LT_AMOUNT_THRESHOLDS: ByLowerThanAmount = ByLowerThanAmount { + _10sats: Sats::_10, + _100sats: Sats::_100, + _1k_sats: Sats::_1K, + _10k_sats: Sats::_10K, + _100k_sats: Sats::_100K, + _1m_sats: Sats::_1M, + _10m_sats: Sats::_10M, + _1btc: Sats::_1BTC, + _10btc: Sats::_10BTC, + _100btc: Sats::_100BTC, + _1k_btc: Sats::_1K_BTC, + _10k_btc: Sats::_10K_BTC, + _100k_btc: Sats::_100K_BTC, +}; + +/// Lower-than amount names +pub const LT_AMOUNT_NAMES: ByLowerThanAmount = ByLowerThanAmount { + _10sats: CohortName::new("under_10sats", "<10 sats", "Under 10 Sats"), + _100sats: CohortName::new("under_100sats", "<100 sats", "Under 100 Sats"), + _1k_sats: CohortName::new("under_1k_sats", "<1k sats", "Under 1K Sats"), + _10k_sats: CohortName::new("under_10k_sats", "<10k sats", "Under 10K Sats"), + _100k_sats: CohortName::new("under_100k_sats", "<100k sats", "Under 100K Sats"), + _1m_sats: CohortName::new("under_1m_sats", "<1M sats", "Under 1M Sats"), + _10m_sats: CohortName::new("under_10m_sats", "<0.1 BTC", "Under 0.1 BTC"), + _1btc: CohortName::new("under_1btc", "<1 BTC", "Under 1 BTC"), + _10btc: CohortName::new("under_10btc", "<10 BTC", "Under 10 BTC"), + _100btc: CohortName::new("under_100btc", "<100 BTC", "Under 100 BTC"), + _1k_btc: CohortName::new("under_1k_btc", "<1k BTC", "Under 1K BTC"), + _10k_btc: CohortName::new("under_10k_btc", "<10k BTC", "Under 10K BTC"), + _100k_btc: CohortName::new("under_100k_btc", "<100k BTC", "Under 100K BTC"), +}; + +/// Lower-than amount filters +pub const LT_AMOUNT_FILTERS: ByLowerThanAmount = ByLowerThanAmount { + _10sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._10sats)), + _100sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._100sats)), + _1k_sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._1k_sats)), + _10k_sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._10k_sats)), + _100k_sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._100k_sats)), + _1m_sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._1m_sats)), + _10m_sats: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._10m_sats)), + _1btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._1btc)), + _10btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._10btc)), + _100btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._100btc)), + _1k_btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._1k_btc)), + _10k_btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._10k_btc)), + _100k_btc: Filter::Amount(AmountFilter::LowerThan(LT_AMOUNT_THRESHOLDS._100k_btc)), +}; + +#[derive(Default, Clone, Traversable, Serialize)] pub struct ByLowerThanAmount { pub _10sats: T, pub _100sats: T, @@ -21,28 +73,59 @@ pub struct ByLowerThanAmount { pub _100k_btc: T, } +impl ByLowerThanAmount { + pub const fn names() -> &'static Self { + <_AMOUNT_NAMES + } +} + impl ByLowerThanAmount { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { + let f = LT_AMOUNT_FILTERS; + let n = LT_AMOUNT_NAMES; Self { - _10sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_10))), - _100sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_100))), - _1k_sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_1K))), - _10k_sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_10K))), - _100k_sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_100K))), - _1m_sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_1M))), - _10m_sats: create(Filter::Amount(AmountFilter::LowerThan(Sats::_10M))), - _1btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_1BTC))), - _10btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_10BTC))), - _100btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_100BTC))), - _1k_btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_1K_BTC))), - _10k_btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_10K_BTC))), - _100k_btc: create(Filter::Amount(AmountFilter::LowerThan(Sats::_100K_BTC))), + _10sats: create(f._10sats.clone(), n._10sats.id), + _100sats: create(f._100sats.clone(), n._100sats.id), + _1k_sats: create(f._1k_sats.clone(), n._1k_sats.id), + _10k_sats: create(f._10k_sats.clone(), n._10k_sats.id), + _100k_sats: create(f._100k_sats.clone(), n._100k_sats.id), + _1m_sats: create(f._1m_sats.clone(), n._1m_sats.id), + _10m_sats: create(f._10m_sats.clone(), n._10m_sats.id), + _1btc: create(f._1btc.clone(), n._1btc.id), + _10btc: create(f._10btc.clone(), n._10btc.id), + _100btc: create(f._100btc.clone(), n._100btc.id), + _1k_btc: create(f._1k_btc.clone(), n._1k_btc.id), + _10k_btc: create(f._10k_btc.clone(), n._10k_btc.id), + _100k_btc: create(f._100k_btc.clone(), n._100k_btc.id), } } + pub fn try_new(mut create: F) -> Result + where + F: FnMut(Filter, &'static str) -> Result, + { + let f = LT_AMOUNT_FILTERS; + let n = LT_AMOUNT_NAMES; + Ok(Self { + _10sats: create(f._10sats.clone(), n._10sats.id)?, + _100sats: create(f._100sats.clone(), n._100sats.id)?, + _1k_sats: create(f._1k_sats.clone(), n._1k_sats.id)?, + _10k_sats: create(f._10k_sats.clone(), n._10k_sats.id)?, + _100k_sats: create(f._100k_sats.clone(), n._100k_sats.id)?, + _1m_sats: create(f._1m_sats.clone(), n._1m_sats.id)?, + _10m_sats: create(f._10m_sats.clone(), n._10m_sats.id)?, + _1btc: create(f._1btc.clone(), n._1btc.id)?, + _10btc: create(f._10btc.clone(), n._10btc.id)?, + _100btc: create(f._100btc.clone(), n._100btc.id)?, + _1k_btc: create(f._1k_btc.clone(), n._1k_btc.id)?, + _10k_btc: create(f._10k_btc.clone(), n._10k_btc.id)?, + _100k_btc: create(f._100k_btc.clone(), n._100k_btc.id)?, + }) + } + pub fn iter(&self) -> impl Iterator { [ &self._10sats, diff --git a/crates/brk_grouper/src/by_max_age.rs b/crates/brk_grouper/src/by_max_age.rs index adc29fcfe..31385e128 100644 --- a/crates/brk_grouper/src/by_max_age.rs +++ b/crates/brk_grouper/src/by_max_age.rs @@ -1,12 +1,80 @@ use brk_traversable::Traversable; use rayon::prelude::*; +use serde::Serialize; use super::{ - Filter, TimeFilter, DAYS_10Y, DAYS_12Y, DAYS_15Y, DAYS_1M, DAYS_1W, DAYS_1Y, DAYS_2M, DAYS_2Y, - DAYS_3M, DAYS_3Y, DAYS_4M, DAYS_4Y, DAYS_5M, DAYS_5Y, DAYS_6M, DAYS_6Y, DAYS_7Y, DAYS_8Y, + CohortName, Filter, TimeFilter, DAYS_10Y, DAYS_12Y, DAYS_15Y, DAYS_1M, DAYS_1W, DAYS_1Y, + DAYS_2M, DAYS_2Y, DAYS_3M, DAYS_3Y, DAYS_4M, DAYS_4Y, DAYS_5M, DAYS_5Y, DAYS_6M, DAYS_6Y, + DAYS_7Y, DAYS_8Y, }; -#[derive(Default, Clone, Traversable)] +/// Max age thresholds in days +pub const MAX_AGE_DAYS: ByMaxAge = ByMaxAge { + _1w: DAYS_1W, + _1m: DAYS_1M, + _2m: DAYS_2M, + _3m: DAYS_3M, + _4m: DAYS_4M, + _5m: DAYS_5M, + _6m: DAYS_6M, + _1y: DAYS_1Y, + _2y: DAYS_2Y, + _3y: DAYS_3Y, + _4y: DAYS_4Y, + _5y: DAYS_5Y, + _6y: DAYS_6Y, + _7y: DAYS_7Y, + _8y: DAYS_8Y, + _10y: DAYS_10Y, + _12y: DAYS_12Y, + _15y: DAYS_15Y, +}; + +/// Max age filters (LowerThan threshold) +pub const MAX_AGE_FILTERS: ByMaxAge = ByMaxAge { + _1w: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._1w)), + _1m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._1m)), + _2m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._2m)), + _3m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._3m)), + _4m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._4m)), + _5m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._5m)), + _6m: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._6m)), + _1y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._1y)), + _2y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._2y)), + _3y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._3y)), + _4y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._4y)), + _5y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._5y)), + _6y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._6y)), + _7y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._7y)), + _8y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._8y)), + _10y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._10y)), + _12y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._12y)), + _15y: Filter::Time(TimeFilter::LowerThan(MAX_AGE_DAYS._15y)), +}; + +/// Max age names +pub const MAX_AGE_NAMES: ByMaxAge = ByMaxAge { + _1w: CohortName::new("up_to_1w_old", "<1w", "Up to 1 Week Old"), + _1m: CohortName::new("up_to_1m_old", "<1m", "Up to 1 Month Old"), + _2m: CohortName::new("up_to_2m_old", "<2m", "Up to 2 Months Old"), + _3m: CohortName::new("up_to_3m_old", "<3m", "Up to 3 Months Old"), + _4m: CohortName::new("up_to_4m_old", "<4m", "Up to 4 Months Old"), + _5m: CohortName::new("up_to_5m_old", "<5m", "Up to 5 Months Old"), + _6m: CohortName::new("up_to_6m_old", "<6m", "Up to 6 Months Old"), + _1y: CohortName::new("up_to_1y_old", "<1y", "Up to 1 Year Old"), + _2y: CohortName::new("up_to_2y_old", "<2y", "Up to 2 Years Old"), + _3y: CohortName::new("up_to_3y_old", "<3y", "Up to 3 Years Old"), + _4y: CohortName::new("up_to_4y_old", "<4y", "Up to 4 Years Old"), + _5y: CohortName::new("up_to_5y_old", "<5y", "Up to 5 Years Old"), + _6y: CohortName::new("up_to_6y_old", "<6y", "Up to 6 Years Old"), + _7y: CohortName::new("up_to_7y_old", "<7y", "Up to 7 Years Old"), + _8y: CohortName::new("up_to_8y_old", "<8y", "Up to 8 Years Old"), + _10y: CohortName::new("up_to_10y_old", "<10y", "Up to 10 Years Old"), + _12y: CohortName::new("up_to_12y_old", "<12y", "Up to 12 Years Old"), + _15y: CohortName::new("up_to_15y_old", "<15y", "Up to 15 Years Old"), +}; + +#[derive(Default, Clone, Traversable, Serialize)] pub struct ByMaxAge { pub _1w: T, pub _1m: T, @@ -28,33 +96,69 @@ pub struct ByMaxAge { pub _15y: T, } +impl ByMaxAge { + pub const fn names() -> &'static Self { + &MAX_AGE_NAMES + } +} + impl ByMaxAge { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { + let f = MAX_AGE_FILTERS; + let n = MAX_AGE_NAMES; Self { - _1w: create(Filter::Time(TimeFilter::LowerThan(DAYS_1W))), - _1m: create(Filter::Time(TimeFilter::LowerThan(DAYS_1M))), - _2m: create(Filter::Time(TimeFilter::LowerThan(DAYS_2M))), - _3m: create(Filter::Time(TimeFilter::LowerThan(DAYS_3M))), - _4m: create(Filter::Time(TimeFilter::LowerThan(DAYS_4M))), - _5m: create(Filter::Time(TimeFilter::LowerThan(DAYS_5M))), - _6m: create(Filter::Time(TimeFilter::LowerThan(DAYS_6M))), - _1y: create(Filter::Time(TimeFilter::LowerThan(DAYS_1Y))), - _2y: create(Filter::Time(TimeFilter::LowerThan(DAYS_2Y))), - _3y: create(Filter::Time(TimeFilter::LowerThan(DAYS_3Y))), - _4y: create(Filter::Time(TimeFilter::LowerThan(DAYS_4Y))), - _5y: create(Filter::Time(TimeFilter::LowerThan(DAYS_5Y))), - _6y: create(Filter::Time(TimeFilter::LowerThan(DAYS_6Y))), - _7y: create(Filter::Time(TimeFilter::LowerThan(DAYS_7Y))), - _8y: create(Filter::Time(TimeFilter::LowerThan(DAYS_8Y))), - _10y: create(Filter::Time(TimeFilter::LowerThan(DAYS_10Y))), - _12y: create(Filter::Time(TimeFilter::LowerThan(DAYS_12Y))), - _15y: create(Filter::Time(TimeFilter::LowerThan(DAYS_15Y))), + _1w: create(f._1w.clone(), n._1w.id), + _1m: create(f._1m.clone(), n._1m.id), + _2m: create(f._2m.clone(), n._2m.id), + _3m: create(f._3m.clone(), n._3m.id), + _4m: create(f._4m.clone(), n._4m.id), + _5m: create(f._5m.clone(), n._5m.id), + _6m: create(f._6m.clone(), n._6m.id), + _1y: create(f._1y.clone(), n._1y.id), + _2y: create(f._2y.clone(), n._2y.id), + _3y: create(f._3y.clone(), n._3y.id), + _4y: create(f._4y.clone(), n._4y.id), + _5y: create(f._5y.clone(), n._5y.id), + _6y: create(f._6y.clone(), n._6y.id), + _7y: create(f._7y.clone(), n._7y.id), + _8y: create(f._8y.clone(), n._8y.id), + _10y: create(f._10y.clone(), n._10y.id), + _12y: create(f._12y.clone(), n._12y.id), + _15y: create(f._15y.clone(), n._15y.id), } } + pub fn try_new(mut create: F) -> Result + where + F: FnMut(Filter, &'static str) -> Result, + { + let f = MAX_AGE_FILTERS; + let n = MAX_AGE_NAMES; + Ok(Self { + _1w: create(f._1w.clone(), n._1w.id)?, + _1m: create(f._1m.clone(), n._1m.id)?, + _2m: create(f._2m.clone(), n._2m.id)?, + _3m: create(f._3m.clone(), n._3m.id)?, + _4m: create(f._4m.clone(), n._4m.id)?, + _5m: create(f._5m.clone(), n._5m.id)?, + _6m: create(f._6m.clone(), n._6m.id)?, + _1y: create(f._1y.clone(), n._1y.id)?, + _2y: create(f._2y.clone(), n._2y.id)?, + _3y: create(f._3y.clone(), n._3y.id)?, + _4y: create(f._4y.clone(), n._4y.id)?, + _5y: create(f._5y.clone(), n._5y.id)?, + _6y: create(f._6y.clone(), n._6y.id)?, + _7y: create(f._7y.clone(), n._7y.id)?, + _8y: create(f._8y.clone(), n._8y.id)?, + _10y: create(f._10y.clone(), n._10y.id)?, + _12y: create(f._12y.clone(), n._12y.id)?, + _15y: create(f._15y.clone(), n._15y.id)?, + }) + } + pub fn iter(&self) -> impl Iterator { [ &self._1w, &self._1m, &self._2m, &self._3m, &self._4m, &self._5m, &self._6m, &self._1y, diff --git a/crates/brk_grouper/src/by_min_age.rs b/crates/brk_grouper/src/by_min_age.rs index e4ca16897..fe92946a6 100644 --- a/crates/brk_grouper/src/by_min_age.rs +++ b/crates/brk_grouper/src/by_min_age.rs @@ -1,12 +1,80 @@ use brk_traversable::Traversable; use rayon::prelude::*; +use serde::Serialize; use super::{ - Filter, TimeFilter, DAYS_10Y, DAYS_12Y, DAYS_1D, DAYS_1M, DAYS_1W, DAYS_1Y, DAYS_2M, DAYS_2Y, - DAYS_3M, DAYS_3Y, DAYS_4M, DAYS_4Y, DAYS_5M, DAYS_5Y, DAYS_6M, DAYS_6Y, DAYS_7Y, DAYS_8Y, + CohortName, Filter, TimeFilter, DAYS_10Y, DAYS_12Y, DAYS_1D, DAYS_1M, DAYS_1W, DAYS_1Y, + DAYS_2M, DAYS_2Y, DAYS_3M, DAYS_3Y, DAYS_4M, DAYS_4Y, DAYS_5M, DAYS_5Y, DAYS_6M, DAYS_6Y, + DAYS_7Y, DAYS_8Y, }; -#[derive(Default, Clone, Traversable)] +/// Min age thresholds in days +pub const MIN_AGE_DAYS: ByMinAge = ByMinAge { + _1d: DAYS_1D, + _1w: DAYS_1W, + _1m: DAYS_1M, + _2m: DAYS_2M, + _3m: DAYS_3M, + _4m: DAYS_4M, + _5m: DAYS_5M, + _6m: DAYS_6M, + _1y: DAYS_1Y, + _2y: DAYS_2Y, + _3y: DAYS_3Y, + _4y: DAYS_4Y, + _5y: DAYS_5Y, + _6y: DAYS_6Y, + _7y: DAYS_7Y, + _8y: DAYS_8Y, + _10y: DAYS_10Y, + _12y: DAYS_12Y, +}; + +/// Min age filters (GreaterOrEqual threshold) +pub const MIN_AGE_FILTERS: ByMinAge = ByMinAge { + _1d: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._1d)), + _1w: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._1w)), + _1m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._1m)), + _2m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._2m)), + _3m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._3m)), + _4m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._4m)), + _5m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._5m)), + _6m: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._6m)), + _1y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._1y)), + _2y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._2y)), + _3y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._3y)), + _4y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._4y)), + _5y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._5y)), + _6y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._6y)), + _7y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._7y)), + _8y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._8y)), + _10y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._10y)), + _12y: Filter::Time(TimeFilter::GreaterOrEqual(MIN_AGE_DAYS._12y)), +}; + +/// Min age names +pub const MIN_AGE_NAMES: ByMinAge = ByMinAge { + _1d: CohortName::new("at_least_1d_old", "1d+", "At Least 1 Day Old"), + _1w: CohortName::new("at_least_1w_old", "1w+", "At Least 1 Week Old"), + _1m: CohortName::new("at_least_1m_old", "1m+", "At Least 1 Month Old"), + _2m: CohortName::new("at_least_2m_old", "2m+", "At Least 2 Months Old"), + _3m: CohortName::new("at_least_3m_old", "3m+", "At Least 3 Months Old"), + _4m: CohortName::new("at_least_4m_old", "4m+", "At Least 4 Months Old"), + _5m: CohortName::new("at_least_5m_old", "5m+", "At Least 5 Months Old"), + _6m: CohortName::new("at_least_6m_old", "6m+", "At Least 6 Months Old"), + _1y: CohortName::new("at_least_1y_old", "1y+", "At Least 1 Year Old"), + _2y: CohortName::new("at_least_2y_old", "2y+", "At Least 2 Years Old"), + _3y: CohortName::new("at_least_3y_old", "3y+", "At Least 3 Years Old"), + _4y: CohortName::new("at_least_4y_old", "4y+", "At Least 4 Years Old"), + _5y: CohortName::new("at_least_5y_old", "5y+", "At Least 5 Years Old"), + _6y: CohortName::new("at_least_6y_old", "6y+", "At Least 6 Years Old"), + _7y: CohortName::new("at_least_7y_old", "7y+", "At Least 7 Years Old"), + _8y: CohortName::new("at_least_8y_old", "8y+", "At Least 8 Years Old"), + _10y: CohortName::new("at_least_10y_old", "10y+", "At Least 10 Years Old"), + _12y: CohortName::new("at_least_12y_old", "12y+", "At Least 12 Years Old"), +}; + +#[derive(Default, Clone, Traversable, Serialize)] pub struct ByMinAge { pub _1d: T, pub _1w: T, @@ -28,33 +96,69 @@ pub struct ByMinAge { pub _12y: T, } +impl ByMinAge { + pub const fn names() -> &'static Self { + &MIN_AGE_NAMES + } +} + impl ByMinAge { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { + let f = MIN_AGE_FILTERS; + let n = MIN_AGE_NAMES; Self { - _1d: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1D))), - _1w: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1W))), - _1m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1M))), - _2m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2M))), - _3m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3M))), - _4m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4M))), - _5m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5M))), - _6m: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6M))), - _1y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1Y))), - _2y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2Y))), - _3y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3Y))), - _4y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4Y))), - _5y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5Y))), - _6y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6Y))), - _7y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_7Y))), - _8y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_8Y))), - _10y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_10Y))), - _12y: create(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_12Y))), + _1d: create(f._1d.clone(), n._1d.id), + _1w: create(f._1w.clone(), n._1w.id), + _1m: create(f._1m.clone(), n._1m.id), + _2m: create(f._2m.clone(), n._2m.id), + _3m: create(f._3m.clone(), n._3m.id), + _4m: create(f._4m.clone(), n._4m.id), + _5m: create(f._5m.clone(), n._5m.id), + _6m: create(f._6m.clone(), n._6m.id), + _1y: create(f._1y.clone(), n._1y.id), + _2y: create(f._2y.clone(), n._2y.id), + _3y: create(f._3y.clone(), n._3y.id), + _4y: create(f._4y.clone(), n._4y.id), + _5y: create(f._5y.clone(), n._5y.id), + _6y: create(f._6y.clone(), n._6y.id), + _7y: create(f._7y.clone(), n._7y.id), + _8y: create(f._8y.clone(), n._8y.id), + _10y: create(f._10y.clone(), n._10y.id), + _12y: create(f._12y.clone(), n._12y.id), } } + pub fn try_new(mut create: F) -> Result + where + F: FnMut(Filter, &'static str) -> Result, + { + let f = MIN_AGE_FILTERS; + let n = MIN_AGE_NAMES; + Ok(Self { + _1d: create(f._1d.clone(), n._1d.id)?, + _1w: create(f._1w.clone(), n._1w.id)?, + _1m: create(f._1m.clone(), n._1m.id)?, + _2m: create(f._2m.clone(), n._2m.id)?, + _3m: create(f._3m.clone(), n._3m.id)?, + _4m: create(f._4m.clone(), n._4m.id)?, + _5m: create(f._5m.clone(), n._5m.id)?, + _6m: create(f._6m.clone(), n._6m.id)?, + _1y: create(f._1y.clone(), n._1y.id)?, + _2y: create(f._2y.clone(), n._2y.id)?, + _3y: create(f._3y.clone(), n._3y.id)?, + _4y: create(f._4y.clone(), n._4y.id)?, + _5y: create(f._5y.clone(), n._5y.id)?, + _6y: create(f._6y.clone(), n._6y.id)?, + _7y: create(f._7y.clone(), n._7y.id)?, + _8y: create(f._8y.clone(), n._8y.id)?, + _10y: create(f._10y.clone(), n._10y.id)?, + _12y: create(f._12y.clone(), n._12y.id)?, + }) + } + pub fn iter(&self) -> impl Iterator { [ &self._1d, &self._1w, &self._1m, &self._2m, &self._3m, &self._4m, &self._5m, &self._6m, diff --git a/crates/brk_grouper/src/by_spendable_type.rs b/crates/brk_grouper/src/by_spendable_type.rs index 43fdd03c2..f4bbc301a 100644 --- a/crates/brk_grouper/src/by_spendable_type.rs +++ b/crates/brk_grouper/src/by_spendable_type.rs @@ -3,10 +3,56 @@ use std::ops::{Add, AddAssign}; use brk_traversable::Traversable; use brk_types::OutputType; use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use serde::Serialize; -use super::Filter; +use super::{CohortName, Filter}; -#[derive(Default, Clone, Debug, Traversable)] +/// Spendable type values +pub const SPENDABLE_TYPE_VALUES: BySpendableType = BySpendableType { + p2pk65: OutputType::P2PK65, + p2pk33: OutputType::P2PK33, + p2pkh: OutputType::P2PKH, + p2ms: OutputType::P2MS, + p2sh: OutputType::P2SH, + p2wpkh: OutputType::P2WPKH, + p2wsh: OutputType::P2WSH, + p2tr: OutputType::P2TR, + p2a: OutputType::P2A, + unknown: OutputType::Unknown, + empty: OutputType::Empty, +}; + +/// Spendable type filters +pub const SPENDABLE_TYPE_FILTERS: BySpendableType = BySpendableType { + p2pk65: Filter::Type(SPENDABLE_TYPE_VALUES.p2pk65), + p2pk33: Filter::Type(SPENDABLE_TYPE_VALUES.p2pk33), + p2pkh: Filter::Type(SPENDABLE_TYPE_VALUES.p2pkh), + p2ms: Filter::Type(SPENDABLE_TYPE_VALUES.p2ms), + p2sh: Filter::Type(SPENDABLE_TYPE_VALUES.p2sh), + p2wpkh: Filter::Type(SPENDABLE_TYPE_VALUES.p2wpkh), + p2wsh: Filter::Type(SPENDABLE_TYPE_VALUES.p2wsh), + p2tr: Filter::Type(SPENDABLE_TYPE_VALUES.p2tr), + p2a: Filter::Type(SPENDABLE_TYPE_VALUES.p2a), + unknown: Filter::Type(SPENDABLE_TYPE_VALUES.unknown), + empty: Filter::Type(SPENDABLE_TYPE_VALUES.empty), +}; + +/// Spendable type names +pub const SPENDABLE_TYPE_NAMES: BySpendableType = BySpendableType { + p2pk65: CohortName::new("p2pk65", "P2PK65", "Pay to Public Key (65 bytes)"), + p2pk33: CohortName::new("p2pk33", "P2PK33", "Pay to Public Key (33 bytes)"), + p2pkh: CohortName::new("p2pkh", "P2PKH", "Pay to Public Key Hash"), + p2ms: CohortName::new("p2ms", "P2MS", "Pay to Multisig"), + p2sh: CohortName::new("p2sh", "P2SH", "Pay to Script Hash"), + p2wpkh: CohortName::new("p2wpkh", "P2WPKH", "Pay to Witness Public Key Hash"), + p2wsh: CohortName::new("p2wsh", "P2WSH", "Pay to Witness Script Hash"), + p2tr: CohortName::new("p2tr", "P2TR", "Pay to Taproot"), + p2a: CohortName::new("p2a", "P2A", "Pay to Anchor"), + unknown: CohortName::new("unknown_outputs", "Unknown", "Unknown Output Type"), + empty: CohortName::new("empty_outputs", "Empty", "Empty Output"), +}; + +#[derive(Default, Clone, Debug, Traversable, Serialize)] pub struct BySpendableType { pub p2pk65: T, pub p2pk33: T, @@ -21,26 +67,55 @@ pub struct BySpendableType { pub empty: T, } +impl BySpendableType { + pub const fn names() -> &'static Self { + &SPENDABLE_TYPE_NAMES + } +} + impl BySpendableType { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { + let f = SPENDABLE_TYPE_FILTERS; + let n = SPENDABLE_TYPE_NAMES; Self { - p2pk65: create(Filter::Type(OutputType::P2PK65)), - p2pk33: create(Filter::Type(OutputType::P2PK33)), - p2pkh: create(Filter::Type(OutputType::P2PKH)), - p2ms: create(Filter::Type(OutputType::P2MS)), - p2sh: create(Filter::Type(OutputType::P2SH)), - p2wpkh: create(Filter::Type(OutputType::P2WPKH)), - p2wsh: create(Filter::Type(OutputType::P2WSH)), - p2tr: create(Filter::Type(OutputType::P2TR)), - p2a: create(Filter::Type(OutputType::P2A)), - unknown: create(Filter::Type(OutputType::Unknown)), - empty: create(Filter::Type(OutputType::Empty)), + p2pk65: create(f.p2pk65, n.p2pk65.id), + p2pk33: create(f.p2pk33, n.p2pk33.id), + p2pkh: create(f.p2pkh, n.p2pkh.id), + p2ms: create(f.p2ms, n.p2ms.id), + p2sh: create(f.p2sh, n.p2sh.id), + p2wpkh: create(f.p2wpkh, n.p2wpkh.id), + p2wsh: create(f.p2wsh, n.p2wsh.id), + p2tr: create(f.p2tr, n.p2tr.id), + p2a: create(f.p2a, n.p2a.id), + unknown: create(f.unknown, n.unknown.id), + empty: create(f.empty, n.empty.id), } } + pub fn try_new(mut create: F) -> Result + where + F: FnMut(Filter, &'static str) -> Result, + { + let f = SPENDABLE_TYPE_FILTERS; + let n = SPENDABLE_TYPE_NAMES; + Ok(Self { + p2pk65: create(f.p2pk65, n.p2pk65.id)?, + p2pk33: create(f.p2pk33, n.p2pk33.id)?, + p2pkh: create(f.p2pkh, n.p2pkh.id)?, + p2ms: create(f.p2ms, n.p2ms.id)?, + p2sh: create(f.p2sh, n.p2sh.id)?, + p2wpkh: create(f.p2wpkh, n.p2wpkh.id)?, + p2wsh: create(f.p2wsh, n.p2wsh.id)?, + p2tr: create(f.p2tr, n.p2tr.id)?, + p2a: create(f.p2a, n.p2a.id)?, + unknown: create(f.unknown, n.unknown.id)?, + empty: create(f.empty, n.empty.id)?, + }) + } + pub fn get_mut(&mut self, output_type: OutputType) -> &mut T { match output_type { OutputType::P2PK65 => &mut self.p2pk65, diff --git a/crates/brk_grouper/src/by_term.rs b/crates/brk_grouper/src/by_term.rs index b7bfb7c39..4e46d8759 100644 --- a/crates/brk_grouper/src/by_term.rs +++ b/crates/brk_grouper/src/by_term.rs @@ -1,25 +1,64 @@ use brk_traversable::Traversable; use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use serde::Serialize; -use super::{Filter, Term}; +use super::{CohortName, Filter, Term}; -#[derive(Default, Clone, Traversable)] +/// Term values +pub const TERM_VALUES: ByTerm = ByTerm { + short: Term::Sth, + long: Term::Lth, +}; + +/// Term filters +pub const TERM_FILTERS: ByTerm = ByTerm { + short: Filter::Term(TERM_VALUES.short), + long: Filter::Term(TERM_VALUES.long), +}; + +/// Term names +pub const TERM_NAMES: ByTerm = ByTerm { + short: CohortName::new("sth", "STH", "Short Term Holders"), + long: CohortName::new("lth", "LTH", "Long Term Holders"), +}; + +#[derive(Default, Clone, Traversable, Serialize)] pub struct ByTerm { pub short: T, pub long: T, } +impl ByTerm { + pub const fn names() -> &'static Self { + &TERM_NAMES + } +} + impl ByTerm { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { + let f = TERM_FILTERS; + let n = TERM_NAMES; Self { - short: create(Filter::Term(Term::Sth)), - long: create(Filter::Term(Term::Lth)), + short: create(f.short, n.short.id), + long: create(f.long, n.long.id), } } + pub fn try_new(mut create: F) -> Result + where + F: FnMut(Filter, &'static str) -> Result, + { + let f = TERM_FILTERS; + let n = TERM_NAMES; + Ok(Self { + short: create(f.short, n.short.id)?, + long: create(f.long, n.long.id)?, + }) + } + pub fn iter(&self) -> impl Iterator { [&self.short, &self.long].into_iter() } diff --git a/crates/brk_grouper/src/by_year.rs b/crates/brk_grouper/src/by_year.rs index a559aa393..dc5c81dfc 100644 --- a/crates/brk_grouper/src/by_year.rs +++ b/crates/brk_grouper/src/by_year.rs @@ -1,10 +1,77 @@ use brk_traversable::Traversable; use brk_types::{Timestamp, Year}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use serde::Serialize; -use super::Filter; +use super::{CohortName, Filter}; -#[derive(Default, Clone, Traversable)] +/// Year values +pub const YEAR_VALUES: ByYear = ByYear { + _2009: Year::new(2009), + _2010: Year::new(2010), + _2011: Year::new(2011), + _2012: Year::new(2012), + _2013: Year::new(2013), + _2014: Year::new(2014), + _2015: Year::new(2015), + _2016: Year::new(2016), + _2017: Year::new(2017), + _2018: Year::new(2018), + _2019: Year::new(2019), + _2020: Year::new(2020), + _2021: Year::new(2021), + _2022: Year::new(2022), + _2023: Year::new(2023), + _2024: Year::new(2024), + _2025: Year::new(2025), + _2026: Year::new(2026), +}; + +/// Year filters +pub const YEAR_FILTERS: ByYear = ByYear { + _2009: Filter::Year(YEAR_VALUES._2009), + _2010: Filter::Year(YEAR_VALUES._2010), + _2011: Filter::Year(YEAR_VALUES._2011), + _2012: Filter::Year(YEAR_VALUES._2012), + _2013: Filter::Year(YEAR_VALUES._2013), + _2014: Filter::Year(YEAR_VALUES._2014), + _2015: Filter::Year(YEAR_VALUES._2015), + _2016: Filter::Year(YEAR_VALUES._2016), + _2017: Filter::Year(YEAR_VALUES._2017), + _2018: Filter::Year(YEAR_VALUES._2018), + _2019: Filter::Year(YEAR_VALUES._2019), + _2020: Filter::Year(YEAR_VALUES._2020), + _2021: Filter::Year(YEAR_VALUES._2021), + _2022: Filter::Year(YEAR_VALUES._2022), + _2023: Filter::Year(YEAR_VALUES._2023), + _2024: Filter::Year(YEAR_VALUES._2024), + _2025: Filter::Year(YEAR_VALUES._2025), + _2026: Filter::Year(YEAR_VALUES._2026), +}; + +/// Year names +pub const YEAR_NAMES: ByYear = ByYear { + _2009: CohortName::new("year_2009", "2009", "Year 2009"), + _2010: CohortName::new("year_2010", "2010", "Year 2010"), + _2011: CohortName::new("year_2011", "2011", "Year 2011"), + _2012: CohortName::new("year_2012", "2012", "Year 2012"), + _2013: CohortName::new("year_2013", "2013", "Year 2013"), + _2014: CohortName::new("year_2014", "2014", "Year 2014"), + _2015: CohortName::new("year_2015", "2015", "Year 2015"), + _2016: CohortName::new("year_2016", "2016", "Year 2016"), + _2017: CohortName::new("year_2017", "2017", "Year 2017"), + _2018: CohortName::new("year_2018", "2018", "Year 2018"), + _2019: CohortName::new("year_2019", "2019", "Year 2019"), + _2020: CohortName::new("year_2020", "2020", "Year 2020"), + _2021: CohortName::new("year_2021", "2021", "Year 2021"), + _2022: CohortName::new("year_2022", "2022", "Year 2022"), + _2023: CohortName::new("year_2023", "2023", "Year 2023"), + _2024: CohortName::new("year_2024", "2024", "Year 2024"), + _2025: CohortName::new("year_2025", "2025", "Year 2025"), + _2026: CohortName::new("year_2026", "2026", "Year 2026"), +}; + +#[derive(Default, Clone, Traversable, Serialize)] pub struct ByYear { pub _2009: T, pub _2010: T, @@ -26,33 +93,69 @@ pub struct ByYear { pub _2026: T, } +impl ByYear { + pub const fn names() -> &'static Self { + &YEAR_NAMES + } +} + impl ByYear { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { + let f = YEAR_FILTERS; + let n = YEAR_NAMES; Self { - _2009: create(Filter::Year(Year::new(2009))), - _2010: create(Filter::Year(Year::new(2010))), - _2011: create(Filter::Year(Year::new(2011))), - _2012: create(Filter::Year(Year::new(2012))), - _2013: create(Filter::Year(Year::new(2013))), - _2014: create(Filter::Year(Year::new(2014))), - _2015: create(Filter::Year(Year::new(2015))), - _2016: create(Filter::Year(Year::new(2016))), - _2017: create(Filter::Year(Year::new(2017))), - _2018: create(Filter::Year(Year::new(2018))), - _2019: create(Filter::Year(Year::new(2019))), - _2020: create(Filter::Year(Year::new(2020))), - _2021: create(Filter::Year(Year::new(2021))), - _2022: create(Filter::Year(Year::new(2022))), - _2023: create(Filter::Year(Year::new(2023))), - _2024: create(Filter::Year(Year::new(2024))), - _2025: create(Filter::Year(Year::new(2025))), - _2026: create(Filter::Year(Year::new(2026))), + _2009: create(f._2009, n._2009.id), + _2010: create(f._2010, n._2010.id), + _2011: create(f._2011, n._2011.id), + _2012: create(f._2012, n._2012.id), + _2013: create(f._2013, n._2013.id), + _2014: create(f._2014, n._2014.id), + _2015: create(f._2015, n._2015.id), + _2016: create(f._2016, n._2016.id), + _2017: create(f._2017, n._2017.id), + _2018: create(f._2018, n._2018.id), + _2019: create(f._2019, n._2019.id), + _2020: create(f._2020, n._2020.id), + _2021: create(f._2021, n._2021.id), + _2022: create(f._2022, n._2022.id), + _2023: create(f._2023, n._2023.id), + _2024: create(f._2024, n._2024.id), + _2025: create(f._2025, n._2025.id), + _2026: create(f._2026, n._2026.id), } } + pub fn try_new(mut create: F) -> Result + where + F: FnMut(Filter, &'static str) -> Result, + { + let f = YEAR_FILTERS; + let n = YEAR_NAMES; + Ok(Self { + _2009: create(f._2009, n._2009.id)?, + _2010: create(f._2010, n._2010.id)?, + _2011: create(f._2011, n._2011.id)?, + _2012: create(f._2012, n._2012.id)?, + _2013: create(f._2013, n._2013.id)?, + _2014: create(f._2014, n._2014.id)?, + _2015: create(f._2015, n._2015.id)?, + _2016: create(f._2016, n._2016.id)?, + _2017: create(f._2017, n._2017.id)?, + _2018: create(f._2018, n._2018.id)?, + _2019: create(f._2019, n._2019.id)?, + _2020: create(f._2020, n._2020.id)?, + _2021: create(f._2021, n._2021.id)?, + _2022: create(f._2022, n._2022.id)?, + _2023: create(f._2023, n._2023.id)?, + _2024: create(f._2024, n._2024.id)?, + _2025: create(f._2025, n._2025.id)?, + _2026: create(f._2026, n._2026.id)?, + }) + } + pub fn iter(&self) -> impl Iterator { [ &self._2009, diff --git a/crates/brk_grouper/src/cohort_context.rs b/crates/brk_grouper/src/cohort_context.rs index 420ded95a..cdfbd9fcf 100644 --- a/crates/brk_grouper/src/cohort_context.rs +++ b/crates/brk_grouper/src/cohort_context.rs @@ -1,3 +1,5 @@ +use super::Filter; + /// Context for cohort naming - determines whether a prefix is needed. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CohortContext { @@ -14,4 +16,22 @@ impl CohortContext { CohortContext::Address => "addrs", } } + + pub fn prefixed(&self, name: &str) -> String { + format!("{}_{}", self.prefix(), name) + } + + /// Build full name for a filter, adding prefix only for Time/Amount filters. + /// + /// Prefix rules: + /// - No prefix: `All`, `Term`, `Epoch`, `Year`, `Type` + /// - Context prefix: `Time`, `Amount` + pub fn full_name(&self, filter: &Filter, name: &str) -> String { + match filter { + Filter::All | Filter::Term(_) | Filter::Epoch(_) | Filter::Year(_) | Filter::Type(_) => { + name.to_string() + } + Filter::Time(_) | Filter::Amount(_) => self.prefixed(name), + } + } } diff --git a/crates/brk_grouper/src/cohort_name.rs b/crates/brk_grouper/src/cohort_name.rs new file mode 100644 index 000000000..1111e7cf2 --- /dev/null +++ b/crates/brk_grouper/src/cohort_name.rs @@ -0,0 +1,15 @@ +use serde::Serialize; + +/// Display names for a cohort with id (for storage/API), short (for charts), and long (for tooltips/labels) +#[derive(Clone, Copy, Serialize)] +pub struct CohortName { + pub id: &'static str, + pub short: &'static str, + pub long: &'static str, +} + +impl CohortName { + pub const fn new(id: &'static str, short: &'static str, long: &'static str) -> Self { + Self { id, short, long } + } +} diff --git a/crates/brk_grouper/src/filter.rs b/crates/brk_grouper/src/filter.rs index 6937aaac5..f266fd0b4 100644 --- a/crates/brk_grouper/src/filter.rs +++ b/crates/brk_grouper/src/filter.rs @@ -29,47 +29,6 @@ impl Filter { } } - pub fn to_name_suffix(&self) -> String { - match self { - Filter::All => String::new(), - Filter::Term(t) => t.to_name().to_string(), - Filter::Time(t) => t.to_name_suffix(), - Filter::Amount(a) => a.to_name_suffix(), - Filter::Epoch(e) => format!("epoch_{}", usize::from(*e)), - Filter::Year(y) => format!("year_{}", u16::from(*y)), - Filter::Type(t) => match t { - OutputType::P2MS => "p2ms_outputs".to_string(), - OutputType::Empty => "empty_outputs".to_string(), - OutputType::Unknown => "unknown_outputs".to_string(), - _ => format!("{:?}", t).to_lowercase(), - }, - } - } - - /// Returns the full name for this filter, including context-based prefix. - /// - /// Prefix rules: - /// - No prefix: `All`, `Term`, `Epoch`, `Type` - /// - `utxos_` prefix: `Time` or `Amount` with `CohortContext::Utxo` - /// - `addrs_` prefix: `Amount` with `CohortContext::Address` - pub fn to_full_name(&self, context: CohortContext) -> String { - let suffix = self.to_name_suffix(); - if suffix.is_empty() { - return suffix; - } - - let needs_prefix = match self { - Filter::All | Filter::Term(_) | Filter::Epoch(_) | Filter::Year(_) | Filter::Type(_) => false, - Filter::Time(_) | Filter::Amount(_) => true, - }; - - if needs_prefix { - format!("{}_{}", context.prefix(), suffix) - } else { - suffix - } - } - /// Check if a time value (days) is contained by this filter pub fn contains_time(&self, days: usize) -> bool { match self { diff --git a/crates/brk_grouper/src/filtered.rs b/crates/brk_grouper/src/filtered.rs index 475ff1fb0..bed4c15f9 100644 --- a/crates/brk_grouper/src/filtered.rs +++ b/crates/brk_grouper/src/filtered.rs @@ -1,4 +1,4 @@ -use super::{CohortContext, Filter}; +use super::Filter; pub trait Filtered { fn filter(&self) -> &Filter; @@ -10,12 +10,4 @@ pub trait Filtered { fn includes_first_day(&self) -> bool { self.filter().includes_first_day() } - - fn name_suffix(&self) -> String { - self.filter().to_name_suffix() - } - - fn full_name(&self, context: CohortContext) -> String { - self.filter().to_full_name(context) - } } diff --git a/crates/brk_grouper/src/lib.rs b/crates/brk_grouper/src/lib.rs index 89eff122f..11c08bcd4 100644 --- a/crates/brk_grouper/src/lib.rs +++ b/crates/brk_grouper/src/lib.rs @@ -17,6 +17,7 @@ mod by_term; mod by_type; mod by_unspendable_type; mod cohort_context; +mod cohort_name; mod filter; mod filtered; mod state_level; @@ -41,6 +42,7 @@ pub use by_term::*; pub use by_type::*; pub use by_unspendable_type::*; pub use cohort_context::*; +pub use cohort_name::*; pub use filter::*; pub use filtered::*; pub use state_level::*; diff --git a/crates/brk_grouper/src/term.rs b/crates/brk_grouper/src/term.rs index 818a73a78..372faa9c5 100644 --- a/crates/brk_grouper/src/term.rs +++ b/crates/brk_grouper/src/term.rs @@ -12,11 +12,4 @@ pub enum Term { impl Term { pub const THRESHOLD_DAYS: usize = DAYS_5M; - - pub fn to_name(&self) -> &'static str { - match self { - Term::Sth => "sth", - Term::Lth => "lth", - } - } } diff --git a/crates/brk_grouper/src/time_filter.rs b/crates/brk_grouper/src/time_filter.rs index d5db1030d..6569a6ae3 100644 --- a/crates/brk_grouper/src/time_filter.rs +++ b/crates/brk_grouper/src/time_filter.rs @@ -40,75 +40,4 @@ impl TimeFilter { TimeFilter::GreaterOrEqual(_) => false, } } - - pub fn to_name_suffix(&self) -> String { - match self { - // Special cases for common filters - TimeFilter::LowerThan(1) => "up_to_1d_old".to_string(), - TimeFilter::LowerThan(7) => "up_to_1w_old".to_string(), - TimeFilter::LowerThan(30) => "up_to_1m_old".to_string(), - TimeFilter::LowerThan(60) => "up_to_2m_old".to_string(), - TimeFilter::LowerThan(90) => "up_to_3m_old".to_string(), - TimeFilter::LowerThan(120) => "up_to_4m_old".to_string(), - TimeFilter::LowerThan(150) => "sth".to_string(), - TimeFilter::LowerThan(180) => "up_to_6m_old".to_string(), - TimeFilter::LowerThan(365) => "up_to_1y_old".to_string(), - TimeFilter::LowerThan(730) => "up_to_2y_old".to_string(), - TimeFilter::LowerThan(1095) => "up_to_3y_old".to_string(), - TimeFilter::LowerThan(1460) => "up_to_4y_old".to_string(), - TimeFilter::LowerThan(1825) => "up_to_5y_old".to_string(), - TimeFilter::LowerThan(2190) => "up_to_6y_old".to_string(), - TimeFilter::LowerThan(2555) => "up_to_7y_old".to_string(), - TimeFilter::LowerThan(2920) => "up_to_8y_old".to_string(), - TimeFilter::LowerThan(3650) => "up_to_10y_old".to_string(), - TimeFilter::LowerThan(4380) => "up_to_12y_old".to_string(), - TimeFilter::LowerThan(5475) => "up_to_15y_old".to_string(), - - TimeFilter::GreaterOrEqual(1) => "at_least_1d_old".to_string(), - TimeFilter::GreaterOrEqual(7) => "at_least_1w_old".to_string(), - TimeFilter::GreaterOrEqual(30) => "at_least_1m_old".to_string(), - TimeFilter::GreaterOrEqual(60) => "at_least_2m_old".to_string(), - TimeFilter::GreaterOrEqual(90) => "at_least_3m_old".to_string(), - TimeFilter::GreaterOrEqual(120) => "at_least_4m_old".to_string(), - TimeFilter::GreaterOrEqual(150) => "lth".to_string(), - TimeFilter::GreaterOrEqual(180) => "at_least_6m_old".to_string(), - TimeFilter::GreaterOrEqual(365) => "at_least_1y_old".to_string(), - TimeFilter::GreaterOrEqual(730) => "at_least_2y_old".to_string(), - TimeFilter::GreaterOrEqual(1095) => "at_least_3y_old".to_string(), - TimeFilter::GreaterOrEqual(1460) => "at_least_4y_old".to_string(), - TimeFilter::GreaterOrEqual(1825) => "at_least_5y_old".to_string(), - TimeFilter::GreaterOrEqual(2190) => "at_least_6y_old".to_string(), - TimeFilter::GreaterOrEqual(2555) => "at_least_7y_old".to_string(), - TimeFilter::GreaterOrEqual(2920) => "at_least_8y_old".to_string(), - TimeFilter::GreaterOrEqual(3650) => "at_least_10y_old".to_string(), - TimeFilter::GreaterOrEqual(4380) => "at_least_12y_old".to_string(), - TimeFilter::GreaterOrEqual(5475) => "at_least_15y_old".to_string(), - - // Range special cases - TimeFilter::Range(r) if *r == (0..1) => "up_to_1d".to_string(), - TimeFilter::Range(r) if *r == (1..7) => "at_least_1d_up_to_1w_old".to_string(), - TimeFilter::Range(r) if *r == (7..30) => "at_least_1w_up_to_1m_old".to_string(), - TimeFilter::Range(r) if *r == (30..60) => "at_least_1m_up_to_2m_old".to_string(), - TimeFilter::Range(r) if *r == (60..90) => "at_least_2m_up_to_3m_old".to_string(), - TimeFilter::Range(r) if *r == (90..120) => "at_least_3m_up_to_4m_old".to_string(), - TimeFilter::Range(r) if *r == (120..150) => "at_least_4m_up_to_5m_old".to_string(), - TimeFilter::Range(r) if *r == (150..180) => "at_least_5m_up_to_6m_old".to_string(), - TimeFilter::Range(r) if *r == (180..365) => "at_least_6m_up_to_1y_old".to_string(), - TimeFilter::Range(r) if *r == (365..730) => "at_least_1y_up_to_2y_old".to_string(), - TimeFilter::Range(r) if *r == (730..1095) => "at_least_2y_up_to_3y_old".to_string(), - TimeFilter::Range(r) if *r == (1095..1460) => "at_least_3y_up_to_4y_old".to_string(), - TimeFilter::Range(r) if *r == (1460..1825) => "at_least_4y_up_to_5y_old".to_string(), - TimeFilter::Range(r) if *r == (1825..2190) => "at_least_5y_up_to_6y_old".to_string(), - TimeFilter::Range(r) if *r == (2190..2555) => "at_least_6y_up_to_7y_old".to_string(), - TimeFilter::Range(r) if *r == (2555..2920) => "at_least_7y_up_to_8y_old".to_string(), - TimeFilter::Range(r) if *r == (2920..3650) => "at_least_8y_up_to_10y_old".to_string(), - TimeFilter::Range(r) if *r == (3650..4380) => "at_least_10y_up_to_12y_old".to_string(), - TimeFilter::Range(r) if *r == (4380..5475) => "at_least_12y_up_to_15y_old".to_string(), - - // Fallback generic names - TimeFilter::LowerThan(d) => format!("up_to_{}d", d), - TimeFilter::GreaterOrEqual(d) => format!("at_least_{}d", d), - TimeFilter::Range(r) => format!("{}d_to_{}d", r.start, r.end), - } - } } diff --git a/crates/brk_grouper/src/utxo.rs b/crates/brk_grouper/src/utxo.rs index 176033f5a..0ad67aa49 100644 --- a/crates/brk_grouper/src/utxo.rs +++ b/crates/brk_grouper/src/utxo.rs @@ -24,10 +24,10 @@ pub struct UTXOGroups { impl UTXOGroups { pub fn new(mut create: F) -> Self where - F: FnMut(Filter) -> T, + F: FnMut(Filter, &'static str) -> T, { Self { - all: create(Filter::All), + all: create(Filter::All, ""), age_range: ByAgeRange::new(&mut create), epoch: ByEpoch::new(&mut create), year: ByYear::new(&mut create), diff --git a/crates/brk_types/src/halvingepoch.rs b/crates/brk_types/src/halvingepoch.rs index 377d43471..bcc77493b 100644 --- a/crates/brk_types/src/halvingepoch.rs +++ b/crates/brk_types/src/halvingepoch.rs @@ -28,7 +28,7 @@ pub const BLOCKS_PER_HALVING: u32 = 210_000; pub struct HalvingEpoch(u16); impl HalvingEpoch { - pub fn new(value: u16) -> Self { + pub const fn new(value: u16) -> Self { Self(value) } } diff --git a/crates/brk_types/src/year.rs b/crates/brk_types/src/year.rs index cd6b2ed7c..2f393c95a 100644 --- a/crates/brk_types/src/year.rs +++ b/crates/brk_types/src/year.rs @@ -17,7 +17,7 @@ pub struct Year(u16); impl Year { pub const GENESIS: Self = Self(2009); - pub fn new(value: u16) -> Self { + pub const fn new(value: u16) -> Self { Self(value) } diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index 93d74d011..2ff7b5fcf 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -196,25 +196,722 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ zulupool: "Zulupool", }); +// Cohort names + +export const TERM_NAMES = /** @type {const} */ ({ + "short": { + "id": "sth", + "short": "STH", + "long": "Short Term Holders" + }, + "long": { + "id": "lth", + "short": "LTH", + "long": "Long Term Holders" + } +}); + +export const EPOCH_NAMES = /** @type {const} */ ({ + "_0": { + "id": "epoch_0", + "short": "Epoch 0", + "long": "Epoch 0" + }, + "_1": { + "id": "epoch_1", + "short": "Epoch 1", + "long": "Epoch 1" + }, + "_2": { + "id": "epoch_2", + "short": "Epoch 2", + "long": "Epoch 2" + }, + "_3": { + "id": "epoch_3", + "short": "Epoch 3", + "long": "Epoch 3" + }, + "_4": { + "id": "epoch_4", + "short": "Epoch 4", + "long": "Epoch 4" + } +}); + +export const YEAR_NAMES = /** @type {const} */ ({ + "_2009": { + "id": "year_2009", + "short": "2009", + "long": "Year 2009" + }, + "_2010": { + "id": "year_2010", + "short": "2010", + "long": "Year 2010" + }, + "_2011": { + "id": "year_2011", + "short": "2011", + "long": "Year 2011" + }, + "_2012": { + "id": "year_2012", + "short": "2012", + "long": "Year 2012" + }, + "_2013": { + "id": "year_2013", + "short": "2013", + "long": "Year 2013" + }, + "_2014": { + "id": "year_2014", + "short": "2014", + "long": "Year 2014" + }, + "_2015": { + "id": "year_2015", + "short": "2015", + "long": "Year 2015" + }, + "_2016": { + "id": "year_2016", + "short": "2016", + "long": "Year 2016" + }, + "_2017": { + "id": "year_2017", + "short": "2017", + "long": "Year 2017" + }, + "_2018": { + "id": "year_2018", + "short": "2018", + "long": "Year 2018" + }, + "_2019": { + "id": "year_2019", + "short": "2019", + "long": "Year 2019" + }, + "_2020": { + "id": "year_2020", + "short": "2020", + "long": "Year 2020" + }, + "_2021": { + "id": "year_2021", + "short": "2021", + "long": "Year 2021" + }, + "_2022": { + "id": "year_2022", + "short": "2022", + "long": "Year 2022" + }, + "_2023": { + "id": "year_2023", + "short": "2023", + "long": "Year 2023" + }, + "_2024": { + "id": "year_2024", + "short": "2024", + "long": "Year 2024" + }, + "_2025": { + "id": "year_2025", + "short": "2025", + "long": "Year 2025" + }, + "_2026": { + "id": "year_2026", + "short": "2026", + "long": "Year 2026" + } +}); + +export const SPENDABLE_TYPE_NAMES = /** @type {const} */ ({ + "p2pk65": { + "id": "p2pk65", + "short": "P2PK65", + "long": "Pay to Public Key (65 bytes)" + }, + "p2pk33": { + "id": "p2pk33", + "short": "P2PK33", + "long": "Pay to Public Key (33 bytes)" + }, + "p2pkh": { + "id": "p2pkh", + "short": "P2PKH", + "long": "Pay to Public Key Hash" + }, + "p2ms": { + "id": "p2ms", + "short": "P2MS", + "long": "Pay to Multisig" + }, + "p2sh": { + "id": "p2sh", + "short": "P2SH", + "long": "Pay to Script Hash" + }, + "p2wpkh": { + "id": "p2wpkh", + "short": "P2WPKH", + "long": "Pay to Witness Public Key Hash" + }, + "p2wsh": { + "id": "p2wsh", + "short": "P2WSH", + "long": "Pay to Witness Script Hash" + }, + "p2tr": { + "id": "p2tr", + "short": "P2TR", + "long": "Pay to Taproot" + }, + "p2a": { + "id": "p2a", + "short": "P2A", + "long": "Pay to Anchor" + }, + "unknown": { + "id": "unknown_outputs", + "short": "Unknown", + "long": "Unknown Output Type" + }, + "empty": { + "id": "empty_outputs", + "short": "Empty", + "long": "Empty Output" + } +}); + +export const AGE_RANGE_NAMES = /** @type {const} */ ({ + "up_to_1d": { + "id": "up_to_1d_old", + "short": "<1d", + "long": "Up to 1 Day Old" + }, + "_1d_to_1w": { + "id": "at_least_1d_up_to_1w_old", + "short": "1d-1w", + "long": "1 Day to 1 Week Old" + }, + "_1w_to_1m": { + "id": "at_least_1w_up_to_1m_old", + "short": "1w-1m", + "long": "1 Week to 1 Month Old" + }, + "_1m_to_2m": { + "id": "at_least_1m_up_to_2m_old", + "short": "1m-2m", + "long": "1 to 2 Months Old" + }, + "_2m_to_3m": { + "id": "at_least_2m_up_to_3m_old", + "short": "2m-3m", + "long": "2 to 3 Months Old" + }, + "_3m_to_4m": { + "id": "at_least_3m_up_to_4m_old", + "short": "3m-4m", + "long": "3 to 4 Months Old" + }, + "_4m_to_5m": { + "id": "at_least_4m_up_to_5m_old", + "short": "4m-5m", + "long": "4 to 5 Months Old" + }, + "_5m_to_6m": { + "id": "at_least_5m_up_to_6m_old", + "short": "5m-6m", + "long": "5 to 6 Months Old" + }, + "_6m_to_1y": { + "id": "at_least_6m_up_to_1y_old", + "short": "6m-1y", + "long": "6 Months to 1 Year Old" + }, + "_1y_to_2y": { + "id": "at_least_1y_up_to_2y_old", + "short": "1y-2y", + "long": "1 to 2 Years Old" + }, + "_2y_to_3y": { + "id": "at_least_2y_up_to_3y_old", + "short": "2y-3y", + "long": "2 to 3 Years Old" + }, + "_3y_to_4y": { + "id": "at_least_3y_up_to_4y_old", + "short": "3y-4y", + "long": "3 to 4 Years Old" + }, + "_4y_to_5y": { + "id": "at_least_4y_up_to_5y_old", + "short": "4y-5y", + "long": "4 to 5 Years Old" + }, + "_5y_to_6y": { + "id": "at_least_5y_up_to_6y_old", + "short": "5y-6y", + "long": "5 to 6 Years Old" + }, + "_6y_to_7y": { + "id": "at_least_6y_up_to_7y_old", + "short": "6y-7y", + "long": "6 to 7 Years Old" + }, + "_7y_to_8y": { + "id": "at_least_7y_up_to_8y_old", + "short": "7y-8y", + "long": "7 to 8 Years Old" + }, + "_8y_to_10y": { + "id": "at_least_8y_up_to_10y_old", + "short": "8y-10y", + "long": "8 to 10 Years Old" + }, + "_10y_to_12y": { + "id": "at_least_10y_up_to_12y_old", + "short": "10y-12y", + "long": "10 to 12 Years Old" + }, + "_12y_to_15y": { + "id": "at_least_12y_up_to_15y_old", + "short": "12y-15y", + "long": "12 to 15 Years Old" + }, + "from_15y": { + "id": "at_least_15y_old", + "short": "15y+", + "long": "15+ Years Old" + } +}); + +export const MAX_AGE_NAMES = /** @type {const} */ ({ + "_1w": { + "id": "up_to_1w_old", + "short": "<1w", + "long": "Up to 1 Week Old" + }, + "_1m": { + "id": "up_to_1m_old", + "short": "<1m", + "long": "Up to 1 Month Old" + }, + "_2m": { + "id": "up_to_2m_old", + "short": "<2m", + "long": "Up to 2 Months Old" + }, + "_3m": { + "id": "up_to_3m_old", + "short": "<3m", + "long": "Up to 3 Months Old" + }, + "_4m": { + "id": "up_to_4m_old", + "short": "<4m", + "long": "Up to 4 Months Old" + }, + "_5m": { + "id": "up_to_5m_old", + "short": "<5m", + "long": "Up to 5 Months Old" + }, + "_6m": { + "id": "up_to_6m_old", + "short": "<6m", + "long": "Up to 6 Months Old" + }, + "_1y": { + "id": "up_to_1y_old", + "short": "<1y", + "long": "Up to 1 Year Old" + }, + "_2y": { + "id": "up_to_2y_old", + "short": "<2y", + "long": "Up to 2 Years Old" + }, + "_3y": { + "id": "up_to_3y_old", + "short": "<3y", + "long": "Up to 3 Years Old" + }, + "_4y": { + "id": "up_to_4y_old", + "short": "<4y", + "long": "Up to 4 Years Old" + }, + "_5y": { + "id": "up_to_5y_old", + "short": "<5y", + "long": "Up to 5 Years Old" + }, + "_6y": { + "id": "up_to_6y_old", + "short": "<6y", + "long": "Up to 6 Years Old" + }, + "_7y": { + "id": "up_to_7y_old", + "short": "<7y", + "long": "Up to 7 Years Old" + }, + "_8y": { + "id": "up_to_8y_old", + "short": "<8y", + "long": "Up to 8 Years Old" + }, + "_10y": { + "id": "up_to_10y_old", + "short": "<10y", + "long": "Up to 10 Years Old" + }, + "_12y": { + "id": "up_to_12y_old", + "short": "<12y", + "long": "Up to 12 Years Old" + }, + "_15y": { + "id": "up_to_15y_old", + "short": "<15y", + "long": "Up to 15 Years Old" + } +}); + +export const MIN_AGE_NAMES = /** @type {const} */ ({ + "_1d": { + "id": "at_least_1d_old", + "short": "1d+", + "long": "At Least 1 Day Old" + }, + "_1w": { + "id": "at_least_1w_old", + "short": "1w+", + "long": "At Least 1 Week Old" + }, + "_1m": { + "id": "at_least_1m_old", + "short": "1m+", + "long": "At Least 1 Month Old" + }, + "_2m": { + "id": "at_least_2m_old", + "short": "2m+", + "long": "At Least 2 Months Old" + }, + "_3m": { + "id": "at_least_3m_old", + "short": "3m+", + "long": "At Least 3 Months Old" + }, + "_4m": { + "id": "at_least_4m_old", + "short": "4m+", + "long": "At Least 4 Months Old" + }, + "_5m": { + "id": "at_least_5m_old", + "short": "5m+", + "long": "At Least 5 Months Old" + }, + "_6m": { + "id": "at_least_6m_old", + "short": "6m+", + "long": "At Least 6 Months Old" + }, + "_1y": { + "id": "at_least_1y_old", + "short": "1y+", + "long": "At Least 1 Year Old" + }, + "_2y": { + "id": "at_least_2y_old", + "short": "2y+", + "long": "At Least 2 Years Old" + }, + "_3y": { + "id": "at_least_3y_old", + "short": "3y+", + "long": "At Least 3 Years Old" + }, + "_4y": { + "id": "at_least_4y_old", + "short": "4y+", + "long": "At Least 4 Years Old" + }, + "_5y": { + "id": "at_least_5y_old", + "short": "5y+", + "long": "At Least 5 Years Old" + }, + "_6y": { + "id": "at_least_6y_old", + "short": "6y+", + "long": "At Least 6 Years Old" + }, + "_7y": { + "id": "at_least_7y_old", + "short": "7y+", + "long": "At Least 7 Years Old" + }, + "_8y": { + "id": "at_least_8y_old", + "short": "8y+", + "long": "At Least 8 Years Old" + }, + "_10y": { + "id": "at_least_10y_old", + "short": "10y+", + "long": "At Least 10 Years Old" + }, + "_12y": { + "id": "at_least_12y_old", + "short": "12y+", + "long": "At Least 12 Years Old" + } +}); + +export const AMOUNT_RANGE_NAMES = /** @type {const} */ ({ + "_0sats": { + "id": "with_0sats", + "short": "0 sats", + "long": "0 Sats" + }, + "_1sat_to_10sats": { + "id": "above_1sat_under_10sats", + "short": "1-10 sats", + "long": "1 to 10 Sats" + }, + "_10sats_to_100sats": { + "id": "above_10sats_under_100sats", + "short": "10-100 sats", + "long": "10 to 100 Sats" + }, + "_100sats_to_1k_sats": { + "id": "above_100sats_under_1k_sats", + "short": "100-1k sats", + "long": "100 to 1K Sats" + }, + "_1k_sats_to_10k_sats": { + "id": "above_1k_sats_under_10k_sats", + "short": "1k-10k sats", + "long": "1K to 10K Sats" + }, + "_10k_sats_to_100k_sats": { + "id": "above_10k_sats_under_100k_sats", + "short": "10k-100k sats", + "long": "10K to 100K Sats" + }, + "_100k_sats_to_1m_sats": { + "id": "above_100k_sats_under_1m_sats", + "short": "100k-1M sats", + "long": "100K to 1M Sats" + }, + "_1m_sats_to_10m_sats": { + "id": "above_1m_sats_under_10m_sats", + "short": "1M-10M sats", + "long": "1M to 10M Sats" + }, + "_10m_sats_to_1btc": { + "id": "above_10m_sats_under_1btc", + "short": "0.1-1 BTC", + "long": "0.1 to 1 BTC" + }, + "_1btc_to_10btc": { + "id": "above_1btc_under_10btc", + "short": "1-10 BTC", + "long": "1 to 10 BTC" + }, + "_10btc_to_100btc": { + "id": "above_10btc_under_100btc", + "short": "10-100 BTC", + "long": "10 to 100 BTC" + }, + "_100btc_to_1k_btc": { + "id": "above_100btc_under_1k_btc", + "short": "100-1k BTC", + "long": "100 to 1K BTC" + }, + "_1k_btc_to_10k_btc": { + "id": "above_1k_btc_under_10k_btc", + "short": "1k-10k BTC", + "long": "1K to 10K BTC" + }, + "_10k_btc_to_100k_btc": { + "id": "above_10k_btc_under_100k_btc", + "short": "10k-100k BTC", + "long": "10K to 100K BTC" + }, + "_100k_btc_or_more": { + "id": "above_100k_btc", + "short": "100k+ BTC", + "long": "100K+ BTC" + } +}); + +export const GE_AMOUNT_NAMES = /** @type {const} */ ({ + "_1sat": { + "id": "above_1sat", + "short": "1+ sats", + "long": "Above 1 Sat" + }, + "_10sats": { + "id": "above_10sats", + "short": "10+ sats", + "long": "Above 10 Sats" + }, + "_100sats": { + "id": "above_100sats", + "short": "100+ sats", + "long": "Above 100 Sats" + }, + "_1k_sats": { + "id": "above_1k_sats", + "short": "1k+ sats", + "long": "Above 1K Sats" + }, + "_10k_sats": { + "id": "above_10k_sats", + "short": "10k+ sats", + "long": "Above 10K Sats" + }, + "_100k_sats": { + "id": "above_100k_sats", + "short": "100k+ sats", + "long": "Above 100K Sats" + }, + "_1m_sats": { + "id": "above_1m_sats", + "short": "1M+ sats", + "long": "Above 1M Sats" + }, + "_10m_sats": { + "id": "above_10m_sats", + "short": "0.1+ BTC", + "long": "Above 0.1 BTC" + }, + "_1btc": { + "id": "above_1btc", + "short": "1+ BTC", + "long": "Above 1 BTC" + }, + "_10btc": { + "id": "above_10btc", + "short": "10+ BTC", + "long": "Above 10 BTC" + }, + "_100btc": { + "id": "above_100btc", + "short": "100+ BTC", + "long": "Above 100 BTC" + }, + "_1k_btc": { + "id": "above_1k_btc", + "short": "1k+ BTC", + "long": "Above 1K BTC" + }, + "_10k_btc": { + "id": "above_10k_btc", + "short": "10k+ BTC", + "long": "Above 10K BTC" + } +}); + +export const LT_AMOUNT_NAMES = /** @type {const} */ ({ + "_10sats": { + "id": "under_10sats", + "short": "<10 sats", + "long": "Under 10 Sats" + }, + "_100sats": { + "id": "under_100sats", + "short": "<100 sats", + "long": "Under 100 Sats" + }, + "_1k_sats": { + "id": "under_1k_sats", + "short": "<1k sats", + "long": "Under 1K Sats" + }, + "_10k_sats": { + "id": "under_10k_sats", + "short": "<10k sats", + "long": "Under 10K Sats" + }, + "_100k_sats": { + "id": "under_100k_sats", + "short": "<100k sats", + "long": "Under 100K Sats" + }, + "_1m_sats": { + "id": "under_1m_sats", + "short": "<1M sats", + "long": "Under 1M Sats" + }, + "_10m_sats": { + "id": "under_10m_sats", + "short": "<0.1 BTC", + "long": "Under 0.1 BTC" + }, + "_1btc": { + "id": "under_1btc", + "short": "<1 BTC", + "long": "Under 1 BTC" + }, + "_10btc": { + "id": "under_10btc", + "short": "<10 BTC", + "long": "Under 10 BTC" + }, + "_100btc": { + "id": "under_100btc", + "short": "<100 BTC", + "long": "Under 100 BTC" + }, + "_1k_btc": { + "id": "under_1k_btc", + "short": "<1k BTC", + "long": "Under 1K BTC" + }, + "_10k_btc": { + "id": "under_10k_btc", + "short": "<10k BTC", + "long": "Under 10K BTC" + }, + "_100k_btc": { + "id": "under_100k_btc", + "short": "<100k BTC", + "long": "Under 100K BTC" + } +}); + // Type definitions /** @typedef {string} Address */ /** * @typedef {Object} AddressChainStats - * @property {number} funded_txo_count - * @property {Sats} funded_txo_sum - * @property {number} spent_txo_count - * @property {Sats} spent_txo_sum - * @property {number} tx_count - * @property {TypeIndex} type_index + * @property {number} fundedTxoCount + * @property {Sats} fundedTxoSum + * @property {number} spentTxoCount + * @property {Sats} spentTxoSum + * @property {number} txCount + * @property {TypeIndex} typeIndex */ /** * @typedef {Object} AddressMempoolStats - * @property {number} funded_txo_count - * @property {Sats} funded_txo_sum - * @property {number} spent_txo_count - * @property {Sats} spent_txo_sum - * @property {number} tx_count + * @property {number} fundedTxoCount + * @property {Sats} fundedTxoSum + * @property {number} spentTxoCount + * @property {Sats} spentTxoSum + * @property {number} txCount */ /** * @typedef {Object} AddressParam @@ -223,12 +920,12 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ /** * @typedef {Object} AddressStats * @property {Address} address - * @property {AddressChainStats} chain_stats - * @property {(AddressMempoolStats|null)=} mempool_stats + * @property {AddressChainStats} chainStats + * @property {(AddressMempoolStats|null)=} mempoolStats */ /** * @typedef {Object} AddressTxidsParam - * @property {(Txid|null)=} after_txid + * @property {(Txid|null)=} afterTxid * @property {number=} limit */ /** @@ -238,15 +935,15 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ * @property {?string=} scriptPubKey * @property {?boolean=} isscript * @property {?boolean=} iswitness - * @property {?number=} witness_version - * @property {?string=} witness_program + * @property {?number=} witnessVersion + * @property {?string=} witnessProgram */ /** @typedef {TypeIndex} AnyAddressIndex */ /** @typedef {number} Bitcoin */ /** @typedef {number} BlkPosition */ /** * @typedef {Object} BlockCountParam - * @property {number} block_count + * @property {number} blockCount */ /** * @typedef {Object} BlockFeesEntry @@ -262,7 +959,7 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ /** * @typedef {Object} BlockHashStartIndex * @property {BlockHash} hash - * @property {TxIndex} start_index + * @property {TxIndex} startIndex */ /** * @typedef {Object} BlockHashTxIndex @@ -273,7 +970,7 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ * @typedef {Object} BlockInfo * @property {BlockHash} id * @property {Height} height - * @property {number} tx_count + * @property {number} txCount * @property {number} size * @property {Weight} weight * @property {Timestamp} timestamp @@ -298,9 +995,9 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ */ /** * @typedef {Object} BlockStatus - * @property {boolean} in_best_chain + * @property {boolean} inBestChain * @property {(Height|null)=} height - * @property {(BlockHash|null)=} next_best + * @property {(BlockHash|null)=} nextBest */ /** * @typedef {Object} BlockTimestamp @@ -344,7 +1041,7 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ * @property {Timestamp} timestamp * @property {Height} height * @property {number} difficulty - * @property {number} change_percent + * @property {number} changePercent */ /** * @typedef {Object} DifficultyEntry @@ -356,8 +1053,8 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ /** @typedef {number} Dollars */ /** * @typedef {Object} EmptyAddressData - * @property {number} tx_count - * @property {number} funded_txo_count + * @property {number} txCount + * @property {number} fundedTxoCount * @property {Sats} transfered */ /** @typedef {TypeIndex} EmptyAddressIndex */ @@ -403,12 +1100,12 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ */ /** * @typedef {Object} LoadedAddressData - * @property {number} tx_count - * @property {number} funded_txo_count - * @property {number} spent_txo_count + * @property {number} txCount + * @property {number} fundedTxoCount + * @property {number} spentTxoCount * @property {Sats} received * @property {Sats} sent - * @property {Dollars} realized_cap + * @property {Dollars} realizedCap */ /** @typedef {TypeIndex} LoadedAddressIndex */ /** @typedef {Cents} Low */ @@ -425,15 +1122,15 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ * @typedef {Object} MempoolInfo * @property {number} count * @property {VSize} vsize - * @property {Sats} total_fee + * @property {Sats} totalFee */ /** @typedef {string} Metric */ /** * @typedef {Object} MetricCount - * @property {number} distinct_metrics - * @property {number} total_endpoints - * @property {number} lazy_endpoints - * @property {number} stored_endpoints + * @property {number} distinctMetrics + * @property {number} totalEndpoints + * @property {number} lazyEndpoints + * @property {number} storedEndpoints */ /** * @typedef {Object} MetricData @@ -445,7 +1142,7 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ /** * @typedef {Object} MetricLeafWithSchema * @property {string} name - * @property {string} value_type + * @property {string} valueType * @property {Index[]} indexes */ /** @@ -521,8 +1218,8 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ /** @typedef {U8x32} P2WSHBytes */ /** * @typedef {Object} PaginatedMetrics - * @property {number} current_page - * @property {number} max_page + * @property {number} currentPage + * @property {number} maxPage * @property {string[]} metrics */ /** @@ -532,14 +1229,14 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ /** * @typedef {Object} PoolBlockCounts * @property {number} all - * @property {number} 24h - * @property {number} 1w + * @property {number} _24h + * @property {number} _1w */ /** * @typedef {Object} PoolBlockShares * @property {number} all - * @property {number} 24h - * @property {number} 1w + * @property {number} _24h + * @property {number} _1w */ /** * @typedef {Object} PoolDetail @@ -562,7 +1259,7 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ * @typedef {Object} PoolInfo * @property {string} name * @property {PoolSlug} slug - * @property {number} unique_id + * @property {number} uniqueId */ /** @typedef {("unknown"|"blockfills"|"ultimuspool"|"terrapool"|"luxor"|"onethash"|"btccom"|"bitfarms"|"huobipool"|"wayicn"|"canoepool"|"btctop"|"bitcoincom"|"pool175btc"|"gbminers"|"axbt"|"asicminer"|"bitminter"|"bitcoinrussia"|"btcserv"|"simplecoinus"|"btcguild"|"eligius"|"ozcoin"|"eclipsemc"|"maxbtc"|"triplemining"|"coinlab"|"pool50btc"|"ghashio"|"stminingcorp"|"bitparking"|"mmpool"|"polmine"|"kncminer"|"bitalo"|"f2pool"|"hhtt"|"megabigpower"|"mtred"|"nmcbit"|"yourbtcnet"|"givemecoins"|"braiinspool"|"antpool"|"multicoinco"|"bcpoolio"|"cointerra"|"kanopool"|"solock"|"ckpool"|"nicehash"|"bitclub"|"bitcoinaffiliatenetwork"|"btcc"|"bwpool"|"exxbw"|"bitsolo"|"bitfury"|"twentyoneinc"|"digitalbtc"|"eightbaochi"|"mybtccoinpool"|"tbdice"|"hashpool"|"nexious"|"bravomining"|"hotpool"|"okexpool"|"bcmonster"|"onehash"|"bixin"|"tatmaspool"|"viabtc"|"connectbtc"|"batpool"|"waterhole"|"dcexploration"|"dcex"|"btpool"|"fiftyeightcoin"|"bitcoinindia"|"shawnp0wers"|"phashio"|"rigpool"|"haozhuzhu"|"sevenpool"|"miningkings"|"hashbx"|"dpool"|"rawpool"|"haominer"|"helix"|"bitcoinukraine"|"poolin"|"secretsuperstar"|"tigerpoolnet"|"sigmapoolcom"|"okpooltop"|"hummerpool"|"tangpool"|"bytepool"|"spiderpool"|"novablock"|"miningcity"|"binancepool"|"minerium"|"lubiancom"|"okkong"|"aaopool"|"emcdpool"|"foundryusa"|"sbicrypto"|"arkpool"|"purebtccom"|"marapool"|"kucoinpool"|"entrustcharitypool"|"okminer"|"titan"|"pegapool"|"btcnuggets"|"cloudhashing"|"digitalxmintsy"|"telco214"|"btcpoolparty"|"multipool"|"transactioncoinmining"|"btcdig"|"trickysbtcpool"|"btcmp"|"eobot"|"unomp"|"patels"|"gogreenlight"|"ekanembtc"|"canoe"|"tiger"|"onem1x"|"zulupool"|"secpool"|"ocean"|"whitepool"|"wk057"|"futurebitapollosolo"|"carbonnegative"|"portlandhodl"|"phoenix"|"neopool"|"maxipool"|"bitfufupool"|"luckypool"|"miningdutch"|"publicpool"|"miningsquared"|"innopolistech"|"btclab"|"parasite")} PoolSlug */ /** @@ -615,13 +1312,13 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ /** @typedef {number} StoredU64 */ /** * @typedef {Object} SupplyState - * @property {number} utxo_count + * @property {number} utxoCount * @property {Sats} value */ /** @typedef {("24h"|"3d"|"1w"|"1m"|"3m"|"6m"|"1y"|"2y"|"3y")} TimePeriod */ /** * @typedef {Object} TimePeriodParam - * @property {TimePeriod} time_period + * @property {TimePeriod} timePeriod */ /** @typedef {number} Timestamp */ /** @@ -649,10 +1346,10 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ * @property {Vout} vout * @property {(TxOut|null)=} prevout * @property {string} scriptsig - * @property {string} scriptsig_asm - * @property {boolean} is_coinbase + * @property {string} scriptsigAsm + * @property {boolean} isCoinbase * @property {number} sequence - * @property {?string=} inner_redeemscript_asm + * @property {?string=} innerRedeemscriptAsm */ /** @typedef {number} TxInIndex */ /** @typedef {number} TxIndex */ @@ -672,9 +1369,9 @@ export const POOL_ID_TO_POOL_NAME = /** @type {const} */ ({ /** * @typedef {Object} TxStatus * @property {boolean} confirmed - * @property {(Height|null)=} block_height - * @property {(BlockHash|null)=} block_hash - * @property {(Timestamp|null)=} block_time + * @property {(Height|null)=} blockHeight + * @property {(BlockHash|null)=} blockHash + * @property {(Timestamp|null)=} blockTime */ /** @typedef {number} TxVersion */ /** @typedef {string} Txid */ @@ -766,13 +1463,17 @@ class MetricNode { /** * Fetch data points within a range. - * @param {string | number} from - * @param {string | number} to + * @param {string | number} [from] + * @param {string | number} [to] * @param {(value: T[]) => void} [onUpdate] - Called when data is available (may be called twice: cache then fresh) * @returns {Promise} */ getRange(from, to, onUpdate) { - return this._client.get(`${this._path}?from=${from}&to=${to}`, onUpdate); + const params = new URLSearchParams(); + if (from !== undefined) params.set('from', String(from)); + if (to !== undefined) params.set('to', String(to)); + const query = params.toString(); + return this._client.get(query ? `${this._path}?${query}` : this._path, onUpdate); } } @@ -828,18 +1529,24 @@ class BrkClientBase { // Index accessor factory functions +/** + * @template T + * @typedef {Object} Indexes3By + * @property {MetricNode} dateindex + * @property {MetricNode} decadeindex + * @property {MetricNode} difficultyepoch + * @property {MetricNode} height + * @property {MetricNode} monthindex + * @property {MetricNode} quarterindex + * @property {MetricNode} semesterindex + * @property {MetricNode} weekindex + * @property {MetricNode} yearindex + */ + /** * @template T * @typedef {Object} Indexes3 - * @property {MetricNode} byDateindex - * @property {MetricNode} byDecadeindex - * @property {MetricNode} byDifficultyepoch - * @property {MetricNode} byHeight - * @property {MetricNode} byMonthindex - * @property {MetricNode} byQuarterindex - * @property {MetricNode} bySemesterindex - * @property {MetricNode} byWeekindex - * @property {MetricNode} byYearindex + * @property {Indexes3By} by */ /** @@ -851,29 +1558,37 @@ class BrkClientBase { */ function createIndexes3(client, basePath) { return { - byDateindex: new MetricNode(client, `${basePath}/dateindex`), - byDecadeindex: new MetricNode(client, `${basePath}/decadeindex`), - byDifficultyepoch: new MetricNode(client, `${basePath}/difficultyepoch`), - byHeight: new MetricNode(client, `${basePath}/height`), - byMonthindex: new MetricNode(client, `${basePath}/monthindex`), - byQuarterindex: new MetricNode(client, `${basePath}/quarterindex`), - bySemesterindex: new MetricNode(client, `${basePath}/semesterindex`), - byWeekindex: new MetricNode(client, `${basePath}/weekindex`), - byYearindex: new MetricNode(client, `${basePath}/yearindex`) + by: { + dateindex: new MetricNode(client, `${basePath}/dateindex`), + decadeindex: new MetricNode(client, `${basePath}/decadeindex`), + difficultyepoch: new MetricNode(client, `${basePath}/difficultyepoch`), + height: new MetricNode(client, `${basePath}/height`), + monthindex: new MetricNode(client, `${basePath}/monthindex`), + quarterindex: new MetricNode(client, `${basePath}/quarterindex`), + semesterindex: new MetricNode(client, `${basePath}/semesterindex`), + weekindex: new MetricNode(client, `${basePath}/weekindex`), + yearindex: new MetricNode(client, `${basePath}/yearindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes4By + * @property {MetricNode} dateindex + * @property {MetricNode} decadeindex + * @property {MetricNode} difficultyepoch + * @property {MetricNode} monthindex + * @property {MetricNode} quarterindex + * @property {MetricNode} semesterindex + * @property {MetricNode} weekindex + * @property {MetricNode} yearindex + */ + /** * @template T * @typedef {Object} Indexes4 - * @property {MetricNode} byDateindex - * @property {MetricNode} byDecadeindex - * @property {MetricNode} byDifficultyepoch - * @property {MetricNode} byMonthindex - * @property {MetricNode} byQuarterindex - * @property {MetricNode} bySemesterindex - * @property {MetricNode} byWeekindex - * @property {MetricNode} byYearindex + * @property {Indexes4By} by */ /** @@ -885,28 +1600,36 @@ function createIndexes3(client, basePath) { */ function createIndexes4(client, basePath) { return { - byDateindex: new MetricNode(client, `${basePath}/dateindex`), - byDecadeindex: new MetricNode(client, `${basePath}/decadeindex`), - byDifficultyepoch: new MetricNode(client, `${basePath}/difficultyepoch`), - byMonthindex: new MetricNode(client, `${basePath}/monthindex`), - byQuarterindex: new MetricNode(client, `${basePath}/quarterindex`), - bySemesterindex: new MetricNode(client, `${basePath}/semesterindex`), - byWeekindex: new MetricNode(client, `${basePath}/weekindex`), - byYearindex: new MetricNode(client, `${basePath}/yearindex`) + by: { + dateindex: new MetricNode(client, `${basePath}/dateindex`), + decadeindex: new MetricNode(client, `${basePath}/decadeindex`), + difficultyepoch: new MetricNode(client, `${basePath}/difficultyepoch`), + monthindex: new MetricNode(client, `${basePath}/monthindex`), + quarterindex: new MetricNode(client, `${basePath}/quarterindex`), + semesterindex: new MetricNode(client, `${basePath}/semesterindex`), + weekindex: new MetricNode(client, `${basePath}/weekindex`), + yearindex: new MetricNode(client, `${basePath}/yearindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes26By + * @property {MetricNode} dateindex + * @property {MetricNode} decadeindex + * @property {MetricNode} height + * @property {MetricNode} monthindex + * @property {MetricNode} quarterindex + * @property {MetricNode} semesterindex + * @property {MetricNode} weekindex + * @property {MetricNode} yearindex + */ + /** * @template T * @typedef {Object} Indexes26 - * @property {MetricNode} byDateindex - * @property {MetricNode} byDecadeindex - * @property {MetricNode} byHeight - * @property {MetricNode} byMonthindex - * @property {MetricNode} byQuarterindex - * @property {MetricNode} bySemesterindex - * @property {MetricNode} byWeekindex - * @property {MetricNode} byYearindex + * @property {Indexes26By} by */ /** @@ -918,27 +1641,35 @@ function createIndexes4(client, basePath) { */ function createIndexes26(client, basePath) { return { - byDateindex: new MetricNode(client, `${basePath}/dateindex`), - byDecadeindex: new MetricNode(client, `${basePath}/decadeindex`), - byHeight: new MetricNode(client, `${basePath}/height`), - byMonthindex: new MetricNode(client, `${basePath}/monthindex`), - byQuarterindex: new MetricNode(client, `${basePath}/quarterindex`), - bySemesterindex: new MetricNode(client, `${basePath}/semesterindex`), - byWeekindex: new MetricNode(client, `${basePath}/weekindex`), - byYearindex: new MetricNode(client, `${basePath}/yearindex`) + by: { + dateindex: new MetricNode(client, `${basePath}/dateindex`), + decadeindex: new MetricNode(client, `${basePath}/decadeindex`), + height: new MetricNode(client, `${basePath}/height`), + monthindex: new MetricNode(client, `${basePath}/monthindex`), + quarterindex: new MetricNode(client, `${basePath}/quarterindex`), + semesterindex: new MetricNode(client, `${basePath}/semesterindex`), + weekindex: new MetricNode(client, `${basePath}/weekindex`), + yearindex: new MetricNode(client, `${basePath}/yearindex`) + } }; } +/** + * @template T + * @typedef {Object} IndexesBy + * @property {MetricNode} dateindex + * @property {MetricNode} decadeindex + * @property {MetricNode} monthindex + * @property {MetricNode} quarterindex + * @property {MetricNode} semesterindex + * @property {MetricNode} weekindex + * @property {MetricNode} yearindex + */ + /** * @template T * @typedef {Object} Indexes - * @property {MetricNode} byDateindex - * @property {MetricNode} byDecadeindex - * @property {MetricNode} byMonthindex - * @property {MetricNode} byQuarterindex - * @property {MetricNode} bySemesterindex - * @property {MetricNode} byWeekindex - * @property {MetricNode} byYearindex + * @property {IndexesBy} by */ /** @@ -950,26 +1681,34 @@ function createIndexes26(client, basePath) { */ function createIndexes(client, basePath) { return { - byDateindex: new MetricNode(client, `${basePath}/dateindex`), - byDecadeindex: new MetricNode(client, `${basePath}/decadeindex`), - byMonthindex: new MetricNode(client, `${basePath}/monthindex`), - byQuarterindex: new MetricNode(client, `${basePath}/quarterindex`), - bySemesterindex: new MetricNode(client, `${basePath}/semesterindex`), - byWeekindex: new MetricNode(client, `${basePath}/weekindex`), - byYearindex: new MetricNode(client, `${basePath}/yearindex`) + by: { + dateindex: new MetricNode(client, `${basePath}/dateindex`), + decadeindex: new MetricNode(client, `${basePath}/decadeindex`), + monthindex: new MetricNode(client, `${basePath}/monthindex`), + quarterindex: new MetricNode(client, `${basePath}/quarterindex`), + semesterindex: new MetricNode(client, `${basePath}/semesterindex`), + weekindex: new MetricNode(client, `${basePath}/weekindex`), + yearindex: new MetricNode(client, `${basePath}/yearindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes27By + * @property {MetricNode} decadeindex + * @property {MetricNode} height + * @property {MetricNode} monthindex + * @property {MetricNode} quarterindex + * @property {MetricNode} semesterindex + * @property {MetricNode} weekindex + * @property {MetricNode} yearindex + */ + /** * @template T * @typedef {Object} Indexes27 - * @property {MetricNode} byDecadeindex - * @property {MetricNode} byHeight - * @property {MetricNode} byMonthindex - * @property {MetricNode} byQuarterindex - * @property {MetricNode} bySemesterindex - * @property {MetricNode} byWeekindex - * @property {MetricNode} byYearindex + * @property {Indexes27By} by */ /** @@ -981,25 +1720,33 @@ function createIndexes(client, basePath) { */ function createIndexes27(client, basePath) { return { - byDecadeindex: new MetricNode(client, `${basePath}/decadeindex`), - byHeight: new MetricNode(client, `${basePath}/height`), - byMonthindex: new MetricNode(client, `${basePath}/monthindex`), - byQuarterindex: new MetricNode(client, `${basePath}/quarterindex`), - bySemesterindex: new MetricNode(client, `${basePath}/semesterindex`), - byWeekindex: new MetricNode(client, `${basePath}/weekindex`), - byYearindex: new MetricNode(client, `${basePath}/yearindex`) + by: { + decadeindex: new MetricNode(client, `${basePath}/decadeindex`), + height: new MetricNode(client, `${basePath}/height`), + monthindex: new MetricNode(client, `${basePath}/monthindex`), + quarterindex: new MetricNode(client, `${basePath}/quarterindex`), + semesterindex: new MetricNode(client, `${basePath}/semesterindex`), + weekindex: new MetricNode(client, `${basePath}/weekindex`), + yearindex: new MetricNode(client, `${basePath}/yearindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes28By + * @property {MetricNode} decadeindex + * @property {MetricNode} monthindex + * @property {MetricNode} quarterindex + * @property {MetricNode} semesterindex + * @property {MetricNode} weekindex + * @property {MetricNode} yearindex + */ + /** * @template T * @typedef {Object} Indexes28 - * @property {MetricNode} byDecadeindex - * @property {MetricNode} byMonthindex - * @property {MetricNode} byQuarterindex - * @property {MetricNode} bySemesterindex - * @property {MetricNode} byWeekindex - * @property {MetricNode} byYearindex + * @property {Indexes28By} by */ /** @@ -1011,21 +1758,29 @@ function createIndexes27(client, basePath) { */ function createIndexes28(client, basePath) { return { - byDecadeindex: new MetricNode(client, `${basePath}/decadeindex`), - byMonthindex: new MetricNode(client, `${basePath}/monthindex`), - byQuarterindex: new MetricNode(client, `${basePath}/quarterindex`), - bySemesterindex: new MetricNode(client, `${basePath}/semesterindex`), - byWeekindex: new MetricNode(client, `${basePath}/weekindex`), - byYearindex: new MetricNode(client, `${basePath}/yearindex`) + by: { + decadeindex: new MetricNode(client, `${basePath}/decadeindex`), + monthindex: new MetricNode(client, `${basePath}/monthindex`), + quarterindex: new MetricNode(client, `${basePath}/quarterindex`), + semesterindex: new MetricNode(client, `${basePath}/semesterindex`), + weekindex: new MetricNode(client, `${basePath}/weekindex`), + yearindex: new MetricNode(client, `${basePath}/yearindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes15By + * @property {MetricNode} quarterindex + * @property {MetricNode} semesterindex + * @property {MetricNode} yearindex + */ + /** * @template T * @typedef {Object} Indexes15 - * @property {MetricNode} byQuarterindex - * @property {MetricNode} bySemesterindex - * @property {MetricNode} byYearindex + * @property {Indexes15By} by */ /** @@ -1037,17 +1792,25 @@ function createIndexes28(client, basePath) { */ function createIndexes15(client, basePath) { return { - byQuarterindex: new MetricNode(client, `${basePath}/quarterindex`), - bySemesterindex: new MetricNode(client, `${basePath}/semesterindex`), - byYearindex: new MetricNode(client, `${basePath}/yearindex`) + by: { + quarterindex: new MetricNode(client, `${basePath}/quarterindex`), + semesterindex: new MetricNode(client, `${basePath}/semesterindex`), + yearindex: new MetricNode(client, `${basePath}/yearindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes13By + * @property {MetricNode} dateindex + * @property {MetricNode} height + */ + /** * @template T * @typedef {Object} Indexes13 - * @property {MetricNode} byDateindex - * @property {MetricNode} byHeight + * @property {Indexes13By} by */ /** @@ -1059,16 +1822,24 @@ function createIndexes15(client, basePath) { */ function createIndexes13(client, basePath) { return { - byDateindex: new MetricNode(client, `${basePath}/dateindex`), - byHeight: new MetricNode(client, `${basePath}/height`) + by: { + dateindex: new MetricNode(client, `${basePath}/dateindex`), + height: new MetricNode(client, `${basePath}/height`) + } }; } +/** + * @template T + * @typedef {Object} Indexes14By + * @property {MetricNode} monthindex + * @property {MetricNode} weekindex + */ + /** * @template T * @typedef {Object} Indexes14 - * @property {MetricNode} byMonthindex - * @property {MetricNode} byWeekindex + * @property {Indexes14By} by */ /** @@ -1080,15 +1851,23 @@ function createIndexes13(client, basePath) { */ function createIndexes14(client, basePath) { return { - byMonthindex: new MetricNode(client, `${basePath}/monthindex`), - byWeekindex: new MetricNode(client, `${basePath}/weekindex`) + by: { + monthindex: new MetricNode(client, `${basePath}/monthindex`), + weekindex: new MetricNode(client, `${basePath}/weekindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes2By + * @property {MetricNode} height + */ + /** * @template T * @typedef {Object} Indexes2 - * @property {MetricNode} byHeight + * @property {Indexes2By} by */ /** @@ -1100,14 +1879,22 @@ function createIndexes14(client, basePath) { */ function createIndexes2(client, basePath) { return { - byHeight: new MetricNode(client, `${basePath}/height`) + by: { + height: new MetricNode(client, `${basePath}/height`) + } }; } +/** + * @template T + * @typedef {Object} Indexes5By + * @property {MetricNode} dateindex + */ + /** * @template T * @typedef {Object} Indexes5 - * @property {MetricNode} byDateindex + * @property {Indexes5By} by */ /** @@ -1119,14 +1906,22 @@ function createIndexes2(client, basePath) { */ function createIndexes5(client, basePath) { return { - byDateindex: new MetricNode(client, `${basePath}/dateindex`) + by: { + dateindex: new MetricNode(client, `${basePath}/dateindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes6By + * @property {MetricNode} txindex + */ + /** * @template T * @typedef {Object} Indexes6 - * @property {MetricNode} byTxindex + * @property {Indexes6By} by */ /** @@ -1138,14 +1933,22 @@ function createIndexes5(client, basePath) { */ function createIndexes6(client, basePath) { return { - byTxindex: new MetricNode(client, `${basePath}/txindex`) + by: { + txindex: new MetricNode(client, `${basePath}/txindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes7By + * @property {MetricNode} decadeindex + */ + /** * @template T * @typedef {Object} Indexes7 - * @property {MetricNode} byDecadeindex + * @property {Indexes7By} by */ /** @@ -1157,14 +1960,22 @@ function createIndexes6(client, basePath) { */ function createIndexes7(client, basePath) { return { - byDecadeindex: new MetricNode(client, `${basePath}/decadeindex`) + by: { + decadeindex: new MetricNode(client, `${basePath}/decadeindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes8By + * @property {MetricNode} monthindex + */ + /** * @template T * @typedef {Object} Indexes8 - * @property {MetricNode} byMonthindex + * @property {Indexes8By} by */ /** @@ -1176,14 +1987,22 @@ function createIndexes7(client, basePath) { */ function createIndexes8(client, basePath) { return { - byMonthindex: new MetricNode(client, `${basePath}/monthindex`) + by: { + monthindex: new MetricNode(client, `${basePath}/monthindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes9By + * @property {MetricNode} quarterindex + */ + /** * @template T * @typedef {Object} Indexes9 - * @property {MetricNode} byQuarterindex + * @property {Indexes9By} by */ /** @@ -1195,14 +2014,22 @@ function createIndexes8(client, basePath) { */ function createIndexes9(client, basePath) { return { - byQuarterindex: new MetricNode(client, `${basePath}/quarterindex`) + by: { + quarterindex: new MetricNode(client, `${basePath}/quarterindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes10By + * @property {MetricNode} semesterindex + */ + /** * @template T * @typedef {Object} Indexes10 - * @property {MetricNode} bySemesterindex + * @property {Indexes10By} by */ /** @@ -1214,14 +2041,22 @@ function createIndexes9(client, basePath) { */ function createIndexes10(client, basePath) { return { - bySemesterindex: new MetricNode(client, `${basePath}/semesterindex`) + by: { + semesterindex: new MetricNode(client, `${basePath}/semesterindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes11By + * @property {MetricNode} weekindex + */ + /** * @template T * @typedef {Object} Indexes11 - * @property {MetricNode} byWeekindex + * @property {Indexes11By} by */ /** @@ -1233,14 +2068,22 @@ function createIndexes10(client, basePath) { */ function createIndexes11(client, basePath) { return { - byWeekindex: new MetricNode(client, `${basePath}/weekindex`) + by: { + weekindex: new MetricNode(client, `${basePath}/weekindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes12By + * @property {MetricNode} yearindex + */ + /** * @template T * @typedef {Object} Indexes12 - * @property {MetricNode} byYearindex + * @property {Indexes12By} by */ /** @@ -1252,14 +2095,22 @@ function createIndexes11(client, basePath) { */ function createIndexes12(client, basePath) { return { - byYearindex: new MetricNode(client, `${basePath}/yearindex`) + by: { + yearindex: new MetricNode(client, `${basePath}/yearindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes16By + * @property {MetricNode} p2aaddressindex + */ + /** * @template T * @typedef {Object} Indexes16 - * @property {MetricNode} byP2aaddressindex + * @property {Indexes16By} by */ /** @@ -1271,14 +2122,22 @@ function createIndexes12(client, basePath) { */ function createIndexes16(client, basePath) { return { - byP2aaddressindex: new MetricNode(client, `${basePath}/p2aaddressindex`) + by: { + p2aaddressindex: new MetricNode(client, `${basePath}/p2aaddressindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes17By + * @property {MetricNode} p2pk33addressindex + */ + /** * @template T * @typedef {Object} Indexes17 - * @property {MetricNode} byP2pk33addressindex + * @property {Indexes17By} by */ /** @@ -1290,14 +2149,22 @@ function createIndexes16(client, basePath) { */ function createIndexes17(client, basePath) { return { - byP2pk33addressindex: new MetricNode(client, `${basePath}/p2pk33addressindex`) + by: { + p2pk33addressindex: new MetricNode(client, `${basePath}/p2pk33addressindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes18By + * @property {MetricNode} p2pk65addressindex + */ + /** * @template T * @typedef {Object} Indexes18 - * @property {MetricNode} byP2pk65addressindex + * @property {Indexes18By} by */ /** @@ -1309,14 +2176,22 @@ function createIndexes17(client, basePath) { */ function createIndexes18(client, basePath) { return { - byP2pk65addressindex: new MetricNode(client, `${basePath}/p2pk65addressindex`) + by: { + p2pk65addressindex: new MetricNode(client, `${basePath}/p2pk65addressindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes19By + * @property {MetricNode} p2pkhaddressindex + */ + /** * @template T * @typedef {Object} Indexes19 - * @property {MetricNode} byP2pkhaddressindex + * @property {Indexes19By} by */ /** @@ -1328,14 +2203,22 @@ function createIndexes18(client, basePath) { */ function createIndexes19(client, basePath) { return { - byP2pkhaddressindex: new MetricNode(client, `${basePath}/p2pkhaddressindex`) + by: { + p2pkhaddressindex: new MetricNode(client, `${basePath}/p2pkhaddressindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes20By + * @property {MetricNode} p2shaddressindex + */ + /** * @template T * @typedef {Object} Indexes20 - * @property {MetricNode} byP2shaddressindex + * @property {Indexes20By} by */ /** @@ -1347,14 +2230,22 @@ function createIndexes19(client, basePath) { */ function createIndexes20(client, basePath) { return { - byP2shaddressindex: new MetricNode(client, `${basePath}/p2shaddressindex`) + by: { + p2shaddressindex: new MetricNode(client, `${basePath}/p2shaddressindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes21By + * @property {MetricNode} p2traddressindex + */ + /** * @template T * @typedef {Object} Indexes21 - * @property {MetricNode} byP2traddressindex + * @property {Indexes21By} by */ /** @@ -1366,14 +2257,22 @@ function createIndexes20(client, basePath) { */ function createIndexes21(client, basePath) { return { - byP2traddressindex: new MetricNode(client, `${basePath}/p2traddressindex`) + by: { + p2traddressindex: new MetricNode(client, `${basePath}/p2traddressindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes22By + * @property {MetricNode} p2wpkhaddressindex + */ + /** * @template T * @typedef {Object} Indexes22 - * @property {MetricNode} byP2wpkhaddressindex + * @property {Indexes22By} by */ /** @@ -1385,14 +2284,22 @@ function createIndexes21(client, basePath) { */ function createIndexes22(client, basePath) { return { - byP2wpkhaddressindex: new MetricNode(client, `${basePath}/p2wpkhaddressindex`) + by: { + p2wpkhaddressindex: new MetricNode(client, `${basePath}/p2wpkhaddressindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes23By + * @property {MetricNode} p2wshaddressindex + */ + /** * @template T * @typedef {Object} Indexes23 - * @property {MetricNode} byP2wshaddressindex + * @property {Indexes23By} by */ /** @@ -1404,14 +2311,22 @@ function createIndexes22(client, basePath) { */ function createIndexes23(client, basePath) { return { - byP2wshaddressindex: new MetricNode(client, `${basePath}/p2wshaddressindex`) + by: { + p2wshaddressindex: new MetricNode(client, `${basePath}/p2wshaddressindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes24By + * @property {MetricNode} txinindex + */ + /** * @template T * @typedef {Object} Indexes24 - * @property {MetricNode} byTxinindex + * @property {Indexes24By} by */ /** @@ -1423,14 +2338,22 @@ function createIndexes23(client, basePath) { */ function createIndexes24(client, basePath) { return { - byTxinindex: new MetricNode(client, `${basePath}/txinindex`) + by: { + txinindex: new MetricNode(client, `${basePath}/txinindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes25By + * @property {MetricNode} txoutindex + */ + /** * @template T * @typedef {Object} Indexes25 - * @property {MetricNode} byTxoutindex + * @property {Indexes25By} by */ /** @@ -1442,14 +2365,22 @@ function createIndexes24(client, basePath) { */ function createIndexes25(client, basePath) { return { - byTxoutindex: new MetricNode(client, `${basePath}/txoutindex`) + by: { + txoutindex: new MetricNode(client, `${basePath}/txoutindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes29By + * @property {MetricNode} emptyaddressindex + */ + /** * @template T * @typedef {Object} Indexes29 - * @property {MetricNode} byEmptyaddressindex + * @property {Indexes29By} by */ /** @@ -1461,14 +2392,22 @@ function createIndexes25(client, basePath) { */ function createIndexes29(client, basePath) { return { - byEmptyaddressindex: new MetricNode(client, `${basePath}/emptyaddressindex`) + by: { + emptyaddressindex: new MetricNode(client, `${basePath}/emptyaddressindex`) + } }; } +/** + * @template T + * @typedef {Object} Indexes30By + * @property {MetricNode} loadedaddressindex + */ + /** * @template T * @typedef {Object} Indexes30 - * @property {MetricNode} byLoadedaddressindex + * @property {Indexes30By} by */ /** @@ -1480,7 +2419,9 @@ function createIndexes29(client, basePath) { */ function createIndexes30(client, basePath) { return { - byLoadedaddressindex: new MetricNode(client, `${basePath}/loadedaddressindex`) + by: { + loadedaddressindex: new MetricNode(client, `${basePath}/loadedaddressindex`) + } }; } @@ -1966,6 +2907,47 @@ function createRatio1ySdPattern(client, basePath) { }; } +/** + * @typedef {Object} AXbtPattern + * @property {BlockCountPattern} _1dDominance + * @property {Indexes} _1mBlocksMined + * @property {Indexes} _1mDominance + * @property {Indexes} _1wBlocksMined + * @property {Indexes} _1wDominance + * @property {Indexes} _1yBlocksMined + * @property {Indexes} _1yDominance + * @property {BlockCountPattern} blocksMined + * @property {UnclaimedRewardsPattern} coinbase + * @property {Indexes} daysSinceBlock + * @property {BlockCountPattern} dominance + * @property {FeePattern2} fee + * @property {FeePattern2} subsidy + */ + +/** + * Create a AXbtPattern pattern node + * @param {BrkClientBase} client + * @param {string} basePath + * @returns {AXbtPattern} + */ +function createAXbtPattern(client, basePath) { + return { + _1dDominance: createBlockCountPattern(client, `${basePath}/1d_dominance`), + _1mBlocksMined: createIndexes(client, `${basePath}/1m_blocks_mined`), + _1mDominance: createIndexes(client, `${basePath}/1m_dominance`), + _1wBlocksMined: createIndexes(client, `${basePath}/1w_blocks_mined`), + _1wDominance: createIndexes(client, `${basePath}/1w_dominance`), + _1yBlocksMined: createIndexes(client, `${basePath}/1y_blocks_mined`), + _1yDominance: createIndexes(client, `${basePath}/1y_dominance`), + blocksMined: createBlockCountPattern(client, `${basePath}/blocks_mined`), + coinbase: createUnclaimedRewardsPattern(client, `${basePath}/coinbase`), + daysSinceBlock: createIndexes(client, `${basePath}/days_since_block`), + dominance: createBlockCountPattern(client, `${basePath}/dominance`), + fee: createFeePattern2(client, `${basePath}/fee`), + subsidy: createFeePattern2(client, `${basePath}/subsidy`) + }; +} + /** * @typedef {Object} ActivePriceRatioPattern * @property {Indexes} ratio @@ -2007,47 +2989,6 @@ function createActivePriceRatioPattern(client, basePath) { }; } -/** - * @typedef {Object} AXbtPattern - * @property {BlockCountPattern} _1dDominance - * @property {Indexes} _1mBlocksMined - * @property {Indexes} _1mDominance - * @property {Indexes} _1wBlocksMined - * @property {Indexes} _1wDominance - * @property {Indexes} _1yBlocksMined - * @property {Indexes} _1yDominance - * @property {BlockCountPattern} blocksMined - * @property {UnclaimedRewardsPattern} coinbase - * @property {Indexes} daysSinceBlock - * @property {BlockCountPattern} dominance - * @property {UnclaimedRewardsPattern} fee - * @property {UnclaimedRewardsPattern} subsidy - */ - -/** - * Create a AXbtPattern pattern node - * @param {BrkClientBase} client - * @param {string} basePath - * @returns {AXbtPattern} - */ -function createAXbtPattern(client, basePath) { - return { - _1dDominance: createBlockCountPattern(client, `${basePath}/1d_dominance`), - _1mBlocksMined: createIndexes(client, `${basePath}/1m_blocks_mined`), - _1mDominance: createIndexes(client, `${basePath}/1m_dominance`), - _1wBlocksMined: createIndexes(client, `${basePath}/1w_blocks_mined`), - _1wDominance: createIndexes(client, `${basePath}/1w_dominance`), - _1yBlocksMined: createIndexes(client, `${basePath}/1y_blocks_mined`), - _1yDominance: createIndexes(client, `${basePath}/1y_dominance`), - blocksMined: createBlockCountPattern(client, `${basePath}/blocks_mined`), - coinbase: createUnclaimedRewardsPattern(client, `${basePath}/coinbase`), - daysSinceBlock: createIndexes(client, `${basePath}/days_since_block`), - dominance: createBlockCountPattern(client, `${basePath}/dominance`), - fee: createUnclaimedRewardsPattern(client, `${basePath}/fee`), - subsidy: createUnclaimedRewardsPattern(client, `${basePath}/subsidy`) - }; -} - /** * @template T * @typedef {Object} BitcoinPattern @@ -2124,6 +3065,39 @@ function createBlockSizePattern(client, basePath) { }; } +/** + * @typedef {Object} RelativePattern + * @property {Indexes27} negUnrealizedLossRelToMarketCap + * @property {Indexes26} netUnrealizedPnlRelToMarketCap + * @property {Indexes27} supplyInLossRelToCirculatingSupply + * @property {Indexes27} supplyInLossRelToOwnSupply + * @property {Indexes27} supplyInProfitRelToCirculatingSupply + * @property {Indexes27} supplyInProfitRelToOwnSupply + * @property {Indexes} supplyRelToCirculatingSupply + * @property {Indexes27} unrealizedLossRelToMarketCap + * @property {Indexes27} unrealizedProfitRelToMarketCap + */ + +/** + * Create a RelativePattern pattern node + * @param {BrkClientBase} client + * @param {string} basePath + * @returns {RelativePattern} + */ +function createRelativePattern(client, basePath) { + return { + negUnrealizedLossRelToMarketCap: createIndexes27(client, `${basePath}/neg_unrealized_loss_rel_to_market_cap`), + netUnrealizedPnlRelToMarketCap: createIndexes26(client, `${basePath}/net_unrealized_pnl_rel_to_market_cap`), + supplyInLossRelToCirculatingSupply: createIndexes27(client, `${basePath}/supply_in_loss_rel_to_circulating_supply`), + supplyInLossRelToOwnSupply: createIndexes27(client, `${basePath}/supply_in_loss_rel_to_own_supply`), + supplyInProfitRelToCirculatingSupply: createIndexes27(client, `${basePath}/supply_in_profit_rel_to_circulating_supply`), + supplyInProfitRelToOwnSupply: createIndexes27(client, `${basePath}/supply_in_profit_rel_to_own_supply`), + supplyRelToCirculatingSupply: createIndexes(client, `${basePath}/supply_rel_to_circulating_supply`), + unrealizedLossRelToMarketCap: createIndexes27(client, `${basePath}/unrealized_loss_rel_to_market_cap`), + unrealizedProfitRelToMarketCap: createIndexes27(client, `${basePath}/unrealized_profit_rel_to_market_cap`) + }; +} + /** * @typedef {Object} UnrealizedPattern * @property {Indexes26} negUnrealizedLoss @@ -2158,35 +3132,35 @@ function createUnrealizedPattern(client, basePath) { } /** - * @typedef {Object} RelativePattern - * @property {Indexes27} negUnrealizedLossRelToMarketCap - * @property {Indexes26} netUnrealizedPnlRelToMarketCap - * @property {Indexes27} supplyInLossRelToCirculatingSupply - * @property {Indexes27} supplyInLossRelToOwnSupply - * @property {Indexes27} supplyInProfitRelToCirculatingSupply - * @property {Indexes27} supplyInProfitRelToOwnSupply - * @property {Indexes} supplyRelToCirculatingSupply - * @property {Indexes27} unrealizedLossRelToMarketCap - * @property {Indexes27} unrealizedProfitRelToMarketCap + * @template T + * @typedef {Object} Constant0Pattern + * @property {Indexes5} dateindex + * @property {Indexes7} decadeindex + * @property {Indexes2} height + * @property {Indexes8} monthindex + * @property {Indexes9} quarterindex + * @property {Indexes10} semesterindex + * @property {Indexes11} weekindex + * @property {Indexes12} yearindex */ /** - * Create a RelativePattern pattern node + * Create a Constant0Pattern pattern node + * @template T * @param {BrkClientBase} client - * @param {string} basePath - * @returns {RelativePattern} + * @param {string} acc - Accumulated metric name + * @returns {Constant0Pattern} */ -function createRelativePattern(client, basePath) { +function createConstant0Pattern(client, acc) { return { - negUnrealizedLossRelToMarketCap: createIndexes27(client, `${basePath}/neg_unrealized_loss_rel_to_market_cap`), - netUnrealizedPnlRelToMarketCap: createIndexes26(client, `${basePath}/net_unrealized_pnl_rel_to_market_cap`), - supplyInLossRelToCirculatingSupply: createIndexes27(client, `${basePath}/supply_in_loss_rel_to_circulating_supply`), - supplyInLossRelToOwnSupply: createIndexes27(client, `${basePath}/supply_in_loss_rel_to_own_supply`), - supplyInProfitRelToCirculatingSupply: createIndexes27(client, `${basePath}/supply_in_profit_rel_to_circulating_supply`), - supplyInProfitRelToOwnSupply: createIndexes27(client, `${basePath}/supply_in_profit_rel_to_own_supply`), - supplyRelToCirculatingSupply: createIndexes(client, `${basePath}/supply_rel_to_circulating_supply`), - unrealizedLossRelToMarketCap: createIndexes27(client, `${basePath}/unrealized_loss_rel_to_market_cap`), - unrealizedProfitRelToMarketCap: createIndexes27(client, `${basePath}/unrealized_profit_rel_to_market_cap`) + dateindex: createIndexes5(client, `/${acc}`), + decadeindex: createIndexes7(client, `/${acc}`), + height: createIndexes2(client, `/${acc}`), + monthindex: createIndexes8(client, `/${acc}`), + quarterindex: createIndexes9(client, `/${acc}`), + semesterindex: createIndexes10(client, `/${acc}`), + weekindex: createIndexes11(client, `/${acc}`), + yearindex: createIndexes12(client, `/${acc}`) }; } @@ -2256,39 +3230,6 @@ function createBlockIntervalPattern(client, acc) { }; } -/** - * @template T - * @typedef {Object} Constant0Pattern - * @property {Indexes5} dateindex - * @property {Indexes7} decadeindex - * @property {Indexes2} height - * @property {Indexes8} monthindex - * @property {Indexes9} quarterindex - * @property {Indexes10} semesterindex - * @property {Indexes11} weekindex - * @property {Indexes12} yearindex - */ - -/** - * Create a Constant0Pattern pattern node - * @template T - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {Constant0Pattern} - */ -function createConstant0Pattern(client, acc) { - return { - dateindex: createIndexes5(client, `/${acc}`), - decadeindex: createIndexes7(client, `/${acc}`), - height: createIndexes2(client, `/${acc}`), - monthindex: createIndexes8(client, `/${acc}`), - quarterindex: createIndexes9(client, `/${acc}`), - semesterindex: createIndexes10(client, `/${acc}`), - weekindex: createIndexes11(client, `/${acc}`), - yearindex: createIndexes12(client, `/${acc}`) - }; -} - /** * @typedef {Object} _0satsPattern * @property {ActivityPattern} activity @@ -2399,6 +3340,31 @@ function create_10yTo12yPattern(client, basePath) { }; } +/** + * @typedef {Object} ActivityPattern + * @property {BlockCountPattern} coinblocksDestroyed + * @property {BlockCountPattern} coindaysDestroyed + * @property {Indexes2} satblocksDestroyed + * @property {Indexes2} satdaysDestroyed + * @property {FeePattern2} sent + */ + +/** + * Create a ActivityPattern pattern node + * @param {BrkClientBase} client + * @param {string} basePath + * @returns {ActivityPattern} + */ +function createActivityPattern(client, basePath) { + return { + coinblocksDestroyed: createBlockCountPattern(client, `${basePath}/coinblocks_destroyed`), + coindaysDestroyed: createBlockCountPattern(client, `${basePath}/coindays_destroyed`), + satblocksDestroyed: createIndexes2(client, `${basePath}/satblocks_destroyed`), + satdaysDestroyed: createIndexes2(client, `${basePath}/satdays_destroyed`), + sent: createFeePattern2(client, `${basePath}/sent`) + }; +} + /** * @typedef {Object} SupplyPattern2 * @property {SupplyPattern} supply @@ -2425,32 +3391,7 @@ function createSupplyPattern2(client, basePath) { } /** - * @typedef {Object} ActivityPattern - * @property {BlockCountPattern} coinblocksDestroyed - * @property {BlockCountPattern} coindaysDestroyed - * @property {Indexes2} satblocksDestroyed - * @property {Indexes2} satdaysDestroyed - * @property {SentPattern} sent - */ - -/** - * Create a ActivityPattern pattern node - * @param {BrkClientBase} client - * @param {string} basePath - * @returns {ActivityPattern} - */ -function createActivityPattern(client, basePath) { - return { - coinblocksDestroyed: createBlockCountPattern(client, `${basePath}/coinblocks_destroyed`), - coindaysDestroyed: createBlockCountPattern(client, `${basePath}/coindays_destroyed`), - satblocksDestroyed: createIndexes2(client, `${basePath}/satblocks_destroyed`), - satdaysDestroyed: createIndexes2(client, `${basePath}/satdays_destroyed`), - sent: createSentPattern(client, `${basePath}/sent`) - }; -} - -/** - * @typedef {Object} SentPattern + * @typedef {Object} FeePattern2 * @property {Indexes2} base * @property {BlockCountPattern} bitcoin * @property {BlockCountPattern} dollars @@ -2458,12 +3399,12 @@ function createActivityPattern(client, basePath) { */ /** - * Create a SentPattern pattern node + * Create a FeePattern2 pattern node * @param {BrkClientBase} client * @param {string} basePath - * @returns {SentPattern} + * @returns {FeePattern2} */ -function createSentPattern(client, basePath) { +function createFeePattern2(client, basePath) { return { base: createIndexes2(client, `${basePath}/base`), bitcoin: createBlockCountPattern(client, `${basePath}/bitcoin`), @@ -2495,48 +3436,6 @@ function createSupplyPattern(client, basePath) { }; } -/** - * @typedef {Object} CoinbasePattern - * @property {BitcoinPattern} bitcoin - * @property {BitcoinPattern} dollars - * @property {BitcoinPattern} sats - */ - -/** - * Create a CoinbasePattern pattern node - * @param {BrkClientBase} client - * @param {string} basePath - * @returns {CoinbasePattern} - */ -function createCoinbasePattern(client, basePath) { - return { - bitcoin: createBitcoinPattern(client, `${basePath}/bitcoin`), - dollars: createBitcoinPattern(client, `${basePath}/dollars`), - sats: createBitcoinPattern(client, `${basePath}/sats`) - }; -} - -/** - * @typedef {Object} ActiveSupplyPattern - * @property {Indexes3} bitcoin - * @property {Indexes3} dollars - * @property {Indexes3} sats - */ - -/** - * Create a ActiveSupplyPattern pattern node - * @param {BrkClientBase} client - * @param {string} basePath - * @returns {ActiveSupplyPattern} - */ -function createActiveSupplyPattern(client, basePath) { - return { - bitcoin: createIndexes3(client, `${basePath}/bitcoin`), - dollars: createIndexes3(client, `${basePath}/dollars`), - sats: createIndexes3(client, `${basePath}/sats`) - }; -} - /** * @typedef {Object} UnclaimedRewardsPattern * @property {BlockCountPattern} bitcoin @@ -2579,6 +3478,48 @@ function createPricePaidPattern2(client, basePath) { }; } +/** + * @typedef {Object} CoinbasePattern + * @property {BitcoinPattern} bitcoin + * @property {BitcoinPattern} dollars + * @property {BitcoinPattern} sats + */ + +/** + * Create a CoinbasePattern pattern node + * @param {BrkClientBase} client + * @param {string} basePath + * @returns {CoinbasePattern} + */ +function createCoinbasePattern(client, basePath) { + return { + bitcoin: createBitcoinPattern(client, `${basePath}/bitcoin`), + dollars: createBitcoinPattern(client, `${basePath}/dollars`), + sats: createBitcoinPattern(client, `${basePath}/sats`) + }; +} + +/** + * @typedef {Object} ActiveSupplyPattern + * @property {Indexes3} bitcoin + * @property {Indexes3} dollars + * @property {Indexes3} sats + */ + +/** + * Create a ActiveSupplyPattern pattern node + * @param {BrkClientBase} client + * @param {string} basePath + * @returns {ActiveSupplyPattern} + */ +function createActiveSupplyPattern(client, basePath) { + return { + bitcoin: createIndexes3(client, `${basePath}/bitcoin`), + dollars: createIndexes3(client, `${basePath}/dollars`), + sats: createIndexes3(client, `${basePath}/sats`) + }; +} + /** * @template T * @typedef {Object} BlockCountPattern @@ -2602,25 +3543,6 @@ function createBlockCountPattern(client, basePath) { }; } -/** - * @typedef {Object} SupplyValuePattern - * @property {Indexes2} bitcoin - * @property {Indexes2} dollars - */ - -/** - * Create a SupplyValuePattern pattern node - * @param {BrkClientBase} client - * @param {string} basePath - * @returns {SupplyValuePattern} - */ -function createSupplyValuePattern(client, basePath) { - return { - bitcoin: createIndexes2(client, `${basePath}/bitcoin`), - dollars: createIndexes2(client, `${basePath}/dollars`) - }; -} - /** * @typedef {Object} PricePaidPattern * @property {Indexes3} maxPricePaid @@ -2640,25 +3562,6 @@ function createPricePaidPattern(client, basePath) { }; } -/** - * @typedef {Object} SatsPattern - * @property {Indexes3} cumulative - * @property {Indexes4} sum - */ - -/** - * Create a SatsPattern pattern node - * @param {BrkClientBase} client - * @param {string} basePath - * @returns {SatsPattern} - */ -function createSatsPattern(client, basePath) { - return { - cumulative: createIndexes3(client, `${basePath}/cumulative`), - sum: createIndexes4(client, `${basePath}/sum`) - }; -} - /** * @typedef {Object} _1dReturns1mSdPattern * @property {Indexes} sd @@ -2678,6 +3581,44 @@ function create_1dReturns1mSdPattern(client, acc) { }; } +/** + * @typedef {Object} SupplyValuePattern + * @property {Indexes2} bitcoin + * @property {Indexes2} dollars + */ + +/** + * Create a SupplyValuePattern pattern node + * @param {BrkClientBase} client + * @param {string} basePath + * @returns {SupplyValuePattern} + */ +function createSupplyValuePattern(client, basePath) { + return { + bitcoin: createIndexes2(client, `${basePath}/bitcoin`), + dollars: createIndexes2(client, `${basePath}/dollars`) + }; +} + +/** + * @typedef {Object} SatsPattern + * @property {Indexes3} cumulative + * @property {Indexes4} sum + */ + +/** + * Create a SatsPattern pattern node + * @param {BrkClientBase} client + * @param {string} basePath + * @returns {SatsPattern} + */ +function createSatsPattern(client, basePath) { + return { + cumulative: createIndexes3(client, `${basePath}/cumulative`), + sum: createIndexes4(client, `${basePath}/sum`) + }; +} + /** * @template T * @typedef {Object} BitcoinPattern2 diff --git a/packages/brk_client/__init__.py b/packages/brk_client/__init__.py index cedd42228..82df7f0e7 100644 --- a/packages/brk_client/__init__.py +++ b/packages/brk_client/__init__.py @@ -202,6 +202,703 @@ POOL_ID_TO_POOL_NAME: Final[dict[str, str]] = { "zulupool": "Zulupool", } +# Cohort names + +TERM_NAMES: Final = { + "short": { + "id": "sth", + "short": "STH", + "long": "Short Term Holders" + }, + "long": { + "id": "lth", + "short": "LTH", + "long": "Long Term Holders" + } +} + +EPOCH_NAMES: Final = { + "_0": { + "id": "epoch_0", + "short": "Epoch 0", + "long": "Epoch 0" + }, + "_1": { + "id": "epoch_1", + "short": "Epoch 1", + "long": "Epoch 1" + }, + "_2": { + "id": "epoch_2", + "short": "Epoch 2", + "long": "Epoch 2" + }, + "_3": { + "id": "epoch_3", + "short": "Epoch 3", + "long": "Epoch 3" + }, + "_4": { + "id": "epoch_4", + "short": "Epoch 4", + "long": "Epoch 4" + } +} + +YEAR_NAMES: Final = { + "_2009": { + "id": "year_2009", + "short": "2009", + "long": "Year 2009" + }, + "_2010": { + "id": "year_2010", + "short": "2010", + "long": "Year 2010" + }, + "_2011": { + "id": "year_2011", + "short": "2011", + "long": "Year 2011" + }, + "_2012": { + "id": "year_2012", + "short": "2012", + "long": "Year 2012" + }, + "_2013": { + "id": "year_2013", + "short": "2013", + "long": "Year 2013" + }, + "_2014": { + "id": "year_2014", + "short": "2014", + "long": "Year 2014" + }, + "_2015": { + "id": "year_2015", + "short": "2015", + "long": "Year 2015" + }, + "_2016": { + "id": "year_2016", + "short": "2016", + "long": "Year 2016" + }, + "_2017": { + "id": "year_2017", + "short": "2017", + "long": "Year 2017" + }, + "_2018": { + "id": "year_2018", + "short": "2018", + "long": "Year 2018" + }, + "_2019": { + "id": "year_2019", + "short": "2019", + "long": "Year 2019" + }, + "_2020": { + "id": "year_2020", + "short": "2020", + "long": "Year 2020" + }, + "_2021": { + "id": "year_2021", + "short": "2021", + "long": "Year 2021" + }, + "_2022": { + "id": "year_2022", + "short": "2022", + "long": "Year 2022" + }, + "_2023": { + "id": "year_2023", + "short": "2023", + "long": "Year 2023" + }, + "_2024": { + "id": "year_2024", + "short": "2024", + "long": "Year 2024" + }, + "_2025": { + "id": "year_2025", + "short": "2025", + "long": "Year 2025" + }, + "_2026": { + "id": "year_2026", + "short": "2026", + "long": "Year 2026" + } +} + +SPENDABLE_TYPE_NAMES: Final = { + "p2pk65": { + "id": "p2pk65", + "short": "P2PK65", + "long": "Pay to Public Key (65 bytes)" + }, + "p2pk33": { + "id": "p2pk33", + "short": "P2PK33", + "long": "Pay to Public Key (33 bytes)" + }, + "p2pkh": { + "id": "p2pkh", + "short": "P2PKH", + "long": "Pay to Public Key Hash" + }, + "p2ms": { + "id": "p2ms", + "short": "P2MS", + "long": "Pay to Multisig" + }, + "p2sh": { + "id": "p2sh", + "short": "P2SH", + "long": "Pay to Script Hash" + }, + "p2wpkh": { + "id": "p2wpkh", + "short": "P2WPKH", + "long": "Pay to Witness Public Key Hash" + }, + "p2wsh": { + "id": "p2wsh", + "short": "P2WSH", + "long": "Pay to Witness Script Hash" + }, + "p2tr": { + "id": "p2tr", + "short": "P2TR", + "long": "Pay to Taproot" + }, + "p2a": { + "id": "p2a", + "short": "P2A", + "long": "Pay to Anchor" + }, + "unknown": { + "id": "unknown_outputs", + "short": "Unknown", + "long": "Unknown Output Type" + }, + "empty": { + "id": "empty_outputs", + "short": "Empty", + "long": "Empty Output" + } +} + +AGE_RANGE_NAMES: Final = { + "up_to_1d": { + "id": "up_to_1d_old", + "short": "<1d", + "long": "Up to 1 Day Old" + }, + "_1d_to_1w": { + "id": "at_least_1d_up_to_1w_old", + "short": "1d-1w", + "long": "1 Day to 1 Week Old" + }, + "_1w_to_1m": { + "id": "at_least_1w_up_to_1m_old", + "short": "1w-1m", + "long": "1 Week to 1 Month Old" + }, + "_1m_to_2m": { + "id": "at_least_1m_up_to_2m_old", + "short": "1m-2m", + "long": "1 to 2 Months Old" + }, + "_2m_to_3m": { + "id": "at_least_2m_up_to_3m_old", + "short": "2m-3m", + "long": "2 to 3 Months Old" + }, + "_3m_to_4m": { + "id": "at_least_3m_up_to_4m_old", + "short": "3m-4m", + "long": "3 to 4 Months Old" + }, + "_4m_to_5m": { + "id": "at_least_4m_up_to_5m_old", + "short": "4m-5m", + "long": "4 to 5 Months Old" + }, + "_5m_to_6m": { + "id": "at_least_5m_up_to_6m_old", + "short": "5m-6m", + "long": "5 to 6 Months Old" + }, + "_6m_to_1y": { + "id": "at_least_6m_up_to_1y_old", + "short": "6m-1y", + "long": "6 Months to 1 Year Old" + }, + "_1y_to_2y": { + "id": "at_least_1y_up_to_2y_old", + "short": "1y-2y", + "long": "1 to 2 Years Old" + }, + "_2y_to_3y": { + "id": "at_least_2y_up_to_3y_old", + "short": "2y-3y", + "long": "2 to 3 Years Old" + }, + "_3y_to_4y": { + "id": "at_least_3y_up_to_4y_old", + "short": "3y-4y", + "long": "3 to 4 Years Old" + }, + "_4y_to_5y": { + "id": "at_least_4y_up_to_5y_old", + "short": "4y-5y", + "long": "4 to 5 Years Old" + }, + "_5y_to_6y": { + "id": "at_least_5y_up_to_6y_old", + "short": "5y-6y", + "long": "5 to 6 Years Old" + }, + "_6y_to_7y": { + "id": "at_least_6y_up_to_7y_old", + "short": "6y-7y", + "long": "6 to 7 Years Old" + }, + "_7y_to_8y": { + "id": "at_least_7y_up_to_8y_old", + "short": "7y-8y", + "long": "7 to 8 Years Old" + }, + "_8y_to_10y": { + "id": "at_least_8y_up_to_10y_old", + "short": "8y-10y", + "long": "8 to 10 Years Old" + }, + "_10y_to_12y": { + "id": "at_least_10y_up_to_12y_old", + "short": "10y-12y", + "long": "10 to 12 Years Old" + }, + "_12y_to_15y": { + "id": "at_least_12y_up_to_15y_old", + "short": "12y-15y", + "long": "12 to 15 Years Old" + }, + "from_15y": { + "id": "at_least_15y_old", + "short": "15y+", + "long": "15+ Years Old" + } +} + +MAX_AGE_NAMES: Final = { + "_1w": { + "id": "up_to_1w_old", + "short": "<1w", + "long": "Up to 1 Week Old" + }, + "_1m": { + "id": "up_to_1m_old", + "short": "<1m", + "long": "Up to 1 Month Old" + }, + "_2m": { + "id": "up_to_2m_old", + "short": "<2m", + "long": "Up to 2 Months Old" + }, + "_3m": { + "id": "up_to_3m_old", + "short": "<3m", + "long": "Up to 3 Months Old" + }, + "_4m": { + "id": "up_to_4m_old", + "short": "<4m", + "long": "Up to 4 Months Old" + }, + "_5m": { + "id": "up_to_5m_old", + "short": "<5m", + "long": "Up to 5 Months Old" + }, + "_6m": { + "id": "up_to_6m_old", + "short": "<6m", + "long": "Up to 6 Months Old" + }, + "_1y": { + "id": "up_to_1y_old", + "short": "<1y", + "long": "Up to 1 Year Old" + }, + "_2y": { + "id": "up_to_2y_old", + "short": "<2y", + "long": "Up to 2 Years Old" + }, + "_3y": { + "id": "up_to_3y_old", + "short": "<3y", + "long": "Up to 3 Years Old" + }, + "_4y": { + "id": "up_to_4y_old", + "short": "<4y", + "long": "Up to 4 Years Old" + }, + "_5y": { + "id": "up_to_5y_old", + "short": "<5y", + "long": "Up to 5 Years Old" + }, + "_6y": { + "id": "up_to_6y_old", + "short": "<6y", + "long": "Up to 6 Years Old" + }, + "_7y": { + "id": "up_to_7y_old", + "short": "<7y", + "long": "Up to 7 Years Old" + }, + "_8y": { + "id": "up_to_8y_old", + "short": "<8y", + "long": "Up to 8 Years Old" + }, + "_10y": { + "id": "up_to_10y_old", + "short": "<10y", + "long": "Up to 10 Years Old" + }, + "_12y": { + "id": "up_to_12y_old", + "short": "<12y", + "long": "Up to 12 Years Old" + }, + "_15y": { + "id": "up_to_15y_old", + "short": "<15y", + "long": "Up to 15 Years Old" + } +} + +MIN_AGE_NAMES: Final = { + "_1d": { + "id": "at_least_1d_old", + "short": "1d+", + "long": "At Least 1 Day Old" + }, + "_1w": { + "id": "at_least_1w_old", + "short": "1w+", + "long": "At Least 1 Week Old" + }, + "_1m": { + "id": "at_least_1m_old", + "short": "1m+", + "long": "At Least 1 Month Old" + }, + "_2m": { + "id": "at_least_2m_old", + "short": "2m+", + "long": "At Least 2 Months Old" + }, + "_3m": { + "id": "at_least_3m_old", + "short": "3m+", + "long": "At Least 3 Months Old" + }, + "_4m": { + "id": "at_least_4m_old", + "short": "4m+", + "long": "At Least 4 Months Old" + }, + "_5m": { + "id": "at_least_5m_old", + "short": "5m+", + "long": "At Least 5 Months Old" + }, + "_6m": { + "id": "at_least_6m_old", + "short": "6m+", + "long": "At Least 6 Months Old" + }, + "_1y": { + "id": "at_least_1y_old", + "short": "1y+", + "long": "At Least 1 Year Old" + }, + "_2y": { + "id": "at_least_2y_old", + "short": "2y+", + "long": "At Least 2 Years Old" + }, + "_3y": { + "id": "at_least_3y_old", + "short": "3y+", + "long": "At Least 3 Years Old" + }, + "_4y": { + "id": "at_least_4y_old", + "short": "4y+", + "long": "At Least 4 Years Old" + }, + "_5y": { + "id": "at_least_5y_old", + "short": "5y+", + "long": "At Least 5 Years Old" + }, + "_6y": { + "id": "at_least_6y_old", + "short": "6y+", + "long": "At Least 6 Years Old" + }, + "_7y": { + "id": "at_least_7y_old", + "short": "7y+", + "long": "At Least 7 Years Old" + }, + "_8y": { + "id": "at_least_8y_old", + "short": "8y+", + "long": "At Least 8 Years Old" + }, + "_10y": { + "id": "at_least_10y_old", + "short": "10y+", + "long": "At Least 10 Years Old" + }, + "_12y": { + "id": "at_least_12y_old", + "short": "12y+", + "long": "At Least 12 Years Old" + } +} + +AMOUNT_RANGE_NAMES: Final = { + "_0sats": { + "id": "with_0sats", + "short": "0 sats", + "long": "0 Sats" + }, + "_1sat_to_10sats": { + "id": "above_1sat_under_10sats", + "short": "1-10 sats", + "long": "1 to 10 Sats" + }, + "_10sats_to_100sats": { + "id": "above_10sats_under_100sats", + "short": "10-100 sats", + "long": "10 to 100 Sats" + }, + "_100sats_to_1k_sats": { + "id": "above_100sats_under_1k_sats", + "short": "100-1k sats", + "long": "100 to 1K Sats" + }, + "_1k_sats_to_10k_sats": { + "id": "above_1k_sats_under_10k_sats", + "short": "1k-10k sats", + "long": "1K to 10K Sats" + }, + "_10k_sats_to_100k_sats": { + "id": "above_10k_sats_under_100k_sats", + "short": "10k-100k sats", + "long": "10K to 100K Sats" + }, + "_100k_sats_to_1m_sats": { + "id": "above_100k_sats_under_1m_sats", + "short": "100k-1M sats", + "long": "100K to 1M Sats" + }, + "_1m_sats_to_10m_sats": { + "id": "above_1m_sats_under_10m_sats", + "short": "1M-10M sats", + "long": "1M to 10M Sats" + }, + "_10m_sats_to_1btc": { + "id": "above_10m_sats_under_1btc", + "short": "0.1-1 BTC", + "long": "0.1 to 1 BTC" + }, + "_1btc_to_10btc": { + "id": "above_1btc_under_10btc", + "short": "1-10 BTC", + "long": "1 to 10 BTC" + }, + "_10btc_to_100btc": { + "id": "above_10btc_under_100btc", + "short": "10-100 BTC", + "long": "10 to 100 BTC" + }, + "_100btc_to_1k_btc": { + "id": "above_100btc_under_1k_btc", + "short": "100-1k BTC", + "long": "100 to 1K BTC" + }, + "_1k_btc_to_10k_btc": { + "id": "above_1k_btc_under_10k_btc", + "short": "1k-10k BTC", + "long": "1K to 10K BTC" + }, + "_10k_btc_to_100k_btc": { + "id": "above_10k_btc_under_100k_btc", + "short": "10k-100k BTC", + "long": "10K to 100K BTC" + }, + "_100k_btc_or_more": { + "id": "above_100k_btc", + "short": "100k+ BTC", + "long": "100K+ BTC" + } +} + +GE_AMOUNT_NAMES: Final = { + "_1sat": { + "id": "above_1sat", + "short": "1+ sats", + "long": "Above 1 Sat" + }, + "_10sats": { + "id": "above_10sats", + "short": "10+ sats", + "long": "Above 10 Sats" + }, + "_100sats": { + "id": "above_100sats", + "short": "100+ sats", + "long": "Above 100 Sats" + }, + "_1k_sats": { + "id": "above_1k_sats", + "short": "1k+ sats", + "long": "Above 1K Sats" + }, + "_10k_sats": { + "id": "above_10k_sats", + "short": "10k+ sats", + "long": "Above 10K Sats" + }, + "_100k_sats": { + "id": "above_100k_sats", + "short": "100k+ sats", + "long": "Above 100K Sats" + }, + "_1m_sats": { + "id": "above_1m_sats", + "short": "1M+ sats", + "long": "Above 1M Sats" + }, + "_10m_sats": { + "id": "above_10m_sats", + "short": "0.1+ BTC", + "long": "Above 0.1 BTC" + }, + "_1btc": { + "id": "above_1btc", + "short": "1+ BTC", + "long": "Above 1 BTC" + }, + "_10btc": { + "id": "above_10btc", + "short": "10+ BTC", + "long": "Above 10 BTC" + }, + "_100btc": { + "id": "above_100btc", + "short": "100+ BTC", + "long": "Above 100 BTC" + }, + "_1k_btc": { + "id": "above_1k_btc", + "short": "1k+ BTC", + "long": "Above 1K BTC" + }, + "_10k_btc": { + "id": "above_10k_btc", + "short": "10k+ BTC", + "long": "Above 10K BTC" + } +} + +LT_AMOUNT_NAMES: Final = { + "_10sats": { + "id": "under_10sats", + "short": "<10 sats", + "long": "Under 10 Sats" + }, + "_100sats": { + "id": "under_100sats", + "short": "<100 sats", + "long": "Under 100 Sats" + }, + "_1k_sats": { + "id": "under_1k_sats", + "short": "<1k sats", + "long": "Under 1K Sats" + }, + "_10k_sats": { + "id": "under_10k_sats", + "short": "<10k sats", + "long": "Under 10K Sats" + }, + "_100k_sats": { + "id": "under_100k_sats", + "short": "<100k sats", + "long": "Under 100K Sats" + }, + "_1m_sats": { + "id": "under_1m_sats", + "short": "<1M sats", + "long": "Under 1M Sats" + }, + "_10m_sats": { + "id": "under_10m_sats", + "short": "<0.1 BTC", + "long": "Under 0.1 BTC" + }, + "_1btc": { + "id": "under_1btc", + "short": "<1 BTC", + "long": "Under 1 BTC" + }, + "_10btc": { + "id": "under_10btc", + "short": "<10 BTC", + "long": "Under 10 BTC" + }, + "_100btc": { + "id": "under_100btc", + "short": "<100 BTC", + "long": "Under 100 BTC" + }, + "_1k_btc": { + "id": "under_1k_btc", + "short": "<1k BTC", + "long": "Under 1K BTC" + }, + "_10k_btc": { + "id": "under_10k_btc", + "short": "<10k BTC", + "long": "Under 10K BTC" + }, + "_100k_btc": { + "id": "under_100k_btc", + "short": "<100k BTC", + "long": "Under 100K BTC" + } +} + # Type definitions Address = str @@ -701,9 +1398,15 @@ class MetricNode(Generic[T]): """Fetch all data points for this metric.""" return self._client.get(self._path) - def get_range(self, from_date: str, to_date: str) -> List[T]: - """Fetch data points within a date range.""" - return self._client.get(f"{self._path}?from={from_date}&to={to_date}") + def get_range(self, from_val: Optional[str] = None, to_val: Optional[str] = None) -> List[T]: + """Fetch data points within a range.""" + params = [] + if from_val is not None: + params.append(f"from={from_val}") + if to_val is not None: + params.append(f"to={to_val}") + query = "&".join(params) + return self._client.get(f"{self._path}?{query}" if query else self._path) # Index accessor classes @@ -1154,6 +1857,24 @@ class Ratio1ySdPattern: self.sma: Indexes[StoredF32] = Indexes(client, f'{base_path}/sma') self.zscore: Indexes[StoredF32] = Indexes(client, f'{base_path}/zscore') +class AXbtPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, base_path: str): + self._1d_dominance: BlockCountPattern[StoredF32] = BlockCountPattern(client, f'{base_path}/1d_dominance') + self._1m_blocks_mined: Indexes[StoredU32] = Indexes(client, f'{base_path}/1m_blocks_mined') + self._1m_dominance: Indexes[StoredF32] = Indexes(client, f'{base_path}/1m_dominance') + self._1w_blocks_mined: Indexes[StoredU32] = Indexes(client, f'{base_path}/1w_blocks_mined') + self._1w_dominance: Indexes[StoredF32] = Indexes(client, f'{base_path}/1w_dominance') + self._1y_blocks_mined: Indexes[StoredU32] = Indexes(client, f'{base_path}/1y_blocks_mined') + self._1y_dominance: Indexes[StoredF32] = Indexes(client, f'{base_path}/1y_dominance') + self.blocks_mined: BlockCountPattern[StoredU32] = BlockCountPattern(client, f'{base_path}/blocks_mined') + self.coinbase: UnclaimedRewardsPattern = UnclaimedRewardsPattern(client, f'{base_path}/coinbase') + self.days_since_block: Indexes[StoredU16] = Indexes(client, f'{base_path}/days_since_block') + self.dominance: BlockCountPattern[StoredF32] = BlockCountPattern(client, f'{base_path}/dominance') + self.fee: FeePattern2 = FeePattern2(client, f'{base_path}/fee') + self.subsidy: FeePattern2 = FeePattern2(client, f'{base_path}/subsidy') + class ActivePriceRatioPattern: """Pattern struct for repeated tree structure.""" @@ -1172,24 +1893,6 @@ class ActivePriceRatioPattern: self.ratio_pct99: Indexes[StoredF32] = Indexes(client, f'{base_path}/ratio_pct99') self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, f'{base_path}/ratio_sd') -class AXbtPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, base_path: str): - self._1d_dominance: BlockCountPattern[StoredF32] = BlockCountPattern(client, f'{base_path}/1d_dominance') - self._1m_blocks_mined: Indexes[StoredU32] = Indexes(client, f'{base_path}/1m_blocks_mined') - self._1m_dominance: Indexes[StoredF32] = Indexes(client, f'{base_path}/1m_dominance') - self._1w_blocks_mined: Indexes[StoredU32] = Indexes(client, f'{base_path}/1w_blocks_mined') - self._1w_dominance: Indexes[StoredF32] = Indexes(client, f'{base_path}/1w_dominance') - self._1y_blocks_mined: Indexes[StoredU32] = Indexes(client, f'{base_path}/1y_blocks_mined') - self._1y_dominance: Indexes[StoredF32] = Indexes(client, f'{base_path}/1y_dominance') - self.blocks_mined: BlockCountPattern[StoredU32] = BlockCountPattern(client, f'{base_path}/blocks_mined') - self.coinbase: UnclaimedRewardsPattern = UnclaimedRewardsPattern(client, f'{base_path}/coinbase') - self.days_since_block: Indexes[StoredU16] = Indexes(client, f'{base_path}/days_since_block') - self.dominance: BlockCountPattern[StoredF32] = BlockCountPattern(client, f'{base_path}/dominance') - self.fee: UnclaimedRewardsPattern = UnclaimedRewardsPattern(client, f'{base_path}/fee') - self.subsidy: UnclaimedRewardsPattern = UnclaimedRewardsPattern(client, f'{base_path}/subsidy') - class BitcoinPattern(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -1221,6 +1924,20 @@ class BlockSizePattern(Generic[T]): self.pct90: Indexes5[T] = Indexes5(client, f'{base_path}/pct90') self.sum: Indexes4[T] = Indexes4(client, f'{base_path}/sum') +class RelativePattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, base_path: str): + self.neg_unrealized_loss_rel_to_market_cap: Indexes27[StoredF32] = Indexes27(client, f'{base_path}/neg_unrealized_loss_rel_to_market_cap') + self.net_unrealized_pnl_rel_to_market_cap: Indexes26[StoredF32] = Indexes26(client, f'{base_path}/net_unrealized_pnl_rel_to_market_cap') + self.supply_in_loss_rel_to_circulating_supply: Indexes27[StoredF64] = Indexes27(client, f'{base_path}/supply_in_loss_rel_to_circulating_supply') + self.supply_in_loss_rel_to_own_supply: Indexes27[StoredF64] = Indexes27(client, f'{base_path}/supply_in_loss_rel_to_own_supply') + self.supply_in_profit_rel_to_circulating_supply: Indexes27[StoredF64] = Indexes27(client, f'{base_path}/supply_in_profit_rel_to_circulating_supply') + self.supply_in_profit_rel_to_own_supply: Indexes27[StoredF64] = Indexes27(client, f'{base_path}/supply_in_profit_rel_to_own_supply') + self.supply_rel_to_circulating_supply: Indexes[StoredF64] = Indexes(client, f'{base_path}/supply_rel_to_circulating_supply') + self.unrealized_loss_rel_to_market_cap: Indexes27[StoredF32] = Indexes27(client, f'{base_path}/unrealized_loss_rel_to_market_cap') + self.unrealized_profit_rel_to_market_cap: Indexes27[StoredF32] = Indexes27(client, f'{base_path}/unrealized_profit_rel_to_market_cap') + class UnrealizedPattern: """Pattern struct for repeated tree structure.""" @@ -1235,19 +1952,19 @@ class UnrealizedPattern: self.unrealized_loss: Indexes26[Dollars] = Indexes26(client, f'{base_path}/unrealized_loss') self.unrealized_profit: Indexes26[Dollars] = Indexes26(client, f'{base_path}/unrealized_profit') -class RelativePattern: +class Constant0Pattern(Generic[T]): """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, base_path: str): - self.neg_unrealized_loss_rel_to_market_cap: Indexes27[StoredF32] = Indexes27(client, f'{base_path}/neg_unrealized_loss_rel_to_market_cap') - self.net_unrealized_pnl_rel_to_market_cap: Indexes26[StoredF32] = Indexes26(client, f'{base_path}/net_unrealized_pnl_rel_to_market_cap') - self.supply_in_loss_rel_to_circulating_supply: Indexes27[StoredF64] = Indexes27(client, f'{base_path}/supply_in_loss_rel_to_circulating_supply') - self.supply_in_loss_rel_to_own_supply: Indexes27[StoredF64] = Indexes27(client, f'{base_path}/supply_in_loss_rel_to_own_supply') - self.supply_in_profit_rel_to_circulating_supply: Indexes27[StoredF64] = Indexes27(client, f'{base_path}/supply_in_profit_rel_to_circulating_supply') - self.supply_in_profit_rel_to_own_supply: Indexes27[StoredF64] = Indexes27(client, f'{base_path}/supply_in_profit_rel_to_own_supply') - self.supply_rel_to_circulating_supply: Indexes[StoredF64] = Indexes(client, f'{base_path}/supply_rel_to_circulating_supply') - self.unrealized_loss_rel_to_market_cap: Indexes27[StoredF32] = Indexes27(client, f'{base_path}/unrealized_loss_rel_to_market_cap') - self.unrealized_profit_rel_to_market_cap: Indexes27[StoredF32] = Indexes27(client, f'{base_path}/unrealized_profit_rel_to_market_cap') + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.dateindex: Indexes5[T] = Indexes5(client, f'/{acc}') + self.decadeindex: Indexes7[T] = Indexes7(client, f'/{acc}') + self.height: Indexes2[T] = Indexes2(client, f'/{acc}') + self.monthindex: Indexes8[T] = Indexes8(client, f'/{acc}') + self.quarterindex: Indexes9[T] = Indexes9(client, f'/{acc}') + self.semesterindex: Indexes10[T] = Indexes10(client, f'/{acc}') + self.weekindex: Indexes11[T] = Indexes11(client, f'/{acc}') + self.yearindex: Indexes12[T] = Indexes12(client, f'/{acc}') class AddresstypeToHeightToAddrCountPattern(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -1276,20 +1993,6 @@ class BlockIntervalPattern(Generic[T]): self.pct75: Indexes2[T] = Indexes2(client, f'/{acc}_pct75') self.pct90: Indexes2[T] = Indexes2(client, f'/{acc}_pct90') -class Constant0Pattern(Generic[T]): - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.dateindex: Indexes5[T] = Indexes5(client, f'/{acc}') - self.decadeindex: Indexes7[T] = Indexes7(client, f'/{acc}') - self.height: Indexes2[T] = Indexes2(client, f'/{acc}') - self.monthindex: Indexes8[T] = Indexes8(client, f'/{acc}') - self.quarterindex: Indexes9[T] = Indexes9(client, f'/{acc}') - self.semesterindex: Indexes10[T] = Indexes10(client, f'/{acc}') - self.weekindex: Indexes11[T] = Indexes11(client, f'/{acc}') - self.yearindex: Indexes12[T] = Indexes12(client, f'/{acc}') - class _0satsPattern: """Pattern struct for repeated tree structure.""" @@ -1335,6 +2038,16 @@ class _10yTo12yPattern: self.supply: SupplyPattern2 = SupplyPattern2(client, f'{base_path}/supply') self.unrealized: UnrealizedPattern = UnrealizedPattern(client, f'{base_path}/unrealized') +class ActivityPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, base_path: str): + self.coinblocks_destroyed: BlockCountPattern[StoredF64] = BlockCountPattern(client, f'{base_path}/coinblocks_destroyed') + self.coindays_destroyed: BlockCountPattern[StoredF64] = BlockCountPattern(client, f'{base_path}/coindays_destroyed') + self.satblocks_destroyed: Indexes2[Sats] = Indexes2(client, f'{base_path}/satblocks_destroyed') + self.satdays_destroyed: Indexes2[Sats] = Indexes2(client, f'{base_path}/satdays_destroyed') + self.sent: FeePattern2 = FeePattern2(client, f'{base_path}/sent') + class SupplyPattern2: """Pattern struct for repeated tree structure.""" @@ -1345,17 +2058,7 @@ class SupplyPattern2: self.supply_value: SupplyValuePattern = SupplyValuePattern(client, f'{base_path}/supply_value') self.utxo_count: Indexes3[StoredU64] = Indexes3(client, f'{base_path}/utxo_count') -class ActivityPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, base_path: str): - self.coinblocks_destroyed: BlockCountPattern[StoredF64] = BlockCountPattern(client, f'{base_path}/coinblocks_destroyed') - self.coindays_destroyed: BlockCountPattern[StoredF64] = BlockCountPattern(client, f'{base_path}/coindays_destroyed') - self.satblocks_destroyed: Indexes2[Sats] = Indexes2(client, f'{base_path}/satblocks_destroyed') - self.satdays_destroyed: Indexes2[Sats] = Indexes2(client, f'{base_path}/satdays_destroyed') - self.sent: SentPattern = SentPattern(client, f'{base_path}/sent') - -class SentPattern: +class FeePattern2: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, base_path: str): @@ -1373,22 +2076,6 @@ class SupplyPattern: self.dollars: Indexes[Dollars] = Indexes(client, f'{base_path}/dollars') self.sats: Indexes[Sats] = Indexes(client, f'{base_path}/sats') -class CoinbasePattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, base_path: str): - self.bitcoin: BitcoinPattern[Bitcoin] = BitcoinPattern(client, f'{base_path}/bitcoin') - self.dollars: BitcoinPattern[Dollars] = BitcoinPattern(client, f'{base_path}/dollars') - self.sats: BitcoinPattern[Sats] = BitcoinPattern(client, f'{base_path}/sats') - -class ActiveSupplyPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, base_path: str): - self.bitcoin: Indexes3[Bitcoin] = Indexes3(client, f'{base_path}/bitcoin') - self.dollars: Indexes3[Dollars] = Indexes3(client, f'{base_path}/dollars') - self.sats: Indexes3[Sats] = Indexes3(client, f'{base_path}/sats') - class UnclaimedRewardsPattern: """Pattern struct for repeated tree structure.""" @@ -1405,6 +2092,22 @@ class PricePaidPattern2: self.min_price_paid: Indexes3[Dollars] = Indexes3(client, f'{base_path}/min_price_paid') self.price_percentiles: PricePercentilesPattern = PricePercentilesPattern(client, f'{base_path}/price_percentiles') +class CoinbasePattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, base_path: str): + self.bitcoin: BitcoinPattern[Bitcoin] = BitcoinPattern(client, f'{base_path}/bitcoin') + self.dollars: BitcoinPattern[Dollars] = BitcoinPattern(client, f'{base_path}/dollars') + self.sats: BitcoinPattern[Sats] = BitcoinPattern(client, f'{base_path}/sats') + +class ActiveSupplyPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, base_path: str): + self.bitcoin: Indexes3[Bitcoin] = Indexes3(client, f'{base_path}/bitcoin') + self.dollars: Indexes3[Dollars] = Indexes3(client, f'{base_path}/dollars') + self.sats: Indexes3[Sats] = Indexes3(client, f'{base_path}/sats') + class BlockCountPattern(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -1413,13 +2116,6 @@ class BlockCountPattern(Generic[T]): self.cumulative: Indexes3[T] = Indexes3(client, f'{base_path}/cumulative') self.sum: Indexes4[T] = Indexes4(client, f'{base_path}/sum') -class SupplyValuePattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, base_path: str): - self.bitcoin: Indexes2[Bitcoin] = Indexes2(client, f'{base_path}/bitcoin') - self.dollars: Indexes2[Dollars] = Indexes2(client, f'{base_path}/dollars') - class PricePaidPattern: """Pattern struct for repeated tree structure.""" @@ -1427,13 +2123,6 @@ class PricePaidPattern: self.max_price_paid: Indexes3[Dollars] = Indexes3(client, f'{base_path}/max_price_paid') self.min_price_paid: Indexes3[Dollars] = Indexes3(client, f'{base_path}/min_price_paid') -class SatsPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, base_path: str): - self.cumulative: Indexes3[Sats] = Indexes3(client, f'{base_path}/cumulative') - self.sum: Indexes4[Sats] = Indexes4(client, f'{base_path}/sum') - class _1dReturns1mSdPattern: """Pattern struct for repeated tree structure.""" @@ -1442,6 +2131,20 @@ class _1dReturns1mSdPattern: self.sd: Indexes[StoredF32] = Indexes(client, f'/{acc}_sd') self.sma: Indexes[StoredF32] = Indexes(client, f'/{acc}_sma') +class SupplyValuePattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, base_path: str): + self.bitcoin: Indexes2[Bitcoin] = Indexes2(client, f'{base_path}/bitcoin') + self.dollars: Indexes2[Dollars] = Indexes2(client, f'{base_path}/dollars') + +class SatsPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, base_path: str): + self.cumulative: Indexes3[Sats] = Indexes3(client, f'{base_path}/cumulative') + self.sum: Indexes4[Sats] = Indexes4(client, f'{base_path}/sum') + class BitcoinPattern2(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -1886,164 +2589,164 @@ class CatalogTree_Computed_Pools_Vecs: """Catalog tree node.""" def __init__(self, client: BrkClientBase, base_path: str = ''): - self.AXbt: AXbtPattern = AXbtPattern(client, f'{base_path}/AXbt') - self.AaoPool: AXbtPattern = AXbtPattern(client, f'{base_path}/AaoPool') - self.AntPool: AXbtPattern = AXbtPattern(client, f'{base_path}/AntPool') - self.ArkPool: AXbtPattern = AXbtPattern(client, f'{base_path}/ArkPool') - self.AsicMiner: AXbtPattern = AXbtPattern(client, f'{base_path}/AsicMiner') - self.BatPool: AXbtPattern = AXbtPattern(client, f'{base_path}/BatPool') - self.BcMonster: AXbtPattern = AXbtPattern(client, f'{base_path}/BcMonster') - self.BcpoolIo: AXbtPattern = AXbtPattern(client, f'{base_path}/BcpoolIo') - self.BinancePool: AXbtPattern = AXbtPattern(client, f'{base_path}/BinancePool') - self.BitClub: AXbtPattern = AXbtPattern(client, f'{base_path}/BitClub') - self.BitFuFuPool: AXbtPattern = AXbtPattern(client, f'{base_path}/BitFuFuPool') - self.BitFury: AXbtPattern = AXbtPattern(client, f'{base_path}/BitFury') - self.BitMinter: AXbtPattern = AXbtPattern(client, f'{base_path}/BitMinter') - self.Bitalo: AXbtPattern = AXbtPattern(client, f'{base_path}/Bitalo') - self.BitcoinAffiliateNetwork: AXbtPattern = AXbtPattern(client, f'{base_path}/BitcoinAffiliateNetwork') - self.BitcoinCom: AXbtPattern = AXbtPattern(client, f'{base_path}/BitcoinCom') - self.BitcoinIndia: AXbtPattern = AXbtPattern(client, f'{base_path}/BitcoinIndia') - self.BitcoinRussia: AXbtPattern = AXbtPattern(client, f'{base_path}/BitcoinRussia') - self.BitcoinUkraine: AXbtPattern = AXbtPattern(client, f'{base_path}/BitcoinUkraine') - self.Bitfarms: AXbtPattern = AXbtPattern(client, f'{base_path}/Bitfarms') - self.Bitparking: AXbtPattern = AXbtPattern(client, f'{base_path}/Bitparking') - self.Bitsolo: AXbtPattern = AXbtPattern(client, f'{base_path}/Bitsolo') - self.Bixin: AXbtPattern = AXbtPattern(client, f'{base_path}/Bixin') - self.BlockFills: AXbtPattern = AXbtPattern(client, f'{base_path}/BlockFills') - self.BraiinsPool: AXbtPattern = AXbtPattern(client, f'{base_path}/BraiinsPool') - self.BravoMining: AXbtPattern = AXbtPattern(client, f'{base_path}/BravoMining') - self.BtPool: AXbtPattern = AXbtPattern(client, f'{base_path}/BtPool') - self.BtcCom: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcCom') - self.BtcDig: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcDig') - self.BtcGuild: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcGuild') - self.BtcLab: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcLab') - self.BtcMp: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcMp') - self.BtcNuggets: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcNuggets') - self.BtcPoolParty: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcPoolParty') - self.BtcServ: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcServ') - self.BtcTop: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcTop') - self.Btcc: AXbtPattern = AXbtPattern(client, f'{base_path}/Btcc') - self.BwPool: AXbtPattern = AXbtPattern(client, f'{base_path}/BwPool') - self.BytePool: AXbtPattern = AXbtPattern(client, f'{base_path}/BytePool') - self.Canoe: AXbtPattern = AXbtPattern(client, f'{base_path}/Canoe') - self.CanoePool: AXbtPattern = AXbtPattern(client, f'{base_path}/CanoePool') - self.CarbonNegative: AXbtPattern = AXbtPattern(client, f'{base_path}/CarbonNegative') - self.CkPool: AXbtPattern = AXbtPattern(client, f'{base_path}/CkPool') - self.CloudHashing: AXbtPattern = AXbtPattern(client, f'{base_path}/CloudHashing') - self.CoinLab: AXbtPattern = AXbtPattern(client, f'{base_path}/CoinLab') - self.Cointerra: AXbtPattern = AXbtPattern(client, f'{base_path}/Cointerra') - self.ConnectBtc: AXbtPattern = AXbtPattern(client, f'{base_path}/ConnectBtc') - self.DPool: AXbtPattern = AXbtPattern(client, f'{base_path}/DPool') - self.DcExploration: AXbtPattern = AXbtPattern(client, f'{base_path}/DcExploration') - self.Dcex: AXbtPattern = AXbtPattern(client, f'{base_path}/Dcex') - self.DigitalBtc: AXbtPattern = AXbtPattern(client, f'{base_path}/DigitalBtc') - self.DigitalXMintsy: AXbtPattern = AXbtPattern(client, f'{base_path}/DigitalXMintsy') - self.EclipseMc: AXbtPattern = AXbtPattern(client, f'{base_path}/EclipseMc') - self.EightBaochi: AXbtPattern = AXbtPattern(client, f'{base_path}/EightBaochi') - self.EkanemBtc: AXbtPattern = AXbtPattern(client, f'{base_path}/EkanemBtc') - self.Eligius: AXbtPattern = AXbtPattern(client, f'{base_path}/Eligius') - self.EmcdPool: AXbtPattern = AXbtPattern(client, f'{base_path}/EmcdPool') - self.EntrustCharityPool: AXbtPattern = AXbtPattern(client, f'{base_path}/EntrustCharityPool') - self.Eobot: AXbtPattern = AXbtPattern(client, f'{base_path}/Eobot') - self.ExxBw: AXbtPattern = AXbtPattern(client, f'{base_path}/ExxBw') - self.F2Pool: AXbtPattern = AXbtPattern(client, f'{base_path}/F2Pool') - self.FiftyEightCoin: AXbtPattern = AXbtPattern(client, f'{base_path}/FiftyEightCoin') - self.FoundryUsa: AXbtPattern = AXbtPattern(client, f'{base_path}/FoundryUsa') - self.FutureBitApolloSolo: AXbtPattern = AXbtPattern(client, f'{base_path}/FutureBitApolloSolo') - self.GbMiners: AXbtPattern = AXbtPattern(client, f'{base_path}/GbMiners') - self.GhashIo: AXbtPattern = AXbtPattern(client, f'{base_path}/GhashIo') - self.GiveMeCoins: AXbtPattern = AXbtPattern(client, f'{base_path}/GiveMeCoins') - self.GoGreenLight: AXbtPattern = AXbtPattern(client, f'{base_path}/GoGreenLight') - self.HaoZhuZhu: AXbtPattern = AXbtPattern(client, f'{base_path}/HaoZhuZhu') - self.Haominer: AXbtPattern = AXbtPattern(client, f'{base_path}/Haominer') - self.HashBx: AXbtPattern = AXbtPattern(client, f'{base_path}/HashBx') - self.HashPool: AXbtPattern = AXbtPattern(client, f'{base_path}/HashPool') - self.Helix: AXbtPattern = AXbtPattern(client, f'{base_path}/Helix') - self.Hhtt: AXbtPattern = AXbtPattern(client, f'{base_path}/Hhtt') - self.HotPool: AXbtPattern = AXbtPattern(client, f'{base_path}/HotPool') - self.Hummerpool: AXbtPattern = AXbtPattern(client, f'{base_path}/Hummerpool') - self.HuobiPool: AXbtPattern = AXbtPattern(client, f'{base_path}/HuobiPool') - self.InnopolisTech: AXbtPattern = AXbtPattern(client, f'{base_path}/InnopolisTech') - self.KanoPool: AXbtPattern = AXbtPattern(client, f'{base_path}/KanoPool') - self.KncMiner: AXbtPattern = AXbtPattern(client, f'{base_path}/KncMiner') - self.KuCoinPool: AXbtPattern = AXbtPattern(client, f'{base_path}/KuCoinPool') - self.LubianCom: AXbtPattern = AXbtPattern(client, f'{base_path}/LubianCom') - self.LuckyPool: AXbtPattern = AXbtPattern(client, f'{base_path}/LuckyPool') - self.Luxor: AXbtPattern = AXbtPattern(client, f'{base_path}/Luxor') - self.MaraPool: AXbtPattern = AXbtPattern(client, f'{base_path}/MaraPool') - self.MaxBtc: AXbtPattern = AXbtPattern(client, f'{base_path}/MaxBtc') - self.MaxiPool: AXbtPattern = AXbtPattern(client, f'{base_path}/MaxiPool') - self.MegaBigPower: AXbtPattern = AXbtPattern(client, f'{base_path}/MegaBigPower') - self.Minerium: AXbtPattern = AXbtPattern(client, f'{base_path}/Minerium') - self.MiningCity: AXbtPattern = AXbtPattern(client, f'{base_path}/MiningCity') - self.MiningDutch: AXbtPattern = AXbtPattern(client, f'{base_path}/MiningDutch') - self.MiningKings: AXbtPattern = AXbtPattern(client, f'{base_path}/MiningKings') - self.MiningSquared: AXbtPattern = AXbtPattern(client, f'{base_path}/MiningSquared') - self.Mmpool: AXbtPattern = AXbtPattern(client, f'{base_path}/Mmpool') - self.MtRed: AXbtPattern = AXbtPattern(client, f'{base_path}/MtRed') - self.MultiCoinCo: AXbtPattern = AXbtPattern(client, f'{base_path}/MultiCoinCo') - self.Multipool: AXbtPattern = AXbtPattern(client, f'{base_path}/Multipool') - self.MyBtcCoinPool: AXbtPattern = AXbtPattern(client, f'{base_path}/MyBtcCoinPool') - self.Neopool: AXbtPattern = AXbtPattern(client, f'{base_path}/Neopool') - self.Nexious: AXbtPattern = AXbtPattern(client, f'{base_path}/Nexious') - self.NiceHash: AXbtPattern = AXbtPattern(client, f'{base_path}/NiceHash') - self.NmcBit: AXbtPattern = AXbtPattern(client, f'{base_path}/NmcBit') - self.NovaBlock: AXbtPattern = AXbtPattern(client, f'{base_path}/NovaBlock') - self.Ocean: AXbtPattern = AXbtPattern(client, f'{base_path}/Ocean') - self.OkExPool: AXbtPattern = AXbtPattern(client, f'{base_path}/OkExPool') - self.OkMiner: AXbtPattern = AXbtPattern(client, f'{base_path}/OkMiner') - self.Okkong: AXbtPattern = AXbtPattern(client, f'{base_path}/Okkong') - self.OkpoolTop: AXbtPattern = AXbtPattern(client, f'{base_path}/OkpoolTop') - self.OneHash: AXbtPattern = AXbtPattern(client, f'{base_path}/OneHash') - self.OneM1x: AXbtPattern = AXbtPattern(client, f'{base_path}/OneM1x') - self.OneThash: AXbtPattern = AXbtPattern(client, f'{base_path}/OneThash') - self.OzCoin: AXbtPattern = AXbtPattern(client, f'{base_path}/OzCoin') - self.PHashIo: AXbtPattern = AXbtPattern(client, f'{base_path}/PHashIo') - self.Parasite: AXbtPattern = AXbtPattern(client, f'{base_path}/Parasite') - self.Patels: AXbtPattern = AXbtPattern(client, f'{base_path}/Patels') - self.PegaPool: AXbtPattern = AXbtPattern(client, f'{base_path}/PegaPool') - self.Phoenix: AXbtPattern = AXbtPattern(client, f'{base_path}/Phoenix') - self.Polmine: AXbtPattern = AXbtPattern(client, f'{base_path}/Polmine') - self.Pool175btc: AXbtPattern = AXbtPattern(client, f'{base_path}/Pool175btc') - self.Pool50btc: AXbtPattern = AXbtPattern(client, f'{base_path}/Pool50btc') - self.Poolin: AXbtPattern = AXbtPattern(client, f'{base_path}/Poolin') - self.PortlandHodl: AXbtPattern = AXbtPattern(client, f'{base_path}/PortlandHodl') - self.PublicPool: AXbtPattern = AXbtPattern(client, f'{base_path}/PublicPool') - self.PureBtcCom: AXbtPattern = AXbtPattern(client, f'{base_path}/PureBtcCom') - self.Rawpool: AXbtPattern = AXbtPattern(client, f'{base_path}/Rawpool') - self.RigPool: AXbtPattern = AXbtPattern(client, f'{base_path}/RigPool') - self.SbiCrypto: AXbtPattern = AXbtPattern(client, f'{base_path}/SbiCrypto') - self.SecPool: AXbtPattern = AXbtPattern(client, f'{base_path}/SecPool') - self.SecretSuperstar: AXbtPattern = AXbtPattern(client, f'{base_path}/SecretSuperstar') - self.SevenPool: AXbtPattern = AXbtPattern(client, f'{base_path}/SevenPool') - self.ShawnP0wers: AXbtPattern = AXbtPattern(client, f'{base_path}/ShawnP0wers') - self.SigmapoolCom: AXbtPattern = AXbtPattern(client, f'{base_path}/SigmapoolCom') - self.SimplecoinUs: AXbtPattern = AXbtPattern(client, f'{base_path}/SimplecoinUs') - self.SoloCk: AXbtPattern = AXbtPattern(client, f'{base_path}/SoloCk') - self.SpiderPool: AXbtPattern = AXbtPattern(client, f'{base_path}/SpiderPool') - self.StMiningCorp: AXbtPattern = AXbtPattern(client, f'{base_path}/StMiningCorp') - self.Tangpool: AXbtPattern = AXbtPattern(client, f'{base_path}/Tangpool') - self.TatmasPool: AXbtPattern = AXbtPattern(client, f'{base_path}/TatmasPool') - self.TbDice: AXbtPattern = AXbtPattern(client, f'{base_path}/TbDice') - self.Telco214: AXbtPattern = AXbtPattern(client, f'{base_path}/Telco214') - self.TerraPool: AXbtPattern = AXbtPattern(client, f'{base_path}/TerraPool') - self.Tiger: AXbtPattern = AXbtPattern(client, f'{base_path}/Tiger') - self.TigerpoolNet: AXbtPattern = AXbtPattern(client, f'{base_path}/TigerpoolNet') - self.Titan: AXbtPattern = AXbtPattern(client, f'{base_path}/Titan') - self.TransactionCoinMining: AXbtPattern = AXbtPattern(client, f'{base_path}/TransactionCoinMining') - self.TrickysBtcPool: AXbtPattern = AXbtPattern(client, f'{base_path}/TrickysBtcPool') - self.TripleMining: AXbtPattern = AXbtPattern(client, f'{base_path}/TripleMining') - self.TwentyOneInc: AXbtPattern = AXbtPattern(client, f'{base_path}/TwentyOneInc') - self.UltimusPool: AXbtPattern = AXbtPattern(client, f'{base_path}/UltimusPool') - self.Unknown: AXbtPattern = AXbtPattern(client, f'{base_path}/Unknown') - self.Unomp: AXbtPattern = AXbtPattern(client, f'{base_path}/Unomp') - self.ViaBtc: AXbtPattern = AXbtPattern(client, f'{base_path}/ViaBtc') - self.Waterhole: AXbtPattern = AXbtPattern(client, f'{base_path}/Waterhole') - self.WayiCn: AXbtPattern = AXbtPattern(client, f'{base_path}/WayiCn') - self.WhitePool: AXbtPattern = AXbtPattern(client, f'{base_path}/WhitePool') - self.Wk057: AXbtPattern = AXbtPattern(client, f'{base_path}/Wk057') - self.YourbtcNet: AXbtPattern = AXbtPattern(client, f'{base_path}/YourbtcNet') - self.Zulupool: AXbtPattern = AXbtPattern(client, f'{base_path}/Zulupool') + self.axbt: AXbtPattern = AXbtPattern(client, f'{base_path}/AXbt') + self.aaopool: AXbtPattern = AXbtPattern(client, f'{base_path}/AaoPool') + self.antpool: AXbtPattern = AXbtPattern(client, f'{base_path}/AntPool') + self.arkpool: AXbtPattern = AXbtPattern(client, f'{base_path}/ArkPool') + self.asicminer: AXbtPattern = AXbtPattern(client, f'{base_path}/AsicMiner') + self.batpool: AXbtPattern = AXbtPattern(client, f'{base_path}/BatPool') + self.bcmonster: AXbtPattern = AXbtPattern(client, f'{base_path}/BcMonster') + self.bcpoolio: AXbtPattern = AXbtPattern(client, f'{base_path}/BcpoolIo') + self.binancepool: AXbtPattern = AXbtPattern(client, f'{base_path}/BinancePool') + self.bitclub: AXbtPattern = AXbtPattern(client, f'{base_path}/BitClub') + self.bitfufupool: AXbtPattern = AXbtPattern(client, f'{base_path}/BitFuFuPool') + self.bitfury: AXbtPattern = AXbtPattern(client, f'{base_path}/BitFury') + self.bitminter: AXbtPattern = AXbtPattern(client, f'{base_path}/BitMinter') + self.bitalo: AXbtPattern = AXbtPattern(client, f'{base_path}/Bitalo') + self.bitcoinaffiliatenetwork: AXbtPattern = AXbtPattern(client, f'{base_path}/BitcoinAffiliateNetwork') + self.bitcoincom: AXbtPattern = AXbtPattern(client, f'{base_path}/BitcoinCom') + self.bitcoinindia: AXbtPattern = AXbtPattern(client, f'{base_path}/BitcoinIndia') + self.bitcoinrussia: AXbtPattern = AXbtPattern(client, f'{base_path}/BitcoinRussia') + self.bitcoinukraine: AXbtPattern = AXbtPattern(client, f'{base_path}/BitcoinUkraine') + self.bitfarms: AXbtPattern = AXbtPattern(client, f'{base_path}/Bitfarms') + self.bitparking: AXbtPattern = AXbtPattern(client, f'{base_path}/Bitparking') + self.bitsolo: AXbtPattern = AXbtPattern(client, f'{base_path}/Bitsolo') + self.bixin: AXbtPattern = AXbtPattern(client, f'{base_path}/Bixin') + self.blockfills: AXbtPattern = AXbtPattern(client, f'{base_path}/BlockFills') + self.braiinspool: AXbtPattern = AXbtPattern(client, f'{base_path}/BraiinsPool') + self.bravomining: AXbtPattern = AXbtPattern(client, f'{base_path}/BravoMining') + self.btpool: AXbtPattern = AXbtPattern(client, f'{base_path}/BtPool') + self.btccom: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcCom') + self.btcdig: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcDig') + self.btcguild: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcGuild') + self.btclab: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcLab') + self.btcmp: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcMp') + self.btcnuggets: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcNuggets') + self.btcpoolparty: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcPoolParty') + self.btcserv: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcServ') + self.btctop: AXbtPattern = AXbtPattern(client, f'{base_path}/BtcTop') + self.btcc: AXbtPattern = AXbtPattern(client, f'{base_path}/Btcc') + self.bwpool: AXbtPattern = AXbtPattern(client, f'{base_path}/BwPool') + self.bytepool: AXbtPattern = AXbtPattern(client, f'{base_path}/BytePool') + self.canoe: AXbtPattern = AXbtPattern(client, f'{base_path}/Canoe') + self.canoepool: AXbtPattern = AXbtPattern(client, f'{base_path}/CanoePool') + self.carbonnegative: AXbtPattern = AXbtPattern(client, f'{base_path}/CarbonNegative') + self.ckpool: AXbtPattern = AXbtPattern(client, f'{base_path}/CkPool') + self.cloudhashing: AXbtPattern = AXbtPattern(client, f'{base_path}/CloudHashing') + self.coinlab: AXbtPattern = AXbtPattern(client, f'{base_path}/CoinLab') + self.cointerra: AXbtPattern = AXbtPattern(client, f'{base_path}/Cointerra') + self.connectbtc: AXbtPattern = AXbtPattern(client, f'{base_path}/ConnectBtc') + self.dpool: AXbtPattern = AXbtPattern(client, f'{base_path}/DPool') + self.dcexploration: AXbtPattern = AXbtPattern(client, f'{base_path}/DcExploration') + self.dcex: AXbtPattern = AXbtPattern(client, f'{base_path}/Dcex') + self.digitalbtc: AXbtPattern = AXbtPattern(client, f'{base_path}/DigitalBtc') + self.digitalxmintsy: AXbtPattern = AXbtPattern(client, f'{base_path}/DigitalXMintsy') + self.eclipsemc: AXbtPattern = AXbtPattern(client, f'{base_path}/EclipseMc') + self.eightbaochi: AXbtPattern = AXbtPattern(client, f'{base_path}/EightBaochi') + self.ekanembtc: AXbtPattern = AXbtPattern(client, f'{base_path}/EkanemBtc') + self.eligius: AXbtPattern = AXbtPattern(client, f'{base_path}/Eligius') + self.emcdpool: AXbtPattern = AXbtPattern(client, f'{base_path}/EmcdPool') + self.entrustcharitypool: AXbtPattern = AXbtPattern(client, f'{base_path}/EntrustCharityPool') + self.eobot: AXbtPattern = AXbtPattern(client, f'{base_path}/Eobot') + self.exxbw: AXbtPattern = AXbtPattern(client, f'{base_path}/ExxBw') + self.f2pool: AXbtPattern = AXbtPattern(client, f'{base_path}/F2Pool') + self.fiftyeightcoin: AXbtPattern = AXbtPattern(client, f'{base_path}/FiftyEightCoin') + self.foundryusa: AXbtPattern = AXbtPattern(client, f'{base_path}/FoundryUsa') + self.futurebitapollosolo: AXbtPattern = AXbtPattern(client, f'{base_path}/FutureBitApolloSolo') + self.gbminers: AXbtPattern = AXbtPattern(client, f'{base_path}/GbMiners') + self.ghashio: AXbtPattern = AXbtPattern(client, f'{base_path}/GhashIo') + self.givemecoins: AXbtPattern = AXbtPattern(client, f'{base_path}/GiveMeCoins') + self.gogreenlight: AXbtPattern = AXbtPattern(client, f'{base_path}/GoGreenLight') + self.haozhuzhu: AXbtPattern = AXbtPattern(client, f'{base_path}/HaoZhuZhu') + self.haominer: AXbtPattern = AXbtPattern(client, f'{base_path}/Haominer') + self.hashbx: AXbtPattern = AXbtPattern(client, f'{base_path}/HashBx') + self.hashpool: AXbtPattern = AXbtPattern(client, f'{base_path}/HashPool') + self.helix: AXbtPattern = AXbtPattern(client, f'{base_path}/Helix') + self.hhtt: AXbtPattern = AXbtPattern(client, f'{base_path}/Hhtt') + self.hotpool: AXbtPattern = AXbtPattern(client, f'{base_path}/HotPool') + self.hummerpool: AXbtPattern = AXbtPattern(client, f'{base_path}/Hummerpool') + self.huobipool: AXbtPattern = AXbtPattern(client, f'{base_path}/HuobiPool') + self.innopolistech: AXbtPattern = AXbtPattern(client, f'{base_path}/InnopolisTech') + self.kanopool: AXbtPattern = AXbtPattern(client, f'{base_path}/KanoPool') + self.kncminer: AXbtPattern = AXbtPattern(client, f'{base_path}/KncMiner') + self.kucoinpool: AXbtPattern = AXbtPattern(client, f'{base_path}/KuCoinPool') + self.lubiancom: AXbtPattern = AXbtPattern(client, f'{base_path}/LubianCom') + self.luckypool: AXbtPattern = AXbtPattern(client, f'{base_path}/LuckyPool') + self.luxor: AXbtPattern = AXbtPattern(client, f'{base_path}/Luxor') + self.marapool: AXbtPattern = AXbtPattern(client, f'{base_path}/MaraPool') + self.maxbtc: AXbtPattern = AXbtPattern(client, f'{base_path}/MaxBtc') + self.maxipool: AXbtPattern = AXbtPattern(client, f'{base_path}/MaxiPool') + self.megabigpower: AXbtPattern = AXbtPattern(client, f'{base_path}/MegaBigPower') + self.minerium: AXbtPattern = AXbtPattern(client, f'{base_path}/Minerium') + self.miningcity: AXbtPattern = AXbtPattern(client, f'{base_path}/MiningCity') + self.miningdutch: AXbtPattern = AXbtPattern(client, f'{base_path}/MiningDutch') + self.miningkings: AXbtPattern = AXbtPattern(client, f'{base_path}/MiningKings') + self.miningsquared: AXbtPattern = AXbtPattern(client, f'{base_path}/MiningSquared') + self.mmpool: AXbtPattern = AXbtPattern(client, f'{base_path}/Mmpool') + self.mtred: AXbtPattern = AXbtPattern(client, f'{base_path}/MtRed') + self.multicoinco: AXbtPattern = AXbtPattern(client, f'{base_path}/MultiCoinCo') + self.multipool: AXbtPattern = AXbtPattern(client, f'{base_path}/Multipool') + self.mybtccoinpool: AXbtPattern = AXbtPattern(client, f'{base_path}/MyBtcCoinPool') + self.neopool: AXbtPattern = AXbtPattern(client, f'{base_path}/Neopool') + self.nexious: AXbtPattern = AXbtPattern(client, f'{base_path}/Nexious') + self.nicehash: AXbtPattern = AXbtPattern(client, f'{base_path}/NiceHash') + self.nmcbit: AXbtPattern = AXbtPattern(client, f'{base_path}/NmcBit') + self.novablock: AXbtPattern = AXbtPattern(client, f'{base_path}/NovaBlock') + self.ocean: AXbtPattern = AXbtPattern(client, f'{base_path}/Ocean') + self.okexpool: AXbtPattern = AXbtPattern(client, f'{base_path}/OkExPool') + self.okminer: AXbtPattern = AXbtPattern(client, f'{base_path}/OkMiner') + self.okkong: AXbtPattern = AXbtPattern(client, f'{base_path}/Okkong') + self.okpooltop: AXbtPattern = AXbtPattern(client, f'{base_path}/OkpoolTop') + self.onehash: AXbtPattern = AXbtPattern(client, f'{base_path}/OneHash') + self.onem1x: AXbtPattern = AXbtPattern(client, f'{base_path}/OneM1x') + self.onethash: AXbtPattern = AXbtPattern(client, f'{base_path}/OneThash') + self.ozcoin: AXbtPattern = AXbtPattern(client, f'{base_path}/OzCoin') + self.phashio: AXbtPattern = AXbtPattern(client, f'{base_path}/PHashIo') + self.parasite: AXbtPattern = AXbtPattern(client, f'{base_path}/Parasite') + self.patels: AXbtPattern = AXbtPattern(client, f'{base_path}/Patels') + self.pegapool: AXbtPattern = AXbtPattern(client, f'{base_path}/PegaPool') + self.phoenix: AXbtPattern = AXbtPattern(client, f'{base_path}/Phoenix') + self.polmine: AXbtPattern = AXbtPattern(client, f'{base_path}/Polmine') + self.pool175btc: AXbtPattern = AXbtPattern(client, f'{base_path}/Pool175btc') + self.pool50btc: AXbtPattern = AXbtPattern(client, f'{base_path}/Pool50btc') + self.poolin: AXbtPattern = AXbtPattern(client, f'{base_path}/Poolin') + self.portlandhodl: AXbtPattern = AXbtPattern(client, f'{base_path}/PortlandHodl') + self.publicpool: AXbtPattern = AXbtPattern(client, f'{base_path}/PublicPool') + self.purebtccom: AXbtPattern = AXbtPattern(client, f'{base_path}/PureBtcCom') + self.rawpool: AXbtPattern = AXbtPattern(client, f'{base_path}/Rawpool') + self.rigpool: AXbtPattern = AXbtPattern(client, f'{base_path}/RigPool') + self.sbicrypto: AXbtPattern = AXbtPattern(client, f'{base_path}/SbiCrypto') + self.secpool: AXbtPattern = AXbtPattern(client, f'{base_path}/SecPool') + self.secretsuperstar: AXbtPattern = AXbtPattern(client, f'{base_path}/SecretSuperstar') + self.sevenpool: AXbtPattern = AXbtPattern(client, f'{base_path}/SevenPool') + self.shawnp0wers: AXbtPattern = AXbtPattern(client, f'{base_path}/ShawnP0wers') + self.sigmapoolcom: AXbtPattern = AXbtPattern(client, f'{base_path}/SigmapoolCom') + self.simplecoinus: AXbtPattern = AXbtPattern(client, f'{base_path}/SimplecoinUs') + self.solock: AXbtPattern = AXbtPattern(client, f'{base_path}/SoloCk') + self.spiderpool: AXbtPattern = AXbtPattern(client, f'{base_path}/SpiderPool') + self.stminingcorp: AXbtPattern = AXbtPattern(client, f'{base_path}/StMiningCorp') + self.tangpool: AXbtPattern = AXbtPattern(client, f'{base_path}/Tangpool') + self.tatmaspool: AXbtPattern = AXbtPattern(client, f'{base_path}/TatmasPool') + self.tbdice: AXbtPattern = AXbtPattern(client, f'{base_path}/TbDice') + self.telco214: AXbtPattern = AXbtPattern(client, f'{base_path}/Telco214') + self.terrapool: AXbtPattern = AXbtPattern(client, f'{base_path}/TerraPool') + self.tiger: AXbtPattern = AXbtPattern(client, f'{base_path}/Tiger') + self.tigerpoolnet: AXbtPattern = AXbtPattern(client, f'{base_path}/TigerpoolNet') + self.titan: AXbtPattern = AXbtPattern(client, f'{base_path}/Titan') + self.transactioncoinmining: AXbtPattern = AXbtPattern(client, f'{base_path}/TransactionCoinMining') + self.trickysbtcpool: AXbtPattern = AXbtPattern(client, f'{base_path}/TrickysBtcPool') + self.triplemining: AXbtPattern = AXbtPattern(client, f'{base_path}/TripleMining') + self.twentyoneinc: AXbtPattern = AXbtPattern(client, f'{base_path}/TwentyOneInc') + self.ultimuspool: AXbtPattern = AXbtPattern(client, f'{base_path}/UltimusPool') + self.unknown: AXbtPattern = AXbtPattern(client, f'{base_path}/Unknown') + self.unomp: AXbtPattern = AXbtPattern(client, f'{base_path}/Unomp') + self.viabtc: AXbtPattern = AXbtPattern(client, f'{base_path}/ViaBtc') + self.waterhole: AXbtPattern = AXbtPattern(client, f'{base_path}/Waterhole') + self.wayicn: AXbtPattern = AXbtPattern(client, f'{base_path}/WayiCn') + self.whitepool: AXbtPattern = AXbtPattern(client, f'{base_path}/WhitePool') + self.wk057: AXbtPattern = AXbtPattern(client, f'{base_path}/Wk057') + self.yourbtcnet: AXbtPattern = AXbtPattern(client, f'{base_path}/YourbtcNet') + self.zulupool: AXbtPattern = AXbtPattern(client, f'{base_path}/Zulupool') class CatalogTree_Computed_Price: """Catalog tree node."""