mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-23 22:29:59 -07:00
website: snapshot
This commit is contained in:
2
.github/workflows/outdated.yml
vendored
2
.github/workflows/outdated.yml
vendored
@@ -2,7 +2,7 @@ name: Check outdated dependencies
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 * * *'
|
||||
- cron: "0 9 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -758,9 +758,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
|
||||
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||
|
||||
[[package]]
|
||||
name = "byteview"
|
||||
@@ -1241,9 +1241,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369"
|
||||
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
@@ -2434,9 +2434,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rawdb"
|
||||
version = "0.6.5"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89f7fffcea393bd56abe3f7f691b88d5cb321e0b46b9caf1064bbcf2ec22180b"
|
||||
checksum = "be3e770fd2d36882cf78a62f211de8342fe8619801e8d39f4273ee87837e4bae"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
@@ -2509,9 +2509,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
version = "1.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -2521,9 +2521,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -2532,9 +2532,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
@@ -3254,9 +3254,9 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23"
|
||||
|
||||
[[package]]
|
||||
name = "vecdb"
|
||||
version = "0.6.5"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92a5c013b67f59b479557f9ee47b365e3802b33348b5000c9f21381b5249669d"
|
||||
checksum = "e212dbc82f5651d33e30b677df3ff789f2d903b917af482078487872cac76d89"
|
||||
dependencies = [
|
||||
"ctrlc",
|
||||
"log",
|
||||
@@ -3275,9 +3275,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vecdb_derive"
|
||||
version = "0.6.5"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d90acf14f49129f0f205bc45331e2608dcb09c55d218e7fdbfdbf4b7c31b9433"
|
||||
checksum = "704e0810d01c394ff35112f861c4889ec155279a7f0a55cb37e2a33682343dd4"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
|
||||
@@ -83,7 +83,7 @@ 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.6.5", features = ["derive", "serde_json", "pco", "schemars"] }
|
||||
vecdb = { version = "0.6.7", features = ["derive", "serde_json", "pco", "schemars"] }
|
||||
# vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] }
|
||||
|
||||
[workspace.metadata.release]
|
||||
|
||||
@@ -2025,32 +2025,62 @@ impl<T: DeserializeOwned> AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPa
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern {
|
||||
pub activity: CoinblocksCoindaysSatblocksSatdaysSentPattern,
|
||||
pub addr_count: MetricPattern1<StoredU64>,
|
||||
pub addr_count_30d_change: MetricPattern4<StoredF64>,
|
||||
pub cost_basis: MaxMinPattern,
|
||||
pub outputs: UtxoPattern,
|
||||
pub realized: CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern,
|
||||
pub relative: InvestedNegNetNuplSupplyUnrealizedPattern,
|
||||
pub supply: _30dHalvedTotalPattern,
|
||||
pub unrealized: GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern,
|
||||
}
|
||||
|
||||
impl ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
activity: CoinblocksCoindaysSatblocksSatdaysSentPattern::new(client.clone(), acc.clone()),
|
||||
addr_count: MetricPattern1::new(client.clone(), _m(&acc, "addr_count")),
|
||||
addr_count_30d_change: MetricPattern4::new(client.clone(), _m(&acc, "addr_count_30d_change")),
|
||||
cost_basis: MaxMinPattern::new(client.clone(), acc.clone()),
|
||||
outputs: UtxoPattern::new(client.clone(), _m(&acc, "utxo_count")),
|
||||
realized: CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern::new(client.clone(), acc.clone()),
|
||||
relative: InvestedNegNetNuplSupplyUnrealizedPattern::new(client.clone(), acc.clone()),
|
||||
supply: _30dHalvedTotalPattern::new(client.clone(), acc.clone()),
|
||||
unrealized: GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern {
|
||||
pub all: MetricPattern1<StoredU64>,
|
||||
pub p2a: MetricPattern1<StoredU64>,
|
||||
pub p2pk33: MetricPattern1<StoredU64>,
|
||||
pub p2pk65: MetricPattern1<StoredU64>,
|
||||
pub p2pkh: MetricPattern1<StoredU64>,
|
||||
pub p2sh: MetricPattern1<StoredU64>,
|
||||
pub p2tr: MetricPattern1<StoredU64>,
|
||||
pub p2wpkh: MetricPattern1<StoredU64>,
|
||||
pub p2wsh: MetricPattern1<StoredU64>,
|
||||
pub all: _30dCountPattern,
|
||||
pub p2a: _30dCountPattern,
|
||||
pub p2pk33: _30dCountPattern,
|
||||
pub p2pk65: _30dCountPattern,
|
||||
pub p2pkh: _30dCountPattern,
|
||||
pub p2sh: _30dCountPattern,
|
||||
pub p2tr: _30dCountPattern,
|
||||
pub p2wpkh: _30dCountPattern,
|
||||
pub p2wsh: _30dCountPattern,
|
||||
}
|
||||
|
||||
impl AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
all: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
p2a: MetricPattern1::new(client.clone(), _p("p2a", &acc)),
|
||||
p2pk33: MetricPattern1::new(client.clone(), _p("p2pk33", &acc)),
|
||||
p2pk65: MetricPattern1::new(client.clone(), _p("p2pk65", &acc)),
|
||||
p2pkh: MetricPattern1::new(client.clone(), _p("p2pkh", &acc)),
|
||||
p2sh: MetricPattern1::new(client.clone(), _p("p2sh", &acc)),
|
||||
p2tr: MetricPattern1::new(client.clone(), _p("p2tr", &acc)),
|
||||
p2wpkh: MetricPattern1::new(client.clone(), _p("p2wpkh", &acc)),
|
||||
p2wsh: MetricPattern1::new(client.clone(), _p("p2wsh", &acc)),
|
||||
all: _30dCountPattern::new(client.clone(), acc.clone()),
|
||||
p2a: _30dCountPattern::new(client.clone(), _p("p2a", &acc)),
|
||||
p2pk33: _30dCountPattern::new(client.clone(), _p("p2pk33", &acc)),
|
||||
p2pk65: _30dCountPattern::new(client.clone(), _p("p2pk65", &acc)),
|
||||
p2pkh: _30dCountPattern::new(client.clone(), _p("p2pkh", &acc)),
|
||||
p2sh: _30dCountPattern::new(client.clone(), _p("p2sh", &acc)),
|
||||
p2tr: _30dCountPattern::new(client.clone(), _p("p2tr", &acc)),
|
||||
p2wpkh: _30dCountPattern::new(client.clone(), _p("p2wpkh", &acc)),
|
||||
p2wsh: _30dCountPattern::new(client.clone(), _p("p2wsh", &acc)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2115,34 +2145,6 @@ impl<T: DeserializeOwned> AverageBaseMaxMedianMinPct10Pct25Pct75Pct90Pattern<T>
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern {
|
||||
pub activity: CoinblocksCoindaysSatblocksSatdaysSentPattern,
|
||||
pub addr_count: MetricPattern1<StoredU64>,
|
||||
pub cost_basis: MaxMinPattern,
|
||||
pub outputs: UtxoPattern,
|
||||
pub realized: CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern,
|
||||
pub relative: InvestedNegNetNuplSupplyUnrealizedPattern,
|
||||
pub supply: _30dHalvedTotalPattern,
|
||||
pub unrealized: GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern,
|
||||
}
|
||||
|
||||
impl ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
activity: CoinblocksCoindaysSatblocksSatdaysSentPattern::new(client.clone(), acc.clone()),
|
||||
addr_count: MetricPattern1::new(client.clone(), _m(&acc, "addr_count")),
|
||||
cost_basis: MaxMinPattern::new(client.clone(), acc.clone()),
|
||||
outputs: UtxoPattern::new(client.clone(), _m(&acc, "utxo_count")),
|
||||
realized: CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern::new(client.clone(), acc.clone()),
|
||||
relative: InvestedNegNetNuplSupplyUnrealizedPattern::new(client.clone(), acc.clone()),
|
||||
supply: _30dHalvedTotalPattern::new(client.clone(), acc.clone()),
|
||||
unrealized: GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _10y2y3y4y5y6y8yPattern {
|
||||
pub _10y: MetricPattern4<StoredF32>,
|
||||
@@ -2561,6 +2563,22 @@ impl BitcoinDollarsSatsPattern3 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _30dCountPattern {
|
||||
pub _30d_change: MetricPattern4<StoredF64>,
|
||||
pub count: MetricPattern1<StoredU64>,
|
||||
}
|
||||
|
||||
impl _30dCountPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
_30d_change: MetricPattern4::new(client.clone(), _m(&acc, "30d_change")),
|
||||
count: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct DollarsSatsPattern {
|
||||
pub dollars: MetricPattern1<Dollars>,
|
||||
@@ -2625,6 +2643,22 @@ impl SdSmaPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct UtxoPattern {
|
||||
pub utxo_count: MetricPattern1<StoredU64>,
|
||||
pub utxo_count_30d_change: MetricPattern4<StoredF64>,
|
||||
}
|
||||
|
||||
impl UtxoPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
utxo_count: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
utxo_count_30d_change: MetricPattern4::new(client.clone(), _m(&acc, "30d_change")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CumulativeSumPattern<T> {
|
||||
pub cumulative: MetricPattern1<T>,
|
||||
@@ -2687,20 +2721,6 @@ impl RatioPattern2 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct UtxoPattern {
|
||||
pub utxo_count: MetricPattern1<StoredU64>,
|
||||
}
|
||||
|
||||
impl UtxoPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
utxo_count: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics tree
|
||||
|
||||
/// Metrics tree node.
|
||||
@@ -2859,6 +2879,8 @@ pub struct MetricsTree_Blocks_Mining {
|
||||
pub hash_rate_1m_sma: MetricPattern4<StoredF32>,
|
||||
pub hash_rate_2m_sma: MetricPattern4<StoredF32>,
|
||||
pub hash_rate_1y_sma: MetricPattern4<StoredF32>,
|
||||
pub hash_rate_ath: MetricPattern1<StoredF64>,
|
||||
pub hash_rate_drawdown: MetricPattern1<StoredF32>,
|
||||
pub hash_price_ths: MetricPattern1<StoredF32>,
|
||||
pub hash_price_ths_min: MetricPattern1<StoredF32>,
|
||||
pub hash_price_phs: MetricPattern1<StoredF32>,
|
||||
@@ -2879,6 +2901,8 @@ impl MetricsTree_Blocks_Mining {
|
||||
hash_rate_1m_sma: MetricPattern4::new(client.clone(), "hash_rate_1m_sma".to_string()),
|
||||
hash_rate_2m_sma: MetricPattern4::new(client.clone(), "hash_rate_2m_sma".to_string()),
|
||||
hash_rate_1y_sma: MetricPattern4::new(client.clone(), "hash_rate_1y_sma".to_string()),
|
||||
hash_rate_ath: MetricPattern1::new(client.clone(), "hash_rate_ath".to_string()),
|
||||
hash_rate_drawdown: MetricPattern1::new(client.clone(), "hash_rate_drawdown".to_string()),
|
||||
hash_price_ths: MetricPattern1::new(client.clone(), "hash_price_ths".to_string()),
|
||||
hash_price_ths_min: MetricPattern1::new(client.clone(), "hash_price_ths_min".to_string()),
|
||||
hash_price_phs: MetricPattern1::new(client.clone(), "hash_price_phs".to_string()),
|
||||
@@ -3515,7 +3539,7 @@ impl MetricsTree_Cointime_Adjusted {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Cointime_ReserveRisk {
|
||||
pub vocdd_365d_sma: MetricPattern6<StoredF64>,
|
||||
pub vocdd_365d_median: MetricPattern6<StoredF64>,
|
||||
pub hodl_bank: MetricPattern6<StoredF64>,
|
||||
pub reserve_risk: MetricPattern4<StoredF64>,
|
||||
}
|
||||
@@ -3523,7 +3547,7 @@ pub struct MetricsTree_Cointime_ReserveRisk {
|
||||
impl MetricsTree_Cointime_ReserveRisk {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
vocdd_365d_sma: MetricPattern6::new(client.clone(), "vocdd_365d_sma".to_string()),
|
||||
vocdd_365d_median: MetricPattern6::new(client.clone(), "vocdd_365d_median".to_string()),
|
||||
hodl_bank: MetricPattern6::new(client.clone(), "hodl_bank".to_string()),
|
||||
reserve_risk: MetricPattern4::new(client.clone(), "reserve_risk".to_string()),
|
||||
}
|
||||
@@ -5109,7 +5133,7 @@ pub struct MetricsTree_Distribution {
|
||||
pub addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern,
|
||||
pub empty_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern,
|
||||
pub address_activity: MetricsTree_Distribution_AddressActivity,
|
||||
pub total_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern,
|
||||
pub total_addr_count: MetricsTree_Distribution_TotalAddrCount,
|
||||
pub new_addr_count: MetricsTree_Distribution_NewAddrCount,
|
||||
pub growth_rate: MetricsTree_Distribution_GrowthRate,
|
||||
pub fundedaddressindex: MetricPattern31<FundedAddressIndex>,
|
||||
@@ -5127,7 +5151,7 @@ impl MetricsTree_Distribution {
|
||||
addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern::new(client.clone(), "addr_count".to_string()),
|
||||
empty_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern::new(client.clone(), "empty_addr_count".to_string()),
|
||||
address_activity: MetricsTree_Distribution_AddressActivity::new(client.clone(), format!("{base_path}_address_activity")),
|
||||
total_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern::new(client.clone(), "total_addr_count".to_string()),
|
||||
total_addr_count: MetricsTree_Distribution_TotalAddrCount::new(client.clone(), format!("{base_path}_total_addr_count")),
|
||||
new_addr_count: MetricsTree_Distribution_NewAddrCount::new(client.clone(), format!("{base_path}_new_addr_count")),
|
||||
growth_rate: MetricsTree_Distribution_GrowthRate::new(client.clone(), format!("{base_path}_growth_rate")),
|
||||
fundedaddressindex: MetricPattern31::new(client.clone(), "fundedaddressindex".to_string()),
|
||||
@@ -5864,6 +5888,35 @@ impl MetricsTree_Distribution_AddressActivity {
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Distribution_TotalAddrCount {
|
||||
pub all: MetricPattern1<StoredU64>,
|
||||
pub p2pk65: MetricPattern1<StoredU64>,
|
||||
pub p2pk33: MetricPattern1<StoredU64>,
|
||||
pub p2pkh: MetricPattern1<StoredU64>,
|
||||
pub p2sh: MetricPattern1<StoredU64>,
|
||||
pub p2wpkh: MetricPattern1<StoredU64>,
|
||||
pub p2wsh: MetricPattern1<StoredU64>,
|
||||
pub p2tr: MetricPattern1<StoredU64>,
|
||||
pub p2a: MetricPattern1<StoredU64>,
|
||||
}
|
||||
|
||||
impl MetricsTree_Distribution_TotalAddrCount {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
all: MetricPattern1::new(client.clone(), "total_addr_count".to_string()),
|
||||
p2pk65: MetricPattern1::new(client.clone(), "p2pk65_total_addr_count".to_string()),
|
||||
p2pk33: MetricPattern1::new(client.clone(), "p2pk33_total_addr_count".to_string()),
|
||||
p2pkh: MetricPattern1::new(client.clone(), "p2pkh_total_addr_count".to_string()),
|
||||
p2sh: MetricPattern1::new(client.clone(), "p2sh_total_addr_count".to_string()),
|
||||
p2wpkh: MetricPattern1::new(client.clone(), "p2wpkh_total_addr_count".to_string()),
|
||||
p2wsh: MetricPattern1::new(client.clone(), "p2wsh_total_addr_count".to_string()),
|
||||
p2tr: MetricPattern1::new(client.clone(), "p2tr_total_addr_count".to_string()),
|
||||
p2a: MetricPattern1::new(client.clone(), "p2a_total_addr_count".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Distribution_NewAddrCount {
|
||||
pub all: AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2<StoredU64>,
|
||||
@@ -5998,7 +6051,7 @@ pub struct BrkClient {
|
||||
|
||||
impl BrkClient {
|
||||
/// Client version.
|
||||
pub const VERSION: &'static str = "v0.1.2";
|
||||
pub const VERSION: &'static str = "v0.1.3";
|
||||
|
||||
/// Create a new client with the given base URL.
|
||||
pub fn new(base_url: impl Into<String>) -> Self {
|
||||
|
||||
@@ -4,7 +4,7 @@ use vecdb::Exit;
|
||||
|
||||
use super::super::{ONE_TERA_HASH, TARGET_BLOCKS_PER_DAY_F64, count, difficulty, rewards};
|
||||
use super::Vecs;
|
||||
use crate::{ComputeIndexes, indexes};
|
||||
use crate::{ComputeIndexes, indexes, traits::ComputeDrawdown};
|
||||
|
||||
impl Vecs {
|
||||
pub fn compute(
|
||||
@@ -80,6 +80,27 @@ impl Vecs {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_rate_ath
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_all_time_high(
|
||||
starting_indexes.height,
|
||||
&self.hash_rate.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_rate_drawdown
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_drawdown(
|
||||
starting_indexes.height,
|
||||
&self.hash_rate.height,
|
||||
&self.hash_rate_ath.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.hash_price_ths
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_transform2(
|
||||
|
||||
@@ -43,6 +43,18 @@ impl Vecs {
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
hash_rate_ath: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"hash_rate_ath",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
hash_rate_drawdown: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"hash_rate_drawdown",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
hash_price_ths: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
"hash_price_ths",
|
||||
|
||||
@@ -11,6 +11,8 @@ pub struct Vecs {
|
||||
pub hash_rate_1m_sma: ComputedFromDateLast<StoredF32>,
|
||||
pub hash_rate_2m_sma: ComputedFromDateLast<StoredF32>,
|
||||
pub hash_rate_1y_sma: ComputedFromDateLast<StoredF32>,
|
||||
pub hash_rate_ath: ComputedFromHeightLast<StoredF64>,
|
||||
pub hash_rate_drawdown: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_price_ths: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_price_ths_min: ComputedFromHeightLast<StoredF32>,
|
||||
pub hash_price_phs: ComputedFromHeightLast<StoredF32>,
|
||||
|
||||
@@ -15,7 +15,7 @@ impl Vecs {
|
||||
) -> Result<()> {
|
||||
let vocdd_dateindex_sum = &value.vocdd.dateindex.sum.0;
|
||||
|
||||
self.vocdd_365d_sma.compute_sma(
|
||||
self.vocdd_365d_median.compute_rolling_median(
|
||||
starting_indexes.dateindex,
|
||||
vocdd_dateindex_sum,
|
||||
365,
|
||||
@@ -27,8 +27,8 @@ impl Vecs {
|
||||
self.hodl_bank.compute_cumulative_transformed_binary(
|
||||
starting_indexes.dateindex,
|
||||
price_close,
|
||||
&self.vocdd_365d_sma,
|
||||
|price: Close<Dollars>, sma: StoredF64| StoredF64::from(f64::from(price) - f64::from(sma)),
|
||||
&self.vocdd_365d_median,
|
||||
|price: Close<Dollars>, median: StoredF64| StoredF64::from(f64::from(price) - f64::from(median)),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -47,54 +47,3 @@ impl Vecs {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_hodl_bank_formula() {
|
||||
let prices = [100.0, 110.0, 105.0, 120.0, 115.0];
|
||||
let vocdd_sma = [50.0, 55.0, 52.0, 60.0, 58.0];
|
||||
|
||||
let mut hodl_bank = 0.0_f64;
|
||||
let mut expected = Vec::new();
|
||||
|
||||
for i in 0..prices.len() {
|
||||
hodl_bank += prices[i] - vocdd_sma[i];
|
||||
expected.push(hodl_bank);
|
||||
}
|
||||
|
||||
assert!((expected[0] - 50.0).abs() < 0.001);
|
||||
assert!((expected[1] - 105.0).abs() < 0.001);
|
||||
assert!((expected[2] - 158.0).abs() < 0.001);
|
||||
assert!((expected[3] - 218.0).abs() < 0.001);
|
||||
assert!((expected[4] - 275.0).abs() < 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reserve_risk_formula() {
|
||||
let price = 100.0_f64;
|
||||
let hodl_bank = 1000.0_f64;
|
||||
let reserve_risk = price / hodl_bank;
|
||||
assert!((reserve_risk - 0.1).abs() < 0.0001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reserve_risk_interpretation() {
|
||||
let high_confidence = 100.0 / 10000.0;
|
||||
let low_confidence = 100.0 / 100.0;
|
||||
assert!(high_confidence < low_confidence);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hodl_bank_negative_contribution() {
|
||||
let prices = [100.0, 80.0, 90.0];
|
||||
let vocdd_sma = [50.0, 100.0, 85.0];
|
||||
|
||||
let mut hodl_bank = 0.0_f64;
|
||||
for i in 0..prices.len() {
|
||||
hodl_bank += prices[i] - vocdd_sma[i];
|
||||
}
|
||||
|
||||
assert!((hodl_bank - 35.0).abs() < 0.001);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,12 @@ impl Vecs {
|
||||
indexes: &indexes::Vecs,
|
||||
compute_dollars: bool,
|
||||
) -> Result<Self> {
|
||||
let v1 = version + Version::ONE;
|
||||
Ok(Self {
|
||||
vocdd_365d_sma: EagerVec::forced_import(db, "vocdd_365d_sma", version)?,
|
||||
hodl_bank: EagerVec::forced_import(db, "hodl_bank", version)?,
|
||||
vocdd_365d_median: EagerVec::forced_import(db, "vocdd_365d_median", v1)?,
|
||||
hodl_bank: EagerVec::forced_import(db, "hodl_bank", v1)?,
|
||||
reserve_risk: compute_dollars
|
||||
.then(|| ComputedFromDateLast::forced_import(db, "reserve_risk", version, indexes))
|
||||
.then(|| ComputedFromDateLast::forced_import(db, "reserve_risk", v1, indexes))
|
||||
.transpose()?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::internal::ComputedFromDateLast;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub vocdd_365d_sma: EagerVec<PcoVec<DateIndex, StoredF64>>,
|
||||
pub vocdd_365d_median: EagerVec<PcoVec<DateIndex, StoredF64>>,
|
||||
pub hodl_bank: EagerVec<PcoVec<DateIndex, StoredF64>>,
|
||||
pub reserve_risk: Option<ComputedFromDateLast<StoredF64>>,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use brk_error::Result;
|
||||
use vecdb::Exit;
|
||||
use brk_types::{Bitcoin, Close, Dollars, StoredF64};
|
||||
use vecdb::{Exit, TypedVecIterator};
|
||||
|
||||
use super::super::activity;
|
||||
use super::Vecs;
|
||||
@@ -29,6 +30,15 @@ impl Vecs {
|
||||
.activity
|
||||
.coindays_destroyed;
|
||||
|
||||
let circulating_supply = &distribution
|
||||
.utxo_cohorts
|
||||
.all
|
||||
.metrics
|
||||
.supply
|
||||
.total
|
||||
.bitcoin
|
||||
.height;
|
||||
|
||||
self.cointime_value_destroyed
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_multiply(
|
||||
@@ -62,14 +72,27 @@ impl Vecs {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// VOCDD: Value-weighted Coin Days Destroyed = CDD × price
|
||||
// This is a key input for Reserve Risk calculation
|
||||
// VOCDD: Value of Coin Days Destroyed = price × (CDD / circulating_supply)
|
||||
// Supply-adjusted to account for growing supply over time
|
||||
// This is a key input for Reserve Risk / HODL Bank calculation
|
||||
self.vocdd
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_multiply(
|
||||
let mut supply_iter = circulating_supply.into_iter();
|
||||
vec.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&price.usd.split.close.height,
|
||||
&coindays_destroyed.height,
|
||||
|(i, price, cdd, _): (_, Close<Dollars>, StoredF64, _)| {
|
||||
let supply: Bitcoin = supply_iter.get_unwrap(i);
|
||||
let supply_f64 = f64::from(supply);
|
||||
if supply_f64 == 0.0 {
|
||||
(i, StoredF64::from(0.0))
|
||||
} else {
|
||||
// VOCDD = price × (CDD / supply)
|
||||
let vocdd = f64::from(price) * f64::from(cdd) / supply_f64;
|
||||
(i, StoredF64::from(vocdd))
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
|
||||
@@ -29,7 +29,7 @@ impl Vecs {
|
||||
vocdd: ComputedFromHeightSumCum::forced_import(
|
||||
db,
|
||||
"vocdd",
|
||||
version,
|
||||
version + Version::ONE,
|
||||
indexes,
|
||||
)?,
|
||||
})
|
||||
|
||||
@@ -1,14 +1,63 @@
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredU64, Version};
|
||||
use brk_types::{Height, StoredF64, StoredU64, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, Database, EagerVec, Exit, GenericStoredVec, PcoVec, TypedVecIterator,
|
||||
};
|
||||
|
||||
use crate::{ComputeIndexes, indexes, internal::ComputedFromHeightLast};
|
||||
use crate::{ComputeIndexes, indexes, internal::{ComputedFromDateLast, ComputedFromHeightLast}};
|
||||
|
||||
/// Address count with 30d change metric for a single type.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct AddrCountVecs {
|
||||
#[traversable(flatten)]
|
||||
pub count: ComputedFromHeightLast<StoredU64>,
|
||||
pub _30d_change: ComputedFromDateLast<StoredF64>,
|
||||
}
|
||||
|
||||
impl AddrCountVecs {
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
count: ComputedFromHeightLast::forced_import(db, name, version, indexes)?,
|
||||
_30d_change: ComputedFromDateLast::forced_import(
|
||||
db,
|
||||
&format!("{name}_30d_change"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_rest(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.count.compute_rest(indexes, starting_indexes, exit)?;
|
||||
|
||||
self._30d_change
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_change(
|
||||
starting_indexes.dateindex,
|
||||
&*self.count.dateindex,
|
||||
30,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Address count per address type (runtime state).
|
||||
#[derive(Debug, Default, Deref, DerefMut)]
|
||||
@@ -28,47 +77,54 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount {
|
||||
Self(ByAddressType {
|
||||
p2pk65: groups
|
||||
.p2pk65
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2pk33: groups
|
||||
.p2pk33
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2pkh: groups
|
||||
.p2pkh
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2sh: groups
|
||||
.p2sh
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2wpkh: groups
|
||||
.p2wpkh
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2wsh: groups
|
||||
.p2wsh
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2tr: groups
|
||||
.p2tr
|
||||
.count
|
||||
.height
|
||||
.into_iter()
|
||||
.get_unwrap(prev_height)
|
||||
.into(),
|
||||
p2a: groups.p2a.height.into_iter().get_unwrap(prev_height).into(),
|
||||
p2a: groups.p2a.count.height.into_iter().get_unwrap(prev_height).into(),
|
||||
})
|
||||
} else {
|
||||
Default::default()
|
||||
@@ -76,13 +132,13 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount {
|
||||
}
|
||||
}
|
||||
|
||||
/// Address count per address type, with height + derived indexes.
|
||||
/// Address count per address type, with height + derived indexes + 30d change.
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
pub struct AddressTypeToAddrCountVecs(ByAddressType<ComputedFromHeightLast<StoredU64>>);
|
||||
pub struct AddressTypeToAddrCountVecs(ByAddressType<AddrCountVecs>);
|
||||
|
||||
impl From<ByAddressType<ComputedFromHeightLast<StoredU64>>> for AddressTypeToAddrCountVecs {
|
||||
impl From<ByAddressType<AddrCountVecs>> for AddressTypeToAddrCountVecs {
|
||||
#[inline]
|
||||
fn from(value: ByAddressType<ComputedFromHeightLast<StoredU64>>) -> Self {
|
||||
fn from(value: ByAddressType<AddrCountVecs>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
@@ -95,8 +151,8 @@ impl AddressTypeToAddrCountVecs {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self::from(
|
||||
ByAddressType::<ComputedFromHeightLast<StoredU64>>::new_with_name(|type_name| {
|
||||
ComputedFromHeightLast::forced_import(
|
||||
ByAddressType::<AddrCountVecs>::new_with_name(|type_name| {
|
||||
AddrCountVecs::forced_import(
|
||||
db,
|
||||
&format!("{type_name}_{name}"),
|
||||
version,
|
||||
@@ -108,28 +164,29 @@ impl AddressTypeToAddrCountVecs {
|
||||
|
||||
pub fn min_stateful_height(&self) -> usize {
|
||||
self.p2pk65
|
||||
.count
|
||||
.height
|
||||
.len()
|
||||
.min(self.p2pk33.height.len())
|
||||
.min(self.p2pkh.height.len())
|
||||
.min(self.p2sh.height.len())
|
||||
.min(self.p2wpkh.height.len())
|
||||
.min(self.p2wsh.height.len())
|
||||
.min(self.p2tr.height.len())
|
||||
.min(self.p2a.height.len())
|
||||
.min(self.p2pk33.count.height.len())
|
||||
.min(self.p2pkh.count.height.len())
|
||||
.min(self.p2sh.count.height.len())
|
||||
.min(self.p2wpkh.count.height.len())
|
||||
.min(self.p2wsh.count.height.len())
|
||||
.min(self.p2tr.count.height.len())
|
||||
.min(self.p2a.count.height.len())
|
||||
}
|
||||
|
||||
pub fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
let inner = &mut self.0;
|
||||
[
|
||||
&mut inner.p2pk65.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2pk33.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2pkh.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2sh.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2wpkh.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2wsh.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2tr.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2a.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2pk65.count.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2pk33.count.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2pkh.count.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2sh.count.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2wpkh.count.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2wsh.count.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2tr.count.height as &mut dyn AnyStoredVec,
|
||||
&mut inner.p2a.count.height as &mut dyn AnyStoredVec,
|
||||
]
|
||||
.into_par_iter()
|
||||
}
|
||||
@@ -140,27 +197,35 @@ impl AddressTypeToAddrCountVecs {
|
||||
addr_counts: &AddressTypeToAddressCount,
|
||||
) -> Result<()> {
|
||||
self.p2pk65
|
||||
.count
|
||||
.height
|
||||
.truncate_push(height, addr_counts.p2pk65.into())?;
|
||||
self.p2pk33
|
||||
.count
|
||||
.height
|
||||
.truncate_push(height, addr_counts.p2pk33.into())?;
|
||||
self.p2pkh
|
||||
.count
|
||||
.height
|
||||
.truncate_push(height, addr_counts.p2pkh.into())?;
|
||||
self.p2sh
|
||||
.count
|
||||
.height
|
||||
.truncate_push(height, addr_counts.p2sh.into())?;
|
||||
self.p2wpkh
|
||||
.count
|
||||
.height
|
||||
.truncate_push(height, addr_counts.p2wpkh.into())?;
|
||||
self.p2wsh
|
||||
.count
|
||||
.height
|
||||
.truncate_push(height, addr_counts.p2wsh.into())?;
|
||||
self.p2tr
|
||||
.count
|
||||
.height
|
||||
.truncate_push(height, addr_counts.p2tr.into())?;
|
||||
self.p2a
|
||||
.count
|
||||
.height
|
||||
.truncate_push(height, addr_counts.p2a.into())?;
|
||||
Ok(())
|
||||
@@ -168,14 +233,14 @@ impl AddressTypeToAddrCountVecs {
|
||||
|
||||
pub fn reset_height(&mut self) -> Result<()> {
|
||||
use vecdb::GenericStoredVec;
|
||||
self.p2pk65.height.reset()?;
|
||||
self.p2pk33.height.reset()?;
|
||||
self.p2pkh.height.reset()?;
|
||||
self.p2sh.height.reset()?;
|
||||
self.p2wpkh.height.reset()?;
|
||||
self.p2wsh.height.reset()?;
|
||||
self.p2tr.height.reset()?;
|
||||
self.p2a.height.reset()?;
|
||||
self.p2pk65.count.height.reset()?;
|
||||
self.p2pk33.count.height.reset()?;
|
||||
self.p2pkh.count.height.reset()?;
|
||||
self.p2sh.count.height.reset()?;
|
||||
self.p2wpkh.count.height.reset()?;
|
||||
self.p2wsh.count.height.reset()?;
|
||||
self.p2tr.count.height.reset()?;
|
||||
self.p2a.count.height.reset()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -198,26 +263,26 @@ impl AddressTypeToAddrCountVecs {
|
||||
|
||||
pub fn by_height(&self) -> Vec<&EagerVec<PcoVec<Height, StoredU64>>> {
|
||||
vec![
|
||||
&self.p2pk65.height,
|
||||
&self.p2pk33.height,
|
||||
&self.p2pkh.height,
|
||||
&self.p2sh.height,
|
||||
&self.p2wpkh.height,
|
||||
&self.p2wsh.height,
|
||||
&self.p2tr.height,
|
||||
&self.p2a.height,
|
||||
&self.p2pk65.count.height,
|
||||
&self.p2pk33.count.height,
|
||||
&self.p2pkh.count.height,
|
||||
&self.p2sh.count.height,
|
||||
&self.p2wpkh.count.height,
|
||||
&self.p2wsh.count.height,
|
||||
&self.p2tr.count.height,
|
||||
&self.p2a.count.height,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct AddrCountVecs {
|
||||
pub all: ComputedFromHeightLast<StoredU64>,
|
||||
pub struct AddrCountsVecs {
|
||||
pub all: AddrCountVecs,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: AddressTypeToAddrCountVecs,
|
||||
}
|
||||
|
||||
impl AddrCountVecs {
|
||||
impl AddrCountsVecs {
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
@@ -225,22 +290,22 @@ impl AddrCountVecs {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
all: ComputedFromHeightLast::forced_import(db, name, version, indexes)?,
|
||||
all: AddrCountVecs::forced_import(db, name, version, indexes)?,
|
||||
by_addresstype: AddressTypeToAddrCountVecs::forced_import(db, name, version, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn min_stateful_height(&self) -> usize {
|
||||
self.all.height.len().min(self.by_addresstype.min_stateful_height())
|
||||
self.all.count.height.len().min(self.by_addresstype.min_stateful_height())
|
||||
}
|
||||
|
||||
pub fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
rayon::iter::once(&mut self.all.height as &mut dyn AnyStoredVec)
|
||||
rayon::iter::once(&mut self.all.count.height as &mut dyn AnyStoredVec)
|
||||
.chain(self.by_addresstype.par_iter_height_mut())
|
||||
}
|
||||
|
||||
pub fn reset_height(&mut self) -> Result<()> {
|
||||
self.all.height.reset()?;
|
||||
self.all.count.height.reset()?;
|
||||
self.by_addresstype.reset_height()?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -251,7 +316,7 @@ impl AddrCountVecs {
|
||||
total: u64,
|
||||
addr_counts: &AddressTypeToAddressCount,
|
||||
) -> Result<()> {
|
||||
self.all.height.truncate_push(height, total.into())?;
|
||||
self.all.count.height.truncate_push(height, total.into())?;
|
||||
self.by_addresstype
|
||||
.truncate_push_height(height, addr_counts)?;
|
||||
Ok(())
|
||||
@@ -268,10 +333,22 @@ impl AddrCountVecs {
|
||||
|
||||
let sources = self.by_addresstype.by_height();
|
||||
self.all
|
||||
.count
|
||||
.compute_all(indexes, starting_indexes, exit, |height_vec| {
|
||||
Ok(height_vec.compute_sum_of_others(starting_indexes.height, &sources, exit)?)
|
||||
})?;
|
||||
|
||||
self.all._30d_change
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_change(
|
||||
starting_indexes.dateindex,
|
||||
&*self.all.count.dateindex,
|
||||
30,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
internal::{LazyBinaryComputedFromHeightDistribution, RatioU64F32},
|
||||
};
|
||||
|
||||
use super::{AddrCountVecs, NewAddrCountVecs};
|
||||
use super::{AddrCountsVecs, NewAddrCountVecs};
|
||||
|
||||
/// Growth rate by type - lazy ratio with distribution stats
|
||||
pub type GrowthRateByType =
|
||||
@@ -31,7 +31,7 @@ impl GrowthRateVecs {
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
new_addr_count: &NewAddrCountVecs,
|
||||
addr_count: &AddrCountVecs,
|
||||
addr_count: &AddrCountsVecs,
|
||||
) -> Result<Self> {
|
||||
let all = make_growth_rate(
|
||||
db,
|
||||
@@ -39,7 +39,7 @@ impl GrowthRateVecs {
|
||||
version,
|
||||
indexes,
|
||||
&new_addr_count.all.height,
|
||||
&addr_count.all.height,
|
||||
&addr_count.all.count.height,
|
||||
)?;
|
||||
|
||||
let by_addresstype: GrowthRateByType = zip2_by_addresstype(
|
||||
@@ -52,7 +52,7 @@ impl GrowthRateVecs {
|
||||
version,
|
||||
indexes,
|
||||
&new.height,
|
||||
&addr.height,
|
||||
&addr.count.height,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -8,7 +8,7 @@ mod total_addr_count;
|
||||
mod type_map;
|
||||
|
||||
pub use activity::{AddressActivityVecs, AddressTypeToActivityCounts};
|
||||
pub use address_count::{AddrCountVecs, AddressTypeToAddressCount};
|
||||
pub use address_count::{AddrCountVecs, AddrCountsVecs, AddressTypeToAddressCount};
|
||||
pub use data::AddressesDataVecs;
|
||||
pub use growth_rate::GrowthRateVecs;
|
||||
pub use indexes::AnyAddressIndexesVecs;
|
||||
|
||||
@@ -8,7 +8,7 @@ use vecdb::{Database, Exit, IterableCloneableVec};
|
||||
|
||||
use crate::{ComputeIndexes, indexes, internal::{LazyBinaryComputedFromHeightLast, U64Plus}};
|
||||
|
||||
use super::AddrCountVecs;
|
||||
use super::AddrCountsVecs;
|
||||
|
||||
/// Total addresses by type - lazy sum with all derived indexes
|
||||
pub type TotalAddrCountByType =
|
||||
@@ -27,15 +27,15 @@ impl TotalAddrCountVecs {
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
addr_count: &AddrCountVecs,
|
||||
empty_addr_count: &AddrCountVecs,
|
||||
addr_count: &AddrCountsVecs,
|
||||
empty_addr_count: &AddrCountsVecs,
|
||||
) -> Result<Self> {
|
||||
let all = LazyBinaryComputedFromHeightLast::forced_import::<U64Plus>(
|
||||
db,
|
||||
"total_addr_count",
|
||||
version,
|
||||
addr_count.all.height.boxed_clone(),
|
||||
empty_addr_count.all.height.boxed_clone(),
|
||||
addr_count.all.count.height.boxed_clone(),
|
||||
empty_addr_count.all.count.height.boxed_clone(),
|
||||
indexes,
|
||||
)?;
|
||||
|
||||
@@ -47,8 +47,8 @@ impl TotalAddrCountVecs {
|
||||
db,
|
||||
&format!("{name}_total_addr_count"),
|
||||
version,
|
||||
addr.height.boxed_clone(),
|
||||
empty.height.boxed_clone(),
|
||||
addr.count.height.boxed_clone(),
|
||||
empty.count.height.boxed_clone(),
|
||||
indexes,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -3,13 +3,15 @@ use std::path::Path;
|
||||
use brk_cohort::{CohortContext, Filter, Filtered};
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{CentsUnsigned, DateIndex, Dollars, Height, StoredU64, Version};
|
||||
use brk_types::{CentsUnsigned, DateIndex, Dollars, Height, StoredF64, StoredU64, Version};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, GenericStoredVec, IterableVec};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, distribution::state::AddressCohortState, indexes, internal::ComputedFromHeightLast,
|
||||
price,
|
||||
distribution::state::AddressCohortState,
|
||||
indexes,
|
||||
internal::{ComputedFromDateLast, ComputedFromHeightLast},
|
||||
price, ComputeIndexes,
|
||||
};
|
||||
|
||||
use crate::distribution::metrics::{CohortMetrics, ImportConfig, SupplyMetrics};
|
||||
@@ -33,6 +35,7 @@ pub struct AddressCohortVecs {
|
||||
pub metrics: CohortMetrics,
|
||||
|
||||
pub addr_count: ComputedFromHeightLast<StoredU64>,
|
||||
pub addr_count_30d_change: ComputedFromDateLast<StoredF64>,
|
||||
}
|
||||
|
||||
impl AddressCohortVecs {
|
||||
@@ -79,6 +82,12 @@ impl AddressCohortVecs {
|
||||
version + VERSION,
|
||||
indexes,
|
||||
)?,
|
||||
addr_count_30d_change: ComputedFromDateLast::forced_import(
|
||||
db,
|
||||
&cfg.name("addr_count_30d_change"),
|
||||
version + VERSION,
|
||||
indexes,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -234,6 +243,18 @@ impl DynCohortVecs for AddressCohortVecs {
|
||||
) -> Result<()> {
|
||||
self.addr_count
|
||||
.compute_rest(indexes, starting_indexes, exit)?;
|
||||
|
||||
self.addr_count_30d_change
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_change(
|
||||
starting_indexes.dateindex,
|
||||
&*self.addr_count.dateindex,
|
||||
30,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.metrics
|
||||
.compute_rest_part1(indexes, price, starting_indexes, exit)?;
|
||||
Ok(())
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredU64};
|
||||
use brk_types::{Height, StoredF64, StoredU64};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, GenericStoredVec};
|
||||
|
||||
use crate::{ComputeIndexes, indexes, internal::ComputedFromHeightLast};
|
||||
use crate::{ComputeIndexes, indexes, internal::{ComputedFromDateLast, ComputedFromHeightLast}};
|
||||
|
||||
use super::ImportConfig;
|
||||
|
||||
@@ -12,6 +12,7 @@ use super::ImportConfig;
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct OutputsMetrics {
|
||||
pub utxo_count: ComputedFromHeightLast<StoredU64>,
|
||||
pub utxo_count_30d_change: ComputedFromDateLast<StoredF64>,
|
||||
}
|
||||
|
||||
impl OutputsMetrics {
|
||||
@@ -24,6 +25,12 @@ impl OutputsMetrics {
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
utxo_count_30d_change: ComputedFromDateLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("utxo_count_30d_change"),
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,7 +49,11 @@ impl OutputsMetrics {
|
||||
|
||||
/// Returns a parallel iterator over all vecs for parallel writing.
|
||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
vec![&mut self.utxo_count.height as &mut dyn AnyStoredVec].into_par_iter()
|
||||
vec![
|
||||
&mut self.utxo_count.height as &mut dyn AnyStoredVec,
|
||||
&mut self.utxo_count_30d_change.dateindex as &mut dyn AnyStoredVec,
|
||||
]
|
||||
.into_par_iter()
|
||||
}
|
||||
|
||||
/// Compute aggregate values from separate cohorts.
|
||||
@@ -70,6 +81,20 @@ impl OutputsMetrics {
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.utxo_count.compute_rest(indexes, starting_indexes, exit)
|
||||
self.utxo_count
|
||||
.compute_rest(indexes, starting_indexes, exit)?;
|
||||
|
||||
self.utxo_count_30d_change
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_change(
|
||||
starting_indexes.dateindex,
|
||||
&*self.utxo_count.dateindex,
|
||||
30,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ use crate::{
|
||||
use super::{
|
||||
AddressCohorts, AddressesDataVecs, AnyAddressIndexesVecs, UTXOCohorts,
|
||||
address::{
|
||||
AddrCountVecs, AddressActivityVecs, GrowthRateVecs, NewAddrCountVecs, TotalAddrCountVecs,
|
||||
AddrCountsVecs, AddressActivityVecs, GrowthRateVecs, NewAddrCountVecs, TotalAddrCountVecs,
|
||||
},
|
||||
compute::aggregates,
|
||||
};
|
||||
@@ -44,8 +44,8 @@ pub struct Vecs {
|
||||
pub utxo_cohorts: UTXOCohorts,
|
||||
pub address_cohorts: AddressCohorts,
|
||||
|
||||
pub addr_count: AddrCountVecs,
|
||||
pub empty_addr_count: AddrCountVecs,
|
||||
pub addr_count: AddrCountsVecs,
|
||||
pub empty_addr_count: AddrCountsVecs,
|
||||
pub address_activity: AddressActivityVecs,
|
||||
|
||||
/// Total addresses ever seen (addr_count + empty_addr_count) - lazy, global + per-type
|
||||
@@ -115,9 +115,9 @@ impl Vecs {
|
||||
|index, _| Some(index),
|
||||
);
|
||||
|
||||
let addr_count = AddrCountVecs::forced_import(&db, "addr_count", version, indexes)?;
|
||||
let addr_count = AddrCountsVecs::forced_import(&db, "addr_count", version, indexes)?;
|
||||
let empty_addr_count =
|
||||
AddrCountVecs::forced_import(&db, "empty_addr_count", version, indexes)?;
|
||||
AddrCountsVecs::forced_import(&db, "empty_addr_count", version, indexes)?;
|
||||
let address_activity =
|
||||
AddressActivityVecs::forced_import(&db, "address_activity", version, indexes)?;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{Bitcoin, CheckedSub, Close, Date, DateIndex, Dollars, Sats, StoredF32};
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, EagerVec, Exit, GenericStoredVec, IterableVec, PcoVec, VecIndex, Version,
|
||||
AnyStoredVec, AnyVec, EagerVec, Exit, GenericStoredVec, IterableVec, PcoVec, VecIndex, VecValue,
|
||||
Version,
|
||||
};
|
||||
|
||||
mod pricing;
|
||||
@@ -295,37 +296,47 @@ where
|
||||
}
|
||||
|
||||
pub trait ComputeDrawdown<I> {
|
||||
fn compute_drawdown(
|
||||
fn compute_drawdown<C, A>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
close: &impl IterableVec<I, Close<Dollars>>,
|
||||
ath: &impl IterableVec<I, Dollars>,
|
||||
current: &impl IterableVec<I, C>,
|
||||
ath: &impl IterableVec<I, A>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
) -> Result<()>
|
||||
where
|
||||
C: VecValue,
|
||||
A: VecValue,
|
||||
f64: From<C> + From<A>;
|
||||
}
|
||||
|
||||
impl<I> ComputeDrawdown<I> for EagerVec<PcoVec<I, StoredF32>>
|
||||
where
|
||||
I: VecIndex,
|
||||
{
|
||||
fn compute_drawdown(
|
||||
fn compute_drawdown<C, A>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
close: &impl IterableVec<I, Close<Dollars>>,
|
||||
ath: &impl IterableVec<I, Dollars>,
|
||||
current: &impl IterableVec<I, C>,
|
||||
ath: &impl IterableVec<I, A>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
) -> Result<()>
|
||||
where
|
||||
C: VecValue,
|
||||
A: VecValue,
|
||||
f64: From<C> + From<A>,
|
||||
{
|
||||
self.compute_transform2(
|
||||
max_from,
|
||||
current,
|
||||
ath,
|
||||
close,
|
||||
|(i, ath, close, _)| {
|
||||
if ath == Dollars::ZERO {
|
||||
(i, StoredF32::default())
|
||||
|(i, current, ath, _)| {
|
||||
let ath_f64 = f64::from(ath);
|
||||
let drawdown = if ath_f64 == 0.0 {
|
||||
StoredF32::default()
|
||||
} else {
|
||||
let drawdown = StoredF32::from((*ath - **close) / *ath * -100.0);
|
||||
(i, drawdown)
|
||||
}
|
||||
StoredF32::from((f64::from(current) - ath_f64) / ath_f64 * 100.0)
|
||||
};
|
||||
(i, drawdown)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -10,7 +10,7 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vecdb::{CheckedSub, Formattable, Pco, PrintableIndex};
|
||||
|
||||
use crate::{Bitcoin, Dollars};
|
||||
use crate::{Bitcoin, Dollars, StoredU64};
|
||||
|
||||
/// Fixed-size 64-bit floating point value optimized for on-disk storage
|
||||
#[derive(Debug, Deref, Default, Clone, Copy, Serialize, Deserialize, Pco, JsonSchema)]
|
||||
@@ -48,6 +48,13 @@ impl From<usize> for StoredF64 {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StoredU64> for StoredF64 {
|
||||
#[inline]
|
||||
fn from(value: StoredU64) -> Self {
|
||||
Self(*value as f64)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckedSub<StoredF64> for StoredF64 {
|
||||
fn checked_sub(self, rhs: Self) -> Option<Self> {
|
||||
Some(Self(self.0 - rhs.0))
|
||||
|
||||
@@ -2613,17 +2613,50 @@ function createAverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2(clie
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern
|
||||
* @property {CoinblocksCoindaysSatblocksSatdaysSentPattern} activity
|
||||
* @property {MetricPattern1<StoredU64>} addrCount
|
||||
* @property {MetricPattern4<StoredF64>} addrCount30dChange
|
||||
* @property {MaxMinPattern} costBasis
|
||||
* @property {UtxoPattern} outputs
|
||||
* @property {CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern} realized
|
||||
* @property {InvestedNegNetNuplSupplyUnrealizedPattern} relative
|
||||
* @property {_30dHalvedTotalPattern} supply
|
||||
* @property {GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern} unrealized
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern}
|
||||
*/
|
||||
function createActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern(client, acc) {
|
||||
return {
|
||||
activity: createCoinblocksCoindaysSatblocksSatdaysSentPattern(client, acc),
|
||||
addrCount: createMetricPattern1(client, _m(acc, 'addr_count')),
|
||||
addrCount30dChange: createMetricPattern4(client, _m(acc, 'addr_count_30d_change')),
|
||||
costBasis: createMaxMinPattern(client, acc),
|
||||
outputs: createUtxoPattern(client, _m(acc, 'utxo_count')),
|
||||
realized: createCapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern(client, acc),
|
||||
relative: createInvestedNegNetNuplSupplyUnrealizedPattern(client, acc),
|
||||
supply: create_30dHalvedTotalPattern(client, acc),
|
||||
unrealized: createGreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern
|
||||
* @property {MetricPattern1<StoredU64>} all
|
||||
* @property {MetricPattern1<StoredU64>} p2a
|
||||
* @property {MetricPattern1<StoredU64>} p2pk33
|
||||
* @property {MetricPattern1<StoredU64>} p2pk65
|
||||
* @property {MetricPattern1<StoredU64>} p2pkh
|
||||
* @property {MetricPattern1<StoredU64>} p2sh
|
||||
* @property {MetricPattern1<StoredU64>} p2tr
|
||||
* @property {MetricPattern1<StoredU64>} p2wpkh
|
||||
* @property {MetricPattern1<StoredU64>} p2wsh
|
||||
* @property {_30dCountPattern} all
|
||||
* @property {_30dCountPattern} p2a
|
||||
* @property {_30dCountPattern} p2pk33
|
||||
* @property {_30dCountPattern} p2pk65
|
||||
* @property {_30dCountPattern} p2pkh
|
||||
* @property {_30dCountPattern} p2sh
|
||||
* @property {_30dCountPattern} p2tr
|
||||
* @property {_30dCountPattern} p2wpkh
|
||||
* @property {_30dCountPattern} p2wsh
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -2634,15 +2667,15 @@ function createAverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2(clie
|
||||
*/
|
||||
function createAllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern(client, acc) {
|
||||
return {
|
||||
all: createMetricPattern1(client, acc),
|
||||
p2a: createMetricPattern1(client, _p('p2a', acc)),
|
||||
p2pk33: createMetricPattern1(client, _p('p2pk33', acc)),
|
||||
p2pk65: createMetricPattern1(client, _p('p2pk65', acc)),
|
||||
p2pkh: createMetricPattern1(client, _p('p2pkh', acc)),
|
||||
p2sh: createMetricPattern1(client, _p('p2sh', acc)),
|
||||
p2tr: createMetricPattern1(client, _p('p2tr', acc)),
|
||||
p2wpkh: createMetricPattern1(client, _p('p2wpkh', acc)),
|
||||
p2wsh: createMetricPattern1(client, _p('p2wsh', acc)),
|
||||
all: create_30dCountPattern(client, acc),
|
||||
p2a: create_30dCountPattern(client, _p('p2a', acc)),
|
||||
p2pk33: create_30dCountPattern(client, _p('p2pk33', acc)),
|
||||
p2pk65: create_30dCountPattern(client, _p('p2pk65', acc)),
|
||||
p2pkh: create_30dCountPattern(client, _p('p2pkh', acc)),
|
||||
p2sh: create_30dCountPattern(client, _p('p2sh', acc)),
|
||||
p2tr: create_30dCountPattern(client, _p('p2tr', acc)),
|
||||
p2wpkh: create_30dCountPattern(client, _p('p2wpkh', acc)),
|
||||
p2wsh: create_30dCountPattern(client, _p('p2wsh', acc)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2716,37 +2749,6 @@ function createAverageBaseMaxMedianMinPct10Pct25Pct75Pct90Pattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern
|
||||
* @property {CoinblocksCoindaysSatblocksSatdaysSentPattern} activity
|
||||
* @property {MetricPattern1<StoredU64>} addrCount
|
||||
* @property {MaxMinPattern} costBasis
|
||||
* @property {UtxoPattern} outputs
|
||||
* @property {CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern} realized
|
||||
* @property {InvestedNegNetNuplSupplyUnrealizedPattern} relative
|
||||
* @property {_30dHalvedTotalPattern} supply
|
||||
* @property {GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern} unrealized
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern}
|
||||
*/
|
||||
function createActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern(client, acc) {
|
||||
return {
|
||||
activity: createCoinblocksCoindaysSatblocksSatdaysSentPattern(client, acc),
|
||||
addrCount: createMetricPattern1(client, _m(acc, 'addr_count')),
|
||||
costBasis: createMaxMinPattern(client, acc),
|
||||
outputs: createUtxoPattern(client, _m(acc, 'utxo_count')),
|
||||
realized: createCapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern(client, acc),
|
||||
relative: createInvestedNegNetNuplSupplyUnrealizedPattern(client, acc),
|
||||
supply: create_30dHalvedTotalPattern(client, acc),
|
||||
unrealized: createGreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} _10y2y3y4y5y6y8yPattern
|
||||
* @property {MetricPattern4<StoredF32>} _10y
|
||||
@@ -3224,6 +3226,25 @@ function createBitcoinDollarsSatsPattern3(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} _30dCountPattern
|
||||
* @property {MetricPattern4<StoredF64>} _30dChange
|
||||
* @property {MetricPattern1<StoredU64>} count
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a _30dCountPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {_30dCountPattern}
|
||||
*/
|
||||
function create_30dCountPattern(client, acc) {
|
||||
return {
|
||||
_30dChange: createMetricPattern4(client, _m(acc, '30d_change')),
|
||||
count: createMetricPattern1(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} DollarsSatsPattern
|
||||
* @property {MetricPattern1<Dollars>} dollars
|
||||
@@ -3300,6 +3321,25 @@ function createSdSmaPattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} UtxoPattern
|
||||
* @property {MetricPattern1<StoredU64>} utxoCount
|
||||
* @property {MetricPattern4<StoredF64>} utxoCount30dChange
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a UtxoPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {UtxoPattern}
|
||||
*/
|
||||
function createUtxoPattern(client, acc) {
|
||||
return {
|
||||
utxoCount: createMetricPattern1(client, acc),
|
||||
utxoCount30dChange: createMetricPattern4(client, _m(acc, '30d_change')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} CumulativeSumPattern
|
||||
@@ -3380,23 +3420,6 @@ function createRatioPattern2(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} UtxoPattern
|
||||
* @property {MetricPattern1<StoredU64>} utxoCount
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a UtxoPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {UtxoPattern}
|
||||
*/
|
||||
function createUtxoPattern(client, acc) {
|
||||
return {
|
||||
utxoCount: createMetricPattern1(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
// Catalog tree typedefs
|
||||
|
||||
/**
|
||||
@@ -3473,6 +3496,8 @@ function createUtxoPattern(client, acc) {
|
||||
* @property {MetricPattern4<StoredF32>} hashRate1mSma
|
||||
* @property {MetricPattern4<StoredF32>} hashRate2mSma
|
||||
* @property {MetricPattern4<StoredF32>} hashRate1ySma
|
||||
* @property {MetricPattern1<StoredF64>} hashRateAth
|
||||
* @property {MetricPattern1<StoredF32>} hashRateDrawdown
|
||||
* @property {MetricPattern1<StoredF32>} hashPriceThs
|
||||
* @property {MetricPattern1<StoredF32>} hashPriceThsMin
|
||||
* @property {MetricPattern1<StoredF32>} hashPricePhs
|
||||
@@ -3755,7 +3780,7 @@ function createUtxoPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Cointime_ReserveRisk
|
||||
* @property {MetricPattern6<StoredF64>} vocdd365dSma
|
||||
* @property {MetricPattern6<StoredF64>} vocdd365dMedian
|
||||
* @property {MetricPattern6<StoredF64>} hodlBank
|
||||
* @property {MetricPattern4<StoredF64>} reserveRisk
|
||||
*/
|
||||
@@ -4478,7 +4503,7 @@ function createUtxoPattern(client, acc) {
|
||||
* @property {AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern} addrCount
|
||||
* @property {AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern} emptyAddrCount
|
||||
* @property {MetricsTree_Distribution_AddressActivity} addressActivity
|
||||
* @property {AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern} totalAddrCount
|
||||
* @property {MetricsTree_Distribution_TotalAddrCount} totalAddrCount
|
||||
* @property {MetricsTree_Distribution_NewAddrCount} newAddrCount
|
||||
* @property {MetricsTree_Distribution_GrowthRate} growthRate
|
||||
* @property {MetricPattern31<FundedAddressIndex>} fundedaddressindex
|
||||
@@ -4816,6 +4841,19 @@ function createUtxoPattern(client, acc) {
|
||||
* @property {BalanceBothReactivatedReceivingSendingPattern} p2a
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Distribution_TotalAddrCount
|
||||
* @property {MetricPattern1<StoredU64>} all
|
||||
* @property {MetricPattern1<StoredU64>} p2pk65
|
||||
* @property {MetricPattern1<StoredU64>} p2pk33
|
||||
* @property {MetricPattern1<StoredU64>} p2pkh
|
||||
* @property {MetricPattern1<StoredU64>} p2sh
|
||||
* @property {MetricPattern1<StoredU64>} p2wpkh
|
||||
* @property {MetricPattern1<StoredU64>} p2wsh
|
||||
* @property {MetricPattern1<StoredU64>} p2tr
|
||||
* @property {MetricPattern1<StoredU64>} p2a
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Distribution_NewAddrCount
|
||||
* @property {AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2<StoredU64>} all
|
||||
@@ -4875,7 +4913,7 @@ function createUtxoPattern(client, acc) {
|
||||
* @extends BrkClientBase
|
||||
*/
|
||||
class BrkClient extends BrkClientBase {
|
||||
VERSION = "v0.1.2";
|
||||
VERSION = "v0.1.3";
|
||||
|
||||
INDEXES = /** @type {const} */ ([
|
||||
"dateindex",
|
||||
@@ -5840,6 +5878,8 @@ class BrkClient extends BrkClientBase {
|
||||
hashRate1mSma: createMetricPattern4(this, 'hash_rate_1m_sma'),
|
||||
hashRate2mSma: createMetricPattern4(this, 'hash_rate_2m_sma'),
|
||||
hashRate1ySma: createMetricPattern4(this, 'hash_rate_1y_sma'),
|
||||
hashRateAth: createMetricPattern1(this, 'hash_rate_ath'),
|
||||
hashRateDrawdown: createMetricPattern1(this, 'hash_rate_drawdown'),
|
||||
hashPriceThs: createMetricPattern1(this, 'hash_price_ths'),
|
||||
hashPriceThsMin: createMetricPattern1(this, 'hash_price_ths_min'),
|
||||
hashPricePhs: createMetricPattern1(this, 'hash_price_phs'),
|
||||
@@ -6047,7 +6087,7 @@ class BrkClient extends BrkClientBase {
|
||||
cointimeAdjTxUsdVelocity: createMetricPattern4(this, 'cointime_adj_tx_usd_velocity'),
|
||||
},
|
||||
reserveRisk: {
|
||||
vocdd365dSma: createMetricPattern6(this, 'vocdd_365d_sma'),
|
||||
vocdd365dMedian: createMetricPattern6(this, 'vocdd_365d_median'),
|
||||
hodlBank: createMetricPattern6(this, 'hodl_bank'),
|
||||
reserveRisk: createMetricPattern4(this, 'reserve_risk'),
|
||||
},
|
||||
@@ -6886,7 +6926,17 @@ class BrkClient extends BrkClientBase {
|
||||
p2tr: createBalanceBothReactivatedReceivingSendingPattern(this, 'p2tr_address_activity'),
|
||||
p2a: createBalanceBothReactivatedReceivingSendingPattern(this, 'p2a_address_activity'),
|
||||
},
|
||||
totalAddrCount: createAllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern(this, 'total_addr_count'),
|
||||
totalAddrCount: {
|
||||
all: createMetricPattern1(this, 'total_addr_count'),
|
||||
p2pk65: createMetricPattern1(this, 'p2pk65_total_addr_count'),
|
||||
p2pk33: createMetricPattern1(this, 'p2pk33_total_addr_count'),
|
||||
p2pkh: createMetricPattern1(this, 'p2pkh_total_addr_count'),
|
||||
p2sh: createMetricPattern1(this, 'p2sh_total_addr_count'),
|
||||
p2wpkh: createMetricPattern1(this, 'p2wpkh_total_addr_count'),
|
||||
p2wsh: createMetricPattern1(this, 'p2wsh_total_addr_count'),
|
||||
p2tr: createMetricPattern1(this, 'p2tr_total_addr_count'),
|
||||
p2a: createMetricPattern1(this, 'p2a_total_addr_count'),
|
||||
},
|
||||
newAddrCount: {
|
||||
all: createAverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2(this, 'new_addr_count'),
|
||||
p2pk65: createAverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2(this, 'p2pk65_new_addr_count'),
|
||||
|
||||
@@ -2413,20 +2413,35 @@ class AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2(Generic[T]):
|
||||
self.pct90: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'pct90'))
|
||||
self.sum: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'sum'))
|
||||
|
||||
class ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.activity: CoinblocksCoindaysSatblocksSatdaysSentPattern = CoinblocksCoindaysSatblocksSatdaysSentPattern(client, acc)
|
||||
self.addr_count: MetricPattern1[StoredU64] = MetricPattern1(client, _m(acc, 'addr_count'))
|
||||
self.addr_count_30d_change: MetricPattern4[StoredF64] = MetricPattern4(client, _m(acc, 'addr_count_30d_change'))
|
||||
self.cost_basis: MaxMinPattern = MaxMinPattern(client, acc)
|
||||
self.outputs: UtxoPattern = UtxoPattern(client, _m(acc, 'utxo_count'))
|
||||
self.realized: CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern = CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern(client, acc)
|
||||
self.relative: InvestedNegNetNuplSupplyUnrealizedPattern = InvestedNegNetNuplSupplyUnrealizedPattern(client, acc)
|
||||
self.supply: _30dHalvedTotalPattern = _30dHalvedTotalPattern(client, acc)
|
||||
self.unrealized: GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern = GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern(client, acc)
|
||||
|
||||
class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.all: MetricPattern1[StoredU64] = MetricPattern1(client, acc)
|
||||
self.p2a: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2a', acc))
|
||||
self.p2pk33: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2pk33', acc))
|
||||
self.p2pk65: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2pk65', acc))
|
||||
self.p2pkh: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2pkh', acc))
|
||||
self.p2sh: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2sh', acc))
|
||||
self.p2tr: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2tr', acc))
|
||||
self.p2wpkh: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2wpkh', acc))
|
||||
self.p2wsh: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2wsh', acc))
|
||||
self.all: _30dCountPattern = _30dCountPattern(client, acc)
|
||||
self.p2a: _30dCountPattern = _30dCountPattern(client, _p('p2a', acc))
|
||||
self.p2pk33: _30dCountPattern = _30dCountPattern(client, _p('p2pk33', acc))
|
||||
self.p2pk65: _30dCountPattern = _30dCountPattern(client, _p('p2pk65', acc))
|
||||
self.p2pkh: _30dCountPattern = _30dCountPattern(client, _p('p2pkh', acc))
|
||||
self.p2sh: _30dCountPattern = _30dCountPattern(client, _p('p2sh', acc))
|
||||
self.p2tr: _30dCountPattern = _30dCountPattern(client, _p('p2tr', acc))
|
||||
self.p2wpkh: _30dCountPattern = _30dCountPattern(client, _p('p2wpkh', acc))
|
||||
self.p2wsh: _30dCountPattern = _30dCountPattern(client, _p('p2wsh', acc))
|
||||
|
||||
class AverageMaxMedianMinPct10Pct25Pct75Pct90TxindexPattern(Generic[T]):
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
@@ -2458,20 +2473,6 @@ class AverageBaseMaxMedianMinPct10Pct25Pct75Pct90Pattern(Generic[T]):
|
||||
self.pct75: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct75'))
|
||||
self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90'))
|
||||
|
||||
class ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.activity: CoinblocksCoindaysSatblocksSatdaysSentPattern = CoinblocksCoindaysSatblocksSatdaysSentPattern(client, acc)
|
||||
self.addr_count: MetricPattern1[StoredU64] = MetricPattern1(client, _m(acc, 'addr_count'))
|
||||
self.cost_basis: MaxMinPattern = MaxMinPattern(client, acc)
|
||||
self.outputs: UtxoPattern = UtxoPattern(client, _m(acc, 'utxo_count'))
|
||||
self.realized: CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern = CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern(client, acc)
|
||||
self.relative: InvestedNegNetNuplSupplyUnrealizedPattern = InvestedNegNetNuplSupplyUnrealizedPattern(client, acc)
|
||||
self.supply: _30dHalvedTotalPattern = _30dHalvedTotalPattern(client, acc)
|
||||
self.unrealized: GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern = GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern(client, acc)
|
||||
|
||||
class _10y2y3y4y5y6y8yPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -2681,6 +2682,14 @@ class BitcoinDollarsSatsPattern3:
|
||||
self.dollars: CumulativeSumPattern[Dollars] = CumulativeSumPattern(client, _m(acc, 'usd'))
|
||||
self.sats: CumulativeSumPattern[Sats] = CumulativeSumPattern(client, acc)
|
||||
|
||||
class _30dCountPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self._30d_change: MetricPattern4[StoredF64] = MetricPattern4(client, _m(acc, '30d_change'))
|
||||
self.count: MetricPattern1[StoredU64] = MetricPattern1(client, acc)
|
||||
|
||||
class DollarsSatsPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -2713,6 +2722,14 @@ class SdSmaPattern:
|
||||
self.sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sd'))
|
||||
self.sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sma'))
|
||||
|
||||
class UtxoPattern:
|
||||
"""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)
|
||||
self.utxo_count_30d_change: MetricPattern4[StoredF64] = MetricPattern4(client, _m(acc, '30d_change'))
|
||||
|
||||
class CumulativeSumPattern(Generic[T]):
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -2744,13 +2761,6 @@ class RatioPattern2:
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, acc)
|
||||
|
||||
class UtxoPattern:
|
||||
"""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_Blocks_Difficulty:
|
||||
@@ -2796,6 +2806,8 @@ class MetricsTree_Blocks_Mining:
|
||||
self.hash_rate_1m_sma: MetricPattern4[StoredF32] = MetricPattern4(client, 'hash_rate_1m_sma')
|
||||
self.hash_rate_2m_sma: MetricPattern4[StoredF32] = MetricPattern4(client, 'hash_rate_2m_sma')
|
||||
self.hash_rate_1y_sma: MetricPattern4[StoredF32] = MetricPattern4(client, 'hash_rate_1y_sma')
|
||||
self.hash_rate_ath: MetricPattern1[StoredF64] = MetricPattern1(client, 'hash_rate_ath')
|
||||
self.hash_rate_drawdown: MetricPattern1[StoredF32] = MetricPattern1(client, 'hash_rate_drawdown')
|
||||
self.hash_price_ths: MetricPattern1[StoredF32] = MetricPattern1(client, 'hash_price_ths')
|
||||
self.hash_price_ths_min: MetricPattern1[StoredF32] = MetricPattern1(client, 'hash_price_ths_min')
|
||||
self.hash_price_phs: MetricPattern1[StoredF32] = MetricPattern1(client, 'hash_price_phs')
|
||||
@@ -3113,7 +3125,7 @@ class MetricsTree_Cointime_ReserveRisk:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.vocdd_365d_sma: MetricPattern6[StoredF64] = MetricPattern6(client, 'vocdd_365d_sma')
|
||||
self.vocdd_365d_median: MetricPattern6[StoredF64] = MetricPattern6(client, 'vocdd_365d_median')
|
||||
self.hodl_bank: MetricPattern6[StoredF64] = MetricPattern6(client, 'hodl_bank')
|
||||
self.reserve_risk: MetricPattern4[StoredF64] = MetricPattern4(client, 'reserve_risk')
|
||||
|
||||
@@ -4241,6 +4253,20 @@ class MetricsTree_Distribution_AddressActivity:
|
||||
self.p2tr: BalanceBothReactivatedReceivingSendingPattern = BalanceBothReactivatedReceivingSendingPattern(client, 'p2tr_address_activity')
|
||||
self.p2a: BalanceBothReactivatedReceivingSendingPattern = BalanceBothReactivatedReceivingSendingPattern(client, 'p2a_address_activity')
|
||||
|
||||
class MetricsTree_Distribution_TotalAddrCount:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.all: MetricPattern1[StoredU64] = MetricPattern1(client, 'total_addr_count')
|
||||
self.p2pk65: MetricPattern1[StoredU64] = MetricPattern1(client, 'p2pk65_total_addr_count')
|
||||
self.p2pk33: MetricPattern1[StoredU64] = MetricPattern1(client, 'p2pk33_total_addr_count')
|
||||
self.p2pkh: MetricPattern1[StoredU64] = MetricPattern1(client, 'p2pkh_total_addr_count')
|
||||
self.p2sh: MetricPattern1[StoredU64] = MetricPattern1(client, 'p2sh_total_addr_count')
|
||||
self.p2wpkh: MetricPattern1[StoredU64] = MetricPattern1(client, 'p2wpkh_total_addr_count')
|
||||
self.p2wsh: MetricPattern1[StoredU64] = MetricPattern1(client, 'p2wsh_total_addr_count')
|
||||
self.p2tr: MetricPattern1[StoredU64] = MetricPattern1(client, 'p2tr_total_addr_count')
|
||||
self.p2a: MetricPattern1[StoredU64] = MetricPattern1(client, 'p2a_total_addr_count')
|
||||
|
||||
class MetricsTree_Distribution_NewAddrCount:
|
||||
"""Metrics tree node."""
|
||||
|
||||
@@ -4281,7 +4307,7 @@ class MetricsTree_Distribution:
|
||||
self.addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern = AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern(client, 'addr_count')
|
||||
self.empty_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern = AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern(client, 'empty_addr_count')
|
||||
self.address_activity: MetricsTree_Distribution_AddressActivity = MetricsTree_Distribution_AddressActivity(client)
|
||||
self.total_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern = AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern(client, 'total_addr_count')
|
||||
self.total_addr_count: MetricsTree_Distribution_TotalAddrCount = MetricsTree_Distribution_TotalAddrCount(client)
|
||||
self.new_addr_count: MetricsTree_Distribution_NewAddrCount = MetricsTree_Distribution_NewAddrCount(client)
|
||||
self.growth_rate: MetricsTree_Distribution_GrowthRate = MetricsTree_Distribution_GrowthRate(client)
|
||||
self.fundedaddressindex: MetricPattern31[FundedAddressIndex] = MetricPattern31(client, 'fundedaddressindex')
|
||||
@@ -4342,7 +4368,7 @@ class MetricsTree:
|
||||
class BrkClient(BrkClientBase):
|
||||
"""Main BRK client with metrics tree and API methods."""
|
||||
|
||||
VERSION = "v0.1.2"
|
||||
VERSION = "v0.1.3"
|
||||
|
||||
INDEXES = [
|
||||
"dateindex",
|
||||
|
||||
@@ -241,13 +241,14 @@ export function createChart({ parent, id: chartId, brk, fitContent }) {
|
||||
);
|
||||
|
||||
// Debounced range persistence
|
||||
ichart.timeScale().subscribeVisibleLogicalRangeChange(
|
||||
debounce((range) => {
|
||||
if (range && range.from < range.to) {
|
||||
setRange({ from: range.from, to: range.to });
|
||||
}
|
||||
}, 100),
|
||||
);
|
||||
const debouncedSetRange = debounce((/** @type {Range | null} */ range) => {
|
||||
if (range && range.from < range.to) {
|
||||
setRange({ from: range.from, to: range.to });
|
||||
}
|
||||
}, 100);
|
||||
// Cancel pending range saves on index change to prevent saving stale ranges to wrong index
|
||||
index.onChange.add(() => debouncedSetRange.cancel());
|
||||
ichart.timeScale().subscribeVisibleLogicalRangeChange(debouncedSetRange);
|
||||
|
||||
function applyColors() {
|
||||
const defaultColor = colors.default();
|
||||
@@ -734,8 +735,8 @@ export function createChart({ parent, id: chartId, brk, fitContent }) {
|
||||
defaultActive,
|
||||
options,
|
||||
}) {
|
||||
const upColor = customColors?.[0] ?? colors.green;
|
||||
const downColor = customColors?.[1] ?? colors.red;
|
||||
const upColor = customColors?.[0] ?? colors.bi.profitLoss[0];
|
||||
const downColor = customColors?.[1] ?? colors.bi.profitLoss[1];
|
||||
|
||||
/** @type {CandlestickISeries} */
|
||||
const candlestickISeries = /** @type {any} */ (
|
||||
@@ -875,7 +876,7 @@ export function createChart({ parent, id: chartId, brk, fitContent }) {
|
||||
metric,
|
||||
name,
|
||||
key,
|
||||
color = [colors.green, colors.red],
|
||||
color = colors.bi.profitLoss,
|
||||
order,
|
||||
unit,
|
||||
paneIndex = 0,
|
||||
@@ -1188,8 +1189,8 @@ export function createChart({ parent, id: chartId, brk, fitContent }) {
|
||||
unit,
|
||||
paneIndex: _paneIndex,
|
||||
defaultActive,
|
||||
topColor = colors.green,
|
||||
bottomColor = colors.red,
|
||||
topColor = colors.bi.profitLoss[0],
|
||||
bottomColor = colors.bi.profitLoss[1],
|
||||
options,
|
||||
}) {
|
||||
const paneIndex = _paneIndex ?? 0;
|
||||
@@ -1351,7 +1352,7 @@ export function createChart({ parent, id: chartId, brk, fitContent }) {
|
||||
const options = blueprint.options;
|
||||
const indexes = Object.keys(blueprint.metric.by);
|
||||
|
||||
const defaultColor = unit === Unit.usd ? colors.green : colors.orange;
|
||||
const defaultColor = unit === Unit.usd ? colors.usd : colors.bitcoin;
|
||||
|
||||
if (indexes.includes(idx)) {
|
||||
switch (blueprint.type) {
|
||||
|
||||
@@ -27,7 +27,7 @@ export function createCointimeSection() {
|
||||
{
|
||||
metric: all.realized.realizedCap,
|
||||
name: "Realized",
|
||||
color: colors.orange,
|
||||
color: colors.realized,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -36,47 +36,47 @@ export function createCointimeSection() {
|
||||
pricePattern: pricing.trueMarketMean,
|
||||
ratio: pricing.trueMarketMeanRatio,
|
||||
name: "True Market Mean",
|
||||
color: colors.blue,
|
||||
color: colors.trueMarketMean,
|
||||
},
|
||||
{
|
||||
pricePattern: pricing.vaultedPrice,
|
||||
ratio: pricing.vaultedPriceRatio,
|
||||
name: "Vaulted",
|
||||
color: colors.lime,
|
||||
color: colors.vaulted,
|
||||
},
|
||||
{
|
||||
pricePattern: pricing.activePrice,
|
||||
ratio: pricing.activePriceRatio,
|
||||
name: "Active",
|
||||
color: colors.rose,
|
||||
color: colors.active,
|
||||
},
|
||||
{
|
||||
pricePattern: pricing.cointimePrice,
|
||||
ratio: pricing.cointimePriceRatio,
|
||||
name: "Cointime",
|
||||
color: colors.yellow,
|
||||
color: colors.cointime,
|
||||
},
|
||||
]);
|
||||
|
||||
const caps = /** @type {const} */ ([
|
||||
{ metric: cap.vaultedCap, name: "Vaulted", color: colors.lime },
|
||||
{ metric: cap.activeCap, name: "Active", color: colors.rose },
|
||||
{ metric: cap.cointimeCap, name: "Cointime", color: colors.yellow },
|
||||
{ metric: cap.investorCap, name: "Investor", color: colors.fuchsia },
|
||||
{ metric: cap.thermoCap, name: "Thermo", color: colors.emerald },
|
||||
{ metric: cap.vaultedCap, name: "Vaulted", color: colors.vaulted },
|
||||
{ metric: cap.activeCap, name: "Active", color: colors.active },
|
||||
{ metric: cap.cointimeCap, name: "Cointime", color: colors.cointime },
|
||||
{ metric: cap.investorCap, name: "Investor", color: colors.investor },
|
||||
{ metric: cap.thermoCap, name: "Thermo", color: colors.thermo },
|
||||
]);
|
||||
|
||||
const supplyBreakdown = /** @type {const} */ ([
|
||||
{ pattern: all.supply.total, name: "Total", color: colors.orange },
|
||||
{ pattern: all.supply.total, name: "Total", color: colors.bitcoin },
|
||||
{
|
||||
pattern: cointimeSupply.vaultedSupply,
|
||||
name: "Vaulted",
|
||||
color: colors.lime,
|
||||
color: colors.vaulted,
|
||||
},
|
||||
{
|
||||
pattern: cointimeSupply.activeSupply,
|
||||
name: "Active",
|
||||
color: colors.rose,
|
||||
color: colors.active,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -85,19 +85,19 @@ export function createCointimeSection() {
|
||||
pattern: all.activity.coinblocksDestroyed,
|
||||
name: "Destroyed",
|
||||
title: "Coinblocks Destroyed",
|
||||
color: colors.red,
|
||||
color: colors.destroyed,
|
||||
},
|
||||
{
|
||||
pattern: activity.coinblocksCreated,
|
||||
name: "Created",
|
||||
title: "Coinblocks Created",
|
||||
color: colors.orange,
|
||||
color: colors.created,
|
||||
},
|
||||
{
|
||||
pattern: activity.coinblocksStored,
|
||||
name: "Stored",
|
||||
title: "Coinblocks Stored",
|
||||
color: colors.green,
|
||||
color: colors.stored,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -107,19 +107,19 @@ export function createCointimeSection() {
|
||||
pattern: value.cointimeValueCreated,
|
||||
name: "Created",
|
||||
title: "Cointime Value Created",
|
||||
color: colors.orange,
|
||||
color: colors.created,
|
||||
},
|
||||
{
|
||||
pattern: value.cointimeValueDestroyed,
|
||||
name: "Destroyed",
|
||||
title: "Cointime Value Destroyed",
|
||||
color: colors.red,
|
||||
color: colors.destroyed,
|
||||
},
|
||||
{
|
||||
pattern: value.cointimeValueStored,
|
||||
name: "Stored",
|
||||
title: "Cointime Value Stored",
|
||||
color: colors.green,
|
||||
color: colors.stored,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -127,7 +127,7 @@ export function createCointimeSection() {
|
||||
pattern: value.vocdd,
|
||||
name: "VOCDD",
|
||||
title: "Value of Coin Days Destroyed",
|
||||
color: colors.purple,
|
||||
color: colors.vocdd,
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -144,12 +144,12 @@ export function createCointimeSection() {
|
||||
price({
|
||||
metric: all.realized.realizedPrice,
|
||||
name: "Realized",
|
||||
color: colors.orange,
|
||||
color: colors.realized,
|
||||
}),
|
||||
price({
|
||||
metric: all.realized.investorPrice,
|
||||
name: "Investor",
|
||||
color: colors.fuchsia,
|
||||
color: colors.investor,
|
||||
}),
|
||||
...prices.map(({ pricePattern, name, color }) =>
|
||||
price({ metric: pricePattern, name, color }),
|
||||
@@ -168,7 +168,7 @@ export function createCointimeSection() {
|
||||
price({
|
||||
metric: all.realized.realizedPrice,
|
||||
name: "Realized",
|
||||
color: colors.orange,
|
||||
color: colors.realized,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
@@ -228,19 +228,19 @@ export function createCointimeSection() {
|
||||
line({
|
||||
metric: activity.liveliness,
|
||||
name: "Liveliness",
|
||||
color: colors.rose,
|
||||
color: colors.liveliness,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
metric: activity.vaultedness,
|
||||
name: "Vaultedness",
|
||||
color: colors.lime,
|
||||
color: colors.vaulted,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
metric: activity.activityToVaultednessRatio,
|
||||
name: "L/V Ratio",
|
||||
color: colors.purple,
|
||||
color: colors.activity,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -394,9 +394,9 @@ export function createCointimeSection() {
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: reserveRisk.vocdd365dSma,
|
||||
name: "365d SMA",
|
||||
color: colors.cyan,
|
||||
metric: reserveRisk.vocdd365dMedian,
|
||||
name: "365d Median",
|
||||
color: colors.ma._1y,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
@@ -429,7 +429,7 @@ export function createCointimeSection() {
|
||||
line({
|
||||
metric: reserveRisk.reserveRisk,
|
||||
name: "Ratio",
|
||||
color: colors.orange,
|
||||
color: colors.reserveRisk,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
@@ -441,7 +441,7 @@ export function createCointimeSection() {
|
||||
line({
|
||||
metric: reserveRisk.hodlBank,
|
||||
name: "Value",
|
||||
color: colors.blue,
|
||||
color: colors.hodlBank,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
@@ -460,13 +460,13 @@ export function createCointimeSection() {
|
||||
dots({
|
||||
metric: supply.inflation,
|
||||
name: "Base",
|
||||
color: colors.orange,
|
||||
color: colors.base,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
dots({
|
||||
metric: adjusted.cointimeAdjInflationRate,
|
||||
name: "Cointime-Adjusted",
|
||||
color: colors.purple,
|
||||
color: colors.adjusted,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
@@ -481,13 +481,13 @@ export function createCointimeSection() {
|
||||
line({
|
||||
metric: supply.velocity.btc,
|
||||
name: "Base",
|
||||
color: colors.orange,
|
||||
color: colors.base,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
metric: adjusted.cointimeAdjTxBtcVelocity,
|
||||
name: "Cointime-Adjusted",
|
||||
color: colors.red,
|
||||
color: colors.adjusted,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
@@ -499,13 +499,13 @@ export function createCointimeSection() {
|
||||
line({
|
||||
metric: supply.velocity.usd,
|
||||
name: "Base",
|
||||
color: colors.emerald,
|
||||
color: colors.thermo,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
metric: adjusted.cointimeAdjTxUsdVelocity,
|
||||
name: "Cointime-Adjusted",
|
||||
color: colors.lime,
|
||||
color: colors.vaulted,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -242,52 +242,52 @@ function createRealizedPnlSection(args, title) {
|
||||
line({
|
||||
metric: realized.realizedProfit.sum,
|
||||
name: "Profit",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: realized.realizedProfit7dEma,
|
||||
name: "Profit 7d EMA",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: realized.realizedProfit.cumulative,
|
||||
name: "Profit Cumulative",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.realizedLoss.sum,
|
||||
name: "Loss",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: realized.realizedLoss7dEma,
|
||||
name: "Loss 7d EMA",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: realized.realizedLoss.cumulative,
|
||||
name: "Loss Cumulative",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.negRealizedLoss.sum,
|
||||
name: "Negative Loss",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.negRealizedLoss.cumulative,
|
||||
name: "Negative Loss Cumulative",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -301,26 +301,26 @@ function createRealizedPnlSection(args, title) {
|
||||
baseline({
|
||||
metric: realized.realizedProfitRelToRealizedCap.sum,
|
||||
name: "Profit",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
baseline({
|
||||
metric: realized.realizedProfitRelToRealizedCap.cumulative,
|
||||
name: "Profit Cumulative",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.pctRcap,
|
||||
defaultActive: false,
|
||||
}),
|
||||
baseline({
|
||||
metric: realized.realizedLossRelToRealizedCap.sum,
|
||||
name: "Loss",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
baseline({
|
||||
metric: realized.realizedLossRelToRealizedCap.cumulative,
|
||||
name: "Loss Cumulative",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.pctRcap,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -390,7 +390,7 @@ function createRealizedPnlSection(args, title) {
|
||||
name: "SOPR",
|
||||
title: title("SOPR"),
|
||||
bottom: [
|
||||
...createSingleSoprSeries(colors, args.tree),
|
||||
...createSingleSoprSeries(args.tree),
|
||||
priceLine({
|
||||
unit: Unit.ratio,
|
||||
number: 1,
|
||||
@@ -400,7 +400,7 @@ function createRealizedPnlSection(args, title) {
|
||||
{
|
||||
name: "Sell Side Risk",
|
||||
title: title("Sell Side Risk Ratio"),
|
||||
bottom: createSingleSellSideRiskSeries(colors, args.tree),
|
||||
bottom: createSingleSellSideRiskSeries(args.tree),
|
||||
},
|
||||
{
|
||||
name: "Value",
|
||||
@@ -408,17 +408,17 @@ function createRealizedPnlSection(args, title) {
|
||||
{
|
||||
name: "Created & Destroyed",
|
||||
title: title("Value Created & Destroyed"),
|
||||
bottom: createSingleValueCreatedDestroyedSeries(colors, args.tree),
|
||||
bottom: createSingleValueCreatedDestroyedSeries(args.tree),
|
||||
},
|
||||
{
|
||||
name: "Breakdown",
|
||||
title: title("Value Flow Breakdown"),
|
||||
bottom: createSingleValueFlowBreakdownSeries(colors, args.tree),
|
||||
bottom: createSingleValueFlowBreakdownSeries(args.tree),
|
||||
},
|
||||
{
|
||||
name: "Flow",
|
||||
title: title("Capitulation & Profit Flow"),
|
||||
bottom: createSingleCapitulationProfitFlowSeries(colors, args.tree),
|
||||
bottom: createSingleCapitulationProfitFlowSeries(args.tree),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -429,20 +429,20 @@ function createRealizedPnlSection(args, title) {
|
||||
line({
|
||||
metric: realized.peakRegret.sum,
|
||||
name: "Sum",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: realized.peakRegret.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
baseline({
|
||||
metric: realized.peakRegretRelToRealizedCap,
|
||||
name: "Rel. to Realized Cap",
|
||||
color: colors.orange,
|
||||
color: colors.realized,
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
],
|
||||
@@ -457,39 +457,39 @@ function createRealizedPnlSection(args, title) {
|
||||
line({
|
||||
metric: realized.sentInProfit.bitcoin.sum,
|
||||
name: "Sum",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInProfit.bitcoin.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.btc,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInProfit.sats.sum,
|
||||
name: "Sum",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInProfit.sats.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInProfit.dollars.sum,
|
||||
name: "Sum",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInProfit.dollars.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -502,39 +502,39 @@ function createRealizedPnlSection(args, title) {
|
||||
line({
|
||||
metric: realized.sentInLoss.bitcoin.sum,
|
||||
name: "Sum",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInLoss.bitcoin.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.btc,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInLoss.sats.sum,
|
||||
name: "Sum",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInLoss.sats.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInLoss.dollars.sum,
|
||||
name: "Sum",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInLoss.dollars.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -546,7 +546,7 @@ function createRealizedPnlSection(args, title) {
|
||||
bottom: satsBtcUsd({
|
||||
pattern: realized.sentInProfit14dEma,
|
||||
name: "14d EMA",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
}),
|
||||
},
|
||||
{
|
||||
@@ -555,7 +555,7 @@ function createRealizedPnlSection(args, title) {
|
||||
bottom: satsBtcUsd({
|
||||
pattern: realized.sentInLoss14dEma,
|
||||
name: "14d EMA",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
}),
|
||||
},
|
||||
],
|
||||
@@ -820,7 +820,7 @@ function createUnrealizedSection(list, useGroupName, title) {
|
||||
line({
|
||||
metric: tree.unrealized.unrealizedProfit,
|
||||
name: useGroupName ? name : "Profit",
|
||||
color: useGroupName ? color : colors.green,
|
||||
color: useGroupName ? color : colors.profit,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
@@ -832,7 +832,7 @@ function createUnrealizedSection(list, useGroupName, title) {
|
||||
line({
|
||||
metric: tree.unrealized.unrealizedLoss,
|
||||
name: useGroupName ? name : "Loss",
|
||||
color: useGroupName ? color : colors.red,
|
||||
color: useGroupName ? color : colors.loss,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
@@ -856,7 +856,7 @@ function createUnrealizedSection(list, useGroupName, title) {
|
||||
line({
|
||||
metric: tree.unrealized.negUnrealizedLoss,
|
||||
name: useGroupName ? name : "Negative Loss",
|
||||
color: useGroupName ? color : colors.red,
|
||||
color: useGroupName ? color : colors.loss,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
@@ -871,7 +871,7 @@ function createUnrealizedSection(list, useGroupName, title) {
|
||||
line({
|
||||
metric: tree.unrealized.investedCapitalInProfit,
|
||||
name: useGroupName ? name : "In Profit",
|
||||
color: useGroupName ? color : colors.green,
|
||||
color: useGroupName ? color : colors.profit,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
@@ -883,7 +883,7 @@ function createUnrealizedSection(list, useGroupName, title) {
|
||||
line({
|
||||
metric: tree.unrealized.investedCapitalInLoss,
|
||||
name: useGroupName ? name : "In Loss",
|
||||
color: useGroupName ? color : colors.red,
|
||||
color: useGroupName ? color : colors.loss,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
@@ -913,7 +913,7 @@ function createUnrealizedSection(list, useGroupName, title) {
|
||||
line({
|
||||
metric: tree.relative.unrealizedProfitRelToMarketCap,
|
||||
name: useGroupName ? name : "Profit",
|
||||
color: useGroupName ? color : colors.green,
|
||||
color: useGroupName ? color : colors.profit,
|
||||
unit: Unit.pctMcap,
|
||||
}),
|
||||
]),
|
||||
@@ -925,7 +925,7 @@ function createUnrealizedSection(list, useGroupName, title) {
|
||||
line({
|
||||
metric: tree.relative.unrealizedLossRelToMarketCap,
|
||||
name: useGroupName ? name : "Loss",
|
||||
color: useGroupName ? color : colors.red,
|
||||
color: useGroupName ? color : colors.loss,
|
||||
unit: Unit.pctMcap,
|
||||
}),
|
||||
]),
|
||||
@@ -949,7 +949,7 @@ function createUnrealizedSection(list, useGroupName, title) {
|
||||
line({
|
||||
metric: tree.relative.negUnrealizedLossRelToMarketCap,
|
||||
name: useGroupName ? name : "Negative Loss",
|
||||
color: useGroupName ? color : colors.red,
|
||||
color: useGroupName ? color : colors.loss,
|
||||
unit: Unit.pctMcap,
|
||||
}),
|
||||
]),
|
||||
@@ -961,7 +961,7 @@ function createUnrealizedSection(list, useGroupName, title) {
|
||||
baseline({
|
||||
metric: tree.relative.investedCapitalInProfitPct,
|
||||
name: useGroupName ? name : "In Profit",
|
||||
color: useGroupName ? color : colors.green,
|
||||
color: useGroupName ? color : colors.profit,
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
]),
|
||||
@@ -973,7 +973,7 @@ function createUnrealizedSection(list, useGroupName, title) {
|
||||
baseline({
|
||||
metric: tree.relative.investedCapitalInLossPct,
|
||||
name: useGroupName ? name : "In Loss",
|
||||
color: useGroupName ? color : colors.red,
|
||||
color: useGroupName ? color : colors.loss,
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
]),
|
||||
|
||||
453
website/scripts/options/distribution/cost-basis.js
Normal file
453
website/scripts/options/distribution/cost-basis.js
Normal file
@@ -0,0 +1,453 @@
|
||||
/**
|
||||
* Cost Basis section builders
|
||||
*
|
||||
* Structure:
|
||||
* - Summary: Key stats (avg + median active, quartiles/extremes available)
|
||||
* - By Coin: BTC-weighted percentiles (IQR active: p25, p50, p75)
|
||||
* - By Capital: USD-weighted percentiles (IQR active: p25, p50, p75)
|
||||
* - Price Position: Spot percentile (both perspectives active)
|
||||
*
|
||||
* For cohorts WITHOUT percentiles: Summary only
|
||||
*/
|
||||
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { priceLines } from "../constants.js";
|
||||
import { line, price } from "../series.js";
|
||||
|
||||
/**
|
||||
* @param {PercentilesPattern} p
|
||||
* @param {(name: string) => string} [n]
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
function createCorePercentileSeries(p, n = (x) => x) {
|
||||
return [
|
||||
price({
|
||||
metric: p.pct95,
|
||||
name: n("p95"),
|
||||
color: colors.pct._95,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct90,
|
||||
name: n("p90"),
|
||||
color: colors.pct._90,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct85,
|
||||
name: n("p85"),
|
||||
color: colors.pct._85,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct80,
|
||||
name: n("p80"),
|
||||
color: colors.pct._80,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({ metric: p.pct75, name: n("p75"), color: colors.pct._75 }),
|
||||
price({
|
||||
metric: p.pct70,
|
||||
name: n("p70"),
|
||||
color: colors.pct._70,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct65,
|
||||
name: n("p65"),
|
||||
color: colors.pct._65,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct60,
|
||||
name: n("p60"),
|
||||
color: colors.pct._60,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct55,
|
||||
name: n("p55"),
|
||||
color: colors.pct._55,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({ metric: p.pct50, name: n("p50"), color: colors.pct._50 }),
|
||||
price({
|
||||
metric: p.pct45,
|
||||
name: n("p45"),
|
||||
color: colors.pct._45,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct40,
|
||||
name: n("p40"),
|
||||
color: colors.pct._40,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct35,
|
||||
name: n("p35"),
|
||||
color: colors.pct._35,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct30,
|
||||
name: n("p30"),
|
||||
color: colors.pct._30,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({ metric: p.pct25, name: n("p25"), color: colors.pct._25 }),
|
||||
price({
|
||||
metric: p.pct20,
|
||||
name: n("p20"),
|
||||
color: colors.pct._20,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct15,
|
||||
name: n("p15"),
|
||||
color: colors.pct._15,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct10,
|
||||
name: n("p10"),
|
||||
color: colors.pct._10,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct05,
|
||||
name: n("p05"),
|
||||
color: colors.pct._05,
|
||||
defaultActive: false,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleSummarySeriesBasic(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
return [
|
||||
price({ metric: tree.realized.realizedPrice, name: "Average", color }),
|
||||
price({
|
||||
metric: tree.costBasis.max,
|
||||
name: "Max",
|
||||
color: colors.pct._100,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: tree.costBasis.min,
|
||||
name: "Min",
|
||||
color: colors.pct._0,
|
||||
defaultActive: false,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortAll | CohortFull | CohortWithPercentiles} cohort
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleSummarySeriesWithPercentiles(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
const p = tree.costBasis.percentiles;
|
||||
return [
|
||||
price({ metric: tree.realized.realizedPrice, name: "Average", color }),
|
||||
price({
|
||||
metric: tree.costBasis.max,
|
||||
name: "Max (p100)",
|
||||
color: colors.pct._100,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct75,
|
||||
name: "Q3 (p75)",
|
||||
color: colors.pct._75,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({ metric: p.pct50, name: "Median (p50)", color: colors.pct._50 }),
|
||||
price({
|
||||
metric: p.pct25,
|
||||
name: "Q1 (p25)",
|
||||
color: colors.pct._25,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: tree.costBasis.min,
|
||||
name: "Min (p0)",
|
||||
color: colors.pct._0,
|
||||
defaultActive: false,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {readonly { name: string, color: Color, tree: { realized: { realizedPrice: ActivePricePattern } } }[]} T
|
||||
* @param {T} list
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
function createGroupedSummarySeries(list) {
|
||||
return list.map(({ name, color, tree }) =>
|
||||
price({ metric: tree.realized.realizedPrice, name, color }),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortAll | CohortFull | CohortWithPercentiles} cohort
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleByCoinSeries(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
const cb = tree.costBasis;
|
||||
return [
|
||||
price({ metric: tree.realized.realizedPrice, name: "Average", color }),
|
||||
price({
|
||||
metric: cb.max,
|
||||
name: "p100",
|
||||
color: colors.pct._100,
|
||||
defaultActive: false,
|
||||
}),
|
||||
...createCorePercentileSeries(cb.percentiles),
|
||||
price({
|
||||
metric: cb.min,
|
||||
name: "p0",
|
||||
color: colors.pct._0,
|
||||
defaultActive: false,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortAll | CohortFull | CohortWithPercentiles} cohort
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleByCapitalSeries(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
return [
|
||||
price({ metric: tree.realized.investorPrice, name: "Average", color }),
|
||||
...createCorePercentileSeries(tree.costBasis.investedCapital),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortAll | CohortFull | CohortWithPercentiles} cohort
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function createSinglePricePositionSeries(cohort) {
|
||||
const { tree } = cohort;
|
||||
return [
|
||||
line({
|
||||
metric: tree.costBasis.spotCostBasisPercentile,
|
||||
name: "By Coin",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: tree.costBasis.spotInvestedCapitalPercentile,
|
||||
name: "By Capital",
|
||||
color: colors.usd,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.percentage }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ cohort: UtxoCohortObject | CohortWithoutRelative, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCostBasisSection({ cohort, title }) {
|
||||
return {
|
||||
name: "Cost Basis",
|
||||
tree: [
|
||||
{
|
||||
name: "Summary",
|
||||
title: title("Cost Basis Summary"),
|
||||
top: createSingleSummarySeriesBasic(cohort),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ cohort: CohortAll | CohortFull | CohortWithPercentiles, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCostBasisSectionWithPercentiles({ cohort, title }) {
|
||||
return {
|
||||
name: "Cost Basis",
|
||||
tree: [
|
||||
{
|
||||
name: "Summary",
|
||||
title: title("Cost Basis Summary"),
|
||||
top: createSingleSummarySeriesWithPercentiles(cohort),
|
||||
},
|
||||
{
|
||||
name: "By Coin",
|
||||
title: title("Cost Basis Distribution (BTC-weighted)"),
|
||||
top: createSingleByCoinSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "By Capital",
|
||||
title: title("Cost Basis Distribution (USD-weighted)"),
|
||||
top: createSingleByCapitalSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "Price Position",
|
||||
title: title("Current Price Position"),
|
||||
bottom: createSinglePricePositionSeries(cohort),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {readonly (UtxoCohortObject | CohortWithoutRelative)[]} T
|
||||
* @param {{ list: T, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedCostBasisSection({ list, title }) {
|
||||
return {
|
||||
name: "Cost Basis",
|
||||
tree: [
|
||||
{
|
||||
name: "Summary",
|
||||
title: title("Cost Basis Summary"),
|
||||
top: createGroupedSummarySeries(list),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ list: readonly (CohortAll | CohortFull | CohortWithPercentiles)[], title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedCostBasisSectionWithPercentiles({ list, title }) {
|
||||
return {
|
||||
name: "Cost Basis",
|
||||
tree: [
|
||||
{
|
||||
name: "Summary",
|
||||
title: title("Cost Basis Summary"),
|
||||
top: createGroupedSummarySeries(list),
|
||||
},
|
||||
{
|
||||
name: "By Coin",
|
||||
tree: [
|
||||
{
|
||||
name: "Average",
|
||||
title: title("Realized Price Comparison"),
|
||||
top: list.map(({ name, color, tree }) =>
|
||||
price({ metric: tree.realized.realizedPrice, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Median",
|
||||
title: title("Cost Basis Median (BTC-weighted)"),
|
||||
top: list.map(({ name, color, tree }) =>
|
||||
price({ metric: tree.costBasis.percentiles.pct50, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Q3",
|
||||
title: title("Cost Basis Q3 (BTC-weighted)"),
|
||||
top: list.map(({ name, color, tree }) =>
|
||||
price({ metric: tree.costBasis.percentiles.pct75, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Q1",
|
||||
title: title("Cost Basis Q1 (BTC-weighted)"),
|
||||
top: list.map(({ name, color, tree }) =>
|
||||
price({ metric: tree.costBasis.percentiles.pct25, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "By Capital",
|
||||
tree: [
|
||||
{
|
||||
name: "Average",
|
||||
title: title("Investor Price Comparison"),
|
||||
top: list.map(({ name, color, tree }) =>
|
||||
price({ metric: tree.realized.investorPrice, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Median",
|
||||
title: title("Cost Basis Median (USD-weighted)"),
|
||||
top: list.map(({ name, color, tree }) =>
|
||||
price({
|
||||
metric: tree.costBasis.investedCapital.pct50,
|
||||
name,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Q3",
|
||||
title: title("Cost Basis Q3 (USD-weighted)"),
|
||||
top: list.map(({ name, color, tree }) =>
|
||||
price({
|
||||
metric: tree.costBasis.investedCapital.pct75,
|
||||
name,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Q1",
|
||||
title: title("Cost Basis Q1 (USD-weighted)"),
|
||||
top: list.map(({ name, color, tree }) =>
|
||||
price({
|
||||
metric: tree.costBasis.investedCapital.pct25,
|
||||
name,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Price Position",
|
||||
tree: [
|
||||
{
|
||||
name: "By Coin",
|
||||
title: title("Price Position (BTC-weighted)"),
|
||||
bottom: [
|
||||
...list.map(({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.costBasis.spotCostBasisPercentile,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.percentage }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "By Capital",
|
||||
title: title("Price Position (USD-weighted)"),
|
||||
bottom: [
|
||||
...list.map(({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.costBasis.spotInvestedCapitalPercentile,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.percentage }),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -44,7 +44,7 @@ export function buildCohortData() {
|
||||
const cohortAll = {
|
||||
name: "",
|
||||
title: "",
|
||||
color: colors.orange,
|
||||
color: colors.bitcoin,
|
||||
tree: utxoCohorts.all,
|
||||
addrCount: addrCount.all,
|
||||
};
|
||||
|
||||
479
website/scripts/options/distribution/holdings.js
Normal file
479
website/scripts/options/distribution/holdings.js
Normal file
@@ -0,0 +1,479 @@
|
||||
/**
|
||||
* Holdings section builders
|
||||
*
|
||||
* Structure (Option C - optimized for UX):
|
||||
* - Supply: Total BTC held (flat, one click)
|
||||
* - UTXO Count: Number of UTXOs (flat, one click)
|
||||
* - Address Count: Number of addresses (when available, flat)
|
||||
* - 30d Changes/: Folder for change metrics
|
||||
* - Supply: 30d supply change
|
||||
* - Relative: % of circulating supply (when available)
|
||||
*
|
||||
* Rationale: Most-used metrics (Supply, UTXO Count) are immediately accessible.
|
||||
* 30d changes are grouped together for consistency and cleaner navigation.
|
||||
*/
|
||||
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { line, baseline } from "../series.js";
|
||||
import { satsBtcUsd, satsBtcUsdBaseline } from "../shared.js";
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleSupplySeries(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
return [...satsBtcUsd({ pattern: tree.supply.total, name: "Supply", color })];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingle30dChangeSeries(cohort) {
|
||||
return satsBtcUsdBaseline({ pattern: cohort.tree.supply._30dChange, name: "30d Change" });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleUtxoCountSeries(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
return [
|
||||
line({
|
||||
metric: tree.outputs.utxoCount,
|
||||
name: "UTXO Count",
|
||||
color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleUtxoCount30dChangeSeries(cohort) {
|
||||
return [
|
||||
baseline({
|
||||
metric: cohort.tree.outputs.utxoCount30dChange,
|
||||
name: "30d Change",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortAll | CohortAddress} cohort
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleAddrCount30dChangeSeries(cohort) {
|
||||
return [
|
||||
baseline({
|
||||
metric: cohort.addrCount._30dChange,
|
||||
name: "30d Change",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortFull | CohortWithAdjusted | CohortBasicWithMarketCap | CohortMinAge} cohort
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleRelativeSeries(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
return [
|
||||
line({
|
||||
metric: tree.relative.supplyRelToCirculatingSupply,
|
||||
name: "% of Circulating",
|
||||
color,
|
||||
unit: Unit.pctSupply,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ cohort: UtxoCohortObject | CohortWithoutRelative, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createHoldingsSection({ cohort, title }) {
|
||||
return {
|
||||
name: "Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply"),
|
||||
bottom: createSingleSupplySeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count"),
|
||||
bottom: createSingleUtxoCountSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "30d Changes",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply 30d Change"),
|
||||
bottom: createSingle30dChangeSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count 30d Change"),
|
||||
bottom: createSingleUtxoCount30dChangeSeries(cohort),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ cohort: CohortFull | CohortWithAdjusted | CohortBasicWithMarketCap | CohortMinAge, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createHoldingsSectionWithRelative({ cohort, title }) {
|
||||
return {
|
||||
name: "Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply"),
|
||||
bottom: createSingleSupplySeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count"),
|
||||
bottom: createSingleUtxoCountSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "30d Changes",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply 30d Change"),
|
||||
bottom: createSingle30dChangeSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count 30d Change"),
|
||||
bottom: createSingleUtxoCount30dChangeSeries(cohort),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Relative",
|
||||
title: title("Relative to Circulating Supply"),
|
||||
bottom: createSingleRelativeSeries(cohort),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ cohort: CohortAll, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createHoldingsSectionAll({ cohort, title }) {
|
||||
return {
|
||||
name: "Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply"),
|
||||
bottom: createSingleSupplySeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count"),
|
||||
bottom: createSingleUtxoCountSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "Address Count",
|
||||
title: title("Address Count"),
|
||||
bottom: [
|
||||
line({
|
||||
metric: cohort.addrCount.count,
|
||||
name: "Address Count",
|
||||
color: cohort.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "30d Changes",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply 30d Change"),
|
||||
bottom: createSingle30dChangeSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count 30d Change"),
|
||||
bottom: createSingleUtxoCount30dChangeSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "Address Count",
|
||||
title: title("Address Count 30d Change"),
|
||||
bottom: createSingleAddrCount30dChangeSeries(cohort),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ cohort: CohortAddress, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createHoldingsSectionAddress({ cohort, title }) {
|
||||
return {
|
||||
name: "Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply"),
|
||||
bottom: createSingleSupplySeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count"),
|
||||
bottom: createSingleUtxoCountSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "Address Count",
|
||||
title: title("Address Count"),
|
||||
bottom: [
|
||||
line({
|
||||
metric: cohort.addrCount.count,
|
||||
name: "Address Count",
|
||||
color: cohort.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "30d Changes",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply 30d Change"),
|
||||
bottom: createSingle30dChangeSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count 30d Change"),
|
||||
bottom: createSingleUtxoCount30dChangeSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "Address Count",
|
||||
title: title("Address Count 30d Change"),
|
||||
bottom: createSingleAddrCount30dChangeSeries(cohort),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ list: readonly CohortAddress[], title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedHoldingsSectionAddress({ list, title }) {
|
||||
return {
|
||||
name: "Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply"),
|
||||
bottom: list.flatMap(({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.supply.total, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.outputs.utxoCount,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Address Count",
|
||||
title: title("Address Count"),
|
||||
bottom: list.map(({ name, color, addrCount }) =>
|
||||
line({ metric: addrCount.count, name, color, unit: Unit.count }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "30d Changes",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply 30d Change"),
|
||||
bottom: list.flatMap(({ name, color, tree }) =>
|
||||
satsBtcUsdBaseline({ pattern: tree.supply._30dChange, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count 30d Change"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.outputs.utxoCount30dChange,
|
||||
name,
|
||||
unit: Unit.count,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Address Count",
|
||||
title: title("Address Count 30d Change"),
|
||||
bottom: list.map(({ name, color, addrCount }) =>
|
||||
baseline({
|
||||
metric: addrCount._30dChange,
|
||||
name,
|
||||
unit: Unit.count,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {readonly (UtxoCohortObject | CohortWithoutRelative)[]} T
|
||||
* @param {{ list: T, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedHoldingsSection({ list, title }) {
|
||||
return {
|
||||
name: "Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply"),
|
||||
bottom: list.flatMap(({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.supply.total, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.outputs.utxoCount,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "30d Changes",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply 30d Change"),
|
||||
bottom: list.flatMap(({ name, color, tree }) =>
|
||||
satsBtcUsdBaseline({ pattern: tree.supply._30dChange, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count 30d Change"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.outputs.utxoCount30dChange,
|
||||
name,
|
||||
unit: Unit.count,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ list: readonly (CohortFull | CohortWithAdjusted | CohortBasicWithMarketCap | CohortMinAge)[], title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedHoldingsSectionWithRelative({ list, title }) {
|
||||
return {
|
||||
name: "Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply"),
|
||||
bottom: list.flatMap(({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.supply.total, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.outputs.utxoCount,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "30d Changes",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply 30d Change"),
|
||||
bottom: list.flatMap(({ name, color, tree }) =>
|
||||
satsBtcUsdBaseline({ pattern: tree.supply._30dChange, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count 30d Change"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.outputs.utxoCount30dChange,
|
||||
name,
|
||||
unit: Unit.count,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Relative",
|
||||
title: title("Relative to Circulating Supply"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.relative.supplyRelToCirculatingSupply,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.pctSupply,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
225
website/scripts/options/distribution/prices.js
Normal file
225
website/scripts/options/distribution/prices.js
Normal file
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Prices section builders
|
||||
*
|
||||
* Structure (single cohort):
|
||||
* - Compare: Both prices on one chart
|
||||
* - Realized: Price + Ratio (MVRV) + Z-Scores (for full cohorts)
|
||||
* - Investor: Price + Ratio + Z-Scores (for full cohorts)
|
||||
*
|
||||
* Structure (grouped cohorts):
|
||||
* - Realized: Price + Ratio comparison across cohorts
|
||||
* - Investor: Price + Ratio comparison across cohorts
|
||||
*
|
||||
* For cohorts WITHOUT full ratio patterns: basic Price/Ratio charts only (no Z-Scores)
|
||||
*/
|
||||
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { createPriceRatioCharts } from "../shared.js";
|
||||
import { baseline, price } from "../series.js";
|
||||
import { Unit } from "../../utils/units.js";
|
||||
|
||||
/**
|
||||
* Create prices section for cohorts with full ActivePriceRatioPattern
|
||||
* (CohortAll, CohortFull, CohortWithPercentiles)
|
||||
* @param {{ cohort: CohortAll | CohortFull | CohortWithPercentiles, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createPricesSectionFull({ cohort, title }) {
|
||||
const { tree, color } = cohort;
|
||||
return {
|
||||
name: "Prices",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Prices"),
|
||||
top: [
|
||||
price({
|
||||
metric: tree.realized.realizedPrice,
|
||||
name: "Realized",
|
||||
color: colors.realized,
|
||||
}),
|
||||
price({
|
||||
metric: tree.realized.investorPrice,
|
||||
name: "Investor",
|
||||
color: colors.investor,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Realized",
|
||||
tree: createPriceRatioCharts({
|
||||
context: cohort.name,
|
||||
legend: "Realized",
|
||||
pricePattern: tree.realized.realizedPrice,
|
||||
ratio: tree.realized.realizedPriceExtra,
|
||||
color,
|
||||
priceTitle: title("Realized Price"),
|
||||
titlePrefix: "Realized Price",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Investor",
|
||||
tree: createPriceRatioCharts({
|
||||
context: cohort.name,
|
||||
legend: "Investor",
|
||||
pricePattern: tree.realized.investorPrice,
|
||||
ratio: tree.realized.investorPriceExtra,
|
||||
color,
|
||||
priceTitle: title("Investor Price"),
|
||||
titlePrefix: "Investor Price",
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create prices section for cohorts with basic ratio patterns only
|
||||
* (CohortWithAdjusted, CohortBasic, CohortAddress, CohortWithoutRelative)
|
||||
* @param {{ cohort: CohortWithAdjusted | CohortBasic | CohortAddress | CohortWithoutRelative, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createPricesSectionBasic({ cohort, title }) {
|
||||
const { tree, color } = cohort;
|
||||
return {
|
||||
name: "Prices",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Prices"),
|
||||
top: [
|
||||
price({
|
||||
metric: tree.realized.realizedPrice,
|
||||
name: "Realized",
|
||||
color: colors.realized,
|
||||
}),
|
||||
price({
|
||||
metric: tree.realized.investorPrice,
|
||||
name: "Investor",
|
||||
color: colors.investor,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Realized",
|
||||
tree: [
|
||||
{
|
||||
name: "Price",
|
||||
title: title("Realized Price"),
|
||||
top: [
|
||||
price({
|
||||
metric: tree.realized.realizedPrice,
|
||||
name: "Realized",
|
||||
color,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Realized Price Ratio"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: tree.realized.realizedPriceExtra.ratio,
|
||||
name: "Ratio",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Investor",
|
||||
tree: [
|
||||
{
|
||||
name: "Price",
|
||||
title: title("Investor Price"),
|
||||
top: [
|
||||
price({
|
||||
metric: tree.realized.investorPrice,
|
||||
name: "Investor",
|
||||
color,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Investor Price Ratio"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: tree.realized.investorPriceExtra.ratio,
|
||||
name: "Ratio",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create prices section for grouped cohorts
|
||||
* @template {readonly (CohortAll | CohortFull | CohortWithPercentiles | CohortWithAdjusted | CohortBasic | CohortAddress | CohortWithoutRelative)[]} T
|
||||
* @param {{ list: T, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedPricesSection({ list, title }) {
|
||||
return {
|
||||
name: "Prices",
|
||||
tree: [
|
||||
{
|
||||
name: "Realized",
|
||||
tree: [
|
||||
{
|
||||
name: "Price",
|
||||
title: title("Realized Price"),
|
||||
top: list.map(({ name, color, tree }) =>
|
||||
price({ metric: tree.realized.realizedPrice, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Realized Price Ratio"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.realizedPriceExtra.ratio,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Investor",
|
||||
tree: [
|
||||
{
|
||||
name: "Price",
|
||||
title: title("Investor Price"),
|
||||
top: list.map(({ name, color, tree }) =>
|
||||
price({ metric: tree.realized.investorPrice, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Investor Price Ratio"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.investorPriceExtra.ratio,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -176,17 +176,17 @@ function createSingleSupplySeriesBase(cohort) {
|
||||
...satsBtcUsd({
|
||||
pattern: tree.supply._30dChange,
|
||||
name: "30d Change",
|
||||
color: colors.orange,
|
||||
color: colors.bitcoin,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: tree.unrealized.supplyInProfit,
|
||||
name: "In Profit",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: tree.unrealized.supplyInLoss,
|
||||
name: "In Loss",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: tree.supply.halved,
|
||||
@@ -211,13 +211,13 @@ function createSingleSupplyRelativeToOwnMetrics(cohort) {
|
||||
line({
|
||||
metric: tree.relative.supplyInProfitRelToOwnSupply,
|
||||
name: "In Profit",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.pctOwn,
|
||||
}),
|
||||
line({
|
||||
metric: tree.relative.supplyInLossRelToOwnSupply,
|
||||
name: "In Loss",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.pctOwn,
|
||||
}),
|
||||
priceLine({
|
||||
@@ -403,13 +403,13 @@ export function createSupplyPnlRelativeToCirculatingSeries(cohort) {
|
||||
line({
|
||||
metric: cohort.tree.relative.supplyInProfitRelToCirculatingSupply,
|
||||
name: "In Profit",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.pctSupply,
|
||||
}),
|
||||
line({
|
||||
metric: cohort.tree.relative.supplyInLossRelToCirculatingSupply,
|
||||
name: "In Loss",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.pctSupply,
|
||||
}),
|
||||
];
|
||||
@@ -513,7 +513,7 @@ export function createAddressCountSeries(list, useGroupName) {
|
||||
line({
|
||||
metric: tree.addrCount,
|
||||
name: useGroupName ? name : "Count",
|
||||
color: useGroupName ? color : colors.orange,
|
||||
color: useGroupName ? color : colors.bitcoin,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
]);
|
||||
@@ -566,12 +566,11 @@ export function createRealizedCapSeries(list, useGroupName) {
|
||||
/**
|
||||
* Create cost basis percentile series (only for cohorts with CostBasisPattern2)
|
||||
* Includes min (p0) and max (p100) with full rainbow coloring
|
||||
* @param {Colors} colors
|
||||
* @param {readonly CohortWithCostBasisPercentiles[]} list
|
||||
* @param {boolean} useGroupName
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
export function createCostBasisPercentilesSeries(colors, list, useGroupName) {
|
||||
export function createCostBasisPercentilesSeries(list, useGroupName) {
|
||||
return list.flatMap(({ name, tree }) => {
|
||||
const cb = tree.costBasis;
|
||||
const p = cb.percentiles;
|
||||
@@ -581,122 +580,122 @@ export function createCostBasisPercentilesSeries(colors, list, useGroupName) {
|
||||
price({
|
||||
metric: cb.max,
|
||||
name: n(100),
|
||||
color: colors.purple,
|
||||
color: colors.pct._100,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct95,
|
||||
name: n(95),
|
||||
color: colors.fuchsia,
|
||||
color: colors.pct._95,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct90,
|
||||
name: n(90),
|
||||
color: colors.pink,
|
||||
color: colors.pct._90,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct85,
|
||||
name: n(85),
|
||||
color: colors.pink,
|
||||
color: colors.pct._85,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct80,
|
||||
name: n(80),
|
||||
color: colors.rose,
|
||||
color: colors.pct._80,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct75,
|
||||
name: n(75),
|
||||
color: colors.red,
|
||||
color: colors.pct._75,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct70,
|
||||
name: n(70),
|
||||
color: colors.orange,
|
||||
color: colors.pct._70,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct65,
|
||||
name: n(65),
|
||||
color: colors.amber,
|
||||
color: colors.pct._65,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct60,
|
||||
name: n(60),
|
||||
color: colors.yellow,
|
||||
color: colors.pct._60,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct55,
|
||||
name: n(55),
|
||||
color: colors.yellow,
|
||||
color: colors.pct._55,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({ metric: p.pct50, name: n(50), color: colors.avocado }),
|
||||
price({ metric: p.pct50, name: n(50), color: colors.pct._50 }),
|
||||
price({
|
||||
metric: p.pct45,
|
||||
name: n(45),
|
||||
color: colors.lime,
|
||||
color: colors.pct._45,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct40,
|
||||
name: n(40),
|
||||
color: colors.green,
|
||||
color: colors.pct._40,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct35,
|
||||
name: n(35),
|
||||
color: colors.emerald,
|
||||
color: colors.pct._35,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct30,
|
||||
name: n(30),
|
||||
color: colors.teal,
|
||||
color: colors.pct._30,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct25,
|
||||
name: n(25),
|
||||
color: colors.teal,
|
||||
color: colors.pct._25,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct20,
|
||||
name: n(20),
|
||||
color: colors.cyan,
|
||||
color: colors.pct._20,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct15,
|
||||
name: n(15),
|
||||
color: colors.sky,
|
||||
color: colors.pct._15,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct10,
|
||||
name: n(10),
|
||||
color: colors.blue,
|
||||
color: colors.pct._10,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: p.pct05,
|
||||
name: n(5),
|
||||
color: colors.indigo,
|
||||
color: colors.pct._05,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: cb.min,
|
||||
name: n(0),
|
||||
color: colors.violet,
|
||||
color: colors.pct._0,
|
||||
defaultActive: false,
|
||||
}),
|
||||
];
|
||||
@@ -706,16 +705,11 @@ export function createCostBasisPercentilesSeries(colors, list, useGroupName) {
|
||||
/**
|
||||
* Create invested capital percentile series (only for cohorts with CostBasisPattern2)
|
||||
* Shows invested capital at each percentile level
|
||||
* @param {Colors} colors
|
||||
* @param {readonly CohortWithCostBasisPercentiles[]} list
|
||||
* @param {boolean} useGroupName
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
export function createInvestedCapitalPercentilesSeries(
|
||||
colors,
|
||||
list,
|
||||
useGroupName,
|
||||
) {
|
||||
export function createInvestedCapitalPercentilesSeries(list, useGroupName) {
|
||||
return list.flatMap(({ name, tree }) => {
|
||||
const ic = tree.costBasis.investedCapital;
|
||||
const n = (/** @type {number} */ pct) =>
|
||||
@@ -724,110 +718,110 @@ export function createInvestedCapitalPercentilesSeries(
|
||||
price({
|
||||
metric: ic.pct95,
|
||||
name: n(95),
|
||||
color: colors.fuchsia,
|
||||
color: colors.pct._95,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct90,
|
||||
name: n(90),
|
||||
color: colors.pink,
|
||||
color: colors.pct._90,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct85,
|
||||
name: n(85),
|
||||
color: colors.pink,
|
||||
color: colors.pct._85,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct80,
|
||||
name: n(80),
|
||||
color: colors.rose,
|
||||
color: colors.pct._80,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct75,
|
||||
name: n(75),
|
||||
color: colors.red,
|
||||
color: colors.pct._75,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct70,
|
||||
name: n(70),
|
||||
color: colors.orange,
|
||||
color: colors.pct._70,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct65,
|
||||
name: n(65),
|
||||
color: colors.amber,
|
||||
color: colors.pct._65,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct60,
|
||||
name: n(60),
|
||||
color: colors.yellow,
|
||||
color: colors.pct._60,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct55,
|
||||
name: n(55),
|
||||
color: colors.yellow,
|
||||
color: colors.pct._55,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({ metric: ic.pct50, name: n(50), color: colors.avocado }),
|
||||
price({ metric: ic.pct50, name: n(50), color: colors.pct._50 }),
|
||||
price({
|
||||
metric: ic.pct45,
|
||||
name: n(45),
|
||||
color: colors.lime,
|
||||
color: colors.pct._45,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct40,
|
||||
name: n(40),
|
||||
color: colors.green,
|
||||
color: colors.pct._40,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct35,
|
||||
name: n(35),
|
||||
color: colors.emerald,
|
||||
color: colors.pct._35,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct30,
|
||||
name: n(30),
|
||||
color: colors.teal,
|
||||
color: colors.pct._30,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct25,
|
||||
name: n(25),
|
||||
color: colors.teal,
|
||||
color: colors.pct._25,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct20,
|
||||
name: n(20),
|
||||
color: colors.cyan,
|
||||
color: colors.pct._20,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct15,
|
||||
name: n(15),
|
||||
color: colors.sky,
|
||||
color: colors.pct._15,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct10,
|
||||
name: n(10),
|
||||
color: colors.blue,
|
||||
color: colors.pct._10,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ic.pct05,
|
||||
name: n(5),
|
||||
color: colors.indigo,
|
||||
color: colors.pct._05,
|
||||
defaultActive: false,
|
||||
}),
|
||||
];
|
||||
@@ -836,12 +830,11 @@ export function createInvestedCapitalPercentilesSeries(
|
||||
|
||||
/**
|
||||
* Create spot percentile series (shows current percentile of price relative to cost basis/invested capital)
|
||||
* @param {Colors} colors
|
||||
* @param {readonly CohortWithCostBasisPercentiles[]} list
|
||||
* @param {boolean} useGroupName
|
||||
* @returns {FetchedBaselineSeriesBlueprint[]}
|
||||
*/
|
||||
export function createSpotPercentileSeries(colors, list, useGroupName) {
|
||||
export function createSpotPercentileSeries(list, useGroupName) {
|
||||
return list.flatMap(({ name, color, tree }) => [
|
||||
baseline({
|
||||
metric: tree.costBasis.spotCostBasisPercentile,
|
||||
@@ -852,7 +845,7 @@ export function createSpotPercentileSeries(colors, list, useGroupName) {
|
||||
baseline({
|
||||
metric: tree.costBasis.spotInvestedCapitalPercentile,
|
||||
name: useGroupName ? `${name} Invested Capital` : "Invested Capital",
|
||||
color: useGroupName ? color : colors.orange,
|
||||
color: useGroupName ? color : colors.bitcoin,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -990,28 +983,27 @@ export function createSingleSentSeries(cohort) {
|
||||
|
||||
/**
|
||||
* Create sell side risk ratio series for single cohort
|
||||
* @param {Colors} colors
|
||||
* @param {{ realized: AnyRealizedPattern }} tree
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function createSingleSellSideRiskSeries(colors, tree) {
|
||||
export function createSingleSellSideRiskSeries(tree) {
|
||||
return [
|
||||
dots({
|
||||
metric: tree.realized.sellSideRiskRatio,
|
||||
name: "Raw",
|
||||
color: colors.orange,
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sellSideRiskRatio7dEma,
|
||||
name: "7d EMA",
|
||||
color: colors.red,
|
||||
color: colors.ma._1w,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sellSideRiskRatio30dEma,
|
||||
name: "30d EMA",
|
||||
color: colors.pink,
|
||||
color: colors.ma._1m,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
];
|
||||
@@ -1039,22 +1031,21 @@ export function createGroupedSellSideRiskSeries(list) {
|
||||
|
||||
/**
|
||||
* Create value created & destroyed series for single cohort
|
||||
* @param {Colors} colors
|
||||
* @param {{ realized: AnyRealizedPattern }} tree
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function createSingleValueCreatedDestroyedSeries(colors, tree) {
|
||||
export function createSingleValueCreatedDestroyedSeries(tree) {
|
||||
return [
|
||||
line({
|
||||
metric: tree.realized.valueCreated,
|
||||
name: "Created",
|
||||
color: colors.emerald,
|
||||
color: colors.usd,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.valueDestroyed,
|
||||
name: "Destroyed",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
];
|
||||
@@ -1063,36 +1054,35 @@ export function createSingleValueCreatedDestroyedSeries(colors, tree) {
|
||||
/**
|
||||
* Create profit/loss value breakdown series for single cohort
|
||||
* Shows profit value created/destroyed and loss value created/destroyed
|
||||
* @param {Colors} colors
|
||||
* @param {{ realized: AnyRealizedPattern }} tree
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function createSingleValueFlowBreakdownSeries(colors, tree) {
|
||||
export function createSingleValueFlowBreakdownSeries(tree) {
|
||||
return [
|
||||
line({
|
||||
metric: tree.realized.profitValueCreated,
|
||||
name: "Profit Created",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.profitValueDestroyed,
|
||||
name: "Profit Destroyed",
|
||||
color: colors.lime,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.lossValueCreated,
|
||||
name: "Loss Created",
|
||||
color: colors.orange,
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.lossValueDestroyed,
|
||||
name: "Loss Destroyed",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
];
|
||||
@@ -1100,22 +1090,21 @@ export function createSingleValueFlowBreakdownSeries(colors, tree) {
|
||||
|
||||
/**
|
||||
* Create capitulation & profit flow series for single cohort
|
||||
* @param {Colors} colors
|
||||
* @param {{ realized: AnyRealizedPattern }} tree
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function createSingleCapitulationProfitFlowSeries(colors, tree) {
|
||||
export function createSingleCapitulationProfitFlowSeries(tree) {
|
||||
return [
|
||||
line({
|
||||
metric: tree.realized.profitFlow,
|
||||
name: "Profit Flow",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.capitulationFlow,
|
||||
name: "Capitulation Flow",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
];
|
||||
@@ -1127,11 +1116,10 @@ export function createSingleCapitulationProfitFlowSeries(colors, tree) {
|
||||
|
||||
/**
|
||||
* Create base SOPR series for single cohort (all cohorts have base SOPR)
|
||||
* @param {Colors} colors
|
||||
* @param {{ realized: AnyRealizedPattern }} tree
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function createSingleSoprSeries(colors, tree) {
|
||||
export function createSingleSoprSeries(tree) {
|
||||
return [
|
||||
baseline({
|
||||
metric: tree.realized.sopr,
|
||||
@@ -1142,7 +1130,7 @@ export function createSingleSoprSeries(colors, tree) {
|
||||
baseline({
|
||||
metric: tree.realized.sopr7dEma,
|
||||
name: "7d EMA",
|
||||
color: [colors.lime, colors.rose],
|
||||
color: colors.bi.sopr7d,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: false,
|
||||
base: 1,
|
||||
@@ -1150,7 +1138,7 @@ export function createSingleSoprSeries(colors, tree) {
|
||||
baseline({
|
||||
metric: tree.realized.sopr30dEma,
|
||||
name: "30d EMA",
|
||||
color: [colors.avocado, colors.pink],
|
||||
color: colors.bi.sopr30d,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: false,
|
||||
base: 1,
|
||||
@@ -1337,11 +1325,10 @@ export function createGroupedRealizedAthRegretSeries(list) {
|
||||
|
||||
/**
|
||||
* Create sentiment series for single cohort
|
||||
* @param {Colors} colors
|
||||
* @param {{ unrealized: UnrealizedPattern }} tree
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function createSingleSentimentSeries(colors, tree) {
|
||||
export function createSingleSentimentSeries(tree) {
|
||||
return [
|
||||
baseline({
|
||||
metric: tree.unrealized.netSentiment,
|
||||
@@ -1351,13 +1338,13 @@ export function createSingleSentimentSeries(colors, tree) {
|
||||
line({
|
||||
metric: tree.unrealized.greedIndex,
|
||||
name: "Greed Index",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.unrealized.painIndex,
|
||||
name: "Pain Index",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
165
website/scripts/options/distribution/valuation.js
Normal file
165
website/scripts/options/distribution/valuation.js
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Valuation section builders
|
||||
*
|
||||
* Structure:
|
||||
* - Realized Cap: Total value at cost basis (USD)
|
||||
* - 30d Change: Recent realized cap changes
|
||||
* - MVRV: Market Value to Realized Value ratio
|
||||
*
|
||||
* For cohorts WITH full ratio patterns: MVRV uses createRatioChart (price + percentiles)
|
||||
* For cohorts WITHOUT full ratio patterns: MVRV is simple baseline
|
||||
*/
|
||||
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { line, baseline } from "../series.js";
|
||||
import { createRatioChart } from "../shared.js";
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleRealizedCapSeries(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
return [
|
||||
line({
|
||||
metric: tree.realized.realizedCap,
|
||||
name: "Realized Cap",
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingle30dChangeSeries(cohort) {
|
||||
return [
|
||||
baseline({
|
||||
metric: cohort.tree.realized.realizedCap30dDelta,
|
||||
name: "30d Change",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create valuation section for cohorts with full ratio patterns
|
||||
* (CohortAll, CohortFull, CohortWithPercentiles)
|
||||
* @param {{ cohort: CohortAll | CohortFull | CohortWithPercentiles, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createValuationSectionFull({ cohort, title }) {
|
||||
const { tree, color } = cohort;
|
||||
return {
|
||||
name: "Valuation",
|
||||
tree: [
|
||||
{
|
||||
name: "Realized Cap",
|
||||
title: title("Realized Cap"),
|
||||
bottom: createSingleRealizedCapSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "30d Change",
|
||||
title: title("Realized Cap 30d Change"),
|
||||
bottom: createSingle30dChangeSeries(cohort),
|
||||
},
|
||||
createRatioChart({
|
||||
title,
|
||||
pricePattern: tree.realized.realizedPrice,
|
||||
ratio: tree.realized.realizedPriceExtra,
|
||||
color,
|
||||
name: "MVRV",
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create valuation section for cohorts with basic ratio patterns
|
||||
* (CohortWithAdjusted, CohortBasic, CohortAddress, CohortWithoutRelative)
|
||||
* @param {{ cohort: CohortWithAdjusted | CohortBasic | CohortAddress | CohortWithoutRelative, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createValuationSection({ cohort, title }) {
|
||||
const { tree, color } = cohort;
|
||||
return {
|
||||
name: "Valuation",
|
||||
tree: [
|
||||
{
|
||||
name: "Realized Cap",
|
||||
title: title("Realized Cap"),
|
||||
bottom: createSingleRealizedCapSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "30d Change",
|
||||
title: title("Realized Cap 30d Change"),
|
||||
bottom: createSingle30dChangeSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "MVRV",
|
||||
title: title("MVRV"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: tree.realized.realizedPriceExtra.ratio,
|
||||
name: "MVRV",
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {readonly (UtxoCohortObject | CohortWithoutRelative)[]} T
|
||||
* @param {{ list: T, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedValuationSection({ list, title }) {
|
||||
return {
|
||||
name: "Valuation",
|
||||
tree: [
|
||||
{
|
||||
name: "Realized Cap",
|
||||
title: title("Realized Cap"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.realized.realizedCap,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "30d Change",
|
||||
title: title("Realized Cap 30d Change"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.realizedCap30dDelta,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "MVRV",
|
||||
title: title("MVRV"),
|
||||
bottom: list.map(({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.realizedPriceExtra.ratio,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -68,12 +68,11 @@ const periodName = (key) => periodIdToName(key.slice(1), true);
|
||||
|
||||
/**
|
||||
* Build DCA class entry from year
|
||||
* @param {Colors} colors
|
||||
* @param {MarketDca} dca
|
||||
* @param {DcaYear} year
|
||||
* @returns {BaseEntryItem}
|
||||
*/
|
||||
function buildYearEntry(colors, dca, year) {
|
||||
function buildYearEntry(dca, year) {
|
||||
const key = /** @type {DcaYearKey} */ (`_${year}`);
|
||||
return {
|
||||
name: `${year}`,
|
||||
@@ -184,11 +183,10 @@ function createCompareFolder(context, items) {
|
||||
|
||||
/**
|
||||
* Create single entry tree structure
|
||||
* @param {Colors} colors
|
||||
* @param {BaseEntryItem & { titlePrefix?: string }} item
|
||||
* @param {object[]} returnsBottom - Bottom pane items for returns chart
|
||||
*/
|
||||
function createSingleEntryTree(colors, item, returnsBottom) {
|
||||
function createSingleEntryTree(item, returnsBottom) {
|
||||
const {
|
||||
name,
|
||||
titlePrefix = name,
|
||||
@@ -217,13 +215,13 @@ function createSingleEntryTree(colors, item, returnsBottom) {
|
||||
line({
|
||||
metric: daysInProfit,
|
||||
name: "Days in Profit",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
line({
|
||||
metric: daysInLoss,
|
||||
name: "Days in Loss",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
@@ -240,24 +238,23 @@ function createSingleEntryTree(colors, item, returnsBottom) {
|
||||
|
||||
/**
|
||||
* Create a single entry from a base item (no CAGR)
|
||||
* @param {Colors} colors
|
||||
* @param {BaseEntryItem & { titlePrefix?: string }} item
|
||||
*/
|
||||
function createShortSingleEntry(colors, item) {
|
||||
function createShortSingleEntry(item) {
|
||||
const { returns, minReturn, maxReturn } = item;
|
||||
return createSingleEntryTree(colors, item, [
|
||||
return createSingleEntryTree(item, [
|
||||
baseline({ metric: returns, name: "Current", unit: Unit.percentage }),
|
||||
dotted({
|
||||
metric: maxReturn,
|
||||
name: "Max",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
dotted({
|
||||
metric: minReturn,
|
||||
name: "Min",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -266,25 +263,24 @@ function createShortSingleEntry(colors, item) {
|
||||
|
||||
/**
|
||||
* Create a single entry from a long item (with CAGR)
|
||||
* @param {Colors} colors
|
||||
* @param {LongEntryItem & { titlePrefix?: string }} item
|
||||
*/
|
||||
function createLongSingleEntry(colors, item) {
|
||||
function createLongSingleEntry(item) {
|
||||
const { returns, minReturn, maxReturn, cagr } = item;
|
||||
return createSingleEntryTree(colors, item, [
|
||||
return createSingleEntryTree(item, [
|
||||
baseline({ metric: returns, name: "Current", unit: Unit.percentage }),
|
||||
baseline({ metric: cagr, name: "CAGR", unit: Unit.cagr }),
|
||||
dotted({
|
||||
metric: maxReturn,
|
||||
name: "Max",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
dotted({
|
||||
metric: minReturn,
|
||||
name: "Min",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -304,9 +300,9 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
price({
|
||||
metric: dca.periodAveragePrice[key],
|
||||
name: "DCA",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
}),
|
||||
price({ metric: lookback[key], name: "Lump Sum", color: colors.orange }),
|
||||
price({ metric: lookback[key], name: "Lump Sum", color: colors.bitcoin }),
|
||||
];
|
||||
|
||||
/** @param {string} name @param {AllPeriodKey} key */
|
||||
@@ -331,7 +327,7 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
baseline({
|
||||
metric: dca.periodLumpSumMaxReturn[key],
|
||||
name: "Lump Sum",
|
||||
color: [colors.cyan, colors.orange],
|
||||
color: colors.bi.lumpSum,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
@@ -349,7 +345,7 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
baseline({
|
||||
metric: dca.periodLumpSumMinReturn[key],
|
||||
name: "Lump Sum",
|
||||
color: [colors.cyan, colors.orange],
|
||||
color: colors.bi.lumpSum,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
@@ -373,7 +369,7 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
baseline({
|
||||
metric: dca.periodLumpSumReturns[key],
|
||||
name: "Lump Sum",
|
||||
color: [colors.cyan, colors.orange],
|
||||
color: colors.bi.lumpSum,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
@@ -399,7 +395,7 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
baseline({
|
||||
metric: dca.periodLumpSumReturns[key],
|
||||
name: "Lump Sum",
|
||||
color: [colors.cyan, colors.orange],
|
||||
color: colors.bi.lumpSum,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
baseline({
|
||||
@@ -410,7 +406,7 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
baseline({
|
||||
metric: returns.cagr[key],
|
||||
name: "Lump Sum",
|
||||
color: [colors.cyan, colors.orange],
|
||||
color: colors.bi.lumpSum,
|
||||
unit: Unit.cagr,
|
||||
}),
|
||||
],
|
||||
@@ -431,13 +427,13 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
line({
|
||||
metric: dca.periodDaysInProfit[key],
|
||||
name: "DCA",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
line({
|
||||
metric: dca.periodLumpSumDaysInProfit[key],
|
||||
name: "Lump Sum",
|
||||
color: colors.orange,
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
@@ -450,13 +446,13 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
line({
|
||||
metric: dca.periodDaysInLoss[key],
|
||||
name: "DCA",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
line({
|
||||
metric: dca.periodLumpSumDaysInLoss[key],
|
||||
name: "Lump Sum",
|
||||
color: colors.orange,
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
@@ -473,12 +469,12 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
...satsBtcUsd({
|
||||
pattern: dca.periodStack[key],
|
||||
name: "DCA",
|
||||
color: colors.green,
|
||||
color: colors.profit,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: dca.periodLumpSumStack[key],
|
||||
name: "Lump Sum",
|
||||
color: colors.orange,
|
||||
color: colors.bitcoin,
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -570,14 +566,14 @@ function createPeriodSection({ dca, lookback, returns }) {
|
||||
|
||||
/** @param {BaseEntryItem} entry */
|
||||
const createShortEntry = (entry) =>
|
||||
createShortSingleEntry(colors, {
|
||||
createShortSingleEntry({
|
||||
...entry,
|
||||
titlePrefix: `${entry.name} ${suffix}`,
|
||||
});
|
||||
|
||||
/** @param {LongEntryItem} entry */
|
||||
const createLongEntry = (entry) =>
|
||||
createLongSingleEntry(colors, {
|
||||
createLongSingleEntry({
|
||||
...entry,
|
||||
titlePrefix: `${entry.name} ${suffix}`,
|
||||
});
|
||||
@@ -647,7 +643,7 @@ export function createDcaByStartYearSection({ dca }) {
|
||||
tree: [
|
||||
createCompareFolder(`${name} DCA`, entries),
|
||||
...entries.map((entry) =>
|
||||
createShortSingleEntry(colors, {
|
||||
createShortSingleEntry({
|
||||
...entry,
|
||||
titlePrefix: `${entry.name} DCA`,
|
||||
}),
|
||||
@@ -655,12 +651,8 @@ export function createDcaByStartYearSection({ dca }) {
|
||||
],
|
||||
});
|
||||
|
||||
const entries2020s = YEARS_2020S.map((year) =>
|
||||
buildYearEntry(colors, dca, year),
|
||||
);
|
||||
const entries2010s = YEARS_2010S.map((year) =>
|
||||
buildYearEntry(colors, dca, year),
|
||||
);
|
||||
const entries2020s = YEARS_2020S.map((year) => buildYearEntry(dca, year));
|
||||
const entries2010s = YEARS_2010S.map((year) => buildYearEntry(dca, year));
|
||||
|
||||
return {
|
||||
name: "DCA by Start Year",
|
||||
|
||||
@@ -75,20 +75,19 @@ function createMaSubSection(label, averages) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Colors} colors
|
||||
* @param {string} name
|
||||
* @param {string} title
|
||||
* @param {Unit} unit
|
||||
* @param {{ _1w: AnyMetricPattern, _1m: AnyMetricPattern, _1y: AnyMetricPattern }} metrics
|
||||
*/
|
||||
function volatilityChart(colors, name, title, unit, metrics) {
|
||||
function volatilityChart(name, title, unit, metrics) {
|
||||
return {
|
||||
name,
|
||||
title,
|
||||
bottom: [
|
||||
line({ metric: metrics._1w, name: "1w", color: colors.red, unit }),
|
||||
line({ metric: metrics._1m, name: "1m", color: colors.orange, unit }),
|
||||
line({ metric: metrics._1y, name: "1y", color: colors.lime, unit }),
|
||||
line({ metric: metrics._1w, name: "1w", color: colors.time._1w, unit }),
|
||||
line({ metric: metrics._1m, name: "1m", color: colors.time._1m, unit }),
|
||||
line({ metric: metrics._1y, name: "1y", color: colors.time._1y, unit }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -423,7 +422,7 @@ export function createMarketSection() {
|
||||
line({
|
||||
metric: ath.priceDrawdown,
|
||||
name: "Drawdown",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
@@ -446,13 +445,13 @@ export function createMarketSection() {
|
||||
line({
|
||||
metric: ath.maxDaysBetweenPriceAths,
|
||||
name: "Max",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
line({
|
||||
metric: ath.maxYearsBetweenPriceAths,
|
||||
name: "Max",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.years,
|
||||
}),
|
||||
],
|
||||
@@ -484,17 +483,11 @@ export function createMarketSection() {
|
||||
{
|
||||
name: "Volatility",
|
||||
tree: [
|
||||
volatilityChart(
|
||||
colors,
|
||||
"Index",
|
||||
"Volatility Index",
|
||||
Unit.percentage,
|
||||
{
|
||||
_1w: volatility.price1wVolatility,
|
||||
_1m: volatility.price1mVolatility,
|
||||
_1y: volatility.price1yVolatility,
|
||||
},
|
||||
),
|
||||
volatilityChart("Index", "Volatility Index", Unit.percentage, {
|
||||
_1w: volatility.price1wVolatility,
|
||||
_1m: volatility.price1mVolatility,
|
||||
_1y: volatility.price1yVolatility,
|
||||
}),
|
||||
{
|
||||
name: "True Range",
|
||||
title: "True Range",
|
||||
@@ -502,13 +495,13 @@ export function createMarketSection() {
|
||||
line({
|
||||
metric: range.priceTrueRange,
|
||||
name: "Daily",
|
||||
color: colors.yellow,
|
||||
color: colors.time._24h,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: range.priceTrueRange2wSum,
|
||||
name: "2w Sum",
|
||||
color: colors.orange,
|
||||
color: colors.time._1w,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -521,28 +514,22 @@ export function createMarketSection() {
|
||||
line({
|
||||
metric: range.price2wChoppinessIndex,
|
||||
name: "2w",
|
||||
color: colors.red,
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
...priceLines({ unit: Unit.index, numbers: [61.8, 38.2] }),
|
||||
],
|
||||
},
|
||||
volatilityChart(colors, "Sharpe Ratio", "Sharpe Ratio", Unit.ratio, {
|
||||
volatilityChart("Sharpe Ratio", "Sharpe Ratio", Unit.ratio, {
|
||||
_1w: volatility.sharpe1w,
|
||||
_1m: volatility.sharpe1m,
|
||||
_1y: volatility.sharpe1y,
|
||||
}),
|
||||
volatilityChart(
|
||||
colors,
|
||||
"Sortino Ratio",
|
||||
"Sortino Ratio",
|
||||
Unit.ratio,
|
||||
{
|
||||
_1w: volatility.sortino1w,
|
||||
_1m: volatility.sortino1m,
|
||||
_1y: volatility.sortino1y,
|
||||
},
|
||||
),
|
||||
volatilityChart("Sortino Ratio", "Sortino Ratio", Unit.ratio, {
|
||||
_1w: volatility.sortino1w,
|
||||
_1m: volatility.sortino1m,
|
||||
_1y: volatility.sortino1y,
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
||||
@@ -623,17 +610,17 @@ export function createMarketSection() {
|
||||
name: p.id,
|
||||
title: `${p.name} MinMax`,
|
||||
top: [
|
||||
price({
|
||||
metric: p.min,
|
||||
name: "Min",
|
||||
key: "price-min",
|
||||
color: colors.red,
|
||||
}),
|
||||
price({
|
||||
metric: p.max,
|
||||
name: "Max",
|
||||
key: "price-max",
|
||||
color: colors.green,
|
||||
color: colors.stat.max,
|
||||
}),
|
||||
price({
|
||||
metric: p.min,
|
||||
name: "Min",
|
||||
key: "price-min",
|
||||
color: colors.stat.min,
|
||||
}),
|
||||
],
|
||||
})),
|
||||
@@ -645,17 +632,17 @@ export function createMarketSection() {
|
||||
price({
|
||||
metric: ma.price200dSma.price,
|
||||
name: "200d SMA",
|
||||
color: colors.yellow,
|
||||
color: colors.ma._200d,
|
||||
}),
|
||||
price({
|
||||
metric: ma.price200dSmaX24,
|
||||
name: "200d SMA x2.4",
|
||||
color: colors.green,
|
||||
color: colors.indicator.upper,
|
||||
}),
|
||||
price({
|
||||
metric: ma.price200dSmaX08,
|
||||
name: "200d SMA x0.8",
|
||||
color: colors.red,
|
||||
color: colors.indicator.lower,
|
||||
}),
|
||||
],
|
||||
},
|
||||
@@ -672,20 +659,20 @@ export function createMarketSection() {
|
||||
line({
|
||||
metric: indicators.rsi14d,
|
||||
name: "RSI",
|
||||
color: colors.indigo,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi14dMin,
|
||||
name: "Min",
|
||||
color: colors.red,
|
||||
defaultActive: false,
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi14dMax,
|
||||
name: "Max",
|
||||
color: colors.green,
|
||||
color: colors.stat.max,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi14dMin,
|
||||
name: "Min",
|
||||
color: colors.stat.min,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
@@ -701,13 +688,13 @@ export function createMarketSection() {
|
||||
line({
|
||||
metric: indicators.stochRsiK,
|
||||
name: "K",
|
||||
color: colors.blue,
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.stochRsiD,
|
||||
name: "D",
|
||||
color: colors.orange,
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
...priceLines({ unit: Unit.index, numbers: [80, 20] }),
|
||||
@@ -720,13 +707,13 @@ export function createMarketSection() {
|
||||
line({
|
||||
metric: indicators.macdLine,
|
||||
name: "MACD",
|
||||
color: colors.blue,
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.macdSignal,
|
||||
name: "Signal",
|
||||
color: colors.orange,
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
histogram({
|
||||
@@ -769,12 +756,12 @@ export function createMarketSection() {
|
||||
price({
|
||||
metric: ma.price111dSma.price,
|
||||
name: "111d SMA",
|
||||
color: colors.green,
|
||||
color: colors.indicator.upper,
|
||||
}),
|
||||
price({
|
||||
metric: ma.price350dSmaX2,
|
||||
name: "350d SMA x2",
|
||||
color: colors.red,
|
||||
color: colors.indicator.lower,
|
||||
}),
|
||||
],
|
||||
bottom: [
|
||||
@@ -793,7 +780,7 @@ export function createMarketSection() {
|
||||
line({
|
||||
metric: indicators.puellMultiple,
|
||||
name: "Puell",
|
||||
color: colors.green,
|
||||
color: colors.usd,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
@@ -805,7 +792,7 @@ export function createMarketSection() {
|
||||
line({
|
||||
metric: indicators.nvt,
|
||||
name: "NVT",
|
||||
color: colors.orange,
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
@@ -817,7 +804,7 @@ export function createMarketSection() {
|
||||
line({
|
||||
metric: indicators.gini,
|
||||
name: "Gini",
|
||||
color: colors.red,
|
||||
color: colors.loss,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { Unit } from "../utils/units.js";
|
||||
import { entries, includes } from "../utils/array.js";
|
||||
import { colorAt, colors } from "../utils/colors.js";
|
||||
import { colors } from "../utils/colors.js";
|
||||
import {
|
||||
line,
|
||||
baseline,
|
||||
@@ -217,47 +217,89 @@ export function createMiningSection() {
|
||||
// Hashrate
|
||||
{
|
||||
name: "Hashrate",
|
||||
title: "Network Hashrate",
|
||||
bottom: [
|
||||
dots({
|
||||
metric: blocks.mining.hashRate,
|
||||
name: "Hashrate",
|
||||
unit: Unit.hashRate,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRate1wSma,
|
||||
name: "1w SMA",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRate1mSma,
|
||||
name: "1m SMA",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRate2mSma,
|
||||
name: "2m SMA",
|
||||
color: colors.orange,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRate1ySma,
|
||||
name: "1y SMA",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
dotted({
|
||||
metric: blocks.difficulty.asHash,
|
||||
name: "Difficulty",
|
||||
color: colors.default,
|
||||
unit: Unit.hashRate,
|
||||
}),
|
||||
tree: [
|
||||
{
|
||||
name: "Current",
|
||||
title: "Network Hashrate",
|
||||
bottom: [
|
||||
dots({
|
||||
metric: blocks.mining.hashRate,
|
||||
name: "Hashrate",
|
||||
unit: Unit.hashRate,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRate1wSma,
|
||||
name: "1w SMA",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRate1mSma,
|
||||
name: "1m SMA",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRate2mSma,
|
||||
name: "2m SMA",
|
||||
color: colors.ma._2m,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRate1ySma,
|
||||
name: "1y SMA",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
dotted({
|
||||
metric: blocks.difficulty.asHash,
|
||||
name: "Difficulty",
|
||||
color: colors.default,
|
||||
unit: Unit.hashRate,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRateAth,
|
||||
name: "ATH",
|
||||
color: colors.loss,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "ATH",
|
||||
title: "Network Hashrate ATH",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.mining.hashRateAth,
|
||||
name: "ATH",
|
||||
color: colors.loss,
|
||||
unit: Unit.hashRate,
|
||||
}),
|
||||
dots({
|
||||
metric: blocks.mining.hashRate,
|
||||
name: "Hashrate",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.hashRate,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Drawdown",
|
||||
title: "Network Hashrate Drawdown",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.mining.hashRateDrawdown,
|
||||
name: "Drawdown",
|
||||
unit: Unit.percentage,
|
||||
color: colors.loss,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -305,13 +347,11 @@ export function createMiningSection() {
|
||||
line({
|
||||
metric: blocks.difficulty.blocksBeforeNextAdjustment,
|
||||
name: "Remaining",
|
||||
color: colors.indigo,
|
||||
unit: Unit.blocks,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.difficulty.daysBeforeNextAdjustment,
|
||||
name: "Remaining",
|
||||
color: colors.purple,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
@@ -466,13 +506,13 @@ export function createMiningSection() {
|
||||
line({
|
||||
metric: blocks.rewards.subsidyDominance,
|
||||
name: "Subsidy",
|
||||
color: colors.lime,
|
||||
color: colors.mining.subsidy,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.rewards.feeDominance,
|
||||
name: "Fees",
|
||||
color: colors.cyan,
|
||||
color: colors.mining.fee,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
@@ -514,25 +554,25 @@ export function createMiningSection() {
|
||||
line({
|
||||
metric: blocks.mining.hashPriceThs,
|
||||
name: "TH/s",
|
||||
color: colors.emerald,
|
||||
color: colors.usd,
|
||||
unit: Unit.usdPerThsPerDay,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashPricePhs,
|
||||
name: "PH/s",
|
||||
color: colors.emerald,
|
||||
color: colors.usd,
|
||||
unit: Unit.usdPerPhsPerDay,
|
||||
}),
|
||||
dotted({
|
||||
metric: blocks.mining.hashPriceThsMin,
|
||||
name: "TH/s Min",
|
||||
color: colors.red,
|
||||
color: colors.stat.min,
|
||||
unit: Unit.usdPerThsPerDay,
|
||||
}),
|
||||
dotted({
|
||||
metric: blocks.mining.hashPricePhsMin,
|
||||
name: "PH/s Min",
|
||||
color: colors.red,
|
||||
color: colors.stat.min,
|
||||
unit: Unit.usdPerPhsPerDay,
|
||||
}),
|
||||
],
|
||||
@@ -544,25 +584,25 @@ export function createMiningSection() {
|
||||
line({
|
||||
metric: blocks.mining.hashValueThs,
|
||||
name: "TH/s",
|
||||
color: colors.orange,
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.satsPerThsPerDay,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashValuePhs,
|
||||
name: "PH/s",
|
||||
color: colors.orange,
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.satsPerPhsPerDay,
|
||||
}),
|
||||
dotted({
|
||||
metric: blocks.mining.hashValueThsMin,
|
||||
name: "TH/s Min",
|
||||
color: colors.red,
|
||||
color: colors.stat.min,
|
||||
unit: Unit.satsPerThsPerDay,
|
||||
}),
|
||||
dotted({
|
||||
metric: blocks.mining.hashValuePhsMin,
|
||||
name: "PH/s Min",
|
||||
color: colors.red,
|
||||
color: colors.stat.min,
|
||||
unit: Unit.satsPerPhsPerDay,
|
||||
}),
|
||||
],
|
||||
@@ -574,13 +614,13 @@ export function createMiningSection() {
|
||||
line({
|
||||
metric: blocks.mining.hashPriceRebound,
|
||||
name: "Hash Price",
|
||||
color: colors.emerald,
|
||||
color: colors.usd,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashValueRebound,
|
||||
name: "Hash Value",
|
||||
color: colors.orange,
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
@@ -637,7 +677,7 @@ export function createMiningSection() {
|
||||
line({
|
||||
metric: p.pool._1mDominance,
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
color: colors.at(i),
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
@@ -649,7 +689,7 @@ export function createMiningSection() {
|
||||
line({
|
||||
metric: p.pool._1mBlocksMined,
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
color: colors.at(i),
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
@@ -662,7 +702,7 @@ export function createMiningSection() {
|
||||
source: p.pool.coinbase,
|
||||
key: "sum",
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
color: colors.at(i),
|
||||
}),
|
||||
),
|
||||
},
|
||||
@@ -679,7 +719,7 @@ export function createMiningSection() {
|
||||
line({
|
||||
metric: p.pool._1mDominance,
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
color: colors.at(i),
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
@@ -691,7 +731,7 @@ export function createMiningSection() {
|
||||
line({
|
||||
metric: p.pool._1mBlocksMined,
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
color: colors.at(i),
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
@@ -704,7 +744,7 @@ export function createMiningSection() {
|
||||
source: p.pool.coinbase,
|
||||
key: "sum",
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
color: colors.at(i),
|
||||
}),
|
||||
),
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import { priceLine } from "./constants.js";
|
||||
import {
|
||||
line,
|
||||
dots,
|
||||
baseline,
|
||||
fromSupplyPattern,
|
||||
fromBaseStatsPattern,
|
||||
chartsFromFull,
|
||||
@@ -126,17 +127,25 @@ export function createNetworkSection() {
|
||||
]);
|
||||
|
||||
// Count types for comparison charts
|
||||
// addrCount and emptyAddrCount have .count, totalAddrCount doesn't
|
||||
const countTypes = /** @type {const} */ ([
|
||||
{ key: "addrCount", name: "Funded", title: "Address Count by Type" },
|
||||
{
|
||||
key: "emptyAddrCount",
|
||||
name: "Empty",
|
||||
title: "Empty Address Count by Type",
|
||||
name: "Funded",
|
||||
title: "Address Count by Type",
|
||||
/** @param {AddressableType} t */
|
||||
getMetric: (t) => distribution.addrCount[t].count,
|
||||
},
|
||||
{
|
||||
name: "Empty",
|
||||
title: "Empty Address Count by Type",
|
||||
/** @param {AddressableType} t */
|
||||
getMetric: (t) => distribution.emptyAddrCount[t].count,
|
||||
},
|
||||
{
|
||||
key: "totalAddrCount",
|
||||
name: "Total",
|
||||
title: "Total Address Count by Type",
|
||||
/** @param {AddressableType} t */
|
||||
getMetric: (t) => distribution.totalAddrCount[t],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -151,7 +160,7 @@ export function createNetworkSection() {
|
||||
title: `${titlePrefix}Address Count`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: distribution.addrCount[key],
|
||||
metric: distribution.addrCount[key].count,
|
||||
name: "Funded",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
@@ -163,7 +172,7 @@ export function createNetworkSection() {
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.emptyAddrCount[key],
|
||||
metric: distribution.emptyAddrCount[key].count,
|
||||
name: "Empty",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
@@ -172,28 +181,44 @@ export function createNetworkSection() {
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "New",
|
||||
tree: chartsFromFull({
|
||||
pattern: distribution.newAddrCount[key],
|
||||
title: `${titlePrefix}New Address Count`,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Reactivated",
|
||||
title: `${titlePrefix}Reactivated Address Count`,
|
||||
bottom: fromBaseStatsPattern({
|
||||
pattern: distribution.addressActivity[key].reactivated,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Growth Rate",
|
||||
title: `${titlePrefix}Address Growth Rate`,
|
||||
bottom: fromBaseStatsPattern({
|
||||
pattern: distribution.growthRate[key],
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
name: "Trends",
|
||||
tree: [
|
||||
{
|
||||
name: "30d Change",
|
||||
title: `${titlePrefix}Address Count 30d Change`,
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: distribution.addrCount[key]._30dChange,
|
||||
name: "30d Change",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "New",
|
||||
tree: chartsFromFull({
|
||||
pattern: distribution.newAddrCount[key],
|
||||
title: `${titlePrefix}New Address Count`,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Reactivated",
|
||||
title: `${titlePrefix}Reactivated Address Count`,
|
||||
bottom: fromBaseStatsPattern({
|
||||
pattern: distribution.addressActivity[key].reactivated,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Growth Rate",
|
||||
title: `${titlePrefix}Address Growth Rate`,
|
||||
bottom: fromBaseStatsPattern({
|
||||
pattern: distribution.growthRate[key],
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Transacting",
|
||||
@@ -235,7 +260,7 @@ export function createNetworkSection() {
|
||||
title: `${groupName} ${c.title}`,
|
||||
bottom: types.map((t) =>
|
||||
line({
|
||||
metric: distribution[c.key][t.key],
|
||||
metric: c.getMetric(t.key),
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
@@ -353,7 +378,7 @@ export function createNetworkSection() {
|
||||
{ key: "p2ms", name: "P2MS", color: st.p2ms },
|
||||
]);
|
||||
const segwitScripts = /** @type {const} */ ([
|
||||
{ key: "segwit", name: "All SegWit", color: colors.cyan },
|
||||
{ key: "segwit", name: "All SegWit", color: colors.segwit },
|
||||
{ key: "p2wsh", name: "P2WSH", color: st.p2wsh },
|
||||
{ key: "p2wpkh", name: "P2WPKH", color: st.p2wpkh },
|
||||
]);
|
||||
@@ -492,7 +517,7 @@ export function createNetworkSection() {
|
||||
...satsBtcUsd({
|
||||
pattern: transactions.volume.receivedSum,
|
||||
name: "Received",
|
||||
color: colors.cyan,
|
||||
color: colors.entity.output,
|
||||
}),
|
||||
],
|
||||
},
|
||||
@@ -530,19 +555,19 @@ export function createNetworkSection() {
|
||||
line({
|
||||
metric: transactions.versions.v1.sum,
|
||||
name: "v1",
|
||||
color: colors.orange,
|
||||
color: colors.txVersion.v1,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.versions.v2.sum,
|
||||
name: "v2",
|
||||
color: colors.cyan,
|
||||
color: colors.txVersion.v2,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.versions.v3.sum,
|
||||
name: "v3",
|
||||
color: colors.lime,
|
||||
color: colors.txVersion.v3,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
@@ -554,19 +579,19 @@ export function createNetworkSection() {
|
||||
line({
|
||||
metric: transactions.versions.v1.cumulative,
|
||||
name: "v1",
|
||||
color: colors.orange,
|
||||
color: colors.txVersion.v1,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.versions.v2.cumulative,
|
||||
name: "v2",
|
||||
color: colors.cyan,
|
||||
color: colors.txVersion.v2,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.versions.v3.cumulative,
|
||||
name: "v3",
|
||||
color: colors.lime,
|
||||
color: colors.txVersion.v3,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
@@ -585,7 +610,7 @@ export function createNetworkSection() {
|
||||
line({
|
||||
metric: supply.velocity.usd,
|
||||
name: "USD",
|
||||
color: colors.emerald,
|
||||
color: colors.usd,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
@@ -625,25 +650,25 @@ export function createNetworkSection() {
|
||||
line({
|
||||
metric: blocks.count._24hBlockCount,
|
||||
name: "24h",
|
||||
color: colors.pink,
|
||||
color: colors.time._24h,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.count._1wBlockCount,
|
||||
name: "1w",
|
||||
color: colors.red,
|
||||
color: colors.time._1w,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.count._1mBlockCount,
|
||||
name: "1m",
|
||||
color: colors.orange,
|
||||
color: colors.time._1m,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.count._1yBlockCount,
|
||||
name: "1y",
|
||||
color: colors.purple,
|
||||
color: colors.time._1y,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
@@ -839,6 +864,17 @@ export function createNetworkSection() {
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "30d Change",
|
||||
title: "UTXO Count 30d Change",
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: distribution.utxoCohorts.all.outputs.utxoCount30dChange,
|
||||
name: "30d Change",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Flow",
|
||||
title: "UTXO Flow",
|
||||
@@ -846,13 +882,13 @@ export function createNetworkSection() {
|
||||
line({
|
||||
metric: outputs.count.totalCount.sum,
|
||||
name: "Created",
|
||||
color: colors.lime,
|
||||
color: colors.entity.output,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: inputs.count.sum,
|
||||
name: "Spent",
|
||||
color: colors.red,
|
||||
color: colors.entity.input,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
@@ -882,18 +918,19 @@ export function createNetworkSection() {
|
||||
dots({
|
||||
metric: transactions.volume.txPerSec,
|
||||
name: "TX/sec",
|
||||
color: colors.red,
|
||||
color: colors.entity.tx,
|
||||
unit: Unit.perSec,
|
||||
}),
|
||||
dots({
|
||||
metric: transactions.volume.inputsPerSec,
|
||||
name: "Inputs/sec",
|
||||
color: colors.entity.input,
|
||||
unit: Unit.perSec,
|
||||
}),
|
||||
dots({
|
||||
metric: transactions.volume.outputsPerSec,
|
||||
name: "Outputs/sec",
|
||||
color: colors.cyan,
|
||||
color: colors.entity.output,
|
||||
unit: Unit.perSec,
|
||||
}),
|
||||
],
|
||||
@@ -917,7 +954,7 @@ export function createNetworkSection() {
|
||||
title: c.title,
|
||||
bottom: addressTypes.map((t) =>
|
||||
line({
|
||||
metric: distribution[c.key][t.key],
|
||||
metric: c.getMetric(t.key),
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
@@ -1198,13 +1235,13 @@ export function createNetworkSection() {
|
||||
line({
|
||||
metric: scripts.count.segwitAdoption.cumulative,
|
||||
name: "SegWit",
|
||||
color: colors.cyan,
|
||||
color: colors.segwit,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: scripts.count.taprootAdoption.cumulative,
|
||||
name: "Taproot",
|
||||
color: colors.orange,
|
||||
color: colors.scriptType.p2tr,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
@@ -1226,7 +1263,7 @@ export function createNetworkSection() {
|
||||
line({
|
||||
metric: scripts.count.segwitAdoption.cumulative,
|
||||
name: "All-Time",
|
||||
color: colors.red,
|
||||
color: colors.time.all,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
@@ -1248,7 +1285,7 @@ export function createNetworkSection() {
|
||||
line({
|
||||
metric: scripts.count.taprootAdoption.cumulative,
|
||||
name: "All-Time",
|
||||
color: colors.red,
|
||||
color: colors.time.all,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -80,22 +80,22 @@ export function createPartialOptions() {
|
||||
// Overview - All UTXOs (adjustedSopr + percentiles but no RelToMarketCap)
|
||||
createCohortFolderAll({ ...cohortAll, name: "Overview" }),
|
||||
|
||||
// STH - Short term holder cohort (Full capability)
|
||||
createCohortFolderFull(termShort),
|
||||
|
||||
// LTH - Long term holder cohort (nupl)
|
||||
createCohortFolderWithNupl(termLong),
|
||||
|
||||
// STH vs LTH - Direct comparison
|
||||
// STH vs LTH - Direct comparison (before individual cohorts)
|
||||
createCohortFolderWithNupl({
|
||||
name: "STH vs LTH",
|
||||
title: "STH vs LTH",
|
||||
list: [termShort, termLong],
|
||||
}),
|
||||
|
||||
// STH - Short term holder cohort (Full capability)
|
||||
createCohortFolderFull(termShort),
|
||||
|
||||
// LTH - Long term holder cohort (nupl)
|
||||
createCohortFolderWithNupl(termLong),
|
||||
|
||||
// Ages cohorts
|
||||
{
|
||||
name: "Ages",
|
||||
name: "UTXO Ages",
|
||||
tree: [
|
||||
// Younger Than (< X old)
|
||||
{
|
||||
@@ -138,7 +138,7 @@ export function createPartialOptions() {
|
||||
|
||||
// Sizes cohorts (UTXO size)
|
||||
{
|
||||
name: "Sizes",
|
||||
name: "UTXO Sizes",
|
||||
tree: [
|
||||
// Less Than (< X sats)
|
||||
{
|
||||
@@ -187,7 +187,7 @@ export function createPartialOptions() {
|
||||
|
||||
// Balances cohorts (Address balance)
|
||||
{
|
||||
name: "Balances",
|
||||
name: "Address Balances",
|
||||
tree: [
|
||||
// Less Than (< X sats)
|
||||
{
|
||||
|
||||
@@ -42,6 +42,35 @@ export function satsBtcUsd({ pattern, name, color, defaultActive }) {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sats/btc/usd baseline series from a value pattern
|
||||
* @param {Object} args
|
||||
* @param {{ bitcoin: AnyMetricPattern, sats: AnyMetricPattern, dollars: AnyMetricPattern }} args.pattern
|
||||
* @param {string} args.name
|
||||
* @param {Color} [args.color]
|
||||
* @param {boolean} [args.defaultActive]
|
||||
* @returns {FetchedBaselineSeriesBlueprint[]}
|
||||
*/
|
||||
export function satsBtcUsdBaseline({ pattern, name, color, defaultActive }) {
|
||||
return [
|
||||
baseline({
|
||||
metric: pattern.bitcoin,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
defaultActive,
|
||||
}),
|
||||
baseline({ metric: pattern.sats, name, color, unit: Unit.sats, defaultActive }),
|
||||
baseline({
|
||||
metric: pattern.dollars,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
defaultActive,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sats/btc/usd series from any value pattern using sum or cumulative key
|
||||
* @param {Object} args
|
||||
@@ -109,15 +138,15 @@ export function revenueBtcSatsUsd({ coinbase, subsidy, fee, key }) {
|
||||
source: coinbase,
|
||||
key,
|
||||
name: "Coinbase",
|
||||
color: colors.orange,
|
||||
color: colors.mining.coinbase,
|
||||
}),
|
||||
...satsBtcUsdFrom({
|
||||
source: subsidy,
|
||||
key,
|
||||
name: "Subsidy",
|
||||
color: colors.lime,
|
||||
color: colors.mining.subsidy,
|
||||
}),
|
||||
...satsBtcUsdFrom({ source: fee, key, name: "Fees", color: colors.cyan }),
|
||||
...satsBtcUsdFrom({ source: fee, key, name: "Fees", color: colors.mining.fee }),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -127,12 +156,12 @@ export function revenueBtcSatsUsd({ coinbase, subsidy, fee, key }) {
|
||||
*/
|
||||
export function percentileUsdMap(ratio) {
|
||||
return /** @type {const} */ ([
|
||||
{ name: "pct95", prop: ratio.ratioPct95Usd, color: colors.fuchsia },
|
||||
{ name: "pct5", prop: ratio.ratioPct5Usd, color: colors.cyan },
|
||||
{ name: "pct98", prop: ratio.ratioPct98Usd, color: colors.pink },
|
||||
{ name: "pct2", prop: ratio.ratioPct2Usd, color: colors.sky },
|
||||
{ name: "pct99", prop: ratio.ratioPct99Usd, color: colors.rose },
|
||||
{ name: "pct1", prop: ratio.ratioPct1Usd, color: colors.blue },
|
||||
{ name: "pct95", prop: ratio.ratioPct95Usd, color: colors.ratioPct._95 },
|
||||
{ name: "pct5", prop: ratio.ratioPct5Usd, color: colors.ratioPct._5 },
|
||||
{ name: "pct98", prop: ratio.ratioPct98Usd, color: colors.ratioPct._98 },
|
||||
{ name: "pct2", prop: ratio.ratioPct2Usd, color: colors.ratioPct._2 },
|
||||
{ name: "pct99", prop: ratio.ratioPct99Usd, color: colors.ratioPct._99 },
|
||||
{ name: "pct1", prop: ratio.ratioPct1Usd, color: colors.ratioPct._1 },
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -142,12 +171,12 @@ export function percentileUsdMap(ratio) {
|
||||
*/
|
||||
export function percentileMap(ratio) {
|
||||
return /** @type {const} */ ([
|
||||
{ name: "pct95", prop: ratio.ratioPct95, color: colors.fuchsia },
|
||||
{ name: "pct5", prop: ratio.ratioPct5, color: colors.cyan },
|
||||
{ name: "pct98", prop: ratio.ratioPct98, color: colors.pink },
|
||||
{ name: "pct2", prop: ratio.ratioPct2, color: colors.sky },
|
||||
{ name: "pct99", prop: ratio.ratioPct99, color: colors.rose },
|
||||
{ name: "pct1", prop: ratio.ratioPct1, color: colors.blue },
|
||||
{ name: "pct95", prop: ratio.ratioPct95, color: colors.ratioPct._95 },
|
||||
{ name: "pct5", prop: ratio.ratioPct5, color: colors.ratioPct._5 },
|
||||
{ name: "pct98", prop: ratio.ratioPct98, color: colors.ratioPct._98 },
|
||||
{ name: "pct2", prop: ratio.ratioPct2, color: colors.ratioPct._2 },
|
||||
{ name: "pct99", prop: ratio.ratioPct99, color: colors.ratioPct._99 },
|
||||
{ name: "pct1", prop: ratio.ratioPct1, color: colors.ratioPct._1 },
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -170,19 +199,19 @@ export function sdPatterns(ratio) {
|
||||
*/
|
||||
export function sdBandsUsd(sd) {
|
||||
return /** @type {const} */ ([
|
||||
{ name: "0σ", prop: sd._0sdUsd, color: colors.lime },
|
||||
{ name: "+0.5σ", prop: sd.p05sdUsd, color: colors.yellow },
|
||||
{ name: "−0.5σ", prop: sd.m05sdUsd, color: colors.teal },
|
||||
{ name: "+1σ", prop: sd.p1sdUsd, color: colors.amber },
|
||||
{ name: "−1σ", prop: sd.m1sdUsd, color: colors.cyan },
|
||||
{ name: "+1.5σ", prop: sd.p15sdUsd, color: colors.orange },
|
||||
{ name: "−1.5σ", prop: sd.m15sdUsd, color: colors.sky },
|
||||
{ name: "+2σ", prop: sd.p2sdUsd, color: colors.red },
|
||||
{ name: "−2σ", prop: sd.m2sdUsd, color: colors.blue },
|
||||
{ name: "+2.5σ", prop: sd.p25sdUsd, color: colors.rose },
|
||||
{ name: "−2.5σ", prop: sd.m25sdUsd, color: colors.indigo },
|
||||
{ name: "+3σ", prop: sd.p3sdUsd, color: colors.pink },
|
||||
{ name: "−3σ", prop: sd.m3sdUsd, color: colors.violet },
|
||||
{ name: "0σ", prop: sd._0sdUsd, color: colors.sd._0 },
|
||||
{ name: "+0.5σ", prop: sd.p05sdUsd, color: colors.sd.p05 },
|
||||
{ name: "−0.5σ", prop: sd.m05sdUsd, color: colors.sd.m05 },
|
||||
{ name: "+1σ", prop: sd.p1sdUsd, color: colors.sd.p1 },
|
||||
{ name: "−1σ", prop: sd.m1sdUsd, color: colors.sd.m1 },
|
||||
{ name: "+1.5σ", prop: sd.p15sdUsd, color: colors.sd.p15 },
|
||||
{ name: "−1.5σ", prop: sd.m15sdUsd, color: colors.sd.m15 },
|
||||
{ name: "+2σ", prop: sd.p2sdUsd, color: colors.sd.p2 },
|
||||
{ name: "−2σ", prop: sd.m2sdUsd, color: colors.sd.m2 },
|
||||
{ name: "+2.5σ", prop: sd.p25sdUsd, color: colors.sd.p25 },
|
||||
{ name: "−2.5σ", prop: sd.m25sdUsd, color: colors.sd.m25 },
|
||||
{ name: "+3σ", prop: sd.p3sdUsd, color: colors.sd.p3 },
|
||||
{ name: "−3σ", prop: sd.m3sdUsd, color: colors.sd.m3 },
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -192,19 +221,19 @@ export function sdBandsUsd(sd) {
|
||||
*/
|
||||
export function sdBandsRatio(sd) {
|
||||
return /** @type {const} */ ([
|
||||
{ name: "0σ", prop: sd.sma, color: colors.lime },
|
||||
{ name: "+0.5σ", prop: sd.p05sd, color: colors.yellow },
|
||||
{ name: "−0.5σ", prop: sd.m05sd, color: colors.teal },
|
||||
{ name: "+1σ", prop: sd.p1sd, color: colors.amber },
|
||||
{ name: "−1σ", prop: sd.m1sd, color: colors.cyan },
|
||||
{ name: "+1.5σ", prop: sd.p15sd, color: colors.orange },
|
||||
{ name: "−1.5σ", prop: sd.m15sd, color: colors.sky },
|
||||
{ name: "+2σ", prop: sd.p2sd, color: colors.red },
|
||||
{ name: "−2σ", prop: sd.m2sd, color: colors.blue },
|
||||
{ name: "+2.5σ", prop: sd.p25sd, color: colors.rose },
|
||||
{ name: "−2.5σ", prop: sd.m25sd, color: colors.indigo },
|
||||
{ name: "+3σ", prop: sd.p3sd, color: colors.pink },
|
||||
{ name: "−3σ", prop: sd.m3sd, color: colors.violet },
|
||||
{ name: "0σ", prop: sd.sma, color: colors.sd._0 },
|
||||
{ name: "+0.5σ", prop: sd.p05sd, color: colors.sd.p05 },
|
||||
{ name: "−0.5σ", prop: sd.m05sd, color: colors.sd.m05 },
|
||||
{ name: "+1σ", prop: sd.p1sd, color: colors.sd.p1 },
|
||||
{ name: "−1σ", prop: sd.m1sd, color: colors.sd.m1 },
|
||||
{ name: "+1.5σ", prop: sd.p15sd, color: colors.sd.p15 },
|
||||
{ name: "−1.5σ", prop: sd.m15sd, color: colors.sd.m15 },
|
||||
{ name: "+2σ", prop: sd.p2sd, color: colors.sd.p2 },
|
||||
{ name: "−2σ", prop: sd.m2sd, color: colors.sd.m2 },
|
||||
{ name: "+2.5σ", prop: sd.p25sd, color: colors.sd.p25 },
|
||||
{ name: "−2.5σ", prop: sd.m25sd, color: colors.sd.m25 },
|
||||
{ name: "+3σ", prop: sd.p3sd, color: colors.sd.p3 },
|
||||
{ name: "−3σ", prop: sd.m3sd, color: colors.sd.m3 },
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -214,12 +243,12 @@ export function sdBandsRatio(sd) {
|
||||
*/
|
||||
export function ratioSmas(ratio) {
|
||||
return /** @type {const} */ ([
|
||||
{ name: "1w SMA", metric: ratio.ratio1wSma, color: colors.lime },
|
||||
{ name: "1m SMA", metric: ratio.ratio1mSma, color: colors.teal },
|
||||
{ name: "1y SMA", metric: ratio.ratio1ySd.sma, color: colors.sky },
|
||||
{ name: "2y SMA", metric: ratio.ratio2ySd.sma, color: colors.indigo },
|
||||
{ name: "4y SMA", metric: ratio.ratio4ySd.sma, color: colors.purple },
|
||||
{ name: "All SMA", metric: ratio.ratioSd.sma, color: colors.rose },
|
||||
{ name: "1w SMA", metric: ratio.ratio1wSma, color: colors.ma._1w },
|
||||
{ name: "1m SMA", metric: ratio.ratio1mSma, color: colors.ma._1m },
|
||||
{ name: "1y SMA", metric: ratio.ratio1ySd.sma, color: colors.ma._1y },
|
||||
{ name: "2y SMA", metric: ratio.ratio2ySd.sma, color: colors.ma._2y },
|
||||
{ name: "4y SMA", metric: ratio.ratio4ySd.sma, color: colors.ma._4y },
|
||||
{ name: "All SMA", metric: ratio.ratioSd.sma, color: colors.time.all },
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -303,25 +332,25 @@ export function createZScoresFolder({
|
||||
price({
|
||||
metric: ratio.ratio1ySd._0sdUsd,
|
||||
name: "1y 0σ",
|
||||
color: colors.orange,
|
||||
color: colors.ma._1y,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ratio.ratio2ySd._0sdUsd,
|
||||
name: "2y 0σ",
|
||||
color: colors.yellow,
|
||||
color: colors.ma._2y,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ratio.ratio4ySd._0sdUsd,
|
||||
name: "4y 0σ",
|
||||
color: colors.lime,
|
||||
color: colors.ma._4y,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: ratio.ratioSd._0sdUsd,
|
||||
name: "all 0σ",
|
||||
color: colors.blue,
|
||||
color: colors.time.all,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
@@ -329,25 +358,25 @@ export function createZScoresFolder({
|
||||
line({
|
||||
metric: ratio.ratioSd.zscore,
|
||||
name: "All",
|
||||
color: colors.blue,
|
||||
color: colors.time.all,
|
||||
unit: Unit.sd,
|
||||
}),
|
||||
line({
|
||||
metric: ratio.ratio4ySd.zscore,
|
||||
name: "4y",
|
||||
color: colors.lime,
|
||||
color: colors.ma._4y,
|
||||
unit: Unit.sd,
|
||||
}),
|
||||
line({
|
||||
metric: ratio.ratio2ySd.zscore,
|
||||
name: "2y",
|
||||
color: colors.yellow,
|
||||
color: colors.ma._2y,
|
||||
unit: Unit.sd,
|
||||
}),
|
||||
line({
|
||||
metric: ratio.ratio1ySd.zscore,
|
||||
name: "1y",
|
||||
color: colors.orange,
|
||||
color: colors.ma._1y,
|
||||
unit: Unit.sd,
|
||||
}),
|
||||
...priceLines({
|
||||
@@ -422,9 +451,8 @@ export function createZScoresFolder({
|
||||
* @param {AnyPricePattern} args.pricePattern - The price pattern
|
||||
* @param {AnyRatioPattern} args.ratio - The ratio pattern
|
||||
* @param {Color} args.color
|
||||
* @param {string} [args.ratioName] - Optional custom name for ratio chart (default: "ratio")
|
||||
* @param {string} [args.priceTitle] - Optional override for price chart title (default: context)
|
||||
* @param {string} [args.zScoresSuffix] - Optional suffix appended to context for z-scores (e.g., "MVRV" gives "2y Z-Score: STH MVRV")
|
||||
* @param {string} [args.titlePrefix] - Optional prefix for ratio/z-scores titles (e.g., "Realized Price" gives "Realized Price Ratio: STH")
|
||||
* @param {FetchedPriceSeriesBlueprint[]} [args.priceReferences] - Optional additional price series to show in Price chart
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
@@ -434,15 +462,11 @@ export function createPriceRatioCharts({
|
||||
pricePattern,
|
||||
ratio,
|
||||
color,
|
||||
ratioName,
|
||||
priceTitle,
|
||||
zScoresSuffix,
|
||||
titlePrefix,
|
||||
priceReferences,
|
||||
}) {
|
||||
const titleFn = formatCohortTitle(context);
|
||||
const zScoresTitleFn = zScoresSuffix
|
||||
? formatCohortTitle(`${context} ${zScoresSuffix}`)
|
||||
: titleFn;
|
||||
return [
|
||||
{
|
||||
name: "Price",
|
||||
@@ -453,14 +477,15 @@ export function createPriceRatioCharts({
|
||||
],
|
||||
},
|
||||
createRatioChart({
|
||||
title: titleFn,
|
||||
title: (name) =>
|
||||
titleFn(titlePrefix ? `${titlePrefix} ${name}` : name),
|
||||
pricePattern,
|
||||
ratio,
|
||||
color,
|
||||
name: ratioName,
|
||||
}),
|
||||
createZScoresFolder({
|
||||
formatTitle: zScoresTitleFn,
|
||||
formatTitle: (name) =>
|
||||
titleFn(titlePrefix ? `${titlePrefix} ${name}` : name),
|
||||
legend,
|
||||
pricePattern,
|
||||
ratio,
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
* @property {string} title
|
||||
* @property {Color} color
|
||||
* @property {PatternAll} tree
|
||||
* @property {Brk.MetricPattern1<Brk.StoredU64>} addrCount
|
||||
* @property {Brk._30dCountPattern} addrCount
|
||||
*
|
||||
* Full cohort: adjustedSopr + percentiles + RelToMarketCap (term.short)
|
||||
* @typedef {Object} CohortFull
|
||||
@@ -253,7 +253,7 @@
|
||||
* ============================================================================
|
||||
*
|
||||
* Addressable cohort with address count (for "type" cohorts - no RelToMarketCap)
|
||||
* @typedef {CohortBasicWithoutMarketCap & { addrCount: Brk.MetricPattern1<Brk.StoredU64> }} CohortAddress
|
||||
* @typedef {CohortBasicWithoutMarketCap & { addrCount: Brk._30dCountPattern }} CohortAddress
|
||||
*
|
||||
* ============================================================================
|
||||
* Cohort Group Types (by capability)
|
||||
|
||||
@@ -61,7 +61,7 @@ export function init() {
|
||||
type: "Candlestick",
|
||||
title: "Price",
|
||||
metric: brk.metrics.price.sats.ohlc,
|
||||
colors: [colors.red, colors.green],
|
||||
colors: colors.bi.profitLoss,
|
||||
};
|
||||
result.set(Unit.sats, [satsPrice, ...(optionTop.get(Unit.sats) ?? [])]);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
* @import { SingleValueData, CandlestickData, Series, AnySeries, ISeries, HistogramData, LineData, BaselineData, LineSeriesPartialOptions, BaselineSeriesPartialOptions, HistogramSeriesPartialOptions, CandlestickSeriesPartialOptions, Chart, Legend } from "./chart/index.js"
|
||||
*
|
||||
* @import { Color, ColorName, Colors } from "./utils/colors.js"
|
||||
* @import { Color } from "./utils/colors.js"
|
||||
*
|
||||
* @import { WebSockets } from "./utils/ws.js"
|
||||
*
|
||||
@@ -77,20 +77,22 @@
|
||||
* Relative patterns by capability:
|
||||
* - BasicRelativePattern: minimal relative (investedCapitalIn*Pct, supplyIn*RelToOwnSupply only)
|
||||
* - GlobalRelativePattern: has RelToMarketCap metrics (netUnrealizedPnlRelToMarketCap, etc)
|
||||
* - GlobalPeakRelativePattern: GlobalRelativePattern + unrealizedPeakRegretRelToMarketCap
|
||||
* - OwnRelativePattern: has RelToOwnMarketCap metrics (netUnrealizedPnlRelToOwnMarketCap, etc)
|
||||
* - FullRelativePattern: has BOTH RelToMarketCap AND RelToOwnMarketCap
|
||||
* - FullRelativePattern: has BOTH RelToMarketCap AND RelToOwnMarketCap + unrealizedPeakRegretRelToMarketCap
|
||||
* @typedef {Brk.InvestedSupplyPattern} BasicRelativePattern
|
||||
* @typedef {Brk.InvestedNegNetNuplSupplyUnrealizedPattern} GlobalRelativePattern
|
||||
* @typedef {Brk.InvestedNegNetNuplSupplyUnrealizedPattern3} GlobalPeakRelativePattern
|
||||
* @typedef {Brk.InvestedNegNetSupplyUnrealizedPattern} OwnRelativePattern
|
||||
* @typedef {Brk.InvestedNegNetNuplSupplyUnrealizedPattern2} FullRelativePattern
|
||||
* @typedef {Brk.InvestedNegNetNuplSupplyUnrealizedPattern4} FullRelativePattern
|
||||
* @typedef {Brk.GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern} UnrealizedPattern
|
||||
* @typedef {Brk.GreedInvestedInvestorNegNetPainPeakSupplyTotalUnrealizedPattern} UnrealizedFullPattern
|
||||
*
|
||||
* Realized patterns
|
||||
* @typedef {Brk.CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSoprTotalValuePattern} RealizedPattern
|
||||
* @typedef {Brk.CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSoprTotalValuePattern2} RealizedPattern2
|
||||
* @typedef {Brk.AdjustedCapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSoprTotalValuePattern} RealizedPattern3
|
||||
* @typedef {Brk.AdjustedCapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSoprTotalValuePattern2} RealizedPattern4
|
||||
* @typedef {Brk.CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern} RealizedPattern
|
||||
* @typedef {Brk.CapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern2} RealizedPattern2
|
||||
* @typedef {Brk.AdjustedCapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern} RealizedPattern3
|
||||
* @typedef {Brk.AdjustedCapCapitulationInvestorLossMvrvNegNetPeakProfitRealizedSellSentSoprTotalValuePattern2} RealizedPattern4
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -153,10 +155,11 @@
|
||||
* @typedef {UtxoCohortPattern | AddressCohortPattern} CohortPattern
|
||||
*
|
||||
* Relative pattern capability types
|
||||
* @typedef {GlobalRelativePattern | FullRelativePattern} RelativeWithMarketCap
|
||||
* @typedef {GlobalRelativePattern | FullRelativePattern | AllRelativePattern} RelativeWithMarketCap
|
||||
* @typedef {OwnRelativePattern | FullRelativePattern} RelativeWithOwnMarketCap
|
||||
* @typedef {OwnRelativePattern | FullRelativePattern | AllRelativePattern} RelativeWithOwnPnl
|
||||
* @typedef {GlobalRelativePattern | FullRelativePattern} RelativeWithNupl
|
||||
* @typedef {GlobalRelativePattern | FullRelativePattern | AllRelativePattern} RelativeWithNupl
|
||||
* @typedef {GlobalPeakRelativePattern | FullRelativePattern | AllRelativePattern} RelativeWithPeakRegret
|
||||
* @typedef {BasicRelativePattern | GlobalRelativePattern | OwnRelativePattern | FullRelativePattern | AllRelativePattern} RelativeWithInvestedCapitalPct
|
||||
*
|
||||
* Realized pattern capability types
|
||||
@@ -173,6 +176,7 @@
|
||||
* @typedef {AllUtxoPattern | AgeRangePattern | UtxoAmountPattern} PatternWithCostBasis
|
||||
* @typedef {AllUtxoPattern | AgeRangePattern | UtxoAmountPattern} PatternWithActivity
|
||||
* @typedef {AllUtxoPattern | AgeRangePattern} PatternWithCostBasisPercentiles
|
||||
* @typedef {Brk.Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern} PercentilesPattern
|
||||
*
|
||||
* Cohort objects with specific pattern capabilities
|
||||
* @typedef {{ name: string, title: string, color: Color, tree: PatternWithRealizedPrice }} CohortWithRealizedPrice
|
||||
|
||||
@@ -67,270 +67,381 @@ function getLightDarkValue(property) {
|
||||
return dark ? _dark : light;
|
||||
}
|
||||
|
||||
const red = createColor(() => getColor("red"));
|
||||
const orange = createColor(() => getColor("orange"));
|
||||
const amber = createColor(() => getColor("amber"));
|
||||
const yellow = createColor(() => getColor("yellow"));
|
||||
const avocado = createColor(() => getColor("avocado"));
|
||||
const lime = createColor(() => getColor("lime"));
|
||||
const green = createColor(() => getColor("green"));
|
||||
const emerald = createColor(() => getColor("emerald"));
|
||||
const teal = createColor(() => getColor("teal"));
|
||||
const cyan = createColor(() => getColor("cyan"));
|
||||
const sky = createColor(() => getColor("sky"));
|
||||
const blue = createColor(() => getColor("blue"));
|
||||
const indigo = createColor(() => getColor("indigo"));
|
||||
const violet = createColor(() => getColor("violet"));
|
||||
const purple = createColor(() => getColor("purple"));
|
||||
const fuchsia = createColor(() => getColor("fuchsia"));
|
||||
const pink = createColor(() => getColor("pink"));
|
||||
const rose = createColor(() => getColor("rose"));
|
||||
|
||||
const spectrumColors = {
|
||||
red,
|
||||
orange,
|
||||
amber,
|
||||
yellow,
|
||||
avocado,
|
||||
lime,
|
||||
green,
|
||||
emerald,
|
||||
teal,
|
||||
cyan,
|
||||
sky,
|
||||
blue,
|
||||
indigo,
|
||||
violet,
|
||||
purple,
|
||||
fuchsia,
|
||||
pink,
|
||||
rose,
|
||||
};
|
||||
|
||||
const baseColors = {
|
||||
default: createColor(() => getLightDarkValue("--color")),
|
||||
gray: createColor(() => getColor("gray")),
|
||||
border: createColor(() => getLightDarkValue("--border-color")),
|
||||
...spectrumColors,
|
||||
const palette = {
|
||||
red: createColor(() => getColor("red")),
|
||||
orange: createColor(() => getColor("orange")),
|
||||
amber: createColor(() => getColor("amber")),
|
||||
yellow: createColor(() => getColor("yellow")),
|
||||
avocado: createColor(() => getColor("avocado")),
|
||||
lime: createColor(() => getColor("lime")),
|
||||
green: createColor(() => getColor("green")),
|
||||
emerald: createColor(() => getColor("emerald")),
|
||||
teal: createColor(() => getColor("teal")),
|
||||
cyan: createColor(() => getColor("cyan")),
|
||||
sky: createColor(() => getColor("sky")),
|
||||
blue: createColor(() => getColor("blue")),
|
||||
indigo: createColor(() => getColor("indigo")),
|
||||
violet: createColor(() => getColor("violet")),
|
||||
purple: createColor(() => getColor("purple")),
|
||||
fuchsia: createColor(() => getColor("fuchsia")),
|
||||
pink: createColor(() => getColor("pink")),
|
||||
rose: createColor(() => getColor("rose")),
|
||||
};
|
||||
|
||||
export const colors = {
|
||||
...baseColors,
|
||||
default: createColor(() => getLightDarkValue("--color")),
|
||||
gray: createColor(() => getColor("gray")),
|
||||
border: createColor(() => getLightDarkValue("--border-color")),
|
||||
|
||||
// Directional
|
||||
profit: palette.green,
|
||||
loss: palette.red,
|
||||
bitcoin: palette.orange,
|
||||
usd: palette.green,
|
||||
|
||||
// Bi-color pairs for baselines
|
||||
bi: {
|
||||
/** @type {[Color, Color]} */
|
||||
profitLoss: [palette.green, palette.red],
|
||||
/** @type {[Color, Color]} */
|
||||
sopr7d: [palette.lime, palette.rose],
|
||||
/** @type {[Color, Color]} */
|
||||
sopr30d: [palette.avocado, palette.pink],
|
||||
/** @type {[Color, Color]} */
|
||||
adjustedSopr: [palette.yellow, palette.fuchsia],
|
||||
/** @type {[Color, Color]} */
|
||||
adjustedSopr7d: [palette.amber, palette.purple],
|
||||
/** @type {[Color, Color]} */
|
||||
adjustedSopr30d: [palette.orange, palette.violet],
|
||||
/** @type {[Color, Color]} */
|
||||
lumpSum: [palette.cyan, palette.orange],
|
||||
},
|
||||
|
||||
|
||||
// Cointime economics
|
||||
liveliness: palette.pink,
|
||||
vaulted: palette.lime,
|
||||
active: palette.rose,
|
||||
activity: palette.purple,
|
||||
cointime: palette.yellow,
|
||||
destroyed: palette.red,
|
||||
created: palette.orange,
|
||||
stored: palette.green,
|
||||
|
||||
// Valuations
|
||||
realized: palette.orange,
|
||||
investor: palette.fuchsia,
|
||||
thermo: palette.emerald,
|
||||
trueMarketMean: palette.blue,
|
||||
vocdd: palette.purple,
|
||||
hodlBank: palette.blue,
|
||||
reserveRisk: palette.orange,
|
||||
|
||||
// Comparisons (base vs adjusted)
|
||||
base: palette.orange,
|
||||
adjusted: palette.purple,
|
||||
adjustedCreated: palette.lime,
|
||||
adjustedDestroyed: palette.pink,
|
||||
|
||||
// Ratios
|
||||
plRatio: palette.yellow,
|
||||
|
||||
// Mining
|
||||
mining: {
|
||||
coinbase: palette.orange,
|
||||
subsidy: palette.lime,
|
||||
fee: palette.cyan,
|
||||
},
|
||||
|
||||
// Network
|
||||
segwit: palette.cyan,
|
||||
|
||||
// Entity (transactions, inputs, outputs)
|
||||
entity: {
|
||||
tx: palette.orange,
|
||||
input: palette.red,
|
||||
output: palette.cyan,
|
||||
},
|
||||
|
||||
// Technical indicators
|
||||
indicator: {
|
||||
main: palette.indigo,
|
||||
fast: palette.blue,
|
||||
slow: palette.orange,
|
||||
upper: palette.green,
|
||||
lower: palette.red,
|
||||
mid: palette.yellow,
|
||||
},
|
||||
|
||||
stat: {
|
||||
sum: blue,
|
||||
cumulative: indigo,
|
||||
avg: orange,
|
||||
max: green,
|
||||
pct90: cyan,
|
||||
pct75: blue,
|
||||
median: yellow,
|
||||
pct25: violet,
|
||||
pct10: fuchsia,
|
||||
min: red,
|
||||
sum: palette.blue,
|
||||
cumulative: palette.indigo,
|
||||
avg: palette.orange,
|
||||
max: palette.green,
|
||||
pct90: palette.cyan,
|
||||
pct75: palette.blue,
|
||||
median: palette.yellow,
|
||||
pct25: palette.violet,
|
||||
pct10: palette.fuchsia,
|
||||
min: palette.red,
|
||||
},
|
||||
|
||||
// Ratio percentile bands (extreme values)
|
||||
ratioPct: {
|
||||
_99: palette.rose,
|
||||
_98: palette.pink,
|
||||
_95: palette.fuchsia,
|
||||
_5: palette.cyan,
|
||||
_2: palette.sky,
|
||||
_1: palette.blue,
|
||||
},
|
||||
|
||||
// Standard deviation bands (warm = positive, cool = negative)
|
||||
sd: {
|
||||
_0: palette.lime,
|
||||
p05: palette.yellow,
|
||||
m05: palette.teal,
|
||||
p1: palette.amber,
|
||||
m1: palette.cyan,
|
||||
p15: palette.orange,
|
||||
m15: palette.sky,
|
||||
p2: palette.red,
|
||||
m2: palette.blue,
|
||||
p25: palette.rose,
|
||||
m25: palette.indigo,
|
||||
p3: palette.pink,
|
||||
m3: palette.violet,
|
||||
},
|
||||
|
||||
// Transaction versions
|
||||
txVersion: {
|
||||
v1: palette.orange,
|
||||
v2: palette.cyan,
|
||||
v3: palette.lime,
|
||||
},
|
||||
|
||||
pct: {
|
||||
_100: palette.red,
|
||||
_95: palette.orange,
|
||||
_90: palette.amber,
|
||||
_85: palette.yellow,
|
||||
_80: palette.avocado,
|
||||
_75: palette.lime,
|
||||
_70: palette.green,
|
||||
_65: palette.emerald,
|
||||
_60: palette.teal,
|
||||
_55: palette.cyan,
|
||||
_50: palette.sky,
|
||||
_45: palette.blue,
|
||||
_40: palette.indigo,
|
||||
_35: palette.violet,
|
||||
_30: palette.purple,
|
||||
_25: palette.fuchsia,
|
||||
_20: palette.pink,
|
||||
_15: palette.rose,
|
||||
_10: palette.pink,
|
||||
_05: palette.fuchsia,
|
||||
_0: palette.purple,
|
||||
},
|
||||
|
||||
time: {
|
||||
_24h: pink,
|
||||
_1w: red,
|
||||
_1m: yellow,
|
||||
_1y: lime,
|
||||
all: teal,
|
||||
_24h: palette.pink,
|
||||
_1w: palette.red,
|
||||
_1m: palette.yellow,
|
||||
_1y: palette.lime,
|
||||
all: palette.teal,
|
||||
},
|
||||
|
||||
term: {
|
||||
short: yellow,
|
||||
long: fuchsia,
|
||||
short: palette.yellow,
|
||||
long: palette.fuchsia,
|
||||
},
|
||||
|
||||
age: {
|
||||
_1d: red,
|
||||
_1w: orange,
|
||||
_1m: yellow,
|
||||
_2m: lime,
|
||||
_3m: green,
|
||||
_4m: teal,
|
||||
_5m: cyan,
|
||||
_6m: blue,
|
||||
_1y: indigo,
|
||||
_2y: violet,
|
||||
_3y: purple,
|
||||
_4y: fuchsia,
|
||||
_5y: pink,
|
||||
_6y: rose,
|
||||
_7y: red,
|
||||
_8y: orange,
|
||||
_10y: yellow,
|
||||
_12y: lime,
|
||||
_15y: green,
|
||||
_1d: palette.red,
|
||||
_1w: palette.orange,
|
||||
_1m: palette.yellow,
|
||||
_2m: palette.lime,
|
||||
_3m: palette.green,
|
||||
_4m: palette.teal,
|
||||
_5m: palette.cyan,
|
||||
_6m: palette.blue,
|
||||
_1y: palette.indigo,
|
||||
_2y: palette.violet,
|
||||
_3y: palette.purple,
|
||||
_4y: palette.fuchsia,
|
||||
_5y: palette.pink,
|
||||
_6y: palette.rose,
|
||||
_7y: palette.red,
|
||||
_8y: palette.orange,
|
||||
_10y: palette.yellow,
|
||||
_12y: palette.lime,
|
||||
_15y: palette.green,
|
||||
},
|
||||
|
||||
ageRange: {
|
||||
upTo1h: rose,
|
||||
_1hTo1d: pink,
|
||||
_1dTo1w: red,
|
||||
_1wTo1m: orange,
|
||||
_1mTo2m: yellow,
|
||||
_2mTo3m: yellow,
|
||||
_3mTo4m: lime,
|
||||
_4mTo5m: lime,
|
||||
_5mTo6m: lime,
|
||||
_6mTo1y: green,
|
||||
_1yTo2y: cyan,
|
||||
_2yTo3y: blue,
|
||||
_3yTo4y: indigo,
|
||||
_4yTo5y: violet,
|
||||
_5yTo6y: purple,
|
||||
_6yTo7y: purple,
|
||||
_7yTo8y: fuchsia,
|
||||
_8yTo10y: fuchsia,
|
||||
_10yTo12y: pink,
|
||||
_12yTo15y: red,
|
||||
from15y: orange,
|
||||
upTo1h: palette.rose,
|
||||
_1hTo1d: palette.pink,
|
||||
_1dTo1w: palette.red,
|
||||
_1wTo1m: palette.orange,
|
||||
_1mTo2m: palette.yellow,
|
||||
_2mTo3m: palette.yellow,
|
||||
_3mTo4m: palette.lime,
|
||||
_4mTo5m: palette.lime,
|
||||
_5mTo6m: palette.lime,
|
||||
_6mTo1y: palette.green,
|
||||
_1yTo2y: palette.cyan,
|
||||
_2yTo3y: palette.blue,
|
||||
_3yTo4y: palette.indigo,
|
||||
_4yTo5y: palette.violet,
|
||||
_5yTo6y: palette.purple,
|
||||
_6yTo7y: palette.purple,
|
||||
_7yTo8y: palette.fuchsia,
|
||||
_8yTo10y: palette.fuchsia,
|
||||
_10yTo12y: palette.pink,
|
||||
_12yTo15y: palette.red,
|
||||
from15y: palette.orange,
|
||||
},
|
||||
|
||||
amount: {
|
||||
_1sat: orange,
|
||||
_10sats: orange,
|
||||
_100sats: yellow,
|
||||
_1kSats: lime,
|
||||
_10kSats: green,
|
||||
_100kSats: cyan,
|
||||
_1mSats: blue,
|
||||
_10mSats: indigo,
|
||||
_1btc: purple,
|
||||
_10btc: violet,
|
||||
_100btc: fuchsia,
|
||||
_1kBtc: pink,
|
||||
_10kBtc: red,
|
||||
_100kBtc: orange,
|
||||
_1sat: palette.orange,
|
||||
_10sats: palette.orange,
|
||||
_100sats: palette.yellow,
|
||||
_1kSats: palette.lime,
|
||||
_10kSats: palette.green,
|
||||
_100kSats: palette.cyan,
|
||||
_1mSats: palette.blue,
|
||||
_10mSats: palette.indigo,
|
||||
_1btc: palette.purple,
|
||||
_10btc: palette.violet,
|
||||
_100btc: palette.fuchsia,
|
||||
_1kBtc: palette.pink,
|
||||
_10kBtc: palette.red,
|
||||
_100kBtc: palette.orange,
|
||||
},
|
||||
|
||||
amountRange: {
|
||||
_0sats: red,
|
||||
_1satTo10sats: orange,
|
||||
_10satsTo100sats: yellow,
|
||||
_100satsTo1kSats: lime,
|
||||
_1kSatsTo10kSats: green,
|
||||
_10kSatsTo100kSats: cyan,
|
||||
_100kSatsTo1mSats: blue,
|
||||
_1mSatsTo10mSats: indigo,
|
||||
_10mSatsTo1btc: purple,
|
||||
_1btcTo10btc: violet,
|
||||
_10btcTo100btc: fuchsia,
|
||||
_100btcTo1kBtc: pink,
|
||||
_1kBtcTo10kBtc: red,
|
||||
_10kBtcTo100kBtc: orange,
|
||||
_100kBtcOrMore: yellow,
|
||||
_0sats: palette.red,
|
||||
_1satTo10sats: palette.orange,
|
||||
_10satsTo100sats: palette.yellow,
|
||||
_100satsTo1kSats: palette.lime,
|
||||
_1kSatsTo10kSats: palette.green,
|
||||
_10kSatsTo100kSats: palette.cyan,
|
||||
_100kSatsTo1mSats: palette.blue,
|
||||
_1mSatsTo10mSats: palette.indigo,
|
||||
_10mSatsTo1btc: palette.purple,
|
||||
_1btcTo10btc: palette.violet,
|
||||
_10btcTo100btc: palette.fuchsia,
|
||||
_100btcTo1kBtc: palette.pink,
|
||||
_1kBtcTo10kBtc: palette.red,
|
||||
_10kBtcTo100kBtc: palette.orange,
|
||||
_100kBtcOrMore: palette.yellow,
|
||||
},
|
||||
|
||||
epoch: {
|
||||
_0: red,
|
||||
_1: yellow,
|
||||
_2: orange,
|
||||
_3: lime,
|
||||
_4: green,
|
||||
_0: palette.red,
|
||||
_1: palette.yellow,
|
||||
_2: palette.orange,
|
||||
_3: palette.lime,
|
||||
_4: palette.green,
|
||||
},
|
||||
|
||||
year: {
|
||||
_2009: red,
|
||||
_2010: orange,
|
||||
_2011: amber,
|
||||
_2012: yellow,
|
||||
_2013: lime,
|
||||
_2014: green,
|
||||
_2015: teal,
|
||||
_2016: cyan,
|
||||
_2017: sky,
|
||||
_2018: blue,
|
||||
_2019: indigo,
|
||||
_2020: violet,
|
||||
_2021: purple,
|
||||
_2022: fuchsia,
|
||||
_2023: pink,
|
||||
_2024: rose,
|
||||
_2025: red,
|
||||
_2026: orange,
|
||||
_2009: palette.red,
|
||||
_2010: palette.orange,
|
||||
_2011: palette.amber,
|
||||
_2012: palette.yellow,
|
||||
_2013: palette.lime,
|
||||
_2014: palette.green,
|
||||
_2015: palette.teal,
|
||||
_2016: palette.cyan,
|
||||
_2017: palette.sky,
|
||||
_2018: palette.blue,
|
||||
_2019: palette.indigo,
|
||||
_2020: palette.violet,
|
||||
_2021: palette.purple,
|
||||
_2022: palette.fuchsia,
|
||||
_2023: palette.pink,
|
||||
_2024: palette.rose,
|
||||
_2025: palette.red,
|
||||
_2026: palette.orange,
|
||||
},
|
||||
|
||||
returns: {
|
||||
_1d: red,
|
||||
_1w: orange,
|
||||
_1m: yellow,
|
||||
_3m: lime,
|
||||
_6m: green,
|
||||
_1y: teal,
|
||||
_2y: cyan,
|
||||
_3y: sky,
|
||||
_4y: blue,
|
||||
_5y: indigo,
|
||||
_6y: violet,
|
||||
_8y: purple,
|
||||
_10y: fuchsia,
|
||||
_1d: palette.red,
|
||||
_1w: palette.orange,
|
||||
_1m: palette.yellow,
|
||||
_3m: palette.lime,
|
||||
_6m: palette.green,
|
||||
_1y: palette.teal,
|
||||
_2y: palette.cyan,
|
||||
_3y: palette.sky,
|
||||
_4y: palette.blue,
|
||||
_5y: palette.indigo,
|
||||
_6y: palette.violet,
|
||||
_8y: palette.purple,
|
||||
_10y: palette.fuchsia,
|
||||
},
|
||||
|
||||
ma: {
|
||||
_1w: red,
|
||||
_8d: orange,
|
||||
_12d: amber,
|
||||
_13d: yellow,
|
||||
_21d: avocado,
|
||||
_26d: lime,
|
||||
_1m: green,
|
||||
_34d: emerald,
|
||||
_55d: teal,
|
||||
_89d: cyan,
|
||||
_111d: sky,
|
||||
_144d: blue,
|
||||
_200d: indigo,
|
||||
_350d: violet,
|
||||
_1y: purple,
|
||||
_2y: fuchsia,
|
||||
_200w: pink,
|
||||
_4y: rose,
|
||||
_1w: palette.red,
|
||||
_8d: palette.orange,
|
||||
_12d: palette.amber,
|
||||
_13d: palette.yellow,
|
||||
_21d: palette.avocado,
|
||||
_26d: palette.lime,
|
||||
_1m: palette.green,
|
||||
_34d: palette.emerald,
|
||||
_55d: palette.teal,
|
||||
_2m: palette.cyan,
|
||||
_89d: palette.sky,
|
||||
_111d: palette.blue,
|
||||
_144d: palette.indigo,
|
||||
_200d: palette.violet,
|
||||
_350d: palette.purple,
|
||||
_1y: palette.fuchsia,
|
||||
_2y: palette.pink,
|
||||
_200w: palette.rose,
|
||||
_4y: palette.red,
|
||||
},
|
||||
|
||||
dca: {
|
||||
_1w: red,
|
||||
_1m: orange,
|
||||
_3m: yellow,
|
||||
_6m: lime,
|
||||
_1y: green,
|
||||
_2y: teal,
|
||||
_3y: cyan,
|
||||
_4y: sky,
|
||||
_5y: blue,
|
||||
_6y: indigo,
|
||||
_8y: violet,
|
||||
_10y: purple,
|
||||
_1w: palette.red,
|
||||
_1m: palette.orange,
|
||||
_3m: palette.yellow,
|
||||
_6m: palette.lime,
|
||||
_1y: palette.green,
|
||||
_2y: palette.teal,
|
||||
_3y: palette.cyan,
|
||||
_4y: palette.sky,
|
||||
_5y: palette.blue,
|
||||
_6y: palette.indigo,
|
||||
_8y: palette.violet,
|
||||
_10y: palette.purple,
|
||||
},
|
||||
|
||||
scriptType: {
|
||||
p2pk65: red,
|
||||
p2pk33: orange,
|
||||
p2pkh: yellow,
|
||||
p2ms: lime,
|
||||
p2sh: green,
|
||||
p2wpkh: teal,
|
||||
p2wsh: blue,
|
||||
p2tr: indigo,
|
||||
p2a: purple,
|
||||
opreturn: pink,
|
||||
unknown: violet,
|
||||
empty: fuchsia,
|
||||
p2pk65: palette.red,
|
||||
p2pk33: palette.orange,
|
||||
p2pkh: palette.yellow,
|
||||
p2ms: palette.lime,
|
||||
p2sh: palette.green,
|
||||
p2wpkh: palette.teal,
|
||||
p2wsh: palette.blue,
|
||||
p2tr: palette.indigo,
|
||||
p2a: palette.purple,
|
||||
opreturn: palette.pink,
|
||||
unknown: palette.violet,
|
||||
empty: palette.fuchsia,
|
||||
},
|
||||
|
||||
arr: Object.values(palette),
|
||||
|
||||
/**
|
||||
* Get a color by index (cycles through palette)
|
||||
* @param {number} index
|
||||
*/
|
||||
at(index) {
|
||||
return this.arr[index % this.arr.length];
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {typeof colors} Colors
|
||||
* @typedef {keyof typeof baseColors} ColorName
|
||||
*/
|
||||
|
||||
/** Palette for indexed series */
|
||||
const palette = Object.values(spectrumColors);
|
||||
|
||||
/**
|
||||
* Get a color by index (cycles through palette)
|
||||
* @param {number} index
|
||||
*/
|
||||
export const colorAt = (index) => palette[index % palette.length];
|
||||
|
||||
@@ -45,12 +45,13 @@ export function throttle(callback, wait = 1000) {
|
||||
* @template {(...args: any[]) => any} F
|
||||
* @param {F} callback
|
||||
* @param {number} [wait]
|
||||
* @returns {((...args: Parameters<F>) => void) & { cancel: () => void }}
|
||||
*/
|
||||
export function debounce(callback, wait = 1000) {
|
||||
/** @type {number | null} */
|
||||
let timeoutId = null;
|
||||
|
||||
return (/** @type {Parameters<F>} */ ...args) => {
|
||||
const fn = (/** @type {Parameters<F>} */ ...args) => {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
@@ -59,4 +60,13 @@ export function debounce(callback, wait = 1000) {
|
||||
timeoutId = null;
|
||||
}, wait);
|
||||
};
|
||||
|
||||
fn.cancel = () => {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
}
|
||||
};
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user