From c90953adbe0390d89dbdc8e4cf2f6b62b51b06f0 Mon Sep 17 00:00:00 2001 From: nym21 Date: Sun, 18 Jan 2026 16:04:24 +0100 Subject: [PATCH] global: snapshot --- .gitignore | 1 + Cargo.lock | 33 +- Cargo.toml | 3 + crates/brk_client/src/lib.rs | 545 +++++++++--------- crates/brk_computer/Cargo.toml | 1 + crates/brk_computer/src/distribution/vecs.rs | 17 +- crates/brk_computer/src/lib.rs | 4 +- crates/brk_playground/.gitignore | 7 + crates/brk_playground/Cargo.toml | 20 + crates/brk_playground/src/lib.rs | 4 + crates/brk_server/Cargo.toml | 9 +- crates/brk_server/src/extended/header_map.rs | 101 +--- crates/brk_server/src/files/mod.rs | 25 - crates/brk_server/src/lib.rs | 50 +- crates/brk_website/Cargo.toml | 21 + crates/brk_website/README.md | 44 ++ crates/brk_website/examples/website.rs | 100 ++++ crates/brk_website/src/error.rs | 23 + .../file.rs => brk_website/src/handlers.rs} | 18 +- crates/brk_website/src/headers.rs | 53 ++ crates/brk_website/src/lib.rs | 17 + crates/brk_website/src/router.rs | 17 + .../src/files => brk_website/src}/website.rs | 22 +- crates/{brk_server => brk_website}/website | 0 modules/brk-client/index.js | 524 ++++++++--------- packages/brk_client/brk_client/__init__.py | 234 ++++---- 26 files changed, 1065 insertions(+), 828 deletions(-) create mode 100644 crates/brk_playground/.gitignore create mode 100644 crates/brk_playground/Cargo.toml create mode 100644 crates/brk_playground/src/lib.rs delete mode 100644 crates/brk_server/src/files/mod.rs create mode 100644 crates/brk_website/Cargo.toml create mode 100644 crates/brk_website/README.md create mode 100644 crates/brk_website/examples/website.rs create mode 100644 crates/brk_website/src/error.rs rename crates/{brk_server/src/files/file.rs => brk_website/src/handlers.rs} (67%) create mode 100644 crates/brk_website/src/headers.rs create mode 100644 crates/brk_website/src/lib.rs create mode 100644 crates/brk_website/src/router.rs rename crates/{brk_server/src/files => brk_website/src}/website.rs (89%) rename crates/{brk_server => brk_website}/website (100%) diff --git a/.gitignore b/.gitignore index 7f734b3bf..416b4b09c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ _* /*.html /research /filter_* +/heatmaps # Logs *.log* diff --git a/Cargo.lock b/Cargo.lock index 448ca1e50..27a69fa0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -463,6 +463,7 @@ dependencies = [ "color-eyre", "derive_more", "pco", + "plotters", "rayon", "rustc-hash", "schemars", @@ -562,6 +563,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "brk_playground" +version = "0.1.0-alpha.6" +dependencies = [ + "brk_computer", + "brk_error", + "brk_fetcher", + "brk_indexer", + "brk_logger", + "brk_types", + "plotters", + "tracing", + "vecdb", +] + [[package]] name = "brk_query" version = "0.1.0-alpha.6" @@ -628,9 +644,8 @@ dependencies = [ "brk_rpc", "brk_traversable", "brk_types", + "brk_website", "derive_more", - "importmap", - "include_dir", "jiff", "quick_cache", "schemars", @@ -695,6 +710,20 @@ dependencies = [ "vecdb", ] +[[package]] +name = "brk_website" +version = "0.1.0-alpha.6" +dependencies = [ + "axum", + "brk_logger", + "importmap", + "include_dir", + "tokio", + "tower-http", + "tower-layer", + "tracing", +] + [[package]] name = "brotli" version = "8.0.2" diff --git a/Cargo.toml b/Cargo.toml index 686c6dfbc..0e55e02c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ brk_store = { version = "0.1.0-alpha.6", path = "crates/brk_store" } brk_traversable = { version = "0.1.0-alpha.6", path = "crates/brk_traversable", features = ["pco", "derive"] } brk_traversable_derive = { version = "0.1.0-alpha.6", path = "crates/brk_traversable_derive" } brk_types = { version = "0.1.0-alpha.6", path = "crates/brk_types" } +brk_website = { version = "0.1.0-alpha.6", path = "crates/brk_website" } byteview = "0.10.0" color-eyre = "0.6.5" derive_more = { version = "2.1.1", features = ["deref", "deref_mut"] } @@ -78,6 +79,8 @@ serde_json = { version = "1.0.149", features = ["float_roundtrip", "preserve_ord smallvec = "1.15.1" tokio = { version = "1.49.0", features = ["rt-multi-thread"] } tracing = { version = "0.1", default-features = false, features = ["std"] } +tower-http = { version = "0.6.8", features = ["catch-panic", "compression-br", "compression-gzip", "compression-zstd", "cors", "normalize-path", "timeout", "trace"] } +tower-layer = "0.3" vecdb = { version = "0.5.11", features = ["derive", "serde_json", "pco", "schemars"] } # vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] } diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index 3bbf4c25b..bb68e33da 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -2689,56 +2689,6 @@ impl Price111dSmaPattern { } } -/// Pattern struct for repeated tree structure. -pub struct PercentilesPattern { - pub pct05: MetricPattern4, - pub pct10: MetricPattern4, - pub pct15: MetricPattern4, - pub pct20: MetricPattern4, - pub pct25: MetricPattern4, - pub pct30: MetricPattern4, - pub pct35: MetricPattern4, - pub pct40: MetricPattern4, - pub pct45: MetricPattern4, - pub pct50: MetricPattern4, - pub pct55: MetricPattern4, - pub pct60: MetricPattern4, - pub pct65: MetricPattern4, - pub pct70: MetricPattern4, - pub pct75: MetricPattern4, - pub pct80: MetricPattern4, - pub pct85: MetricPattern4, - pub pct90: MetricPattern4, - pub pct95: MetricPattern4, -} - -impl PercentilesPattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - pct05: MetricPattern4::new(client.clone(), _m(&acc, "pct05")), - pct10: MetricPattern4::new(client.clone(), _m(&acc, "pct10")), - pct15: MetricPattern4::new(client.clone(), _m(&acc, "pct15")), - pct20: MetricPattern4::new(client.clone(), _m(&acc, "pct20")), - pct25: MetricPattern4::new(client.clone(), _m(&acc, "pct25")), - pct30: MetricPattern4::new(client.clone(), _m(&acc, "pct30")), - pct35: MetricPattern4::new(client.clone(), _m(&acc, "pct35")), - pct40: MetricPattern4::new(client.clone(), _m(&acc, "pct40")), - pct45: MetricPattern4::new(client.clone(), _m(&acc, "pct45")), - pct50: MetricPattern4::new(client.clone(), _m(&acc, "pct50")), - pct55: MetricPattern4::new(client.clone(), _m(&acc, "pct55")), - pct60: MetricPattern4::new(client.clone(), _m(&acc, "pct60")), - pct65: MetricPattern4::new(client.clone(), _m(&acc, "pct65")), - pct70: MetricPattern4::new(client.clone(), _m(&acc, "pct70")), - pct75: MetricPattern4::new(client.clone(), _m(&acc, "pct75")), - pct80: MetricPattern4::new(client.clone(), _m(&acc, "pct80")), - pct85: MetricPattern4::new(client.clone(), _m(&acc, "pct85")), - pct90: MetricPattern4::new(client.clone(), _m(&acc, "pct90")), - pct95: MetricPattern4::new(client.clone(), _m(&acc, "pct95")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct ActivePriceRatioPattern { pub ratio: MetricPattern4, @@ -2789,6 +2739,56 @@ impl ActivePriceRatioPattern { } } +/// Pattern struct for repeated tree structure. +pub struct PercentilesPattern { + pub pct05: MetricPattern4, + pub pct10: MetricPattern4, + pub pct15: MetricPattern4, + pub pct20: MetricPattern4, + pub pct25: MetricPattern4, + pub pct30: MetricPattern4, + pub pct35: MetricPattern4, + pub pct40: MetricPattern4, + pub pct45: MetricPattern4, + pub pct50: MetricPattern4, + pub pct55: MetricPattern4, + pub pct60: MetricPattern4, + pub pct65: MetricPattern4, + pub pct70: MetricPattern4, + pub pct75: MetricPattern4, + pub pct80: MetricPattern4, + pub pct85: MetricPattern4, + pub pct90: MetricPattern4, + pub pct95: MetricPattern4, +} + +impl PercentilesPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + pct05: MetricPattern4::new(client.clone(), _m(&acc, "pct05")), + pct10: MetricPattern4::new(client.clone(), _m(&acc, "pct10")), + pct15: MetricPattern4::new(client.clone(), _m(&acc, "pct15")), + pct20: MetricPattern4::new(client.clone(), _m(&acc, "pct20")), + pct25: MetricPattern4::new(client.clone(), _m(&acc, "pct25")), + pct30: MetricPattern4::new(client.clone(), _m(&acc, "pct30")), + pct35: MetricPattern4::new(client.clone(), _m(&acc, "pct35")), + pct40: MetricPattern4::new(client.clone(), _m(&acc, "pct40")), + pct45: MetricPattern4::new(client.clone(), _m(&acc, "pct45")), + pct50: MetricPattern4::new(client.clone(), _m(&acc, "pct50")), + pct55: MetricPattern4::new(client.clone(), _m(&acc, "pct55")), + pct60: MetricPattern4::new(client.clone(), _m(&acc, "pct60")), + pct65: MetricPattern4::new(client.clone(), _m(&acc, "pct65")), + pct70: MetricPattern4::new(client.clone(), _m(&acc, "pct70")), + pct75: MetricPattern4::new(client.clone(), _m(&acc, "pct75")), + pct80: MetricPattern4::new(client.clone(), _m(&acc, "pct80")), + pct85: MetricPattern4::new(client.clone(), _m(&acc, "pct85")), + pct90: MetricPattern4::new(client.clone(), _m(&acc, "pct90")), + pct95: MetricPattern4::new(client.clone(), _m(&acc, "pct95")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct RelativePattern5 { pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1, @@ -3072,6 +3072,40 @@ impl BitcoinPattern { } } +/// Pattern struct for repeated tree structure. +pub struct ClassAveragePricePattern { + pub _2015: MetricPattern4, + pub _2016: MetricPattern4, + pub _2017: MetricPattern4, + pub _2018: MetricPattern4, + pub _2019: MetricPattern4, + pub _2020: MetricPattern4, + pub _2021: MetricPattern4, + pub _2022: MetricPattern4, + pub _2023: MetricPattern4, + pub _2024: MetricPattern4, + pub _2025: MetricPattern4, +} + +impl ClassAveragePricePattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + _2015: MetricPattern4::new(client.clone(), _m(&acc, "2015_average_price")), + _2016: MetricPattern4::new(client.clone(), _m(&acc, "2016_average_price")), + _2017: MetricPattern4::new(client.clone(), _m(&acc, "2017_average_price")), + _2018: MetricPattern4::new(client.clone(), _m(&acc, "2018_average_price")), + _2019: MetricPattern4::new(client.clone(), _m(&acc, "2019_average_price")), + _2020: MetricPattern4::new(client.clone(), _m(&acc, "2020_average_price")), + _2021: MetricPattern4::new(client.clone(), _m(&acc, "2021_average_price")), + _2022: MetricPattern4::new(client.clone(), _m(&acc, "2022_average_price")), + _2023: MetricPattern4::new(client.clone(), _m(&acc, "2023_average_price")), + _2024: MetricPattern4::new(client.clone(), _m(&acc, "2024_average_price")), + _2025: MetricPattern4::new(client.clone(), _m(&acc, "2025_average_price")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct DollarsPattern { pub average: MetricPattern2, @@ -3106,99 +3140,6 @@ impl DollarsPattern { } } -/// Pattern struct for repeated tree structure. -pub struct ClassAveragePricePattern { - pub _2015: MetricPattern4, - pub _2016: MetricPattern4, - pub _2017: MetricPattern4, - pub _2018: MetricPattern4, - pub _2019: MetricPattern4, - pub _2020: MetricPattern4, - pub _2021: MetricPattern4, - pub _2022: MetricPattern4, - pub _2023: MetricPattern4, - pub _2024: MetricPattern4, - pub _2025: MetricPattern4, -} - -impl ClassAveragePricePattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - _2015: MetricPattern4::new(client.clone(), _m(&acc, "2015_returns")), - _2016: MetricPattern4::new(client.clone(), _m(&acc, "2016_returns")), - _2017: MetricPattern4::new(client.clone(), _m(&acc, "2017_returns")), - _2018: MetricPattern4::new(client.clone(), _m(&acc, "2018_returns")), - _2019: MetricPattern4::new(client.clone(), _m(&acc, "2019_returns")), - _2020: MetricPattern4::new(client.clone(), _m(&acc, "2020_returns")), - _2021: MetricPattern4::new(client.clone(), _m(&acc, "2021_returns")), - _2022: MetricPattern4::new(client.clone(), _m(&acc, "2022_returns")), - _2023: MetricPattern4::new(client.clone(), _m(&acc, "2023_returns")), - _2024: MetricPattern4::new(client.clone(), _m(&acc, "2024_returns")), - _2025: MetricPattern4::new(client.clone(), _m(&acc, "2025_returns")), - } - } -} - -/// Pattern struct for repeated tree structure. -pub struct RelativePattern { - pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1, - pub net_unrealized_pnl_rel_to_market_cap: MetricPattern1, - pub nupl: MetricPattern1, - pub supply_in_loss_rel_to_circulating_supply: MetricPattern1, - pub supply_in_loss_rel_to_own_supply: MetricPattern1, - pub supply_in_profit_rel_to_circulating_supply: MetricPattern1, - pub supply_in_profit_rel_to_own_supply: MetricPattern1, - pub supply_rel_to_circulating_supply: MetricPattern4, - pub unrealized_loss_rel_to_market_cap: MetricPattern1, - pub unrealized_profit_rel_to_market_cap: MetricPattern1, -} - -impl RelativePattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - neg_unrealized_loss_rel_to_market_cap: MetricPattern1::new( - client.clone(), - _m(&acc, "neg_unrealized_loss_rel_to_market_cap"), - ), - net_unrealized_pnl_rel_to_market_cap: MetricPattern1::new( - client.clone(), - _m(&acc, "net_unrealized_pnl_rel_to_market_cap"), - ), - nupl: MetricPattern1::new(client.clone(), _m(&acc, "nupl")), - supply_in_loss_rel_to_circulating_supply: MetricPattern1::new( - client.clone(), - _m(&acc, "supply_in_loss_rel_to_circulating_supply"), - ), - supply_in_loss_rel_to_own_supply: MetricPattern1::new( - client.clone(), - _m(&acc, "supply_in_loss_rel_to_own_supply"), - ), - supply_in_profit_rel_to_circulating_supply: MetricPattern1::new( - client.clone(), - _m(&acc, "supply_in_profit_rel_to_circulating_supply"), - ), - supply_in_profit_rel_to_own_supply: MetricPattern1::new( - client.clone(), - _m(&acc, "supply_in_profit_rel_to_own_supply"), - ), - supply_rel_to_circulating_supply: MetricPattern4::new( - client.clone(), - _m(&acc, "supply_rel_to_circulating_supply"), - ), - unrealized_loss_rel_to_market_cap: MetricPattern1::new( - client.clone(), - _m(&acc, "unrealized_loss_rel_to_market_cap"), - ), - unrealized_profit_rel_to_market_cap: MetricPattern1::new( - client.clone(), - _m(&acc, "unrealized_profit_rel_to_market_cap"), - ), - } - } -} - /// Pattern struct for repeated tree structure. pub struct RelativePattern2 { pub neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1, @@ -3261,6 +3202,65 @@ impl RelativePattern2 { } } +/// Pattern struct for repeated tree structure. +pub struct RelativePattern { + pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1, + pub net_unrealized_pnl_rel_to_market_cap: MetricPattern1, + pub nupl: MetricPattern1, + pub supply_in_loss_rel_to_circulating_supply: MetricPattern1, + pub supply_in_loss_rel_to_own_supply: MetricPattern1, + pub supply_in_profit_rel_to_circulating_supply: MetricPattern1, + pub supply_in_profit_rel_to_own_supply: MetricPattern1, + pub supply_rel_to_circulating_supply: MetricPattern4, + pub unrealized_loss_rel_to_market_cap: MetricPattern1, + pub unrealized_profit_rel_to_market_cap: MetricPattern1, +} + +impl RelativePattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + neg_unrealized_loss_rel_to_market_cap: MetricPattern1::new( + client.clone(), + _m(&acc, "neg_unrealized_loss_rel_to_market_cap"), + ), + net_unrealized_pnl_rel_to_market_cap: MetricPattern1::new( + client.clone(), + _m(&acc, "net_unrealized_pnl_rel_to_market_cap"), + ), + nupl: MetricPattern1::new(client.clone(), _m(&acc, "nupl")), + supply_in_loss_rel_to_circulating_supply: MetricPattern1::new( + client.clone(), + _m(&acc, "supply_in_loss_rel_to_circulating_supply"), + ), + supply_in_loss_rel_to_own_supply: MetricPattern1::new( + client.clone(), + _m(&acc, "supply_in_loss_rel_to_own_supply"), + ), + supply_in_profit_rel_to_circulating_supply: MetricPattern1::new( + client.clone(), + _m(&acc, "supply_in_profit_rel_to_circulating_supply"), + ), + supply_in_profit_rel_to_own_supply: MetricPattern1::new( + client.clone(), + _m(&acc, "supply_in_profit_rel_to_own_supply"), + ), + supply_rel_to_circulating_supply: MetricPattern4::new( + client.clone(), + _m(&acc, "supply_rel_to_circulating_supply"), + ), + unrealized_loss_rel_to_market_cap: MetricPattern1::new( + client.clone(), + _m(&acc, "unrealized_loss_rel_to_market_cap"), + ), + unrealized_profit_rel_to_market_cap: MetricPattern1::new( + client.clone(), + _m(&acc, "unrealized_profit_rel_to_market_cap"), + ), + } + } +} + /// Pattern struct for repeated tree structure. pub struct CountPattern2 { pub average: MetricPattern1, @@ -3466,25 +3466,25 @@ impl PeriodCagrPattern { } /// Pattern struct for repeated tree structure. -pub struct _0satsPattern2 { +pub struct _10yTo12yPattern { pub activity: ActivityPattern2, - pub cost_basis: CostBasisPattern, + pub cost_basis: CostBasisPattern2, pub outputs: OutputsPattern, - pub realized: RealizedPattern, - pub relative: RelativePattern4, + pub realized: RealizedPattern2, + pub relative: RelativePattern2, pub supply: SupplyPattern2, pub unrealized: UnrealizedPattern, } -impl _0satsPattern2 { +impl _10yTo12yPattern { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { activity: ActivityPattern2::new(client.clone(), acc.clone()), - cost_basis: CostBasisPattern::new(client.clone(), acc.clone()), + cost_basis: CostBasisPattern2::new(client.clone(), acc.clone()), outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")), - realized: RealizedPattern::new(client.clone(), acc.clone()), - relative: RelativePattern4::new(client.clone(), _m(&acc, "supply_in")), + realized: RealizedPattern2::new(client.clone(), acc.clone()), + relative: RelativePattern2::new(client.clone(), acc.clone()), supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")), unrealized: UnrealizedPattern::new(client.clone(), acc.clone()), } @@ -3518,25 +3518,25 @@ impl _10yPattern { } /// Pattern struct for repeated tree structure. -pub struct _10yTo12yPattern { +pub struct _0satsPattern2 { pub activity: ActivityPattern2, - pub cost_basis: CostBasisPattern2, + pub cost_basis: CostBasisPattern, pub outputs: OutputsPattern, - pub realized: RealizedPattern2, - pub relative: RelativePattern2, + pub realized: RealizedPattern, + pub relative: RelativePattern4, pub supply: SupplyPattern2, pub unrealized: UnrealizedPattern, } -impl _10yTo12yPattern { +impl _0satsPattern2 { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { activity: ActivityPattern2::new(client.clone(), acc.clone()), - cost_basis: CostBasisPattern2::new(client.clone(), acc.clone()), + cost_basis: CostBasisPattern::new(client.clone(), acc.clone()), outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")), - realized: RealizedPattern2::new(client.clone(), acc.clone()), - relative: RelativePattern2::new(client.clone(), acc.clone()), + realized: RealizedPattern::new(client.clone(), acc.clone()), + relative: RelativePattern4::new(client.clone(), _m(&acc, "supply_in")), supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")), unrealized: UnrealizedPattern::new(client.clone(), acc.clone()), } @@ -3655,24 +3655,6 @@ impl SplitPattern2 { } } -/// Pattern struct for repeated tree structure. -pub struct CostBasisPattern2 { - pub max: MetricPattern1, - pub min: MetricPattern1, - pub percentiles: PercentilesPattern, -} - -impl CostBasisPattern2 { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - max: MetricPattern1::new(client.clone(), _m(&acc, "max_cost_basis")), - min: MetricPattern1::new(client.clone(), _m(&acc, "min_cost_basis")), - percentiles: PercentilesPattern::new(client.clone(), _m(&acc, "cost_basis")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct CoinbasePattern { pub bitcoin: BitcoinPattern, @@ -3692,19 +3674,55 @@ impl CoinbasePattern { } /// Pattern struct for repeated tree structure. -pub struct ActiveSupplyPattern { - pub bitcoin: MetricPattern1, - pub dollars: MetricPattern1, - pub sats: MetricPattern1, +pub struct CoinbasePattern2 { + pub bitcoin: BlockCountPattern, + pub dollars: BlockCountPattern, + pub sats: BlockCountPattern, } -impl ActiveSupplyPattern { +impl CoinbasePattern2 { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { - bitcoin: MetricPattern1::new(client.clone(), _m(&acc, "btc")), - dollars: MetricPattern1::new(client.clone(), _m(&acc, "usd")), - sats: MetricPattern1::new(client.clone(), acc.clone()), + bitcoin: BlockCountPattern::new(client.clone(), _m(&acc, "btc")), + dollars: BlockCountPattern::new(client.clone(), _m(&acc, "usd")), + sats: BlockCountPattern::new(client.clone(), acc.clone()), + } + } +} + +/// Pattern struct for repeated tree structure. +pub struct SegwitAdoptionPattern { + pub base: MetricPattern11, + pub cumulative: MetricPattern2, + pub sum: MetricPattern2, +} + +impl SegwitAdoptionPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + base: MetricPattern11::new(client.clone(), acc.clone()), + cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")), + sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")), + } + } +} + +/// Pattern struct for repeated tree structure. +pub struct CostBasisPattern2 { + pub max: MetricPattern1, + pub min: MetricPattern1, + pub percentiles: PercentilesPattern, +} + +impl CostBasisPattern2 { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + max: MetricPattern1::new(client.clone(), _m(&acc, "max_cost_basis")), + min: MetricPattern1::new(client.clone(), _m(&acc, "min_cost_basis")), + percentiles: PercentilesPattern::new(client.clone(), _m(&acc, "cost_basis")), } } } @@ -3727,24 +3745,6 @@ impl UnclaimedRewardsPattern { } } -/// Pattern struct for repeated tree structure. -pub struct CoinbasePattern2 { - pub bitcoin: BlockCountPattern, - pub dollars: BlockCountPattern, - pub sats: BlockCountPattern, -} - -impl CoinbasePattern2 { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - bitcoin: BlockCountPattern::new(client.clone(), _m(&acc, "btc")), - dollars: BlockCountPattern::new(client.clone(), _m(&acc, "usd")), - sats: BlockCountPattern::new(client.clone(), acc.clone()), - } - } -} - /// Pattern struct for repeated tree structure. pub struct _2015Pattern { pub bitcoin: MetricPattern4, @@ -3764,19 +3764,19 @@ impl _2015Pattern { } /// Pattern struct for repeated tree structure. -pub struct SegwitAdoptionPattern { - pub base: MetricPattern11, - pub cumulative: MetricPattern2, - pub sum: MetricPattern2, +pub struct ActiveSupplyPattern { + pub bitcoin: MetricPattern1, + pub dollars: MetricPattern1, + pub sats: MetricPattern1, } -impl SegwitAdoptionPattern { +impl ActiveSupplyPattern { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { - base: MetricPattern11::new(client.clone(), acc.clone()), - cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")), - sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")), + bitcoin: MetricPattern1::new(client.clone(), _m(&acc, "btc")), + dollars: MetricPattern1::new(client.clone(), _m(&acc, "usd")), + sats: MetricPattern1::new(client.clone(), acc.clone()), } } } @@ -3797,6 +3797,22 @@ impl _1dReturns1mSdPattern { } } +/// Pattern struct for repeated tree structure. +pub struct SupplyPattern2 { + pub halved: ActiveSupplyPattern, + pub total: ActiveSupplyPattern, +} + +impl SupplyPattern2 { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + halved: ActiveSupplyPattern::new(client.clone(), _m(&acc, "halved")), + total: ActiveSupplyPattern::new(client.clone(), acc.clone()), + } + } +} + /// Pattern struct for repeated tree structure. pub struct CostBasisPattern { pub max: MetricPattern1, @@ -3835,22 +3851,6 @@ impl RelativePattern4 { } } -/// Pattern struct for repeated tree structure. -pub struct SupplyPattern2 { - pub halved: ActiveSupplyPattern, - pub total: ActiveSupplyPattern, -} - -impl SupplyPattern2 { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - halved: ActiveSupplyPattern::new(client.clone(), _m(&acc, "halved")), - total: ActiveSupplyPattern::new(client.clone(), acc.clone()), - } - } -} - /// Pattern struct for repeated tree structure. pub struct BitcoinPattern2 { pub cumulative: MetricPattern2, @@ -3899,20 +3899,6 @@ impl BlockCountPattern { } } -/// Pattern struct for repeated tree structure. -pub struct OutputsPattern { - pub utxo_count: MetricPattern1, -} - -impl OutputsPattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - utxo_count: MetricPattern1::new(client.clone(), acc.clone()), - } - } -} - /// Pattern struct for repeated tree structure. pub struct RealizedPriceExtraPattern { pub ratio: MetricPattern4, @@ -3927,6 +3913,20 @@ impl RealizedPriceExtraPattern { } } +/// Pattern struct for repeated tree structure. +pub struct OutputsPattern { + pub utxo_count: MetricPattern1, +} + +impl OutputsPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + utxo_count: MetricPattern1::new(client.clone(), acc.clone()), + } + } +} + // Metrics tree /// Metrics tree node. @@ -6191,8 +6191,8 @@ impl MetricsTree_Market_Ath { /// Metrics tree node. pub struct MetricsTree_Market_Dca { - pub class_average_price: MetricsTree_Market_Dca_ClassAveragePrice, - pub class_returns: ClassAveragePricePattern, + pub class_average_price: ClassAveragePricePattern, + pub class_returns: MetricsTree_Market_Dca_ClassReturns, pub class_stack: MetricsTree_Market_Dca_ClassStack, pub period_average_price: PeriodAveragePricePattern, pub period_cagr: PeriodCagrPattern, @@ -6204,11 +6204,14 @@ pub struct MetricsTree_Market_Dca { impl MetricsTree_Market_Dca { pub fn new(client: Arc, base_path: String) -> Self { Self { - class_average_price: MetricsTree_Market_Dca_ClassAveragePrice::new( + class_average_price: ClassAveragePricePattern::new( client.clone(), - format!("{base_path}_class_average_price"), + "dca_class".to_string(), + ), + class_returns: MetricsTree_Market_Dca_ClassReturns::new( + client.clone(), + format!("{base_path}_class_returns"), ), - class_returns: ClassAveragePricePattern::new(client.clone(), "dca_class".to_string()), class_stack: MetricsTree_Market_Dca_ClassStack::new( client.clone(), format!("{base_path}_class_stack"), @@ -6232,34 +6235,34 @@ impl MetricsTree_Market_Dca { } /// Metrics tree node. -pub struct MetricsTree_Market_Dca_ClassAveragePrice { - pub _2015: MetricPattern4, - pub _2016: MetricPattern4, - pub _2017: MetricPattern4, - pub _2018: MetricPattern4, - pub _2019: MetricPattern4, - pub _2020: MetricPattern4, - pub _2021: MetricPattern4, - pub _2022: MetricPattern4, - pub _2023: MetricPattern4, - pub _2024: MetricPattern4, - pub _2025: MetricPattern4, +pub struct MetricsTree_Market_Dca_ClassReturns { + pub _2015: MetricPattern4, + pub _2016: MetricPattern4, + pub _2017: MetricPattern4, + pub _2018: MetricPattern4, + pub _2019: MetricPattern4, + pub _2020: MetricPattern4, + pub _2021: MetricPattern4, + pub _2022: MetricPattern4, + pub _2023: MetricPattern4, + pub _2024: MetricPattern4, + pub _2025: MetricPattern4, } -impl MetricsTree_Market_Dca_ClassAveragePrice { +impl MetricsTree_Market_Dca_ClassReturns { pub fn new(client: Arc, base_path: String) -> Self { Self { - _2015: MetricPattern4::new(client.clone(), "dca_class_2015_average_price".to_string()), - _2016: MetricPattern4::new(client.clone(), "dca_class_2016_average_price".to_string()), - _2017: MetricPattern4::new(client.clone(), "dca_class_2017_average_price".to_string()), - _2018: MetricPattern4::new(client.clone(), "dca_class_2018_average_price".to_string()), - _2019: MetricPattern4::new(client.clone(), "dca_class_2019_average_price".to_string()), - _2020: MetricPattern4::new(client.clone(), "dca_class_2020_average_price".to_string()), - _2021: MetricPattern4::new(client.clone(), "dca_class_2021_average_price".to_string()), - _2022: MetricPattern4::new(client.clone(), "dca_class_2022_average_price".to_string()), - _2023: MetricPattern4::new(client.clone(), "dca_class_2023_average_price".to_string()), - _2024: MetricPattern4::new(client.clone(), "dca_class_2024_average_price".to_string()), - _2025: MetricPattern4::new(client.clone(), "dca_class_2025_average_price".to_string()), + _2015: MetricPattern4::new(client.clone(), "dca_class_2015_returns".to_string()), + _2016: MetricPattern4::new(client.clone(), "dca_class_2016_returns".to_string()), + _2017: MetricPattern4::new(client.clone(), "dca_class_2017_returns".to_string()), + _2018: MetricPattern4::new(client.clone(), "dca_class_2018_returns".to_string()), + _2019: MetricPattern4::new(client.clone(), "dca_class_2019_returns".to_string()), + _2020: MetricPattern4::new(client.clone(), "dca_class_2020_returns".to_string()), + _2021: MetricPattern4::new(client.clone(), "dca_class_2021_returns".to_string()), + _2022: MetricPattern4::new(client.clone(), "dca_class_2022_returns".to_string()), + _2023: MetricPattern4::new(client.clone(), "dca_class_2023_returns".to_string()), + _2024: MetricPattern4::new(client.clone(), "dca_class_2024_returns".to_string()), + _2025: MetricPattern4::new(client.clone(), "dca_class_2025_returns".to_string()), } } } @@ -7599,7 +7602,7 @@ pub struct BrkClient { impl BrkClient { /// Client version. - pub const VERSION: &'static str = "v0.1.0-alpha.3"; + pub const VERSION: &'static str = "v0.1.0-alpha.6"; /// Create a new client with the given base URL. pub fn new(base_url: impl Into) -> Self { diff --git a/crates/brk_computer/Cargo.toml b/crates/brk_computer/Cargo.toml index b3ff312ab..72192a890 100644 --- a/crates/brk_computer/Cargo.toml +++ b/crates/brk_computer/Cargo.toml @@ -32,5 +32,6 @@ vecdb = { workspace = true } [dev-dependencies] brk_alloc = { workspace = true } +plotters = "0.3" brk_bencher = { workspace = true } color-eyre = { workspace = true } diff --git a/crates/brk_computer/src/distribution/vecs.rs b/crates/brk_computer/src/distribution/vecs.rs index 10f382ded..bbba53b49 100644 --- a/crates/brk_computer/src/distribution/vecs.rs +++ b/crates/brk_computer/src/distribution/vecs.rs @@ -4,7 +4,7 @@ use brk_error::Result; use brk_indexer::Indexer; use brk_traversable::Traversable; use brk_types::{ - EmptyAddressData, EmptyAddressIndex, Height, LoadedAddressData, LoadedAddressIndex, + DateIndex, EmptyAddressData, EmptyAddressIndex, Height, LoadedAddressData, LoadedAddressIndex, SupplyState, Version, }; use tracing::info; @@ -242,6 +242,21 @@ impl Vecs { (recovered_height, chain_state) }; + // Update starting_indexes if we need to recompute from an earlier point + if starting_height < starting_indexes.height { + starting_indexes.height = starting_height; + // Also update dateindex to match + if starting_height.is_zero() { + starting_indexes.dateindex = DateIndex::from(0); + } else { + starting_indexes.dateindex = indexes + .height + .dateindex + .read_once(starting_height.decremented().unwrap())? + .into(); + } + } + // 2b. Validate computed versions let base_version = VERSION; self.utxo_cohorts.validate_computed_versions(base_version)?; diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index 9bb14ff0c..6e9fe4947 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -15,14 +15,14 @@ mod blocks; mod cointime; mod constants; mod distribution; -mod indexes; +pub mod indexes; mod inputs; mod internal; mod market; mod outputs; mod pools; mod positions; -mod price; +pub mod price; mod scripts; mod supply; mod traits; diff --git a/crates/brk_playground/.gitignore b/crates/brk_playground/.gitignore new file mode 100644 index 000000000..cab784871 --- /dev/null +++ b/crates/brk_playground/.gitignore @@ -0,0 +1,7 @@ +# Ignore everything in playground except essential files +* +!.gitignore +!Cargo.toml +!src/ +!src/lib.rs +!README.md diff --git a/crates/brk_playground/Cargo.toml b/crates/brk_playground/Cargo.toml new file mode 100644 index 000000000..ed19b8837 --- /dev/null +++ b/crates/brk_playground/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "brk_playground" +description = "Experimental playground for brk development" +version.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +publish = false + +[dependencies] +brk_computer = { workspace = true } +brk_error = { workspace = true } +brk_fetcher = { workspace = true } +brk_indexer = { workspace = true } +brk_logger = { workspace = true } +brk_types = { workspace = true } +plotters = "0.3" +tracing = { workspace = true } +vecdb = { workspace = true } diff --git a/crates/brk_playground/src/lib.rs b/crates/brk_playground/src/lib.rs new file mode 100644 index 000000000..71504580c --- /dev/null +++ b/crates/brk_playground/src/lib.rs @@ -0,0 +1,4 @@ +//! Experimental playground for brk development. +//! +//! This crate is for experiments and prototypes. +//! Most contents are git-ignored. diff --git a/crates/brk_server/Cargo.toml b/crates/brk_server/Cargo.toml index 00e498f30..0dfedd5a2 100644 --- a/crates/brk_server/Cargo.toml +++ b/crates/brk_server/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true license.workspace = true homepage.workspace = true repository.workspace = true -include = ["src/**/*", "website/**/*", "examples/**/*", "Cargo.toml", "README.md"] [dependencies] aide = { workspace = true } @@ -22,10 +21,8 @@ brk_reader = { workspace = true } brk_rpc = { workspace = true } brk_types = { workspace = true } brk_traversable = { workspace = true } +brk_website = { workspace = true } derive_more = { workspace = true } -include_dir = "0.7" -# importmap = { path = "../../../importmap", features = ["embedded"] } -importmap = { version = "0.3.0", features = ["embedded"] } vecdb = { workspace = true } jiff = { workspace = true } quick_cache = "0.6.18" @@ -34,8 +31,8 @@ serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } -tower-http = { version = "0.6.8", features = ["catch-panic", "compression-br", "compression-gzip", "compression-zstd", "cors", "normalize-path", "timeout", "trace"] } -tower-layer = "0.3" +tower-http = { workspace = true } +tower-layer = { workspace = true } [dev-dependencies] brk_mempool = { workspace = true } diff --git a/crates/brk_server/src/extended/header_map.rs b/crates/brk_server/src/extended/header_map.rs index 1d312e4cc..0f535ccce 100644 --- a/crates/brk_server/src/extended/header_map.rs +++ b/crates/brk_server/src/extended/header_map.rs @@ -1,5 +1,3 @@ -use std::path::Path; - use axum::http::{ HeaderMap, header::{self, IF_NONE_MATCH}, @@ -7,31 +5,29 @@ use axum::http::{ pub trait HeaderMapExtended { fn has_etag(&self, etag: &str) -> bool; + fn insert_etag(&mut self, etag: &str); fn insert_cache_control(&mut self, value: &str); fn insert_cache_control_must_revalidate(&mut self); - fn insert_cache_control_immutable(&mut self); - fn insert_etag(&mut self, etag: &str); fn insert_content_disposition_attachment(&mut self); - fn insert_content_type(&mut self, path: &Path); - fn insert_content_type_image_icon(&mut self); - fn insert_content_type_image_jpeg(&mut self); - fn insert_content_type_image_png(&mut self); - fn insert_content_type_application_javascript(&mut self); fn insert_content_type_application_json(&mut self); - fn insert_content_type_application_manifest_json(&mut self); - fn insert_content_type_application_pdf(&mut self); - fn insert_content_type_text_css(&mut self); fn insert_content_type_text_csv(&mut self); - fn insert_content_type_text_html(&mut self); fn insert_content_type_text_plain(&mut self); - fn insert_content_type_font_woff2(&mut self); fn insert_content_type_octet_stream(&mut self); } impl HeaderMapExtended for HeaderMap { + fn has_etag(&self, etag: &str) -> bool { + self.get(IF_NONE_MATCH) + .is_some_and(|prev_etag| etag == prev_etag) + } + + fn insert_etag(&mut self, etag: &str) { + self.insert(header::ETAG, etag.parse().unwrap()); + } + fn insert_cache_control(&mut self, value: &str) { self.insert(header::CACHE_CONTROL, value.parse().unwrap()); } @@ -40,99 +36,22 @@ impl HeaderMapExtended for HeaderMap { self.insert_cache_control("public, max-age=1, must-revalidate"); } - fn insert_cache_control_immutable(&mut self) { - self.insert_cache_control("public, max-age=31536000, immutable"); - } - fn insert_content_disposition_attachment(&mut self) { self.insert(header::CONTENT_DISPOSITION, "attachment".parse().unwrap()); } - fn insert_etag(&mut self, etag: &str) { - self.insert(header::ETAG, etag.parse().unwrap()); - } - - fn has_etag(&self, etag: &str) -> bool { - self.get(IF_NONE_MATCH) - .is_some_and(|prev_etag| etag == prev_etag) - } - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types - fn insert_content_type(&mut self, path: &Path) { - match path - .extension() - .map(|s| s.to_str().unwrap_or_default()) - .unwrap_or_default() - { - "js" | "mjs" => self.insert_content_type_application_javascript(), - "json" | "map" => self.insert_content_type_application_json(), - "html" => self.insert_content_type_text_html(), - "css" => self.insert_content_type_text_css(), - "toml" | "txt" => self.insert_content_type_text_plain(), - "pdf" => self.insert_content_type_application_pdf(), - "woff2" => self.insert_content_type_font_woff2(), - "ico" => self.insert_content_type_image_icon(), - "jpg" | "jpeg" => self.insert_content_type_image_jpeg(), - "png" => self.insert_content_type_image_png(), - "webmanifest" => self.insert_content_type_application_manifest_json(), - _ => {} - } - } - - fn insert_content_type_image_icon(&mut self) { - self.insert(header::CONTENT_TYPE, "image/x-icon".parse().unwrap()); - } - - fn insert_content_type_image_jpeg(&mut self) { - self.insert(header::CONTENT_TYPE, "image/jpeg".parse().unwrap()); - } - - fn insert_content_type_image_png(&mut self) { - self.insert(header::CONTENT_TYPE, "image/png".parse().unwrap()); - } - - fn insert_content_type_application_javascript(&mut self) { - self.insert( - header::CONTENT_TYPE, - "application/javascript".parse().unwrap(), - ); - } - fn insert_content_type_application_json(&mut self) { self.insert(header::CONTENT_TYPE, "application/json".parse().unwrap()); } - fn insert_content_type_application_manifest_json(&mut self) { - self.insert( - header::CONTENT_TYPE, - "application/manifest+json".parse().unwrap(), - ); - } - - fn insert_content_type_application_pdf(&mut self) { - self.insert(header::CONTENT_TYPE, "application/pdf".parse().unwrap()); - } - - fn insert_content_type_text_css(&mut self) { - self.insert(header::CONTENT_TYPE, "text/css".parse().unwrap()); - } - fn insert_content_type_text_csv(&mut self) { self.insert(header::CONTENT_TYPE, "text/csv".parse().unwrap()); } - fn insert_content_type_text_html(&mut self) { - self.insert(header::CONTENT_TYPE, "text/html".parse().unwrap()); - } - fn insert_content_type_text_plain(&mut self) { self.insert(header::CONTENT_TYPE, "text/plain".parse().unwrap()); } - fn insert_content_type_font_woff2(&mut self) { - self.insert(header::CONTENT_TYPE, "font/woff2".parse().unwrap()); - } - fn insert_content_type_octet_stream(&mut self) { self.insert( header::CONTENT_TYPE, diff --git a/crates/brk_server/src/files/mod.rs b/crates/brk_server/src/files/mod.rs deleted file mode 100644 index f550c3f06..000000000 --- a/crates/brk_server/src/files/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -use aide::axum::ApiRouter; -use axum::{response::Redirect, routing::get}; - -use super::AppState; - -mod file; -mod website; - -use file::{file_handler, index_handler}; -pub use website::*; - -pub trait FilesRoutes { - fn add_files_routes(self, website: &Website) -> Self; -} - -impl FilesRoutes for ApiRouter { - fn add_files_routes(self, website: &Website) -> Self { - if website.is_enabled() { - self.route("/{*path}", get(file_handler)) - .route("/", get(index_handler)) - } else { - self.route("/", get(Redirect::temporary("/api"))) - } - } -} diff --git a/crates/brk_server/src/lib.rs b/crates/brk_server/src/lib.rs index 323cb6e80..99a612885 100644 --- a/crates/brk_server/src/lib.rs +++ b/crates/brk_server/src/lib.rs @@ -18,7 +18,6 @@ use axum::{ serve, }; use brk_query::AsyncQuery; -use include_dir::{Dir, include_dir}; use quick_cache::sync::Cache; use tokio::net::TcpListener; use tower_http::{ @@ -29,23 +28,17 @@ use tower_http::{ use tower_layer::Layer; use tracing::{error, info}; -/// Embedded website assets -pub static EMBEDDED_WEBSITE: Dir = include_dir!("$CARGO_MANIFEST_DIR/website"); - mod api; pub mod cache; mod error; mod extended; -mod files; mod state; use api::*; pub use brk_types::Port; +pub use brk_website::Website; pub use cache::{CacheParams, CacheStrategy}; pub use error::{Error, Result}; -use extended::*; -use files::FilesRoutes; -pub use files::Website; use state::*; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -69,10 +62,7 @@ impl Server { pub async fn serve(self, port: Option) -> brk_error::Result<()> { let state = self.0; - let compression_layer = CompressionLayer::new() - .br(true) - .gzip(true) - .zstd(true); + let compression_layer = CompressionLayer::new().br(true).gzip(true).zstd(true); let response_uri_layer = axum::middleware::from_fn( async |request: Request, next: Next| -> Response { @@ -107,35 +97,23 @@ impl Server { .on_eos(()); let vecs = state.query.inner().vecs(); - let router = ApiRouter::new() - .add_api_routes() - .add_files_routes(&state.website) - .route( - "/discord", - get(Redirect::temporary("https://discord.gg/WACpShCB7M")), - ) - .route("/crate", get(Redirect::temporary("https://crates.io/crates/brk"))) - .route( - "/status", - get(Redirect::temporary("https://status.bitview.space")), - ) - .route("/github", get(Redirect::temporary("https://github.com/bitcoinresearchkit/brk"))) - .route("/changelog", get(Redirect::temporary("https://github.com/bitcoinresearchkit/brk/blob/main/docs/CHANGELOG.md"))) - .route( - "/install", - get(Redirect::temporary("https://github.com/bitcoinresearchkit/brk/blob/main/crates/brk_cli/README.md#brk_cli")), - ) - .route( - "/service", - get(Redirect::temporary("https://github.com/bitcoinresearchkit/brk?tab=readme-ov-file#professional-hosting")), - ) - .route("/nostr", get(Redirect::temporary("https://primal.net/p/npub1jagmm3x39lmwfnrtvxcs9ac7g300y3dusv9lgzhk2e4x5frpxlrqa73v44"))) + + let website_router = brk_website::router(state.website.clone()); + let mut router = ApiRouter::new().add_api_routes(); + if !state.website.is_enabled() { + router = router.route("/", get(Redirect::temporary("/api"))); + } + let router = router .with_state(state) + .merge(website_router) .layer(CatchPanicLayer::new()) .layer(compression_layer) .layer(response_uri_layer) .layer(trace_layer) - .layer(TimeoutLayer::with_status_code(StatusCode::GATEWAY_TIMEOUT, Duration::from_secs(5))) + .layer(TimeoutLayer::with_status_code( + StatusCode::GATEWAY_TIMEOUT, + Duration::from_secs(5), + )) .layer(CorsLayer::permissive()); let (listener, port) = match port { diff --git a/crates/brk_website/Cargo.toml b/crates/brk_website/Cargo.toml new file mode 100644 index 000000000..9622f3ce6 --- /dev/null +++ b/crates/brk_website/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "brk_website" +description = "Standalone website server for BRK" +version.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +include = ["src/**/*", "website/**/*", "examples/**/*", "Cargo.toml", "README.md"] + +[dependencies] +axum = { workspace = true } +include_dir = "0.7" +importmap = { version = "0.3.0", features = ["embedded"] } +tracing = { workspace = true } + +[dev-dependencies] +brk_logger = { workspace = true } +tokio = { workspace = true } +tower-http = { workspace = true } +tower-layer = { workspace = true } diff --git a/crates/brk_website/README.md b/crates/brk_website/README.md new file mode 100644 index 000000000..2ff246df1 --- /dev/null +++ b/crates/brk_website/README.md @@ -0,0 +1,44 @@ +# brk_website + +Website serving for BRK with minimal dependencies. + +## Features + +- **Embedded assets**: Website files compiled into binary +- **Filesystem mode**: Serve from custom path for development +- **SPA support**: Routes without extensions fallback to index.html +- **ImportMap**: Auto-generates import maps for hashed assets + +## Usage + +```rust,ignore +use brk_website::{Website, router}; + +// Create router for website +let website_router = router(Website::Default); + +// Merge with your app +let app = your_api_router.merge(website_router); +``` + +## Website Enum + +| Variant | Description | +|---------|-------------| +| `Default` | Filesystem in debug, embedded in release | +| `Filesystem(path)` | Always serve from specified path | +| `Disabled` | No routes registered | + +## Standalone Server + +See the `website` example for a complete standalone server with compression, tracing, and other middleware. + +```sh +cargo run -p brk_website --example website +``` + +## Dependencies + +- `axum` - HTTP routing +- `include_dir` - embedded assets +- `importmap` - asset hashing diff --git a/crates/brk_website/examples/website.rs b/crates/brk_website/examples/website.rs new file mode 100644 index 000000000..d1fd067f4 --- /dev/null +++ b/crates/brk_website/examples/website.rs @@ -0,0 +1,100 @@ +use std::time::Duration; + +use axum::{ + ServiceExt, + body::Body, + http::{Request, Response, StatusCode, Uri}, + middleware::Next, +}; +use brk_website::{Website, router}; +use tokio::net::TcpListener; +use tower_http::{ + catch_panic::CatchPanicLayer, + classify::ServerErrorsFailureClass, + compression::CompressionLayer, + cors::CorsLayer, + normalize_path::NormalizePathLayer, + timeout::TimeoutLayer, + trace::TraceLayer, +}; +use tower_layer::Layer; +use tracing::{error, info}; + +#[tokio::main] +async fn main() -> std::io::Result<()> { + let _ = brk_logger::init(None); + + // Use the embedded website (default in release mode) + // Or use Website::Filesystem(path) to serve from a custom path + let website = Website::Default; + + if !website.is_enabled() { + eprintln!("Website is disabled"); + return Ok(()); + } + + website.log(); + + let compression_layer = CompressionLayer::new() + .br(true) + .gzip(true) + .zstd(true); + + let response_uri_layer = axum::middleware::from_fn( + async |request: Request, next: Next| -> Response { + let uri = request.uri().clone(); + let mut response = next.run(request).await; + response.extensions_mut().insert(uri); + response + }, + ); + + let trace_layer = TraceLayer::new_for_http() + .on_request(()) + .on_response( + |response: &Response, latency: Duration, _: &tracing::Span| { + let status = response.status().as_u16(); + let Some(uri) = response.extensions().get::() else { + return; + }; + match response.status() { + StatusCode::OK + | StatusCode::NOT_MODIFIED + | StatusCode::TEMPORARY_REDIRECT + | StatusCode::PERMANENT_REDIRECT => info!(status, %uri, ?latency), + _ => error!(status, %uri, ?latency), + } + }, + ) + .on_body_chunk(()) + .on_failure( + |error: ServerErrorsFailureClass, latency: Duration, _: &tracing::Span| { + error!(?error, ?latency, "request failed"); + }, + ) + .on_eos(()); + + let app = router(website) + .layer(CatchPanicLayer::new()) + .layer(compression_layer) + .layer(response_uri_layer) + .layer(trace_layer) + .layer(TimeoutLayer::with_status_code( + StatusCode::GATEWAY_TIMEOUT, + Duration::from_secs(5), + )) + .layer(CorsLayer::permissive()); + + let port = 3110; + let listener = TcpListener::bind(format!("0.0.0.0:{port}")).await?; + + info!("website server listening on port {port}"); + + let service = NormalizePathLayer::trim_trailing_slash().layer(app); + + axum::serve( + listener, + ServiceExt::>::into_make_service(service), + ) + .await +} diff --git a/crates/brk_website/src/error.rs b/crates/brk_website/src/error.rs new file mode 100644 index 000000000..abc92f549 --- /dev/null +++ b/crates/brk_website/src/error.rs @@ -0,0 +1,23 @@ +use axum::{ + body::Body, + http::{Response, StatusCode}, + response::IntoResponse, +}; + +/// Website result type. +pub type Result = std::result::Result; + +/// Website error type that maps to HTTP status codes. +pub struct Error(StatusCode, String); + +impl Error { + pub fn not_found(msg: impl Into) -> Self { + Self(StatusCode::NOT_FOUND, msg.into()) + } +} + +impl IntoResponse for Error { + fn into_response(self) -> Response { + (self.0, self.1).into_response() + } +} diff --git a/crates/brk_server/src/files/file.rs b/crates/brk_website/src/handlers.rs similarity index 67% rename from crates/brk_server/src/files/file.rs rename to crates/brk_website/src/handlers.rs index b62130520..ddeb11e06 100644 --- a/crates/brk_server/src/files/file.rs +++ b/crates/brk_website/src/handlers.rs @@ -1,23 +1,23 @@ use std::path::Path; -use axum::{body::Body, extract::State, response::Response}; +use axum::{body::Body, extract::State, http::Response}; -use crate::{AppState, HeaderMapExtended, Result}; +use crate::{HeaderMapExtended, Result, Website}; pub async fn file_handler( - State(state): State, + State(website): State, path: axum::extract::Path, -) -> Result { - serve(&state, &path.0) +) -> Result> { + serve(&website, &path.0) } -pub async fn index_handler(State(state): State) -> Result { - serve(&state, "") +pub async fn index_handler(State(website): State) -> Result> { + serve(&website, "") } -fn serve(state: &AppState, path: &str) -> Result { +fn serve(website: &Website, path: &str) -> Result> { let path = sanitize(path); - let content = state.website.get_file(&path)?; + let content = website.get_file(&path)?; let mut response = Response::new(Body::from(content)); let headers = response.headers_mut(); diff --git a/crates/brk_website/src/headers.rs b/crates/brk_website/src/headers.rs new file mode 100644 index 000000000..91ca99653 --- /dev/null +++ b/crates/brk_website/src/headers.rs @@ -0,0 +1,53 @@ +use std::path::Path; + +use axum::http::{HeaderMap, header}; + +pub trait HeaderMapExtended { + fn insert_cache_control_must_revalidate(&mut self); + fn insert_cache_control_immutable(&mut self); + fn insert_content_type(&mut self, path: &Path); + fn insert_content_type_text_html(&mut self); +} + +impl HeaderMapExtended for HeaderMap { + fn insert_cache_control_must_revalidate(&mut self) { + self.insert( + header::CACHE_CONTROL, + "public, max-age=1, must-revalidate".parse().unwrap(), + ); + } + + fn insert_cache_control_immutable(&mut self) { + self.insert( + header::CACHE_CONTROL, + "public, max-age=31536000, immutable".parse().unwrap(), + ); + } + + fn insert_content_type(&mut self, path: &Path) { + let content_type = match path + .extension() + .map(|s| s.to_str().unwrap_or_default()) + .unwrap_or_default() + { + "js" | "mjs" => "application/javascript", + "json" | "map" => "application/json", + "html" => "text/html", + "css" => "text/css", + "toml" | "txt" => "text/plain", + "pdf" => "application/pdf", + "woff2" => "font/woff2", + "ico" => "image/x-icon", + "jpg" | "jpeg" => "image/jpeg", + "png" => "image/png", + "svg" => "image/svg+xml", + "webmanifest" => "application/manifest+json", + _ => return, + }; + self.insert(header::CONTENT_TYPE, content_type.parse().unwrap()); + } + + fn insert_content_type_text_html(&mut self) { + self.insert(header::CONTENT_TYPE, "text/html".parse().unwrap()); + } +} diff --git a/crates/brk_website/src/lib.rs b/crates/brk_website/src/lib.rs new file mode 100644 index 000000000..10f1b176c --- /dev/null +++ b/crates/brk_website/src/lib.rs @@ -0,0 +1,17 @@ +//! Standalone website serving for BRK. +//! +//! This crate provides website serving without any BRK data layer dependencies. +//! It can serve the embedded website or from a filesystem path. +//! +//! See the `website` example for how to run a standalone server. + +mod error; +mod handlers; +mod headers; +mod router; +mod website; + +pub use error::{Error, Result}; +pub use headers::HeaderMapExtended; +pub use router::router; +pub use website::{EMBEDDED_WEBSITE, Website}; diff --git a/crates/brk_website/src/router.rs b/crates/brk_website/src/router.rs new file mode 100644 index 000000000..dd722473b --- /dev/null +++ b/crates/brk_website/src/router.rs @@ -0,0 +1,17 @@ +use axum::{Router, routing::get}; + +use crate::{Website, handlers::{file_handler, index_handler}}; + +/// Create a router for serving the website. +/// +/// Returns an empty router if the website is disabled. +pub fn router(website: Website) -> Router { + if website.is_enabled() { + Router::new() + .route("/{*path}", get(file_handler)) + .route("/", get(index_handler)) + .with_state(website) + } else { + Router::new() + } +} diff --git a/crates/brk_server/src/files/website.rs b/crates/brk_website/src/website.rs similarity index 89% rename from crates/brk_server/src/files/website.rs rename to crates/brk_website/src/website.rs index 253e1aa8d..9fc6adb31 100644 --- a/crates/brk_server/src/files/website.rs +++ b/crates/brk_website/src/website.rs @@ -5,17 +5,22 @@ use std::{ }; use importmap::ImportMap; +use include_dir::{Dir, include_dir}; use tracing::{error, info}; -use crate::{EMBEDDED_WEBSITE, Error, Result}; +use crate::{Error, Result}; + +/// Embedded website assets +pub static EMBEDDED_WEBSITE: Dir = include_dir!("$CARGO_MANIFEST_DIR/website"); /// Cached index.html with importmap injected static INDEX_HTML: OnceLock = OnceLock::new(); /// Source for serving the website -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub enum Website { Disabled, + #[default] Default, Filesystem(PathBuf), } @@ -42,7 +47,12 @@ impl Website { } /// Get file content by path (handles hash-stripping, SPA fallback, importmap) + /// + /// Returns an error if the website is disabled. pub fn get_file(&self, path: &str) -> Result> { + if !self.is_enabled() { + return Err(Error::not_found("Website is disabled")); + } match self.filesystem_path() { None => self.get_embedded(path), Some(base) => self.get_filesystem(&base, path), @@ -52,15 +62,15 @@ impl Website { /// Log which website source is being used (call once at startup) pub fn log(&self) { match self { - Self::Disabled => info!("Website: disabled"), + Self::Disabled => info!("website: disabled"), Self::Default => { if let Some(p) = self.filesystem_path() { - info!("Website: filesystem ({})", p.display()); + info!("website: filesystem ({})", p.display()); } else { - info!("Website: embedded"); + info!("website: embedded"); } } - Self::Filesystem(p) => info!("Website: filesystem ({})", p.display()), + Self::Filesystem(p) => info!("website: filesystem ({})", p.display()), } } diff --git a/crates/brk_server/website b/crates/brk_website/website similarity index 100% rename from crates/brk_server/website rename to crates/brk_website/website diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index 6f9187c2c..53462729f 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -1645,59 +1645,6 @@ function createPrice111dSmaPattern(client, acc) { }; } -/** - * @typedef {Object} PercentilesPattern - * @property {MetricPattern4} pct05 - * @property {MetricPattern4} pct10 - * @property {MetricPattern4} pct15 - * @property {MetricPattern4} pct20 - * @property {MetricPattern4} pct25 - * @property {MetricPattern4} pct30 - * @property {MetricPattern4} pct35 - * @property {MetricPattern4} pct40 - * @property {MetricPattern4} pct45 - * @property {MetricPattern4} pct50 - * @property {MetricPattern4} pct55 - * @property {MetricPattern4} pct60 - * @property {MetricPattern4} pct65 - * @property {MetricPattern4} pct70 - * @property {MetricPattern4} pct75 - * @property {MetricPattern4} pct80 - * @property {MetricPattern4} pct85 - * @property {MetricPattern4} pct90 - * @property {MetricPattern4} pct95 - */ - -/** - * Create a PercentilesPattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {PercentilesPattern} - */ -function createPercentilesPattern(client, acc) { - return { - pct05: createMetricPattern4(client, _m(acc, 'pct05')), - pct10: createMetricPattern4(client, _m(acc, 'pct10')), - pct15: createMetricPattern4(client, _m(acc, 'pct15')), - pct20: createMetricPattern4(client, _m(acc, 'pct20')), - pct25: createMetricPattern4(client, _m(acc, 'pct25')), - pct30: createMetricPattern4(client, _m(acc, 'pct30')), - pct35: createMetricPattern4(client, _m(acc, 'pct35')), - pct40: createMetricPattern4(client, _m(acc, 'pct40')), - pct45: createMetricPattern4(client, _m(acc, 'pct45')), - pct50: createMetricPattern4(client, _m(acc, 'pct50')), - pct55: createMetricPattern4(client, _m(acc, 'pct55')), - pct60: createMetricPattern4(client, _m(acc, 'pct60')), - pct65: createMetricPattern4(client, _m(acc, 'pct65')), - pct70: createMetricPattern4(client, _m(acc, 'pct70')), - pct75: createMetricPattern4(client, _m(acc, 'pct75')), - pct80: createMetricPattern4(client, _m(acc, 'pct80')), - pct85: createMetricPattern4(client, _m(acc, 'pct85')), - pct90: createMetricPattern4(client, _m(acc, 'pct90')), - pct95: createMetricPattern4(client, _m(acc, 'pct95')), - }; -} - /** * @typedef {Object} ActivePriceRatioPattern * @property {MetricPattern4} ratio @@ -1751,6 +1698,59 @@ function createActivePriceRatioPattern(client, acc) { }; } +/** + * @typedef {Object} PercentilesPattern + * @property {MetricPattern4} pct05 + * @property {MetricPattern4} pct10 + * @property {MetricPattern4} pct15 + * @property {MetricPattern4} pct20 + * @property {MetricPattern4} pct25 + * @property {MetricPattern4} pct30 + * @property {MetricPattern4} pct35 + * @property {MetricPattern4} pct40 + * @property {MetricPattern4} pct45 + * @property {MetricPattern4} pct50 + * @property {MetricPattern4} pct55 + * @property {MetricPattern4} pct60 + * @property {MetricPattern4} pct65 + * @property {MetricPattern4} pct70 + * @property {MetricPattern4} pct75 + * @property {MetricPattern4} pct80 + * @property {MetricPattern4} pct85 + * @property {MetricPattern4} pct90 + * @property {MetricPattern4} pct95 + */ + +/** + * Create a PercentilesPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {PercentilesPattern} + */ +function createPercentilesPattern(client, acc) { + return { + pct05: createMetricPattern4(client, _m(acc, 'pct05')), + pct10: createMetricPattern4(client, _m(acc, 'pct10')), + pct15: createMetricPattern4(client, _m(acc, 'pct15')), + pct20: createMetricPattern4(client, _m(acc, 'pct20')), + pct25: createMetricPattern4(client, _m(acc, 'pct25')), + pct30: createMetricPattern4(client, _m(acc, 'pct30')), + pct35: createMetricPattern4(client, _m(acc, 'pct35')), + pct40: createMetricPattern4(client, _m(acc, 'pct40')), + pct45: createMetricPattern4(client, _m(acc, 'pct45')), + pct50: createMetricPattern4(client, _m(acc, 'pct50')), + pct55: createMetricPattern4(client, _m(acc, 'pct55')), + pct60: createMetricPattern4(client, _m(acc, 'pct60')), + pct65: createMetricPattern4(client, _m(acc, 'pct65')), + pct70: createMetricPattern4(client, _m(acc, 'pct70')), + pct75: createMetricPattern4(client, _m(acc, 'pct75')), + pct80: createMetricPattern4(client, _m(acc, 'pct80')), + pct85: createMetricPattern4(client, _m(acc, 'pct85')), + pct90: createMetricPattern4(client, _m(acc, 'pct90')), + pct95: createMetricPattern4(client, _m(acc, 'pct95')), + }; +} + /** * @typedef {Object} RelativePattern5 * @property {MetricPattern1} negUnrealizedLossRelToMarketCap @@ -2005,6 +2005,45 @@ function createBitcoinPattern(client, acc) { }; } +/** + * @template T + * @typedef {Object} ClassAveragePricePattern + * @property {MetricPattern4} _2015 + * @property {MetricPattern4} _2016 + * @property {MetricPattern4} _2017 + * @property {MetricPattern4} _2018 + * @property {MetricPattern4} _2019 + * @property {MetricPattern4} _2020 + * @property {MetricPattern4} _2021 + * @property {MetricPattern4} _2022 + * @property {MetricPattern4} _2023 + * @property {MetricPattern4} _2024 + * @property {MetricPattern4} _2025 + */ + +/** + * Create a ClassAveragePricePattern pattern node + * @template T + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {ClassAveragePricePattern} + */ +function createClassAveragePricePattern(client, acc) { + return { + _2015: createMetricPattern4(client, _m(acc, '2015_average_price')), + _2016: createMetricPattern4(client, _m(acc, '2016_average_price')), + _2017: createMetricPattern4(client, _m(acc, '2017_average_price')), + _2018: createMetricPattern4(client, _m(acc, '2018_average_price')), + _2019: createMetricPattern4(client, _m(acc, '2019_average_price')), + _2020: createMetricPattern4(client, _m(acc, '2020_average_price')), + _2021: createMetricPattern4(client, _m(acc, '2021_average_price')), + _2022: createMetricPattern4(client, _m(acc, '2022_average_price')), + _2023: createMetricPattern4(client, _m(acc, '2023_average_price')), + _2024: createMetricPattern4(client, _m(acc, '2024_average_price')), + _2025: createMetricPattern4(client, _m(acc, '2025_average_price')), + }; +} + /** * @template T * @typedef {Object} DollarsPattern @@ -2045,41 +2084,37 @@ function createDollarsPattern(client, acc) { } /** - * @template T - * @typedef {Object} ClassAveragePricePattern - * @property {MetricPattern4} _2015 - * @property {MetricPattern4} _2016 - * @property {MetricPattern4} _2017 - * @property {MetricPattern4} _2018 - * @property {MetricPattern4} _2019 - * @property {MetricPattern4} _2020 - * @property {MetricPattern4} _2021 - * @property {MetricPattern4} _2022 - * @property {MetricPattern4} _2023 - * @property {MetricPattern4} _2024 - * @property {MetricPattern4} _2025 + * @typedef {Object} RelativePattern2 + * @property {MetricPattern1} negUnrealizedLossRelToOwnMarketCap + * @property {MetricPattern1} negUnrealizedLossRelToOwnTotalUnrealizedPnl + * @property {MetricPattern1} netUnrealizedPnlRelToOwnMarketCap + * @property {MetricPattern1} netUnrealizedPnlRelToOwnTotalUnrealizedPnl + * @property {MetricPattern1} supplyInLossRelToOwnSupply + * @property {MetricPattern1} supplyInProfitRelToOwnSupply + * @property {MetricPattern1} unrealizedLossRelToOwnMarketCap + * @property {MetricPattern1} unrealizedLossRelToOwnTotalUnrealizedPnl + * @property {MetricPattern1} unrealizedProfitRelToOwnMarketCap + * @property {MetricPattern1} unrealizedProfitRelToOwnTotalUnrealizedPnl */ /** - * Create a ClassAveragePricePattern pattern node - * @template T + * Create a RelativePattern2 pattern node * @param {BrkClientBase} client * @param {string} acc - Accumulated metric name - * @returns {ClassAveragePricePattern} + * @returns {RelativePattern2} */ -function createClassAveragePricePattern(client, acc) { +function createRelativePattern2(client, acc) { return { - _2015: createMetricPattern4(client, _m(acc, '2015_returns')), - _2016: createMetricPattern4(client, _m(acc, '2016_returns')), - _2017: createMetricPattern4(client, _m(acc, '2017_returns')), - _2018: createMetricPattern4(client, _m(acc, '2018_returns')), - _2019: createMetricPattern4(client, _m(acc, '2019_returns')), - _2020: createMetricPattern4(client, _m(acc, '2020_returns')), - _2021: createMetricPattern4(client, _m(acc, '2021_returns')), - _2022: createMetricPattern4(client, _m(acc, '2022_returns')), - _2023: createMetricPattern4(client, _m(acc, '2023_returns')), - _2024: createMetricPattern4(client, _m(acc, '2024_returns')), - _2025: createMetricPattern4(client, _m(acc, '2025_returns')), + negUnrealizedLossRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_market_cap')), + negUnrealizedLossRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_total_unrealized_pnl')), + netUnrealizedPnlRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_market_cap')), + netUnrealizedPnlRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_total_unrealized_pnl')), + supplyInLossRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')), + supplyInProfitRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')), + unrealizedLossRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_market_cap')), + unrealizedLossRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_total_unrealized_pnl')), + unrealizedProfitRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_market_cap')), + unrealizedProfitRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_total_unrealized_pnl')), }; } @@ -2118,41 +2153,6 @@ function createRelativePattern(client, acc) { }; } -/** - * @typedef {Object} RelativePattern2 - * @property {MetricPattern1} negUnrealizedLossRelToOwnMarketCap - * @property {MetricPattern1} negUnrealizedLossRelToOwnTotalUnrealizedPnl - * @property {MetricPattern1} netUnrealizedPnlRelToOwnMarketCap - * @property {MetricPattern1} netUnrealizedPnlRelToOwnTotalUnrealizedPnl - * @property {MetricPattern1} supplyInLossRelToOwnSupply - * @property {MetricPattern1} supplyInProfitRelToOwnSupply - * @property {MetricPattern1} unrealizedLossRelToOwnMarketCap - * @property {MetricPattern1} unrealizedLossRelToOwnTotalUnrealizedPnl - * @property {MetricPattern1} unrealizedProfitRelToOwnMarketCap - * @property {MetricPattern1} unrealizedProfitRelToOwnTotalUnrealizedPnl - */ - -/** - * Create a RelativePattern2 pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {RelativePattern2} - */ -function createRelativePattern2(client, acc) { - return { - negUnrealizedLossRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_market_cap')), - negUnrealizedLossRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_total_unrealized_pnl')), - netUnrealizedPnlRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_market_cap')), - netUnrealizedPnlRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_total_unrealized_pnl')), - supplyInLossRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')), - supplyInProfitRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')), - unrealizedLossRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_market_cap')), - unrealizedLossRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_total_unrealized_pnl')), - unrealizedProfitRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_market_cap')), - unrealizedProfitRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_total_unrealized_pnl')), - }; -} - /** * @template T * @typedef {Object} CountPattern2 @@ -2387,29 +2387,29 @@ function createPeriodCagrPattern(client, acc) { } /** - * @typedef {Object} _0satsPattern2 + * @typedef {Object} _10yTo12yPattern * @property {ActivityPattern2} activity - * @property {CostBasisPattern} costBasis + * @property {CostBasisPattern2} costBasis * @property {OutputsPattern} outputs - * @property {RealizedPattern} realized - * @property {RelativePattern4} relative + * @property {RealizedPattern2} realized + * @property {RelativePattern2} relative * @property {SupplyPattern2} supply * @property {UnrealizedPattern} unrealized */ /** - * Create a _0satsPattern2 pattern node + * Create a _10yTo12yPattern pattern node * @param {BrkClientBase} client * @param {string} acc - Accumulated metric name - * @returns {_0satsPattern2} + * @returns {_10yTo12yPattern} */ -function create_0satsPattern2(client, acc) { +function create_10yTo12yPattern(client, acc) { return { activity: createActivityPattern2(client, acc), - costBasis: createCostBasisPattern(client, acc), + costBasis: createCostBasisPattern2(client, acc), outputs: createOutputsPattern(client, _m(acc, 'utxo_count')), - realized: createRealizedPattern(client, acc), - relative: createRelativePattern4(client, _m(acc, 'supply_in')), + realized: createRealizedPattern2(client, acc), + relative: createRelativePattern2(client, acc), supply: createSupplyPattern2(client, _m(acc, 'supply')), unrealized: createUnrealizedPattern(client, acc), }; @@ -2445,29 +2445,29 @@ function create_10yPattern(client, acc) { } /** - * @typedef {Object} _10yTo12yPattern + * @typedef {Object} _0satsPattern2 * @property {ActivityPattern2} activity - * @property {CostBasisPattern2} costBasis + * @property {CostBasisPattern} costBasis * @property {OutputsPattern} outputs - * @property {RealizedPattern2} realized - * @property {RelativePattern2} relative + * @property {RealizedPattern} realized + * @property {RelativePattern4} relative * @property {SupplyPattern2} supply * @property {UnrealizedPattern} unrealized */ /** - * Create a _10yTo12yPattern pattern node + * Create a _0satsPattern2 pattern node * @param {BrkClientBase} client * @param {string} acc - Accumulated metric name - * @returns {_10yTo12yPattern} + * @returns {_0satsPattern2} */ -function create_10yTo12yPattern(client, acc) { +function create_0satsPattern2(client, acc) { return { activity: createActivityPattern2(client, acc), - costBasis: createCostBasisPattern2(client, acc), + costBasis: createCostBasisPattern(client, acc), outputs: createOutputsPattern(client, _m(acc, 'utxo_count')), - realized: createRealizedPattern2(client, acc), - relative: createRelativePattern2(client, acc), + realized: createRealizedPattern(client, acc), + relative: createRelativePattern4(client, _m(acc, 'supply_in')), supply: createSupplyPattern2(client, _m(acc, 'supply')), unrealized: createUnrealizedPattern(client, acc), }; @@ -2581,27 +2581,6 @@ function createSplitPattern2(client, acc) { }; } -/** - * @typedef {Object} CostBasisPattern2 - * @property {MetricPattern1} max - * @property {MetricPattern1} min - * @property {PercentilesPattern} percentiles - */ - -/** - * Create a CostBasisPattern2 pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {CostBasisPattern2} - */ -function createCostBasisPattern2(client, acc) { - return { - max: createMetricPattern1(client, _m(acc, 'max_cost_basis')), - min: createMetricPattern1(client, _m(acc, 'min_cost_basis')), - percentiles: createPercentilesPattern(client, _m(acc, 'cost_basis')), - }; -} - /** * @typedef {Object} CoinbasePattern * @property {BitcoinPattern} bitcoin @@ -2624,23 +2603,65 @@ function createCoinbasePattern(client, acc) { } /** - * @typedef {Object} ActiveSupplyPattern - * @property {MetricPattern1} bitcoin - * @property {MetricPattern1} dollars - * @property {MetricPattern1} sats + * @typedef {Object} CoinbasePattern2 + * @property {BlockCountPattern} bitcoin + * @property {BlockCountPattern} dollars + * @property {BlockCountPattern} sats */ /** - * Create a ActiveSupplyPattern pattern node + * Create a CoinbasePattern2 pattern node * @param {BrkClientBase} client * @param {string} acc - Accumulated metric name - * @returns {ActiveSupplyPattern} + * @returns {CoinbasePattern2} */ -function createActiveSupplyPattern(client, acc) { +function createCoinbasePattern2(client, acc) { return { - bitcoin: createMetricPattern1(client, _m(acc, 'btc')), - dollars: createMetricPattern1(client, _m(acc, 'usd')), - sats: createMetricPattern1(client, acc), + bitcoin: createBlockCountPattern(client, _m(acc, 'btc')), + dollars: createBlockCountPattern(client, _m(acc, 'usd')), + sats: createBlockCountPattern(client, acc), + }; +} + +/** + * @typedef {Object} SegwitAdoptionPattern + * @property {MetricPattern11} base + * @property {MetricPattern2} cumulative + * @property {MetricPattern2} sum + */ + +/** + * Create a SegwitAdoptionPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {SegwitAdoptionPattern} + */ +function createSegwitAdoptionPattern(client, acc) { + return { + base: createMetricPattern11(client, acc), + cumulative: createMetricPattern2(client, _m(acc, 'cumulative')), + sum: createMetricPattern2(client, _m(acc, 'sum')), + }; +} + +/** + * @typedef {Object} CostBasisPattern2 + * @property {MetricPattern1} max + * @property {MetricPattern1} min + * @property {PercentilesPattern} percentiles + */ + +/** + * Create a CostBasisPattern2 pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {CostBasisPattern2} + */ +function createCostBasisPattern2(client, acc) { + return { + max: createMetricPattern1(client, _m(acc, 'max_cost_basis')), + min: createMetricPattern1(client, _m(acc, 'min_cost_basis')), + percentiles: createPercentilesPattern(client, _m(acc, 'cost_basis')), }; } @@ -2665,27 +2686,6 @@ function createUnclaimedRewardsPattern(client, acc) { }; } -/** - * @typedef {Object} CoinbasePattern2 - * @property {BlockCountPattern} bitcoin - * @property {BlockCountPattern} dollars - * @property {BlockCountPattern} sats - */ - -/** - * Create a CoinbasePattern2 pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {CoinbasePattern2} - */ -function createCoinbasePattern2(client, acc) { - return { - bitcoin: createBlockCountPattern(client, _m(acc, 'btc')), - dollars: createBlockCountPattern(client, _m(acc, 'usd')), - sats: createBlockCountPattern(client, acc), - }; -} - /** * @typedef {Object} _2015Pattern * @property {MetricPattern4} bitcoin @@ -2708,23 +2708,23 @@ function create_2015Pattern(client, acc) { } /** - * @typedef {Object} SegwitAdoptionPattern - * @property {MetricPattern11} base - * @property {MetricPattern2} cumulative - * @property {MetricPattern2} sum + * @typedef {Object} ActiveSupplyPattern + * @property {MetricPattern1} bitcoin + * @property {MetricPattern1} dollars + * @property {MetricPattern1} sats */ /** - * Create a SegwitAdoptionPattern pattern node + * Create a ActiveSupplyPattern pattern node * @param {BrkClientBase} client * @param {string} acc - Accumulated metric name - * @returns {SegwitAdoptionPattern} + * @returns {ActiveSupplyPattern} */ -function createSegwitAdoptionPattern(client, acc) { +function createActiveSupplyPattern(client, acc) { return { - base: createMetricPattern11(client, acc), - cumulative: createMetricPattern2(client, _m(acc, 'cumulative')), - sum: createMetricPattern2(client, _m(acc, 'sum')), + bitcoin: createMetricPattern1(client, _m(acc, 'btc')), + dollars: createMetricPattern1(client, _m(acc, 'usd')), + sats: createMetricPattern1(client, acc), }; } @@ -2747,6 +2747,25 @@ function create_1dReturns1mSdPattern(client, acc) { }; } +/** + * @typedef {Object} SupplyPattern2 + * @property {ActiveSupplyPattern} halved + * @property {ActiveSupplyPattern} total + */ + +/** + * Create a SupplyPattern2 pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {SupplyPattern2} + */ +function createSupplyPattern2(client, acc) { + return { + halved: createActiveSupplyPattern(client, _m(acc, 'halved')), + total: createActiveSupplyPattern(client, acc), + }; +} + /** * @typedef {Object} CostBasisPattern * @property {MetricPattern1} max @@ -2785,25 +2804,6 @@ function createRelativePattern4(client, acc) { }; } -/** - * @typedef {Object} SupplyPattern2 - * @property {ActiveSupplyPattern} halved - * @property {ActiveSupplyPattern} total - */ - -/** - * Create a SupplyPattern2 pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {SupplyPattern2} - */ -function createSupplyPattern2(client, acc) { - return { - halved: createActiveSupplyPattern(client, _m(acc, 'halved')), - total: createActiveSupplyPattern(client, acc), - }; -} - /** * @template T * @typedef {Object} BitcoinPattern2 @@ -2867,23 +2867,6 @@ function createBlockCountPattern(client, acc) { }; } -/** - * @typedef {Object} OutputsPattern - * @property {MetricPattern1} utxoCount - */ - -/** - * Create a OutputsPattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {OutputsPattern} - */ -function createOutputsPattern(client, acc) { - return { - utxoCount: createMetricPattern1(client, acc), - }; -} - /** * @typedef {Object} RealizedPriceExtraPattern * @property {MetricPattern4} ratio @@ -2901,6 +2884,23 @@ function createRealizedPriceExtraPattern(client, acc) { }; } +/** + * @typedef {Object} OutputsPattern + * @property {MetricPattern1} utxoCount + */ + +/** + * Create a OutputsPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {OutputsPattern} + */ +function createOutputsPattern(client, acc) { + return { + utxoCount: createMetricPattern1(client, acc), + }; +} + // Catalog tree typedefs /** @@ -3696,8 +3696,8 @@ function createRealizedPriceExtraPattern(client, acc) { /** * @typedef {Object} MetricsTree_Market_Dca - * @property {MetricsTree_Market_Dca_ClassAveragePrice} classAveragePrice - * @property {ClassAveragePricePattern} classReturns + * @property {ClassAveragePricePattern} classAveragePrice + * @property {MetricsTree_Market_Dca_ClassReturns} classReturns * @property {MetricsTree_Market_Dca_ClassStack} classStack * @property {PeriodAveragePricePattern} periodAveragePrice * @property {PeriodCagrPattern} periodCagr @@ -3707,18 +3707,18 @@ function createRealizedPriceExtraPattern(client, acc) { */ /** - * @typedef {Object} MetricsTree_Market_Dca_ClassAveragePrice - * @property {MetricPattern4} _2015 - * @property {MetricPattern4} _2016 - * @property {MetricPattern4} _2017 - * @property {MetricPattern4} _2018 - * @property {MetricPattern4} _2019 - * @property {MetricPattern4} _2020 - * @property {MetricPattern4} _2021 - * @property {MetricPattern4} _2022 - * @property {MetricPattern4} _2023 - * @property {MetricPattern4} _2024 - * @property {MetricPattern4} _2025 + * @typedef {Object} MetricsTree_Market_Dca_ClassReturns + * @property {MetricPattern4} _2015 + * @property {MetricPattern4} _2016 + * @property {MetricPattern4} _2017 + * @property {MetricPattern4} _2018 + * @property {MetricPattern4} _2019 + * @property {MetricPattern4} _2020 + * @property {MetricPattern4} _2021 + * @property {MetricPattern4} _2022 + * @property {MetricPattern4} _2023 + * @property {MetricPattern4} _2024 + * @property {MetricPattern4} _2025 */ /** @@ -4259,7 +4259,7 @@ function createRealizedPriceExtraPattern(client, acc) { * @extends BrkClientBase */ class BrkClient extends BrkClientBase { - VERSION = "v0.1.0-alpha.3"; + VERSION = "v0.1.0-alpha.6"; INDEXES = /** @type {const} */ ([ "dateindex", @@ -5726,20 +5726,20 @@ class BrkClient extends BrkClientBase { yearsSincePriceAth: createMetricPattern4(this, 'years_since_price_ath'), }, dca: { - classAveragePrice: { - _2015: createMetricPattern4(this, 'dca_class_2015_average_price'), - _2016: createMetricPattern4(this, 'dca_class_2016_average_price'), - _2017: createMetricPattern4(this, 'dca_class_2017_average_price'), - _2018: createMetricPattern4(this, 'dca_class_2018_average_price'), - _2019: createMetricPattern4(this, 'dca_class_2019_average_price'), - _2020: createMetricPattern4(this, 'dca_class_2020_average_price'), - _2021: createMetricPattern4(this, 'dca_class_2021_average_price'), - _2022: createMetricPattern4(this, 'dca_class_2022_average_price'), - _2023: createMetricPattern4(this, 'dca_class_2023_average_price'), - _2024: createMetricPattern4(this, 'dca_class_2024_average_price'), - _2025: createMetricPattern4(this, 'dca_class_2025_average_price'), + classAveragePrice: createClassAveragePricePattern(this, 'dca_class'), + classReturns: { + _2015: createMetricPattern4(this, 'dca_class_2015_returns'), + _2016: createMetricPattern4(this, 'dca_class_2016_returns'), + _2017: createMetricPattern4(this, 'dca_class_2017_returns'), + _2018: createMetricPattern4(this, 'dca_class_2018_returns'), + _2019: createMetricPattern4(this, 'dca_class_2019_returns'), + _2020: createMetricPattern4(this, 'dca_class_2020_returns'), + _2021: createMetricPattern4(this, 'dca_class_2021_returns'), + _2022: createMetricPattern4(this, 'dca_class_2022_returns'), + _2023: createMetricPattern4(this, 'dca_class_2023_returns'), + _2024: createMetricPattern4(this, 'dca_class_2024_returns'), + _2025: createMetricPattern4(this, 'dca_class_2025_returns'), }, - classReturns: createClassAveragePricePattern(this, 'dca_class'), classStack: { _2015: create_2015Pattern(this, 'dca_class_2015_stack'), _2016: create_2015Pattern(this, 'dca_class_2016_stack'), diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index 0639d0b73..0236f056f 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -1883,31 +1883,6 @@ class Price111dSmaPattern: self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'ratio_pct99_usd')) self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, 'ratio')) -class PercentilesPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.pct05: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct05')) - self.pct10: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct10')) - self.pct15: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct15')) - self.pct20: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct20')) - self.pct25: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct25')) - self.pct30: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct30')) - self.pct35: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct35')) - self.pct40: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct40')) - self.pct45: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct45')) - self.pct50: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct50')) - self.pct55: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct55')) - self.pct60: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct60')) - self.pct65: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct65')) - self.pct70: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct70')) - self.pct75: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct75')) - self.pct80: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct80')) - self.pct85: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct85')) - self.pct90: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct90')) - self.pct95: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95')) - class ActivePriceRatioPattern: """Pattern struct for repeated tree structure.""" @@ -1933,6 +1908,31 @@ class ActivePriceRatioPattern: self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct99_usd')) self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, acc) +class PercentilesPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.pct05: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct05')) + self.pct10: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct10')) + self.pct15: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct15')) + self.pct20: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct20')) + self.pct25: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct25')) + self.pct30: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct30')) + self.pct35: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct35')) + self.pct40: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct40')) + self.pct45: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct45')) + self.pct50: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct50')) + self.pct55: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct55')) + self.pct60: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct60')) + self.pct65: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct65')) + self.pct70: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct70')) + self.pct75: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct75')) + self.pct80: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct80')) + self.pct85: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct85')) + self.pct90: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct90')) + self.pct95: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95')) + class RelativePattern5: """Pattern struct for repeated tree structure.""" @@ -2049,6 +2049,23 @@ class BitcoinPattern: self.pct90: MetricPattern6[Bitcoin] = MetricPattern6(client, _m(acc, 'pct90')) self.sum: MetricPattern2[Bitcoin] = MetricPattern2(client, _m(acc, 'sum')) +class ClassAveragePricePattern(Generic[T]): + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self._2015: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2015_average_price')) + self._2016: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2016_average_price')) + self._2017: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2017_average_price')) + self._2018: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2018_average_price')) + self._2019: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2019_average_price')) + self._2020: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2020_average_price')) + self._2021: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2021_average_price')) + self._2022: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2022_average_price')) + self._2023: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2023_average_price')) + self._2024: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2024_average_price')) + self._2025: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2025_average_price')) + class DollarsPattern(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -2066,22 +2083,21 @@ class DollarsPattern(Generic[T]): self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90')) self.sum: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'sum')) -class ClassAveragePricePattern(Generic[T]): +class RelativePattern2: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" - self._2015: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2015_returns')) - self._2016: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2016_returns')) - self._2017: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2017_returns')) - self._2018: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2018_returns')) - self._2019: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2019_returns')) - self._2020: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2020_returns')) - self._2021: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2021_returns')) - self._2022: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2022_returns')) - self._2023: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2023_returns')) - self._2024: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2024_returns')) - self._2025: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2025_returns')) + self.neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_market_cap')) + self.neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_total_unrealized_pnl')) + self.net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_market_cap')) + self.net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_total_unrealized_pnl')) + self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')) + self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')) + self.unrealized_loss_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_market_cap')) + self.unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_total_unrealized_pnl')) + self.unrealized_profit_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_market_cap')) + self.unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_total_unrealized_pnl')) class RelativePattern: """Pattern struct for repeated tree structure.""" @@ -2099,22 +2115,6 @@ class RelativePattern: self.unrealized_loss_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_market_cap')) self.unrealized_profit_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_market_cap')) -class RelativePattern2: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_market_cap')) - self.neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_total_unrealized_pnl')) - self.net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_market_cap')) - self.net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_total_unrealized_pnl')) - self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')) - self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')) - self.unrealized_loss_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_market_cap')) - self.unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_total_unrealized_pnl')) - self.unrealized_profit_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_market_cap')) - self.unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_total_unrealized_pnl')) - class CountPattern2(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -2217,16 +2217,16 @@ class PeriodCagrPattern: self._6y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('6y', acc)) self._8y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('8y', acc)) -class _0satsPattern2: +class _10yTo12yPattern: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" self.activity: ActivityPattern2 = ActivityPattern2(client, acc) - self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc) + self.cost_basis: CostBasisPattern2 = CostBasisPattern2(client, acc) self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count')) - self.realized: RealizedPattern = RealizedPattern(client, acc) - self.relative: RelativePattern4 = RelativePattern4(client, _m(acc, 'supply_in')) + self.realized: RealizedPattern2 = RealizedPattern2(client, acc) + self.relative: RelativePattern2 = RelativePattern2(client, acc) self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) @@ -2243,16 +2243,16 @@ class _10yPattern: self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) -class _10yTo12yPattern: +class _0satsPattern2: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" self.activity: ActivityPattern2 = ActivityPattern2(client, acc) - self.cost_basis: CostBasisPattern2 = CostBasisPattern2(client, acc) + self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc) self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count')) - self.realized: RealizedPattern2 = RealizedPattern2(client, acc) - self.relative: RelativePattern2 = RelativePattern2(client, acc) + self.realized: RealizedPattern = RealizedPattern(client, acc) + self.relative: RelativePattern4 = RelativePattern4(client, _m(acc, 'supply_in')) self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) @@ -2303,15 +2303,6 @@ class SplitPattern2(Generic[T]): self.low: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'low')) self.open: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'open')) -class CostBasisPattern2: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.max: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'max_cost_basis')) - self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis')) - self.percentiles: PercentilesPattern = PercentilesPattern(client, _m(acc, 'cost_basis')) - class CoinbasePattern: """Pattern struct for repeated tree structure.""" @@ -2321,14 +2312,32 @@ class CoinbasePattern: self.dollars: DollarsPattern[Dollars] = DollarsPattern(client, _m(acc, 'usd')) self.sats: DollarsPattern[Sats] = DollarsPattern(client, acc) -class ActiveSupplyPattern: +class CoinbasePattern2: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" - self.bitcoin: MetricPattern1[Bitcoin] = MetricPattern1(client, _m(acc, 'btc')) - self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd')) - self.sats: MetricPattern1[Sats] = MetricPattern1(client, acc) + self.bitcoin: BlockCountPattern[Bitcoin] = BlockCountPattern(client, _m(acc, 'btc')) + self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd')) + self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc) + +class SegwitAdoptionPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.base: MetricPattern11[StoredF32] = MetricPattern11(client, acc) + self.cumulative: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'cumulative')) + self.sum: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'sum')) + +class CostBasisPattern2: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.max: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'max_cost_basis')) + self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis')) + self.percentiles: PercentilesPattern = PercentilesPattern(client, _m(acc, 'cost_basis')) class UnclaimedRewardsPattern: """Pattern struct for repeated tree structure.""" @@ -2339,15 +2348,6 @@ class UnclaimedRewardsPattern: self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd')) self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc) -class CoinbasePattern2: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.bitcoin: BlockCountPattern[Bitcoin] = BlockCountPattern(client, _m(acc, 'btc')) - self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd')) - self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc) - class _2015Pattern: """Pattern struct for repeated tree structure.""" @@ -2357,14 +2357,14 @@ class _2015Pattern: self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'usd')) self.sats: MetricPattern4[Sats] = MetricPattern4(client, acc) -class SegwitAdoptionPattern: +class ActiveSupplyPattern: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" - self.base: MetricPattern11[StoredF32] = MetricPattern11(client, acc) - self.cumulative: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'cumulative')) - self.sum: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'sum')) + self.bitcoin: MetricPattern1[Bitcoin] = MetricPattern1(client, _m(acc, 'btc')) + self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd')) + self.sats: MetricPattern1[Sats] = MetricPattern1(client, acc) class _1dReturns1mSdPattern: """Pattern struct for repeated tree structure.""" @@ -2374,6 +2374,14 @@ class _1dReturns1mSdPattern: self.sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sd')) self.sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sma')) +class SupplyPattern2: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.halved: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'halved')) + self.total: ActiveSupplyPattern = ActiveSupplyPattern(client, acc) + class CostBasisPattern: """Pattern struct for repeated tree structure.""" @@ -2390,14 +2398,6 @@ class RelativePattern4: self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'loss_rel_to_own_supply')) self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'profit_rel_to_own_supply')) -class SupplyPattern2: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.halved: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'halved')) - self.total: ActiveSupplyPattern = ActiveSupplyPattern(client, acc) - class BitcoinPattern2(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -2422,13 +2422,6 @@ class BlockCountPattern(Generic[T]): self.cumulative: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'cumulative')) self.sum: MetricPattern1[T] = MetricPattern1(client, acc) -class OutputsPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.utxo_count: MetricPattern1[StoredU64] = MetricPattern1(client, acc) - class RealizedPriceExtraPattern: """Pattern struct for repeated tree structure.""" @@ -2436,6 +2429,13 @@ class RealizedPriceExtraPattern: """Create pattern node with accumulated metric name.""" self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, acc) +class OutputsPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.utxo_count: MetricPattern1[StoredU64] = MetricPattern1(client, acc) + # Metrics tree classes class MetricsTree_Addresses: @@ -3269,21 +3269,21 @@ class MetricsTree_Market_Ath: self.price_drawdown: MetricPattern3[StoredF32] = MetricPattern3(client, 'price_drawdown') self.years_since_price_ath: MetricPattern4[StoredF32] = MetricPattern4(client, 'years_since_price_ath') -class MetricsTree_Market_Dca_ClassAveragePrice: +class MetricsTree_Market_Dca_ClassReturns: """Metrics tree node.""" def __init__(self, client: BrkClientBase, base_path: str = ''): - self._2015: MetricPattern4[Dollars] = MetricPattern4(client, 'dca_class_2015_average_price') - self._2016: MetricPattern4[Dollars] = MetricPattern4(client, 'dca_class_2016_average_price') - self._2017: MetricPattern4[Dollars] = MetricPattern4(client, 'dca_class_2017_average_price') - self._2018: MetricPattern4[Dollars] = MetricPattern4(client, 'dca_class_2018_average_price') - self._2019: MetricPattern4[Dollars] = MetricPattern4(client, 'dca_class_2019_average_price') - self._2020: MetricPattern4[Dollars] = MetricPattern4(client, 'dca_class_2020_average_price') - self._2021: MetricPattern4[Dollars] = MetricPattern4(client, 'dca_class_2021_average_price') - self._2022: MetricPattern4[Dollars] = MetricPattern4(client, 'dca_class_2022_average_price') - self._2023: MetricPattern4[Dollars] = MetricPattern4(client, 'dca_class_2023_average_price') - self._2024: MetricPattern4[Dollars] = MetricPattern4(client, 'dca_class_2024_average_price') - self._2025: MetricPattern4[Dollars] = MetricPattern4(client, 'dca_class_2025_average_price') + self._2015: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2015_returns') + self._2016: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2016_returns') + self._2017: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2017_returns') + self._2018: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2018_returns') + self._2019: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2019_returns') + self._2020: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2020_returns') + self._2021: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2021_returns') + self._2022: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2022_returns') + self._2023: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2023_returns') + self._2024: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2024_returns') + self._2025: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2025_returns') class MetricsTree_Market_Dca_ClassStack: """Metrics tree node.""" @@ -3305,8 +3305,8 @@ class MetricsTree_Market_Dca: """Metrics tree node.""" def __init__(self, client: BrkClientBase, base_path: str = ''): - self.class_average_price: MetricsTree_Market_Dca_ClassAveragePrice = MetricsTree_Market_Dca_ClassAveragePrice(client) - self.class_returns: ClassAveragePricePattern[StoredF32] = ClassAveragePricePattern(client, 'dca_class') + self.class_average_price: ClassAveragePricePattern[Dollars] = ClassAveragePricePattern(client, 'dca_class') + self.class_returns: MetricsTree_Market_Dca_ClassReturns = MetricsTree_Market_Dca_ClassReturns(client) self.class_stack: MetricsTree_Market_Dca_ClassStack = MetricsTree_Market_Dca_ClassStack(client) self.period_average_price: PeriodAveragePricePattern[Dollars] = PeriodAveragePricePattern(client, 'dca_average_price') self.period_cagr: PeriodCagrPattern = PeriodCagrPattern(client, 'dca_cagr') @@ -3900,7 +3900,7 @@ class MetricsTree: class BrkClient(BrkClientBase): """Main BRK client with metrics tree and API methods.""" - VERSION = "v0.1.0-alpha.3" + VERSION = "v0.1.0-alpha.6" INDEXES = [ "dateindex",