global: snap

This commit is contained in:
nym21
2026-04-18 17:23:12 +02:00
parent 2a93f51e81
commit fd2b93367d
31 changed files with 1004 additions and 988 deletions
+52 -139
View File
@@ -1685,7 +1685,7 @@ pub struct ActivityAddrOutputsRealizedSupplyUnrealizedPattern {
pub addr_count: BaseDeltaPattern,
pub outputs: SpendingSpentUnspentPattern,
pub realized: CapLossMvrvPriceProfitPattern,
pub supply: DeltaTotalPattern,
pub supply: DeltaDominanceTotalPattern,
pub unrealized: NuplPattern,
}
@@ -1697,7 +1697,7 @@ impl ActivityAddrOutputsRealizedSupplyUnrealizedPattern {
addr_count: BaseDeltaPattern::new(client.clone(), _m(&acc, "addr_count")),
outputs: SpendingSpentUnspentPattern::new(client.clone(), acc.clone()),
realized: CapLossMvrvPriceProfitPattern::new(client.clone(), acc.clone()),
supply: DeltaTotalPattern::new(client.clone(), _m(&acc, "supply")),
supply: DeltaDominanceTotalPattern::new(client.clone(), _m(&acc, "supply")),
unrealized: NuplPattern::new(client.clone(), _m(&acc, "nupl")),
}
}
@@ -1751,30 +1751,6 @@ impl BpsCentsPercentilesRatioSatsUsdPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct BtcCentsSatsToUsdPattern3 {
pub btc: SeriesPattern1<Bitcoin>,
pub cents: SeriesPattern1<Cents>,
pub sats: SeriesPattern1<Sats>,
pub to_circulating: BpsPercentRatioPattern2,
pub to_own: BpsPercentRatioPattern2,
pub usd: SeriesPattern1<Dollars>,
}
impl BtcCentsSatsToUsdPattern3 {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
btc: SeriesPattern1::new(client.clone(), acc.clone()),
cents: SeriesPattern1::new(client.clone(), _m(&acc, "cents")),
sats: SeriesPattern1::new(client.clone(), _m(&acc, "sats")),
to_circulating: BpsPercentRatioPattern2::new(client.clone(), _m(&acc, "to_circulating")),
to_own: BpsPercentRatioPattern2::new(client.clone(), _m(&acc, "to_own")),
usd: SeriesPattern1::new(client.clone(), _m(&acc, "usd")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct CentsNegativeToUsdPattern2 {
pub cents: SeriesPattern1<Cents>,
@@ -1800,48 +1776,48 @@ impl CentsNegativeToUsdPattern2 {
}
/// Pattern struct for repeated tree structure.
pub struct DeltaHalfInToTotalPattern {
pub struct DeltaDominanceHalfInTotalPattern2 {
pub delta: AbsoluteRatePattern,
pub dominance: BpsPercentRatioPattern2,
pub half: BtcCentsSatsUsdPattern,
pub in_loss: BtcCentsSatsToUsdPattern,
pub in_profit: BtcCentsSatsToUsdPattern,
pub to_circulating: BpsPercentRatioPattern2,
pub in_loss: BtcCentsSatsShareUsdPattern,
pub in_profit: BtcCentsSatsShareUsdPattern,
pub total: BtcCentsSatsUsdPattern,
}
impl DeltaHalfInToTotalPattern {
impl DeltaDominanceHalfInTotalPattern2 {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
delta: AbsoluteRatePattern::new(client.clone(), _m(&acc, "delta")),
dominance: BpsPercentRatioPattern2::new(client.clone(), _m(&acc, "dominance")),
half: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "half")),
in_loss: BtcCentsSatsToUsdPattern::new(client.clone(), _m(&acc, "in_loss")),
in_profit: BtcCentsSatsToUsdPattern::new(client.clone(), _m(&acc, "in_profit")),
to_circulating: BpsPercentRatioPattern2::new(client.clone(), _m(&acc, "to_circulating")),
in_loss: BtcCentsSatsShareUsdPattern::new(client.clone(), _m(&acc, "in_loss")),
in_profit: BtcCentsSatsShareUsdPattern::new(client.clone(), _m(&acc, "in_profit")),
total: BtcCentsSatsUsdPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct DeltaHalfInToTotalPattern2 {
pub struct DeltaDominanceHalfInTotalPattern {
pub delta: AbsoluteRatePattern,
pub dominance: BpsPercentRatioPattern2,
pub half: BtcCentsSatsUsdPattern,
pub in_loss: BtcCentsSatsToUsdPattern3,
pub in_profit: BtcCentsSatsToUsdPattern3,
pub to_circulating: BpsPercentRatioPattern2,
pub in_loss: BtcCentsSatsUsdPattern,
pub in_profit: BtcCentsSatsUsdPattern,
pub total: BtcCentsSatsUsdPattern,
}
impl DeltaHalfInToTotalPattern2 {
impl DeltaDominanceHalfInTotalPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
delta: AbsoluteRatePattern::new(client.clone(), _m(&acc, "delta")),
dominance: BpsPercentRatioPattern2::new(client.clone(), _m(&acc, "dominance")),
half: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "half")),
in_loss: BtcCentsSatsToUsdPattern3::new(client.clone(), _m(&acc, "in_loss")),
in_profit: BtcCentsSatsToUsdPattern3::new(client.clone(), _m(&acc, "in_profit")),
to_circulating: BpsPercentRatioPattern2::new(client.clone(), _m(&acc, "to_circulating")),
in_loss: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "in_loss")),
in_profit: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "in_profit")),
total: BtcCentsSatsUsdPattern::new(client.clone(), acc.clone()),
}
}
@@ -1896,7 +1872,7 @@ pub struct ActivityOutputsRealizedSupplyUnrealizedPattern {
pub activity: CoindaysTransferPattern,
pub outputs: SpendingSpentUnspentPattern,
pub realized: CapLossMvrvNetPriceProfitSoprPattern,
pub supply: DeltaHalfInToTotalPattern,
pub supply: DeltaDominanceHalfInTotalPattern,
pub unrealized: LossNetNuplProfitPattern,
}
@@ -1907,7 +1883,7 @@ impl ActivityOutputsRealizedSupplyUnrealizedPattern {
activity: CoindaysTransferPattern::new(client.clone(), acc.clone()),
outputs: SpendingSpentUnspentPattern::new(client.clone(), acc.clone()),
realized: CapLossMvrvNetPriceProfitSoprPattern::new(client.clone(), acc.clone()),
supply: DeltaHalfInToTotalPattern::new(client.clone(), _m(&acc, "supply")),
supply: DeltaDominanceHalfInTotalPattern::new(client.clone(), _m(&acc, "supply")),
unrealized: LossNetNuplProfitPattern::new(client.clone(), acc.clone()),
}
}
@@ -1918,7 +1894,7 @@ pub struct ActivityOutputsRealizedSupplyUnrealizedPattern3 {
pub activity: TransferPattern,
pub outputs: SpendingSpentUnspentPattern,
pub realized: CapLossMvrvPriceProfitPattern,
pub supply: DeltaHalfInTotalPattern2,
pub supply: DeltaDominanceHalfInTotalPattern,
pub unrealized: LossNuplProfitPattern,
}
@@ -1929,7 +1905,7 @@ impl ActivityOutputsRealizedSupplyUnrealizedPattern3 {
activity: TransferPattern::new(client.clone(), _m(&acc, "transfer_volume")),
outputs: SpendingSpentUnspentPattern::new(client.clone(), acc.clone()),
realized: CapLossMvrvPriceProfitPattern::new(client.clone(), acc.clone()),
supply: DeltaHalfInTotalPattern2::new(client.clone(), _m(&acc, "supply")),
supply: DeltaDominanceHalfInTotalPattern::new(client.clone(), _m(&acc, "supply")),
unrealized: LossNuplProfitPattern::new(client.clone(), acc.clone()),
}
}
@@ -1940,7 +1916,7 @@ pub struct ActivityOutputsRealizedSupplyUnrealizedPattern2 {
pub activity: TransferPattern,
pub outputs: SpendingSpentUnspentPattern,
pub realized: CapLossMvrvPriceProfitPattern,
pub supply: DeltaTotalPattern,
pub supply: DeltaDominanceTotalPattern,
pub unrealized: NuplPattern,
}
@@ -1951,7 +1927,7 @@ impl ActivityOutputsRealizedSupplyUnrealizedPattern2 {
activity: TransferPattern::new(client.clone(), _m(&acc, "transfer_volume")),
outputs: SpendingSpentUnspentPattern::new(client.clone(), acc.clone()),
realized: CapLossMvrvPriceProfitPattern::new(client.clone(), acc.clone()),
supply: DeltaTotalPattern::new(client.clone(), _m(&acc, "supply")),
supply: DeltaDominanceTotalPattern::new(client.clone(), _m(&acc, "supply")),
unrealized: NuplPattern::new(client.clone(), _m(&acc, "nupl")),
}
}
@@ -2024,44 +2000,22 @@ impl BtcCentsDeltaSatsUsdPattern {
}
/// Pattern struct for repeated tree structure.
pub struct BtcCentsSatsToUsdPattern {
pub struct BtcCentsSatsShareUsdPattern {
pub btc: SeriesPattern1<Bitcoin>,
pub cents: SeriesPattern1<Cents>,
pub sats: SeriesPattern1<Sats>,
pub to_circulating: BpsPercentRatioPattern2,
pub share: BpsPercentRatioPattern2,
pub usd: SeriesPattern1<Dollars>,
}
impl BtcCentsSatsToUsdPattern {
impl BtcCentsSatsShareUsdPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
btc: SeriesPattern1::new(client.clone(), acc.clone()),
cents: SeriesPattern1::new(client.clone(), _m(&acc, "cents")),
sats: SeriesPattern1::new(client.clone(), _m(&acc, "sats")),
to_circulating: BpsPercentRatioPattern2::new(client.clone(), _m(&acc, "to_circulating")),
usd: SeriesPattern1::new(client.clone(), _m(&acc, "usd")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct BtcCentsSatsToUsdPattern2 {
pub btc: SeriesPattern1<Bitcoin>,
pub cents: SeriesPattern1<Cents>,
pub sats: SeriesPattern1<Sats>,
pub to_own: BpsPercentRatioPattern2,
pub usd: SeriesPattern1<Dollars>,
}
impl BtcCentsSatsToUsdPattern2 {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
btc: SeriesPattern1::new(client.clone(), acc.clone()),
cents: SeriesPattern1::new(client.clone(), _m(&acc, "cents")),
sats: SeriesPattern1::new(client.clone(), _m(&acc, "sats")),
to_own: BpsPercentRatioPattern2::new(client.clone(), _m(&acc, "to_own")),
share: BpsPercentRatioPattern2::new(client.clone(), _m(&acc, "share")),
usd: SeriesPattern1::new(client.clone(), _m(&acc, "usd")),
}
}
@@ -2111,28 +2065,6 @@ impl CentsToUsdPattern4 {
}
}
/// Pattern struct for repeated tree structure.
pub struct DeltaHalfInTotalPattern2 {
pub delta: AbsoluteRatePattern,
pub half: BtcCentsSatsUsdPattern,
pub in_loss: BtcCentsSatsUsdPattern,
pub in_profit: BtcCentsSatsUsdPattern,
pub total: BtcCentsSatsUsdPattern,
}
impl DeltaHalfInTotalPattern2 {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
delta: AbsoluteRatePattern::new(client.clone(), _m(&acc, "delta")),
half: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "half")),
in_loss: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "in_loss")),
in_profit: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "in_profit")),
total: BtcCentsSatsUsdPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct EmaHistogramLineSignalPattern {
pub ema_fast: SeriesPattern1<StoredF32>,
@@ -2826,6 +2758,24 @@ impl CumulativeRollingSumPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct DeltaDominanceTotalPattern {
pub delta: AbsoluteRatePattern,
pub dominance: BpsPercentRatioPattern2,
pub total: BtcCentsSatsUsdPattern,
}
impl DeltaDominanceTotalPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
delta: AbsoluteRatePattern::new(client.clone(), _m(&acc, "delta")),
dominance: BpsPercentRatioPattern2::new(client.clone(), _m(&acc, "dominance")),
total: BtcCentsSatsUsdPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct GreedNetPainPattern {
pub greed_index: CentsUsdPattern3,
@@ -3190,22 +3140,6 @@ impl CoindaysTransferPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct DeltaTotalPattern {
pub delta: AbsoluteRatePattern,
pub total: BtcCentsSatsUsdPattern,
}
impl DeltaTotalPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
delta: AbsoluteRatePattern::new(client.clone(), _m(&acc, "delta")),
total: BtcCentsSatsUsdPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct FundedTotalPattern {
pub funded: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4,
@@ -7046,7 +6980,7 @@ impl SeriesTree_Cohorts_Utxo {
/// Series tree node.
pub struct SeriesTree_Cohorts_Utxo_All {
pub supply: SeriesTree_Cohorts_Utxo_All_Supply,
pub supply: DeltaDominanceHalfInTotalPattern2,
pub outputs: SeriesTree_Cohorts_Utxo_All_Outputs,
pub activity: SeriesTree_Cohorts_Utxo_All_Activity,
pub realized: SeriesTree_Cohorts_Utxo_All_Realized,
@@ -7058,7 +6992,7 @@ pub struct SeriesTree_Cohorts_Utxo_All {
impl SeriesTree_Cohorts_Utxo_All {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
supply: SeriesTree_Cohorts_Utxo_All_Supply::new(client.clone(), format!("{base_path}_supply")),
supply: DeltaDominanceHalfInTotalPattern2::new(client.clone(), "supply".to_string()),
outputs: SeriesTree_Cohorts_Utxo_All_Outputs::new(client.clone(), format!("{base_path}_outputs")),
activity: SeriesTree_Cohorts_Utxo_All_Activity::new(client.clone(), format!("{base_path}_activity")),
realized: SeriesTree_Cohorts_Utxo_All_Realized::new(client.clone(), format!("{base_path}_realized")),
@@ -7069,27 +7003,6 @@ impl SeriesTree_Cohorts_Utxo_All {
}
}
/// Series tree node.
pub struct SeriesTree_Cohorts_Utxo_All_Supply {
pub total: BtcCentsSatsUsdPattern,
pub delta: AbsoluteRatePattern,
pub half: BtcCentsSatsUsdPattern,
pub in_profit: BtcCentsSatsToUsdPattern2,
pub in_loss: BtcCentsSatsToUsdPattern2,
}
impl SeriesTree_Cohorts_Utxo_All_Supply {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
total: BtcCentsSatsUsdPattern::new(client.clone(), "supply".to_string()),
delta: AbsoluteRatePattern::new(client.clone(), "supply_delta".to_string()),
half: BtcCentsSatsUsdPattern::new(client.clone(), "supply_half".to_string()),
in_profit: BtcCentsSatsToUsdPattern2::new(client.clone(), "supply_in_profit".to_string()),
in_loss: BtcCentsSatsToUsdPattern2::new(client.clone(), "supply_in_loss".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Cohorts_Utxo_All_Outputs {
pub unspent_count: BaseDeltaPattern,
@@ -7535,7 +7448,7 @@ impl SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment {
/// Series tree node.
pub struct SeriesTree_Cohorts_Utxo_Sth {
pub supply: DeltaHalfInToTotalPattern2,
pub supply: DeltaDominanceHalfInTotalPattern2,
pub outputs: SpendingSpentUnspentPattern,
pub activity: CoindaysCoinyearsDormancyTransferPattern,
pub realized: SeriesTree_Cohorts_Utxo_Sth_Realized,
@@ -7547,7 +7460,7 @@ pub struct SeriesTree_Cohorts_Utxo_Sth {
impl SeriesTree_Cohorts_Utxo_Sth {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
supply: DeltaHalfInToTotalPattern2::new(client.clone(), "sth_supply".to_string()),
supply: DeltaDominanceHalfInTotalPattern2::new(client.clone(), "sth_supply".to_string()),
outputs: SpendingSpentUnspentPattern::new(client.clone(), "sth".to_string()),
activity: CoindaysCoinyearsDormancyTransferPattern::new(client.clone(), "sth".to_string()),
realized: SeriesTree_Cohorts_Utxo_Sth_Realized::new(client.clone(), format!("{base_path}_realized")),
@@ -7805,7 +7718,7 @@ impl SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_1y {
/// Series tree node.
pub struct SeriesTree_Cohorts_Utxo_Lth {
pub supply: DeltaHalfInToTotalPattern2,
pub supply: DeltaDominanceHalfInTotalPattern2,
pub outputs: SpendingSpentUnspentPattern,
pub activity: CoindaysCoinyearsDormancyTransferPattern,
pub realized: SeriesTree_Cohorts_Utxo_Lth_Realized,
@@ -7817,7 +7730,7 @@ pub struct SeriesTree_Cohorts_Utxo_Lth {
impl SeriesTree_Cohorts_Utxo_Lth {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
supply: DeltaHalfInToTotalPattern2::new(client.clone(), "lth_supply".to_string()),
supply: DeltaDominanceHalfInTotalPattern2::new(client.clone(), "lth_supply".to_string()),
outputs: SpendingSpentUnspentPattern::new(client.clone(), "lth".to_string()),
activity: CoindaysCoinyearsDormancyTransferPattern::new(client.clone(), "lth".to_string()),
realized: SeriesTree_Cohorts_Utxo_Lth_Realized::new(client.clone(), format!("{base_path}_realized")),
@@ -3,7 +3,7 @@ use std::path::Path;
use brk_cohort::{AddrGroups, AmountRange, Filter, Filtered, OverAmount, UnderAmount};
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, StoredU64, Version};
use brk_types::{Height, Indexes, Sats, StoredU64, Version};
use derive_more::{Deref, DerefMut};
use rayon::prelude::*;
use vecdb::{AnyStoredVec, Database, Exit, ReadableVec, Rw, StorageMode};
@@ -109,12 +109,19 @@ impl AddrCohorts {
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_supply_sats: &impl ReadableVec<Height, Sats>,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
self.0
.par_iter_mut()
.try_for_each(|v| v.compute_rest_part2(prices, starting_indexes, all_utxo_count, exit))
self.0.par_iter_mut().try_for_each(|v| {
v.compute_rest_part2(
prices,
starting_indexes,
all_supply_sats,
all_utxo_count,
exit,
)
})
}
/// Returns a parallel iterator over all vecs for parallel writing.
@@ -3,7 +3,7 @@ use std::path::Path;
use brk_cohort::{CohortContext, Filter, Filtered};
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{BasisPointsSigned32, Cents, Height, Indexes, StoredI64, StoredU64, Version};
use brk_types::{BasisPointsSigned32, Cents, Height, Indexes, Sats, StoredI64, StoredU64, Version};
use rayon::prelude::*;
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, ReadableVec, Rw, StorageMode, WritableVec};
@@ -230,10 +230,16 @@ impl CohortVecs for AddrCohortVecs {
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_supply_sats: &impl ReadableVec<Height, Sats>,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
self.metrics
.compute_rest_part2(prices, starting_indexes, all_utxo_count, exit)
self.metrics.compute_rest_part2(
prices,
starting_indexes,
all_supply_sats,
all_utxo_count,
exit,
)
}
}
@@ -1,5 +1,5 @@
use brk_error::Result;
use brk_types::{Cents, Height, Indexes, StoredU64, Version};
use brk_types::{Cents, Height, Indexes, Sats, StoredU64, Version};
use vecdb::{Exit, ReadableVec};
use crate::prices;
@@ -62,6 +62,7 @@ pub trait CohortVecs: DynCohortVecs {
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_supply_sats: &impl ReadableVec<Height, Sats>,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()>;
@@ -660,7 +660,7 @@ impl UTXOCohorts<Rw> {
Box::new(|| {
over_amount.par_iter_mut().try_for_each(|v| {
v.metrics
.compute_rest_part2(prices, starting_indexes, au, exit)
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
})
}),
Box::new(|| {
@@ -678,19 +678,19 @@ impl UTXOCohorts<Rw> {
Box::new(|| {
amount_range.par_iter_mut().try_for_each(|v| {
v.metrics
.compute_rest_part2(prices, starting_indexes, au, exit)
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
})
}),
Box::new(|| {
under_amount.par_iter_mut().try_for_each(|v| {
v.metrics
.compute_rest_part2(prices, starting_indexes, au, exit)
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
})
}),
Box::new(|| {
type_.par_iter_mut().try_for_each(|v| {
v.metrics
.compute_rest_part2(prices, starting_indexes, au, exit)
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
})
}),
];
@@ -2,8 +2,7 @@ use brk_cohort::Filter;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Cents, Dollars, Height, Indexes, Version};
use vecdb::AnyStoredVec;
use vecdb::{Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode};
use vecdb::{AnyStoredVec, Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode};
use crate::{
blocks,
@@ -152,6 +151,10 @@ impl AllCohortMetrics {
self.unrealized
.compute_sentiment(starting_indexes, &prices.spot.cents.height, exit)?;
let own_supply_sats = self.supply.total.sats.height.read_only_clone();
self.supply
.compute_dominance(starting_indexes.height, &own_supply_sats, exit)?;
self.relative.compute(
starting_indexes.height,
&self.supply,
@@ -6,14 +6,13 @@ use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
use crate::{
distribution::metrics::{
ActivityCore, CohortMetricsBase, ImportConfig, OutputsBase, RealizedCore, RelativeToAll,
SupplyCore, UnrealizedCore,
ActivityCore, CohortMetricsBase, ImportConfig, OutputsBase, RealizedCore, SupplyCore,
UnrealizedCore,
},
prices,
};
/// Basic cohort metrics: no extensions, with relative (rel_to_all).
/// Used by: age_range cohorts.
/// Basic cohort metrics: no extensions, used by age_range cohorts.
#[derive(Traversable)]
pub struct BasicCohortMetrics<M: StorageMode = Rw> {
#[traversable(skip)]
@@ -23,8 +22,6 @@ pub struct BasicCohortMetrics<M: StorageMode = Rw> {
pub activity: Box<ActivityCore<M>>,
pub realized: Box<RealizedCore<M>>,
pub unrealized: Box<UnrealizedCore<M>>,
#[traversable(flatten)]
pub relative: Box<RelativeToAll<M>>,
}
impl CohortMetricsBase for BasicCohortMetrics {
@@ -51,8 +48,6 @@ impl BasicCohortMetrics {
let unrealized = UnrealizedCore::forced_import(cfg)?;
let realized = RealizedCore::forced_import(cfg)?;
let relative = RelativeToAll::forced_import(cfg)?;
Ok(Self {
filter: cfg.filter.clone(),
supply: Box::new(supply),
@@ -60,7 +55,6 @@ impl BasicCohortMetrics {
activity: Box::new(ActivityCore::forced_import(cfg)?),
realized: Box::new(realized),
unrealized: Box::new(unrealized),
relative: Box::new(relative),
})
}
@@ -87,8 +81,8 @@ impl BasicCohortMetrics {
exit,
)?;
self.relative
.compute(starting_indexes.height, &self.supply, all_supply_sats, exit)?;
self.supply
.compute_dominance(starting_indexes.height, all_supply_sats, exit)?;
self.outputs
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
@@ -6,8 +6,8 @@ use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
use crate::{
distribution::metrics::{
ActivityCore, CohortMetricsBase, ImportConfig, OutputsBase, RealizedCore, RelativeToAll,
SupplyCore, UnrealizedCore,
ActivityCore, CohortMetricsBase, ImportConfig, OutputsBase, RealizedCore, SupplyCore,
UnrealizedCore,
},
prices,
};
@@ -21,8 +21,6 @@ pub struct CoreCohortMetrics<M: StorageMode = Rw> {
pub activity: Box<ActivityCore<M>>,
pub realized: Box<RealizedCore<M>>,
pub unrealized: Box<UnrealizedCore<M>>,
#[traversable(flatten)]
pub relative: Box<RelativeToAll<M>>,
}
impl CoreCohortMetrics {
@@ -34,7 +32,6 @@ impl CoreCohortMetrics {
activity: Box::new(ActivityCore::forced_import(cfg)?),
realized: Box::new(RealizedCore::forced_import(cfg)?),
unrealized: Box::new(UnrealizedCore::forced_import(cfg)?),
relative: Box::new(RelativeToAll::forced_import(cfg)?),
})
}
@@ -145,8 +142,8 @@ impl CoreCohortMetrics {
exit,
)?;
self.relative
.compute(starting_indexes.height, &self.supply, all_supply_sats, exit)?;
self.supply
.compute_dominance(starting_indexes.height, all_supply_sats, exit)?;
self.outputs
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
@@ -128,13 +128,15 @@ impl ExtendedCohortMetrics {
self.unrealized
.compute_sentiment(starting_indexes, &prices.spot.cents.height, exit)?;
self.supply
.compute_dominance(starting_indexes.height, all_supply_sats, exit)?;
self.relative.compute(
starting_indexes.height,
&self.supply,
&self.unrealized,
&self.realized,
height_to_market_cap,
all_supply_sats,
&self.supply.total.usd.height,
exit,
)?;
@@ -1,7 +1,7 @@
use brk_cohort::Filter;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, StoredU64};
use brk_types::{Height, Indexes, Sats, StoredU64};
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
use crate::{
@@ -112,6 +112,7 @@ impl MinimalCohortMetrics {
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_supply_sats: &impl ReadableVec<Height, Sats>,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
@@ -129,6 +130,9 @@ impl MinimalCohortMetrics {
exit,
)?;
self.supply
.compute_dominance(starting_indexes.height, all_supply_sats, exit)?;
self.outputs
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
@@ -1,7 +1,7 @@
use brk_cohort::Filter;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, StoredU64};
use brk_types::{Height, Indexes, Sats, StoredU64};
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
use crate::{
@@ -74,6 +74,7 @@ impl TypeCohortMetrics {
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
all_supply_sats: &impl ReadableVec<Height, Sats>,
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
exit: &Exit,
) -> Result<()> {
@@ -91,6 +92,9 @@ impl TypeCohortMetrics {
exit,
)?;
self.supply
.compute_dominance(starting_indexes.height, all_supply_sats, exit)?;
self.outputs
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
@@ -132,7 +132,7 @@ pub use profitability::ProfitabilityMetrics;
pub use realized::{
AdjustedSopr, RealizedCore, RealizedFull, RealizedFullAccum, RealizedLike, RealizedMinimal,
};
pub use relative::{RelativeForAll, RelativeToAll, RelativeWithExtended};
pub use relative::{RelativeForAll, RelativeWithExtended};
pub use supply::{AvgAmountMetrics, SupplyBase, SupplyCore};
pub use unrealized::{
UnrealizedBasic, UnrealizedCore, UnrealizedFull, UnrealizedLike, UnrealizedMinimal,
@@ -11,10 +11,10 @@ use crate::{
/// Full relative metrics (sth/lth/all tier).
#[derive(Traversable)]
pub struct RelativeFull<M: StorageMode = Rw> {
#[traversable(wrap = "supply/in_profit", rename = "to_own")]
pub supply_in_profit_to_own: PercentPerBlock<BasisPoints16, M>,
#[traversable(wrap = "supply/in_loss", rename = "to_own")]
pub supply_in_loss_to_own: PercentPerBlock<BasisPoints16, M>,
#[traversable(wrap = "supply/in_profit", rename = "share")]
pub supply_in_profit_share: PercentPerBlock<BasisPoints16, M>,
#[traversable(wrap = "supply/in_loss", rename = "share")]
pub supply_in_loss_share: PercentPerBlock<BasisPoints16, M>,
#[traversable(wrap = "unrealized/profit", rename = "to_mcap")]
pub unrealized_profit_to_mcap: PercentPerBlock<BasisPoints16, M>,
@@ -28,8 +28,8 @@ impl RelativeFull {
let v2 = Version::new(2);
Ok(Self {
supply_in_profit_to_own: cfg.import("supply_in_profit_to_own", v1)?,
supply_in_loss_to_own: cfg.import("supply_in_loss_to_own", v1)?,
supply_in_profit_share: cfg.import("supply_in_profit_share", v1)?,
supply_in_loss_share: cfg.import("supply_in_loss_share", v1)?,
unrealized_profit_to_mcap: cfg.import("unrealized_profit_to_mcap", v2)?,
unrealized_loss_to_mcap: cfg.import("unrealized_loss_to_mcap", v2)?,
})
@@ -43,14 +43,14 @@ impl RelativeFull {
market_cap: &impl ReadableVec<Height, Dollars>,
exit: &Exit,
) -> Result<()> {
self.supply_in_profit_to_own
self.supply_in_profit_share
.compute_binary::<Sats, Sats, RatioSatsBp16>(
max_from,
&supply.in_profit.sats.height,
&supply.total.sats.height,
exit,
)?;
self.supply_in_loss_to_own
self.supply_in_loss_share
.compute_binary::<Sats, Sats, RatioSatsBp16>(
max_from,
&supply.in_loss.sats.height,
@@ -11,18 +11,18 @@ use crate::distribution::metrics::{ImportConfig, RealizedFull, UnrealizedFull};
/// Present for cohorts with `UnrealizedFull` (all, sth, lth).
#[derive(Traversable)]
pub struct RelativeInvestedCapital<M: StorageMode = Rw> {
#[traversable(wrap = "invested_capital/in_profit", rename = "to_own")]
pub in_profit_to_own: PercentPerBlock<BasisPoints16, M>,
#[traversable(wrap = "invested_capital/in_loss", rename = "to_own")]
pub in_loss_to_own: PercentPerBlock<BasisPoints16, M>,
#[traversable(wrap = "invested_capital/in_profit", rename = "share")]
pub in_profit_share: PercentPerBlock<BasisPoints16, M>,
#[traversable(wrap = "invested_capital/in_loss", rename = "share")]
pub in_loss_share: PercentPerBlock<BasisPoints16, M>,
}
impl RelativeInvestedCapital {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
let v0 = Version::ZERO;
Ok(Self {
in_profit_to_own: cfg.import("invested_capital_in_profit_to_own", v0)?,
in_loss_to_own: cfg.import("invested_capital_in_loss_to_own", v0)?,
in_profit_share: cfg.import("invested_capital_in_profit_share", v0)?,
in_loss_share: cfg.import("invested_capital_in_loss_share", v0)?,
})
}
@@ -34,14 +34,14 @@ impl RelativeInvestedCapital {
exit: &Exit,
) -> Result<()> {
let realized_cap = &realized.core.minimal.cap.cents.height;
self.in_profit_to_own
self.in_profit_share
.compute_binary::<Cents, Cents, RatioCentsBp16>(
max_from,
&unrealized.invested_capital.in_profit.cents.height,
realized_cap,
exit,
)?;
self.in_loss_to_own
self.in_loss_share
.compute_binary::<Cents, Cents, RatioCentsBp16>(
max_from,
&unrealized.invested_capital.in_loss.cents.height,
@@ -3,7 +3,6 @@ mod extended_own_pnl;
mod for_all;
mod full;
mod invested_capital;
mod to_all;
mod with_extended;
pub use extended_own_market_cap::RelativeExtendedOwnMarketCap;
@@ -11,5 +10,4 @@ pub use extended_own_pnl::RelativeExtendedOwnPnl;
pub use for_all::RelativeForAll;
pub use full::RelativeFull;
pub use invested_capital::RelativeInvestedCapital;
pub use to_all::RelativeToAll;
pub use with_extended::RelativeWithExtended;
@@ -1,62 +0,0 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{BasisPoints16, Height, Sats, Version};
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use crate::internal::{PercentPerBlock, RatioSatsBp16};
use crate::distribution::metrics::{ImportConfig, SupplyCore};
/// Relative-to-all metrics (not present for the "all" cohort itself).
#[derive(Traversable)]
pub struct RelativeToAll<M: StorageMode = Rw> {
#[traversable(wrap = "supply", rename = "to_circulating")]
pub supply_to_circulating: PercentPerBlock<BasisPoints16, M>,
#[traversable(wrap = "supply/in_profit", rename = "to_circulating")]
pub supply_in_profit_to_circulating: PercentPerBlock<BasisPoints16, M>,
#[traversable(wrap = "supply/in_loss", rename = "to_circulating")]
pub supply_in_loss_to_circulating: PercentPerBlock<BasisPoints16, M>,
}
impl RelativeToAll {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
Ok(Self {
supply_to_circulating: cfg.import("supply_to_circulating", Version::ONE)?,
supply_in_profit_to_circulating: cfg
.import("supply_in_profit_to_circulating", Version::ONE)?,
supply_in_loss_to_circulating: cfg
.import("supply_in_loss_to_circulating", Version::ONE)?,
})
}
pub(crate) fn compute(
&mut self,
max_from: Height,
supply: &SupplyCore,
all_supply_sats: &impl ReadableVec<Height, Sats>,
exit: &Exit,
) -> Result<()> {
self.supply_to_circulating
.compute_binary::<Sats, Sats, RatioSatsBp16>(
max_from,
&supply.total.sats.height,
all_supply_sats,
exit,
)?;
self.supply_in_profit_to_circulating
.compute_binary::<Sats, Sats, RatioSatsBp16>(
max_from,
&supply.in_profit.sats.height,
all_supply_sats,
exit,
)?;
self.supply_in_loss_to_circulating
.compute_binary::<Sats, Sats, RatioSatsBp16>(
max_from,
&supply.in_loss.sats.height,
all_supply_sats,
exit,
)?;
Ok(())
}
}
@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Sats};
use brk_types::{Dollars, Height};
use derive_more::{Deref, DerefMut};
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
@@ -8,10 +8,9 @@ use crate::distribution::metrics::{ImportConfig, RealizedFull, SupplyCore, Unrea
use super::{
RelativeExtendedOwnMarketCap, RelativeExtendedOwnPnl, RelativeFull, RelativeInvestedCapital,
RelativeToAll,
};
/// Full extended relative metrics (base + rel_to_all + own_market_cap + own_pnl).
/// Full extended relative metrics (base + own_market_cap + own_pnl + invested_capital).
/// Used by: sth, lth cohorts.
#[derive(Deref, DerefMut, Traversable)]
pub struct RelativeWithExtended<M: StorageMode = Rw> {
@@ -20,8 +19,6 @@ pub struct RelativeWithExtended<M: StorageMode = Rw> {
#[traversable(flatten)]
pub base: RelativeFull<M>,
#[traversable(flatten)]
pub rel_to_all: RelativeToAll<M>,
#[traversable(flatten)]
pub extended_own_market_cap: RelativeExtendedOwnMarketCap<M>,
#[traversable(flatten)]
pub extended_own_pnl: RelativeExtendedOwnPnl<M>,
@@ -33,7 +30,6 @@ impl RelativeWithExtended {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
Ok(Self {
base: RelativeFull::forced_import(cfg)?,
rel_to_all: RelativeToAll::forced_import(cfg)?,
extended_own_market_cap: RelativeExtendedOwnMarketCap::forced_import(cfg)?,
extended_own_pnl: RelativeExtendedOwnPnl::forced_import(cfg)?,
invested_capital: RelativeInvestedCapital::forced_import(cfg)?,
@@ -48,14 +44,11 @@ impl RelativeWithExtended {
unrealized: &UnrealizedFull,
realized: &RealizedFull,
market_cap: &impl ReadableVec<Height, Dollars>,
all_supply_sats: &impl ReadableVec<Height, Sats>,
own_market_cap: &impl ReadableVec<Height, Dollars>,
exit: &Exit,
) -> Result<()> {
self.base
.compute(max_from, supply, &unrealized.inner.basic, market_cap, exit)?;
self.rel_to_all
.compute(max_from, supply, all_supply_sats, exit)?;
self.extended_own_market_cap
.compute(max_from, &unrealized.inner, own_market_cap, exit)?;
self.extended_own_pnl.compute(
@@ -1,22 +1,26 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{BasisPointsSigned32, Height, Indexes, Sats, SatsSigned, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
use brk_types::{BasisPoints16, BasisPointsSigned32, Height, Indexes, Sats, SatsSigned, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
use crate::{
distribution::state::{CohortState, CostBasisOps, RealizedOps},
prices,
};
use crate::internal::{AmountPerBlock, LazyRollingDeltasFromHeight};
use crate::internal::{
AmountPerBlock, LazyRollingDeltasFromHeight, PercentPerBlock, RatioSatsBp16,
};
use crate::distribution::metrics::ImportConfig;
/// Base supply metrics: total supply only (2 stored vecs).
/// Base supply metrics: total supply + dominance (share of circulating).
#[derive(Traversable)]
pub struct SupplyBase<M: StorageMode = Rw> {
pub total: AmountPerBlock<M>,
pub delta: LazyRollingDeltasFromHeight<Sats, SatsSigned, BasisPointsSigned32>,
#[traversable(rename = "dominance")]
pub dominance: PercentPerBlock<BasisPoints16, M>,
}
impl SupplyBase {
@@ -31,9 +35,12 @@ impl SupplyBase {
cfg.indexes,
);
let dominance = cfg.import("supply_dominance", Version::ZERO)?;
Ok(Self {
total: supply,
delta,
dominance,
})
}
@@ -50,6 +57,7 @@ impl SupplyBase {
vec![
&mut self.total.sats.height as &mut dyn AnyStoredVec,
&mut self.total.cents.height,
&mut self.dominance.bps.height,
]
}
@@ -62,6 +70,21 @@ impl SupplyBase {
self.total.compute(prices, max_from, exit)
}
pub(crate) fn compute_dominance(
&mut self,
max_from: Height,
all_supply_sats: &impl ReadableVec<Height, Sats>,
exit: &Exit,
) -> Result<()> {
self.dominance
.compute_binary::<Sats, Sats, RatioSatsBp16>(
max_from,
&self.total.sats.height,
all_supply_sats,
exit,
)
}
pub(crate) fn compute_from_stateful(
&mut self,
starting_indexes: &Indexes,
+16 -2
View File
@@ -560,6 +560,15 @@ impl Vecs {
exit,
)?;
let all_supply_sats = self
.utxo_cohorts
.all
.metrics
.supply
.total
.sats
.height
.read_only_clone();
let all_utxo_count = self
.utxo_cohorts
.all
@@ -568,8 +577,13 @@ impl Vecs {
.unspent_count
.height
.read_only_clone();
self.addr_cohorts
.compute_rest_part2(prices, starting_indexes, &all_utxo_count, exit)?;
self.addr_cohorts.compute_rest_part2(
prices,
starting_indexes,
&all_supply_sats,
&all_utxo_count,
exit,
)?;
let exit = exit.clone();
self.db.run_bg(move |db| {
+61 -151
View File
@@ -2717,7 +2717,7 @@ function create_1m1w1y2y4yAllPattern(client, acc) {
* @property {BaseDeltaPattern} addrCount
* @property {SpendingSpentUnspentPattern} outputs
* @property {CapLossMvrvPriceProfitPattern} realized
* @property {DeltaTotalPattern} supply
* @property {DeltaDominanceTotalPattern} supply
* @property {NuplPattern} unrealized
*/
@@ -2733,7 +2733,7 @@ function createActivityAddrOutputsRealizedSupplyUnrealizedPattern(client, acc) {
addrCount: createBaseDeltaPattern(client, _m(acc, 'addr_count')),
outputs: createSpendingSpentUnspentPattern(client, acc),
realized: createCapLossMvrvPriceProfitPattern(client, acc),
supply: createDeltaTotalPattern(client, _m(acc, 'supply')),
supply: createDeltaDominanceTotalPattern(client, _m(acc, 'supply')),
unrealized: createNuplPattern(client, _m(acc, 'nupl')),
};
}
@@ -2792,33 +2792,6 @@ function createBpsCentsPercentilesRatioSatsUsdPattern(client, acc) {
};
}
/**
* @typedef {Object} BtcCentsSatsToUsdPattern3
* @property {SeriesPattern1<Bitcoin>} btc
* @property {SeriesPattern1<Cents>} cents
* @property {SeriesPattern1<Sats>} sats
* @property {BpsPercentRatioPattern2} toCirculating
* @property {BpsPercentRatioPattern2} toOwn
* @property {SeriesPattern1<Dollars>} usd
*/
/**
* Create a BtcCentsSatsToUsdPattern3 pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {BtcCentsSatsToUsdPattern3}
*/
function createBtcCentsSatsToUsdPattern3(client, acc) {
return {
btc: createSeriesPattern1(client, acc),
cents: createSeriesPattern1(client, _m(acc, 'cents')),
sats: createSeriesPattern1(client, _m(acc, 'sats')),
toCirculating: createBpsPercentRatioPattern2(client, _m(acc, 'to_circulating')),
toOwn: createBpsPercentRatioPattern2(client, _m(acc, 'to_own')),
usd: createSeriesPattern1(client, _m(acc, 'usd')),
};
}
/**
* @typedef {Object} CentsNegativeToUsdPattern2
* @property {SeriesPattern1<Cents>} cents
@@ -2847,55 +2820,55 @@ function createCentsNegativeToUsdPattern2(client, acc) {
}
/**
* @typedef {Object} DeltaHalfInToTotalPattern
* @typedef {Object} DeltaDominanceHalfInTotalPattern2
* @property {AbsoluteRatePattern} delta
* @property {BpsPercentRatioPattern2} dominance
* @property {BtcCentsSatsUsdPattern} half
* @property {BtcCentsSatsToUsdPattern} inLoss
* @property {BtcCentsSatsToUsdPattern} inProfit
* @property {BpsPercentRatioPattern2} toCirculating
* @property {BtcCentsSatsShareUsdPattern} inLoss
* @property {BtcCentsSatsShareUsdPattern} inProfit
* @property {BtcCentsSatsUsdPattern} total
*/
/**
* Create a DeltaHalfInToTotalPattern pattern node
* Create a DeltaDominanceHalfInTotalPattern2 pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {DeltaHalfInToTotalPattern}
* @returns {DeltaDominanceHalfInTotalPattern2}
*/
function createDeltaHalfInToTotalPattern(client, acc) {
function createDeltaDominanceHalfInTotalPattern2(client, acc) {
return {
delta: createAbsoluteRatePattern(client, _m(acc, 'delta')),
dominance: createBpsPercentRatioPattern2(client, _m(acc, 'dominance')),
half: createBtcCentsSatsUsdPattern(client, _m(acc, 'half')),
inLoss: createBtcCentsSatsToUsdPattern(client, _m(acc, 'in_loss')),
inProfit: createBtcCentsSatsToUsdPattern(client, _m(acc, 'in_profit')),
toCirculating: createBpsPercentRatioPattern2(client, _m(acc, 'to_circulating')),
inLoss: createBtcCentsSatsShareUsdPattern(client, _m(acc, 'in_loss')),
inProfit: createBtcCentsSatsShareUsdPattern(client, _m(acc, 'in_profit')),
total: createBtcCentsSatsUsdPattern(client, acc),
};
}
/**
* @typedef {Object} DeltaHalfInToTotalPattern2
* @typedef {Object} DeltaDominanceHalfInTotalPattern
* @property {AbsoluteRatePattern} delta
* @property {BpsPercentRatioPattern2} dominance
* @property {BtcCentsSatsUsdPattern} half
* @property {BtcCentsSatsToUsdPattern3} inLoss
* @property {BtcCentsSatsToUsdPattern3} inProfit
* @property {BpsPercentRatioPattern2} toCirculating
* @property {BtcCentsSatsUsdPattern} inLoss
* @property {BtcCentsSatsUsdPattern} inProfit
* @property {BtcCentsSatsUsdPattern} total
*/
/**
* Create a DeltaHalfInToTotalPattern2 pattern node
* Create a DeltaDominanceHalfInTotalPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {DeltaHalfInToTotalPattern2}
* @returns {DeltaDominanceHalfInTotalPattern}
*/
function createDeltaHalfInToTotalPattern2(client, acc) {
function createDeltaDominanceHalfInTotalPattern(client, acc) {
return {
delta: createAbsoluteRatePattern(client, _m(acc, 'delta')),
dominance: createBpsPercentRatioPattern2(client, _m(acc, 'dominance')),
half: createBtcCentsSatsUsdPattern(client, _m(acc, 'half')),
inLoss: createBtcCentsSatsToUsdPattern3(client, _m(acc, 'in_loss')),
inProfit: createBtcCentsSatsToUsdPattern3(client, _m(acc, 'in_profit')),
toCirculating: createBpsPercentRatioPattern2(client, _m(acc, 'to_circulating')),
inLoss: createBtcCentsSatsUsdPattern(client, _m(acc, 'in_loss')),
inProfit: createBtcCentsSatsUsdPattern(client, _m(acc, 'in_profit')),
total: createBtcCentsSatsUsdPattern(client, acc),
};
}
@@ -2955,7 +2928,7 @@ function createActiveBidirectionalReactivatedReceivingSendingPattern(client, acc
* @property {CoindaysTransferPattern} activity
* @property {SpendingSpentUnspentPattern} outputs
* @property {CapLossMvrvNetPriceProfitSoprPattern} realized
* @property {DeltaHalfInToTotalPattern} supply
* @property {DeltaDominanceHalfInTotalPattern} supply
* @property {LossNetNuplProfitPattern} unrealized
*/
@@ -2970,7 +2943,7 @@ function createActivityOutputsRealizedSupplyUnrealizedPattern(client, acc) {
activity: createCoindaysTransferPattern(client, acc),
outputs: createSpendingSpentUnspentPattern(client, acc),
realized: createCapLossMvrvNetPriceProfitSoprPattern(client, acc),
supply: createDeltaHalfInToTotalPattern(client, _m(acc, 'supply')),
supply: createDeltaDominanceHalfInTotalPattern(client, _m(acc, 'supply')),
unrealized: createLossNetNuplProfitPattern(client, acc),
};
}
@@ -2980,7 +2953,7 @@ function createActivityOutputsRealizedSupplyUnrealizedPattern(client, acc) {
* @property {TransferPattern} activity
* @property {SpendingSpentUnspentPattern} outputs
* @property {CapLossMvrvPriceProfitPattern} realized
* @property {DeltaHalfInTotalPattern2} supply
* @property {DeltaDominanceHalfInTotalPattern} supply
* @property {LossNuplProfitPattern} unrealized
*/
@@ -2995,7 +2968,7 @@ function createActivityOutputsRealizedSupplyUnrealizedPattern3(client, acc) {
activity: createTransferPattern(client, _m(acc, 'transfer_volume')),
outputs: createSpendingSpentUnspentPattern(client, acc),
realized: createCapLossMvrvPriceProfitPattern(client, acc),
supply: createDeltaHalfInTotalPattern2(client, _m(acc, 'supply')),
supply: createDeltaDominanceHalfInTotalPattern(client, _m(acc, 'supply')),
unrealized: createLossNuplProfitPattern(client, acc),
};
}
@@ -3005,7 +2978,7 @@ function createActivityOutputsRealizedSupplyUnrealizedPattern3(client, acc) {
* @property {TransferPattern} activity
* @property {SpendingSpentUnspentPattern} outputs
* @property {CapLossMvrvPriceProfitPattern} realized
* @property {DeltaTotalPattern} supply
* @property {DeltaDominanceTotalPattern} supply
* @property {NuplPattern} unrealized
*/
@@ -3020,7 +2993,7 @@ function createActivityOutputsRealizedSupplyUnrealizedPattern2(client, acc) {
activity: createTransferPattern(client, _m(acc, 'transfer_volume')),
outputs: createSpendingSpentUnspentPattern(client, acc),
realized: createCapLossMvrvPriceProfitPattern(client, acc),
supply: createDeltaTotalPattern(client, _m(acc, 'supply')),
supply: createDeltaDominanceTotalPattern(client, _m(acc, 'supply')),
unrealized: createNuplPattern(client, _m(acc, 'nupl')),
};
}
@@ -3101,51 +3074,26 @@ function createBtcCentsDeltaSatsUsdPattern(client, acc) {
}
/**
* @typedef {Object} BtcCentsSatsToUsdPattern
* @typedef {Object} BtcCentsSatsShareUsdPattern
* @property {SeriesPattern1<Bitcoin>} btc
* @property {SeriesPattern1<Cents>} cents
* @property {SeriesPattern1<Sats>} sats
* @property {BpsPercentRatioPattern2} toCirculating
* @property {BpsPercentRatioPattern2} share
* @property {SeriesPattern1<Dollars>} usd
*/
/**
* Create a BtcCentsSatsToUsdPattern pattern node
* Create a BtcCentsSatsShareUsdPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {BtcCentsSatsToUsdPattern}
* @returns {BtcCentsSatsShareUsdPattern}
*/
function createBtcCentsSatsToUsdPattern(client, acc) {
function createBtcCentsSatsShareUsdPattern(client, acc) {
return {
btc: createSeriesPattern1(client, acc),
cents: createSeriesPattern1(client, _m(acc, 'cents')),
sats: createSeriesPattern1(client, _m(acc, 'sats')),
toCirculating: createBpsPercentRatioPattern2(client, _m(acc, 'to_circulating')),
usd: createSeriesPattern1(client, _m(acc, 'usd')),
};
}
/**
* @typedef {Object} BtcCentsSatsToUsdPattern2
* @property {SeriesPattern1<Bitcoin>} btc
* @property {SeriesPattern1<Cents>} cents
* @property {SeriesPattern1<Sats>} sats
* @property {BpsPercentRatioPattern2} toOwn
* @property {SeriesPattern1<Dollars>} usd
*/
/**
* Create a BtcCentsSatsToUsdPattern2 pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {BtcCentsSatsToUsdPattern2}
*/
function createBtcCentsSatsToUsdPattern2(client, acc) {
return {
btc: createSeriesPattern1(client, acc),
cents: createSeriesPattern1(client, _m(acc, 'cents')),
sats: createSeriesPattern1(client, _m(acc, 'sats')),
toOwn: createBpsPercentRatioPattern2(client, _m(acc, 'to_own')),
share: createBpsPercentRatioPattern2(client, _m(acc, 'share')),
usd: createSeriesPattern1(client, _m(acc, 'usd')),
};
}
@@ -3200,31 +3148,6 @@ function createCentsToUsdPattern4(client, acc) {
};
}
/**
* @typedef {Object} DeltaHalfInTotalPattern2
* @property {AbsoluteRatePattern} delta
* @property {BtcCentsSatsUsdPattern} half
* @property {BtcCentsSatsUsdPattern} inLoss
* @property {BtcCentsSatsUsdPattern} inProfit
* @property {BtcCentsSatsUsdPattern} total
*/
/**
* Create a DeltaHalfInTotalPattern2 pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {DeltaHalfInTotalPattern2}
*/
function createDeltaHalfInTotalPattern2(client, acc) {
return {
delta: createAbsoluteRatePattern(client, _m(acc, 'delta')),
half: createBtcCentsSatsUsdPattern(client, _m(acc, 'half')),
inLoss: createBtcCentsSatsUsdPattern(client, _m(acc, 'in_loss')),
inProfit: createBtcCentsSatsUsdPattern(client, _m(acc, 'in_profit')),
total: createBtcCentsSatsUsdPattern(client, acc),
};
}
/**
* @typedef {Object} EmaHistogramLineSignalPattern
* @property {SeriesPattern1<StoredF32>} emaFast
@@ -4028,6 +3951,27 @@ function createCumulativeRollingSumPattern(client, acc) {
};
}
/**
* @typedef {Object} DeltaDominanceTotalPattern
* @property {AbsoluteRatePattern} delta
* @property {BpsPercentRatioPattern2} dominance
* @property {BtcCentsSatsUsdPattern} total
*/
/**
* Create a DeltaDominanceTotalPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {DeltaDominanceTotalPattern}
*/
function createDeltaDominanceTotalPattern(client, acc) {
return {
delta: createAbsoluteRatePattern(client, _m(acc, 'delta')),
dominance: createBpsPercentRatioPattern2(client, _m(acc, 'dominance')),
total: createBtcCentsSatsUsdPattern(client, acc),
};
}
/**
* @typedef {Object} GreedNetPainPattern
* @property {CentsUsdPattern3} greedIndex
@@ -4462,25 +4406,6 @@ function createCoindaysTransferPattern(client, acc) {
};
}
/**
* @typedef {Object} DeltaTotalPattern
* @property {AbsoluteRatePattern} delta
* @property {BtcCentsSatsUsdPattern} total
*/
/**
* Create a DeltaTotalPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {DeltaTotalPattern}
*/
function createDeltaTotalPattern(client, acc) {
return {
delta: createAbsoluteRatePattern(client, _m(acc, 'delta')),
total: createBtcCentsSatsUsdPattern(client, acc),
};
}
/**
* @typedef {Object} FundedTotalPattern
* @property {AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4} funded
@@ -6300,7 +6225,7 @@ function createTransferPattern(client, acc) {
/**
* @typedef {Object} SeriesTree_Cohorts_Utxo_All
* @property {SeriesTree_Cohorts_Utxo_All_Supply} supply
* @property {DeltaDominanceHalfInTotalPattern2} supply
* @property {SeriesTree_Cohorts_Utxo_All_Outputs} outputs
* @property {SeriesTree_Cohorts_Utxo_All_Activity} activity
* @property {SeriesTree_Cohorts_Utxo_All_Realized} realized
@@ -6309,15 +6234,6 @@ function createTransferPattern(client, acc) {
* @property {InPattern} investedCapital
*/
/**
* @typedef {Object} SeriesTree_Cohorts_Utxo_All_Supply
* @property {BtcCentsSatsUsdPattern} total
* @property {AbsoluteRatePattern} delta
* @property {BtcCentsSatsUsdPattern} half
* @property {BtcCentsSatsToUsdPattern2} inProfit
* @property {BtcCentsSatsToUsdPattern2} inLoss
*/
/**
* @typedef {Object} SeriesTree_Cohorts_Utxo_All_Outputs
* @property {BaseDeltaPattern} unspentCount
@@ -6516,7 +6432,7 @@ function createTransferPattern(client, acc) {
/**
* @typedef {Object} SeriesTree_Cohorts_Utxo_Sth
* @property {DeltaHalfInToTotalPattern2} supply
* @property {DeltaDominanceHalfInTotalPattern2} supply
* @property {SpendingSpentUnspentPattern} outputs
* @property {CoindaysCoinyearsDormancyTransferPattern} activity
* @property {SeriesTree_Cohorts_Utxo_Sth_Realized} realized
@@ -6639,7 +6555,7 @@ function createTransferPattern(client, acc) {
/**
* @typedef {Object} SeriesTree_Cohorts_Utxo_Lth
* @property {DeltaHalfInToTotalPattern2} supply
* @property {DeltaDominanceHalfInTotalPattern2} supply
* @property {SpendingSpentUnspentPattern} outputs
* @property {CoindaysCoinyearsDormancyTransferPattern} activity
* @property {SeriesTree_Cohorts_Utxo_Lth_Realized} realized
@@ -9387,13 +9303,7 @@ class BrkClient extends BrkClientBase {
cohorts: {
utxo: {
all: {
supply: {
total: createBtcCentsSatsUsdPattern(this, 'supply'),
delta: createAbsoluteRatePattern(this, 'supply_delta'),
half: createBtcCentsSatsUsdPattern(this, 'supply_half'),
inProfit: createBtcCentsSatsToUsdPattern2(this, 'supply_in_profit'),
inLoss: createBtcCentsSatsToUsdPattern2(this, 'supply_in_loss'),
},
supply: createDeltaDominanceHalfInTotalPattern2(this, 'supply'),
outputs: {
unspentCount: createBaseDeltaPattern(this, 'utxo_count'),
spentCount: createAverageBlockCumulativeSumPattern2(this, 'spent_utxo_count'),
@@ -9547,7 +9457,7 @@ class BrkClient extends BrkClientBase {
investedCapital: createInPattern(this, 'invested_capital_in'),
},
sth: {
supply: createDeltaHalfInToTotalPattern2(this, 'sth_supply'),
supply: createDeltaDominanceHalfInTotalPattern2(this, 'sth_supply'),
outputs: createSpendingSpentUnspentPattern(this, 'sth'),
activity: createCoindaysCoinyearsDormancyTransferPattern(this, 'sth'),
realized: {
@@ -9647,7 +9557,7 @@ class BrkClient extends BrkClientBase {
investedCapital: createInPattern(this, 'sth_invested_capital_in'),
},
lth: {
supply: createDeltaHalfInToTotalPattern2(this, 'lth_supply'),
supply: createDeltaDominanceHalfInTotalPattern2(this, 'lth_supply'),
outputs: createSpendingSpentUnspentPattern(this, 'lth'),
activity: createCoindaysCoinyearsDormancyTransferPattern(this, 'lth'),
realized: {
+26 -69
View File
@@ -2930,7 +2930,7 @@ class ActivityAddrOutputsRealizedSupplyUnrealizedPattern:
self.addr_count: BaseDeltaPattern = BaseDeltaPattern(client, _m(acc, 'addr_count'))
self.outputs: SpendingSpentUnspentPattern = SpendingSpentUnspentPattern(client, acc)
self.realized: CapLossMvrvPriceProfitPattern = CapLossMvrvPriceProfitPattern(client, acc)
self.supply: DeltaTotalPattern = DeltaTotalPattern(client, _m(acc, 'supply'))
self.supply: DeltaDominanceTotalPattern = DeltaDominanceTotalPattern(client, _m(acc, 'supply'))
self.unrealized: NuplPattern = NuplPattern(client, _m(acc, 'nupl'))
class AverageBlockCumulativeInSumPattern:
@@ -2957,18 +2957,6 @@ class BpsCentsPercentilesRatioSatsUsdPattern:
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, _m(acc, 'sats'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc)
class BtcCentsSatsToUsdPattern3:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.btc: SeriesPattern1[Bitcoin] = SeriesPattern1(client, acc)
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents'))
self.sats: SeriesPattern1[Sats] = SeriesPattern1(client, _m(acc, 'sats'))
self.to_circulating: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'to_circulating'))
self.to_own: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'to_own'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, 'usd'))
class CentsNegativeToUsdPattern2:
"""Pattern struct for repeated tree structure."""
@@ -2981,28 +2969,28 @@ class CentsNegativeToUsdPattern2:
self.to_own_mcap: BpsPercentRatioPattern4 = BpsPercentRatioPattern4(client, _m(acc, 'to_own_mcap'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc)
class DeltaHalfInToTotalPattern:
class DeltaDominanceHalfInTotalPattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta'))
self.dominance: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'dominance'))
self.half: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'half'))
self.in_loss: BtcCentsSatsToUsdPattern = BtcCentsSatsToUsdPattern(client, _m(acc, 'in_loss'))
self.in_profit: BtcCentsSatsToUsdPattern = BtcCentsSatsToUsdPattern(client, _m(acc, 'in_profit'))
self.to_circulating: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'to_circulating'))
self.in_loss: BtcCentsSatsShareUsdPattern = BtcCentsSatsShareUsdPattern(client, _m(acc, 'in_loss'))
self.in_profit: BtcCentsSatsShareUsdPattern = BtcCentsSatsShareUsdPattern(client, _m(acc, 'in_profit'))
self.total: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, acc)
class DeltaHalfInToTotalPattern2:
class DeltaDominanceHalfInTotalPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta'))
self.dominance: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'dominance'))
self.half: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'half'))
self.in_loss: BtcCentsSatsToUsdPattern3 = BtcCentsSatsToUsdPattern3(client, _m(acc, 'in_loss'))
self.in_profit: BtcCentsSatsToUsdPattern3 = BtcCentsSatsToUsdPattern3(client, _m(acc, 'in_profit'))
self.to_circulating: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'to_circulating'))
self.in_loss: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'in_loss'))
self.in_profit: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'in_profit'))
self.total: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, acc)
class _1m1w1y24hBlockPattern:
@@ -3035,7 +3023,7 @@ class ActivityOutputsRealizedSupplyUnrealizedPattern:
self.activity: CoindaysTransferPattern = CoindaysTransferPattern(client, acc)
self.outputs: SpendingSpentUnspentPattern = SpendingSpentUnspentPattern(client, acc)
self.realized: CapLossMvrvNetPriceProfitSoprPattern = CapLossMvrvNetPriceProfitSoprPattern(client, acc)
self.supply: DeltaHalfInToTotalPattern = DeltaHalfInToTotalPattern(client, _m(acc, 'supply'))
self.supply: DeltaDominanceHalfInTotalPattern = DeltaDominanceHalfInTotalPattern(client, _m(acc, 'supply'))
self.unrealized: LossNetNuplProfitPattern = LossNetNuplProfitPattern(client, acc)
class ActivityOutputsRealizedSupplyUnrealizedPattern3:
@@ -3046,7 +3034,7 @@ class ActivityOutputsRealizedSupplyUnrealizedPattern3:
self.activity: TransferPattern = TransferPattern(client, _m(acc, 'transfer_volume'))
self.outputs: SpendingSpentUnspentPattern = SpendingSpentUnspentPattern(client, acc)
self.realized: CapLossMvrvPriceProfitPattern = CapLossMvrvPriceProfitPattern(client, acc)
self.supply: DeltaHalfInTotalPattern2 = DeltaHalfInTotalPattern2(client, _m(acc, 'supply'))
self.supply: DeltaDominanceHalfInTotalPattern = DeltaDominanceHalfInTotalPattern(client, _m(acc, 'supply'))
self.unrealized: LossNuplProfitPattern = LossNuplProfitPattern(client, acc)
class ActivityOutputsRealizedSupplyUnrealizedPattern2:
@@ -3057,7 +3045,7 @@ class ActivityOutputsRealizedSupplyUnrealizedPattern2:
self.activity: TransferPattern = TransferPattern(client, _m(acc, 'transfer_volume'))
self.outputs: SpendingSpentUnspentPattern = SpendingSpentUnspentPattern(client, acc)
self.realized: CapLossMvrvPriceProfitPattern = CapLossMvrvPriceProfitPattern(client, acc)
self.supply: DeltaTotalPattern = DeltaTotalPattern(client, _m(acc, 'supply'))
self.supply: DeltaDominanceTotalPattern = DeltaDominanceTotalPattern(client, _m(acc, 'supply'))
self.unrealized: NuplPattern = NuplPattern(client, _m(acc, 'nupl'))
class BlockChangeCumulativeDeltaSumPattern:
@@ -3093,7 +3081,7 @@ class BtcCentsDeltaSatsUsdPattern:
self.sats: SeriesPattern1[Sats] = SeriesPattern1(client, _m(acc, 'sats'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, 'usd'))
class BtcCentsSatsToUsdPattern:
class BtcCentsSatsShareUsdPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
@@ -3101,18 +3089,7 @@ class BtcCentsSatsToUsdPattern:
self.btc: SeriesPattern1[Bitcoin] = SeriesPattern1(client, acc)
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents'))
self.sats: SeriesPattern1[Sats] = SeriesPattern1(client, _m(acc, 'sats'))
self.to_circulating: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'to_circulating'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, 'usd'))
class BtcCentsSatsToUsdPattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.btc: SeriesPattern1[Bitcoin] = SeriesPattern1(client, acc)
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents'))
self.sats: SeriesPattern1[Sats] = SeriesPattern1(client, _m(acc, 'sats'))
self.to_own: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'to_own'))
self.share: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'share'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, 'usd'))
class CapLossMvrvPriceProfitPattern:
@@ -3137,17 +3114,6 @@ class CentsToUsdPattern4:
self.to_own_mcap: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'to_own_mcap'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc)
class DeltaHalfInTotalPattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta'))
self.half: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'half'))
self.in_loss: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'in_loss'))
self.in_profit: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'in_profit'))
self.total: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, acc)
class EmaHistogramLineSignalPattern:
"""Pattern struct for repeated tree structure."""
pass
@@ -3494,6 +3460,15 @@ class CumulativeRollingSumPattern:
self.rolling: AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern = AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc)
self.sum: SeriesPattern18[StoredU64] = SeriesPattern18(client, _m(acc, 'sum'))
class DeltaDominanceTotalPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta'))
self.dominance: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'dominance'))
self.total: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, acc)
class GreedNetPainPattern:
"""Pattern struct for repeated tree structure."""
@@ -3676,14 +3651,6 @@ class CoindaysTransferPattern:
self.coindays_destroyed: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, _m(acc, 'coindays_destroyed'))
self.transfer_volume: AverageBlockCumulativeInSumPattern = AverageBlockCumulativeInSumPattern(client, _m(acc, 'transfer_volume'))
class DeltaTotalPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta'))
self.total: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, acc)
class FundedTotalPattern:
"""Pattern struct for repeated tree structure."""
@@ -5479,16 +5446,6 @@ class SeriesTree_Supply:
self.market_minus_realized_cap_growth_rate: _1m1w1y24hPattern[BasisPointsSigned32] = _1m1w1y24hPattern(client, 'market_minus_realized_cap_growth_rate')
self.hodled_or_lost: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'hodled_or_lost_supply')
class SeriesTree_Cohorts_Utxo_All_Supply:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.total: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'supply')
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, 'supply_delta')
self.half: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'supply_half')
self.in_profit: BtcCentsSatsToUsdPattern2 = BtcCentsSatsToUsdPattern2(client, 'supply_in_profit')
self.in_loss: BtcCentsSatsToUsdPattern2 = BtcCentsSatsToUsdPattern2(client, 'supply_in_loss')
class SeriesTree_Cohorts_Utxo_All_Outputs:
"""Series tree node."""
@@ -5706,7 +5663,7 @@ class SeriesTree_Cohorts_Utxo_All:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.supply: SeriesTree_Cohorts_Utxo_All_Supply = SeriesTree_Cohorts_Utxo_All_Supply(client)
self.supply: DeltaDominanceHalfInTotalPattern2 = DeltaDominanceHalfInTotalPattern2(client, 'supply')
self.outputs: SeriesTree_Cohorts_Utxo_All_Outputs = SeriesTree_Cohorts_Utxo_All_Outputs(client)
self.activity: SeriesTree_Cohorts_Utxo_All_Activity = SeriesTree_Cohorts_Utxo_All_Activity(client)
self.realized: SeriesTree_Cohorts_Utxo_All_Realized = SeriesTree_Cohorts_Utxo_All_Realized(client)
@@ -5837,7 +5794,7 @@ class SeriesTree_Cohorts_Utxo_Sth:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.supply: DeltaHalfInToTotalPattern2 = DeltaHalfInToTotalPattern2(client, 'sth_supply')
self.supply: DeltaDominanceHalfInTotalPattern2 = DeltaDominanceHalfInTotalPattern2(client, 'sth_supply')
self.outputs: SpendingSpentUnspentPattern = SpendingSpentUnspentPattern(client, 'sth')
self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'sth')
self.realized: SeriesTree_Cohorts_Utxo_Sth_Realized = SeriesTree_Cohorts_Utxo_Sth_Realized(client)
@@ -5975,7 +5932,7 @@ class SeriesTree_Cohorts_Utxo_Lth:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.supply: DeltaHalfInToTotalPattern2 = DeltaHalfInToTotalPattern2(client, 'lth_supply')
self.supply: DeltaDominanceHalfInTotalPattern2 = DeltaDominanceHalfInTotalPattern2(client, 'lth_supply')
self.outputs: SpendingSpentUnspentPattern = SpendingSpentUnspentPattern(client, 'lth')
self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'lth')
self.realized: SeriesTree_Cohorts_Utxo_Lth_Realized = SeriesTree_Cohorts_Utxo_Lth_Realized(client)
+1
View File
@@ -0,0 +1 @@
3d.html
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+89 -65
View File
@@ -1,14 +1,15 @@
import { brk } from "../utils/client.js";
import { createCube } from "./cube.js";
import { createHeightElement, formatFeeRate } from "./render.js";
const LOOKAHEAD = 15;
/** @type {HTMLDivElement} */ let chainEl;
/** @type {HTMLDivElement} */ let blocksEl;
/** @type {HTMLDivElement | null} */ let selectedCube = null;
/** @type {HTMLAnchorElement | null} */ let selectedCube = null;
/** @type {IntersectionObserver} */ let olderObserver;
/** @type {(block: BlockInfoV1) => void} */ let onSelect = () => {};
/** @type {(cube: HTMLDivElement) => void} */ let onCubeClick = () => {};
/** @type {(cube: HTMLAnchorElement) => void} */ let onCubeClick = () => {};
/** @type {Map<BlockHash, BlockInfoV1>} */
const blocksByHash = new Map();
@@ -21,7 +22,7 @@ let reachedTip = false;
/**
* @param {HTMLElement} parent
* @param {{ onSelect: (block: BlockInfoV1) => void, onCubeClick: (cube: HTMLDivElement) => void }} callbacks
* @param {{ onSelect: (block: BlockInfoV1) => void, onCubeClick: (cube: HTMLAnchorElement) => void }} callbacks
*/
export function initChain(parent, callbacks) {
onSelect = callbacks.onSelect;
@@ -60,11 +61,11 @@ export function initChain(parent, callbacks) {
function findCube(hashOrHeight) {
if (hashOrHeight == null) {
return reachedTip && newestHeight >= 0
? /** @type {HTMLDivElement | null} */ (blocksEl.lastElementChild)
? /** @type {HTMLAnchorElement | null} */ (blocksEl.lastElementChild)
: null;
}
const attr = typeof hashOrHeight === "number" ? "height" : "hash";
return /** @type {HTMLDivElement | null} */ (
return /** @type {HTMLAnchorElement | null} */ (
blocksEl.querySelector(`[data-${attr}="${hashOrHeight}"]`)
);
}
@@ -74,7 +75,7 @@ export function deselectCube() {
selectedCube = null;
}
/** @param {HTMLDivElement} cube @param {{ scroll?: "smooth" | "instant", silent?: boolean }} [opts] */
/** @param {HTMLAnchorElement} cube @param {{ scroll?: "smooth" | "instant", silent?: boolean }} [opts] */
export function selectCube(cube, { scroll, silent } = {}) {
const changed = cube !== selectedCube;
if (changed) {
@@ -181,7 +182,7 @@ export async function goToCube(hashOrHeight, { silent } = {}) {
} catch (e) {
try { startHash = await loadInitial(null); } catch (_) { return; }
}
selectCube(/** @type {HTMLDivElement} */ (findCube(startHash)), { scroll: "instant", silent });
selectCube(/** @type {HTMLAnchorElement} */ (findCube(startHash)), { scroll: "instant", silent });
}
export async function poll() {
@@ -223,80 +224,103 @@ async function loadNewer() {
loadingNewer = false;
}
/** @param {string} name */
const poolSlug = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, "");
const MONTHS = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
/** @param {number} unixSec */
function formatShortDate(unixSec) {
const d = new Date(unixSec * 1000);
return `${MONTHS[d.getMonth()]} ${d.getDate()}`;
}
/** @param {number} unixSec */
function formatHHMM(unixSec) {
const d = new Date(unixSec * 1000);
return [String(d.getHours()).padStart(2, "0"), String(d.getMinutes()).padStart(2, "0")];
}
/** @param {string} text @param {string} [cls] */
function span(text, cls) {
const s = document.createElement("span");
if (cls) s.classList.add(cls);
s.textContent = text;
return s;
}
/** @param {BlockInfoV1} block */
function createBlockCube(block) {
const { cubeElement, leftFaceElement, rightFaceElement, topFaceElement } =
createCube();
const cubeElement = document.createElement("a");
cubeElement.classList.add("cube");
cubeElement.href = `/block/${block.id}`;
cubeElement.dataset.hash = block.id;
cubeElement.dataset.height = String(block.height);
cubeElement.dataset.timestamp = String(block.timestamp);
cubeElement.style.setProperty("--fill", String(Math.min(1, block.weight / 3_990_000)));
const fill = Math.min(1, block.weight / 3_990_000);
const { topFace, rightFace, leftFace } = createCube(cubeElement, fill);
blocksByHash.set(block.id, block);
cubeElement.addEventListener("click", () => onCubeClick(cubeElement));
// Intercept plain left-clicks for SPA nav; let modified clicks
// (cmd/ctrl/shift/middle) and right-click fall through so the
// anchor's native open-in-new-tab / context-menu behavior works.
cubeElement.addEventListener("click", (e) => {
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
e.preventDefault();
onCubeClick(cubeElement);
});
const heightEl = document.createElement("p");
heightEl.append(createHeightElement(block.height));
rightFaceElement.append(heightEl);
const extras = block.extras;
const minerName = extras ? extras.pool.name : "Unknown";
const medianFee = extras ? extras.medianFee : 0;
const feeRange = extras ? extras.feeRange : [0, 0, 0, 0, 0, 0, 0];
// Top: short date / HH:MM (colon dimmed).
const dateP = document.createElement("p");
dateP.textContent = formatShortDate(block.timestamp);
const [hh, mm] = formatHHMM(block.timestamp);
const timeP = document.createElement("p");
timeP.append(hh, span(":", "dim"), mm);
topFace.append(dateP, timeP);
// Right: block height / raw pool-logo + miner name.
const heightP = document.createElement("p");
heightP.classList.add("height");
heightP.append(createHeightElement(block.height));
const poolDiv = document.createElement("div");
poolDiv.classList.add("pool");
const logo = document.createElement("img");
logo.src = `/assets/pools/${poolSlug(minerName)}.svg`;
logo.alt = "";
logo.onerror = () => {
logo.onerror = null;
logo.src = "/assets/pools/default.svg";
};
const nameSpan = document.createElement("span");
nameSpan.textContent = minerName.replace(/\s+(Pool|USA)$/i, "").trim();
poolDiv.append(logo, nameSpan);
rightFace.append(heightP, poolDiv);
// Left: ~median / min-max / sat/vB fees stack.
const feesEl = document.createElement("div");
feesEl.classList.add("fees");
leftFaceElement.append(feesEl);
const extras = block.extras;
const medianFee = extras ? extras.medianFee : 0;
const feeRange = extras ? extras.feeRange : [0, 0, 0, 0, 0, 0, 0];
const avg = document.createElement("p");
avg.innerHTML = `~${formatFeeRate(medianFee)}`;
feesEl.append(avg);
avg.textContent = `~${formatFeeRate(medianFee)}`;
const range = document.createElement("p");
const min = document.createElement("span");
min.innerHTML = formatFeeRate(feeRange[0]);
const dash = document.createElement("span");
dash.classList.add("dim");
dash.innerHTML = `-`;
const max = document.createElement("span");
max.innerHTML = formatFeeRate(feeRange[6]);
range.append(min, dash, max);
feesEl.append(range);
range.append(
formatFeeRate(feeRange[0]),
span("-", "dim"),
formatFeeRate(feeRange[6]),
);
const unit = document.createElement("p");
unit.classList.add("dim");
unit.innerHTML = `sat/vB`;
feesEl.append(unit);
const miner = document.createElement("span");
miner.innerHTML = extras ? extras.pool.name : "Unknown";
topFaceElement.append(miner);
unit.textContent = "sat/vB";
feesEl.append(avg, range, unit);
leftFace.append(feesEl);
return cubeElement;
}
function createCube() {
const cubeElement = document.createElement("div");
cubeElement.classList.add("cube");
const bottomElement = document.createElement("div");
bottomElement.classList.add("face", "bottom");
cubeElement.append(bottomElement);
const rearRightElement = document.createElement("div");
rearRightElement.classList.add("face", "rear-right");
cubeElement.append(rearRightElement);
const rearLeftElement = document.createElement("div");
rearLeftElement.classList.add("face", "rear-left");
cubeElement.append(rearLeftElement);
const innerTopElement = document.createElement("div");
innerTopElement.classList.add("face", "inner-top");
cubeElement.append(innerTopElement);
const rightFaceElement = document.createElement("div");
rightFaceElement.classList.add("face", "right");
cubeElement.append(rightFaceElement);
const leftFaceElement = document.createElement("div");
leftFaceElement.classList.add("face", "left");
cubeElement.append(leftFaceElement);
const topFaceElement = document.createElement("div");
topFaceElement.classList.add("face", "top");
cubeElement.append(topFaceElement);
return { cubeElement, leftFaceElement, rightFaceElement, topFaceElement };
}
/** @param {HTMLElement} cube */
function setGap(cube) {
const prev = /** @type {HTMLElement | null} */ (cube.previousElementSibling);
@@ -305,14 +329,14 @@ function setGap(cube) {
cube.style.setProperty("--dt", String(dt));
}
/** @param {HTMLDivElement} cube */
/** @param {HTMLAnchorElement} cube */
function prependCube(cube) {
const next = /** @type {HTMLElement | null} */ (blocksEl.firstElementChild);
blocksEl.prepend(cube);
if (next) setGap(next);
}
/** @param {HTMLDivElement} cube */
/** @param {HTMLAnchorElement} cube */
function appendCube(cube) {
blocksEl.append(cube);
setGap(cube);
+51
View File
@@ -0,0 +1,51 @@
/**
* HTML cube generator. Populates a .cube element with 15 face divs
* styled in explorer.css. Uses pure CSS transforms (no SVG); the
* earlier SVG-based implementation broke in Safari due to its
* long-standing bugs around SVG transforms on <foreignObject>.
*
* Face order = z-order:
* 3× .glass rear — translucent glass back faces
* 3× .liquid rear — opaque liquid backing (hidden at fill 0)
* 3× .liquid front — opaque liquid front (the visible 3 faces)
* 3× .glass front — translucent glass front
* 3× .face-text — text overlays (top / right / left)
*
* @param {HTMLElement} cube
* @param {number} [fill]
* @returns {{ topFace: HTMLDivElement, rightFace: HTMLDivElement, leftFace: HTMLDivElement }}
*/
export function createCube(cube, fill = 1) {
cube.style.setProperty("--fill", String(fill));
/** @param {...string} cls */
const face = (...cls) => {
const d = document.createElement("div");
d.className = `face ${cls.join(" ")}`;
return /** @type {HTMLDivElement} */ (d);
};
const topFace = face("face-text", "top");
const rightFace = face("face-text", "right");
const leftFace = face("face-text", "left");
cube.append(
face("glass", "bottom"),
face("glass", "rear-right"),
face("glass", "rear-left"),
face("liquid", "bottom"),
face("liquid", "rear-right"),
face("liquid", "rear-left"),
face("liquid", "right"),
face("liquid", "left"),
face("liquid", "top"),
face("glass", "right"),
face("glass", "left"),
face("glass", "top"),
rightFace,
leftFace,
topFace,
);
return { topFace, rightFace, leftFace };
}
+1 -2
View File
@@ -78,8 +78,7 @@ export function init(selected) {
showPanel("block");
},
onCubeClick: (cube) => {
const hash = cube.dataset.hash;
if (hash) history.pushState(null, "", `/block/${hash}`);
history.pushState(null, "", cube.href);
navigate();
selectCube(cube);
},
@@ -2,9 +2,9 @@
* Holdings section builders
*
* Supply pattern capabilities by cohort type:
* - DeltaHalfInRelTotalPattern2 (STH/LTH): inProfit + inLoss + toCirculating + toOwn
* - SeriesTree_Cohorts_Utxo_All_Supply (All): inProfit + inLoss + toOwn (no toCirculating)
* - DeltaHalfInRelTotalPattern (AgeRange/MaxAge/Epoch): inProfit + inLoss + toCirculating (no toOwn)
* - DeltaHalfInRelTotalPattern2 (STH/LTH): inProfit + inLoss + dominance + share
* - SeriesTree_Cohorts_Utxo_All_Supply (All): inProfit + inLoss + share (no dominance)
* - DeltaHalfInRelTotalPattern (AgeRange/MaxAge/Epoch): inProfit + inLoss + dominance (no share)
* - DeltaHalfInTotalPattern2 (Type.*): inProfit + inLoss (no rel)
* - DeltaHalfTotalPattern (Empty/UtxoAmount/AddrAmount): total + half only
*/
@@ -184,18 +184,18 @@ function profitabilityAmountChart(supply, title) {
}
/**
* Share chart: in profit / in loss as % of own supply.
* @param {{ inProfit: { toOwn: { percent: AnySeriesPattern, ratio: AnySeriesPattern } }, inLoss: { toOwn: { percent: AnySeriesPattern, ratio: AnySeriesPattern } } }} supply
* Composition chart: in profit / in loss as % of own supply.
* @param {{ inProfit: { share: { percent: AnySeriesPattern, ratio: AnySeriesPattern } }, inLoss: { share: { percent: AnySeriesPattern, ratio: AnySeriesPattern } } }} supply
* @param {(name: string) => string} title
* @returns {PartialChartOption}
*/
function profitabilityShareChart(supply, title) {
function profitabilityCompositionChart(supply, title) {
return {
name: "Share",
title: title("Supply Profitability"),
name: "Composition",
title: title("Supply Profitability Composition"),
bottom: [
...percentRatio({ pattern: supply.inProfit.toOwn, name: "In Profit", color: colors.profit }),
...percentRatio({ pattern: supply.inLoss.toOwn, name: "In Loss", color: colors.loss }),
...percentRatio({ pattern: supply.inProfit.share, name: "In Profit", color: colors.profit }),
...percentRatio({ pattern: supply.inLoss.share, name: "In Loss", color: colors.loss }),
priceLine({ number: 100, color: colors.default, style: 0, unit: Unit.percentage }),
priceLine({ number: 50, unit: Unit.percentage }),
],
@@ -204,19 +204,16 @@ function profitabilityShareChart(supply, title) {
/**
* @param {{ toCirculating: PercentRatioPattern, inProfit: { toCirculating: PercentRatioPattern }, inLoss: { toCirculating: PercentRatioPattern } }} supply
* @param {{ dominance: PercentRatioPattern }} supply
* @param {Color} color
* @param {(name: string) => string} title
* @returns {PartialChartOption}
*/
function circulatingChart(supply, title) {
function dominanceChart(supply, color, title) {
return {
name: "Dominance",
title: title("Supply Dominance"),
bottom: [
...percentRatio({ pattern: supply.toCirculating, name: "Total", color: colors.default }),
...percentRatio({ pattern: supply.inProfit.toCirculating, name: "In Profit", color: colors.profit }),
...percentRatio({ pattern: supply.inLoss.toCirculating, name: "In Loss", color: colors.loss }),
],
bottom: percentRatio({ pattern: supply.dominance, name: "Dominance", color }),
};
}
@@ -294,6 +291,7 @@ export function createHoldingsSection({ cohort, title }) {
title: title("Supply"),
bottom: simpleSupplySeries(supply),
},
dominanceChart(supply, cohort.color, title),
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
],
},
@@ -320,7 +318,7 @@ export function createHoldingsSectionAll({ cohort, title }) {
name: "Profitability",
tree: [
profitabilityAmountChart(supply, title),
profitabilityShareChart(supply, title),
profitabilityCompositionChart(supply, title),
],
},
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
@@ -346,12 +344,12 @@ export function createHoldingsSectionWithRelative({ cohort, title }) {
title: title("Supply"),
bottom: simpleSupplySeries(supply),
},
dominanceChart(supply, cohort.color, title),
{
name: "Profitability",
tree: [
profitabilityAmountChart(supply, title),
profitabilityShareChart(supply, title),
circulatingChart(supply, title),
profitabilityCompositionChart(supply, title),
],
},
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
@@ -376,12 +374,10 @@ export function createHoldingsSectionWithOwnSupply({ cohort, title }) {
title: title("Supply"),
bottom: simpleSupplySeries(supply),
},
dominanceChart(supply, cohort.color, title),
{
name: "Profitability",
tree: [
profitabilityAmountChart(supply, title),
circulatingChart(supply, title),
],
tree: [profitabilityAmountChart(supply, title)],
},
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
],
@@ -405,6 +401,7 @@ export function createHoldingsSectionWithProfitLoss({ cohort, title }) {
title: title("Supply"),
bottom: simpleSupplySeries(supply),
},
dominanceChart(supply, cohort.color, title),
{
name: "Profitability",
tree: [profitabilityAmountChart(supply, title)],
@@ -431,6 +428,7 @@ export function createHoldingsSectionAddress({ cohort, title }) {
title: title("Supply"),
bottom: simpleSupplySeries(supply),
},
dominanceChart(supply, cohort.color, title),
{
name: "Profitability",
tree: [profitabilityAmountChart(supply, title)],
@@ -458,6 +456,7 @@ export function createHoldingsSectionAddressAmount({ cohort, title }) {
title: title("Supply"),
bottom: simpleSupplySeries(supply),
},
dominanceChart(supply, cohort.color, title),
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
],
},
@@ -495,6 +494,22 @@ function groupedSupplyProfitLoss(list, all, title) {
];
}
/**
* @template {{ name: string, color: Color, tree: { supply: { dominance: PercentRatioPattern } } }} T
* @param {readonly T[]} list
* @param {(name: string) => string} title
* @returns {PartialChartOption}
*/
function groupedDominanceChart(list, title) {
return {
name: "Dominance",
title: title("Supply Dominance"),
bottom: flatMapCohorts(list, ({ name, color, tree }) =>
percentRatio({ pattern: tree.supply.dominance, name, color }),
),
};
}
// ============================================================================
// Grouped Cohort Holdings Sections
// ============================================================================
@@ -509,6 +524,7 @@ export function createGroupedHoldingsSectionAddress({ list, all, title }) {
name: "Supply",
tree: [
groupedSupplyTotal(list, all, title),
groupedDominanceChart(list, title),
{
name: "Profitability",
tree: groupedSupplyProfitLoss(list, all, title),
@@ -563,6 +579,7 @@ export function createGroupedHoldingsSectionAddressAmount({ list, all, title })
name: "Supply",
tree: [
groupedSupplyTotal(list, all, title),
groupedDominanceChart(list, title),
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
],
},
@@ -590,6 +607,7 @@ export function createGroupedHoldingsSection({ list, all, title }) {
name: "Supply",
tree: [
groupedSupplyTotal(list, all, title),
groupedDominanceChart(list, title),
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
],
},
@@ -604,7 +622,11 @@ export function createGroupedHoldingsSectionWithProfitLoss({ list, all, title })
name: "Supply",
tree: [
groupedSupplyTotal(list, all, title),
...groupedSupplyProfitLoss(list, all, title),
groupedDominanceChart(list, title),
{
name: "Profitability",
tree: groupedSupplyProfitLoss(list, all, title),
},
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
],
},
@@ -619,8 +641,11 @@ export function createGroupedHoldingsSectionWithOwnSupply({ list, all, title })
name: "Supply",
tree: [
groupedSupplyTotal(list, all, title),
...groupedSupplyProfitLoss(list, all, title),
{ name: "% of Circulating", title: title("Supply (% of Circulating)"), bottom: flatMapCohorts(list, ({ name, color, tree }) => percentRatio({ pattern: tree.supply.toCirculating, name, color })) },
groupedDominanceChart(list, title),
{
name: "Profitability",
tree: groupedSupplyProfitLoss(list, all, title),
},
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
],
},
@@ -629,7 +654,7 @@ export function createGroupedHoldingsSectionWithOwnSupply({ list, all, title })
}
/**
* Grouped holdings with full relative series (toCirculating + toOwn)
* Grouped holdings with full relative series (dominance + share)
* For: CohortFull, CohortLongTerm
* @param {{ list: readonly (CohortFull | CohortLongTerm)[], all: CohortAll, title: (name: string) => string }} args
* @returns {PartialOptionsTree}
@@ -640,9 +665,20 @@ export function createGroupedHoldingsSectionWithRelative({ list, all, title }) {
name: "Supply",
tree: [
groupedSupplyTotal(list, all, title),
...groupedSupplyProfitLoss(list, all, title),
{ name: "% of Circulating", title: title("Supply (% of Circulating)"), bottom: flatMapCohorts(list, ({ name, color, tree }) => percentRatio({ pattern: tree.supply.toCirculating, name, color })) },
{ name: "% of Own Supply", title: title("Supply (% of Own)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.supply.inProfit.toOwn.percent, name, color, unit: Unit.percentage })) },
groupedDominanceChart(list, title),
{
name: "Profitability",
tree: [
...groupedSupplyProfitLoss(list, all, title),
{
name: "Composition",
tree: [
{ name: "In Profit", title: title("Supply In Profit Composition"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.supply.inProfit.share.percent, name, color, unit: Unit.percentage })) },
{ name: "In Loss", title: title("Supply In Loss Composition"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.supply.inLoss.share.percent, name, color, unit: Unit.percentage })) },
],
},
],
},
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
],
},
@@ -92,11 +92,11 @@ export function createValuationSectionFull({ cohort, title }) {
],
},
{
name: "Share",
title: title("Invested Capital Profitability"),
name: "Composition",
title: title("Invested Capital Composition"),
bottom: [
...percentRatio({ pattern: tree.investedCapital.inProfit.toOwn, name: "In Profit", color: colors.profit }),
...percentRatio({ pattern: tree.investedCapital.inLoss.toOwn, name: "In Loss", color: colors.loss }),
...percentRatio({ pattern: tree.investedCapital.inProfit.share, name: "In Profit", color: colors.profit }),
...percentRatio({ pattern: tree.investedCapital.inLoss.share, name: "In Loss", color: colors.loss }),
priceLine({ number: 100, color: colors.default, style: 0, unit: Unit.percentage }),
priceLine({ number: 50, unit: Unit.percentage }),
],
+2 -2
View File
@@ -256,7 +256,7 @@ summary {
&,
*,
&::after {
color: var(--color) !important;
color: var(--color);
}
}
@@ -264,7 +264,7 @@ summary {
&,
*,
&::after {
color: var(--orange) !important;
color: var(--orange);
}
}
}
+184 -135
View File
@@ -15,6 +15,28 @@
}
--cube: 4.5rem;
--face-step: 0.033;
/* Cube face-color derivations, resolved once at the #explorer
level so .cube state changes (hover / selected) don't force
Safari to re-evaluate `oklch(from var …)` on every paint — a
known source of jank there. Each interaction state gets its own
set; the .cube rule below just swaps which set --face-right /
--face-left / --face-top / --face-bottom reference. */
--cube-neutral-right: light-dark(oklch(from var(--light-gray) calc(l - var(--face-step) * 2) c h), var(--dark-gray));
--cube-neutral-left: light-dark(oklch(from var(--light-gray) calc(l - var(--face-step)) c h), oklch(from var(--dark-gray) calc(l + var(--face-step)) c h));
--cube-neutral-top: light-dark(var(--light-gray), oklch(from var(--dark-gray) calc(l + var(--face-step) * 2) c h));
--cube-neutral-bottom: oklch(from var(--border-color) calc(l - var(--face-step) * 3) c h);
--cube-hover-right: light-dark(oklch(from var(--dark-gray) calc(l - var(--face-step) * 2) c h), var(--light-gray));
--cube-hover-left: light-dark(oklch(from var(--dark-gray) calc(l - var(--face-step)) c h), oklch(from var(--light-gray) calc(l + var(--face-step)) c h));
--cube-hover-top: light-dark(var(--dark-gray), oklch(from var(--light-gray) calc(l + var(--face-step) * 2) c h));
--cube-hover-bottom: oklch(from var(--inv-border-color) calc(l - var(--face-step) * 3) c h);
--cube-selected-right: light-dark(oklch(from var(--orange) calc(l - var(--face-step) * 2) c h), var(--orange));
--cube-selected-left: light-dark(oklch(from var(--orange) calc(l - var(--face-step)) c h), oklch(from var(--orange) calc(l + var(--face-step)) c h));
--cube-selected-top: light-dark(var(--orange), oklch(from var(--orange) calc(l + var(--face-step) * 2) c h));
--cube-selected-bottom: oklch(from var(--orange) calc(l - var(--face-step) * 3) c h);
> * {
padding: 0 var(--main-padding);
@@ -48,7 +70,6 @@
margin-top: calc(var(--cube) * -0.25);
@media (max-width: 767px) {
--min-gap: 0rem;
--max-gap: calc(var(--cube) * 1.5);
flex-direction: row-reverse;
height: 11.5rem;
@@ -72,178 +93,206 @@
--block-gap: calc(
var(--min-gap) + var(--t) * (var(--max-gap) - var(--min-gap))
);
--empty-alpha: 0.3;
--iso-scale: 0.866;
/* Iso projection constants. Changing these reshapes the whole
cube — they drive the per-face transforms below. */
--iso-scale: cos(30deg);
--ox: 0.3;
--oy: 0.6;
--fill-pct: calc(var(--fill, 1) * 100%);
--face-step: 0.033;
--face-right-color: light-dark(
oklch(from var(--face-color) calc(l - var(--face-step) * 2) c h),
var(--face-color)
);
--face-left-color: light-dark(
oklch(from var(--face-color) calc(l - var(--face-step)) c h),
oklch(from var(--face-color) calc(l + var(--face-step)) c h)
);
--face-top-color: light-dark(
var(--face-color),
oklch(from var(--face-color) calc(l + var(--face-step) * 2) c h)
);
--empty-alpha: 0.4;
/* Face colors reference the precomputed sets on #explorer
(see top of file). Hover / selected rules just switch which
set each --face-* points at. */
--face-right: var(--cube-neutral-right);
--face-left: var(--cube-neutral-left);
--face-top: var(--cube-neutral-top);
--face-bottom: var(--cube-neutral-bottom);
/* Fill-driven state. --liquid-y is the liquid's vertical scale;
--glass-y the glass-above-liquid's. --is-full / --is-empty
round to 0 or 1 and drive opacity so scaled-to-0 faces
vanish cleanly (no hairline AA seams). */
--liquid-y: calc(var(--iso-scale) * var(--fill));
--glass-y: calc(var(--iso-scale) * (1 - var(--fill)));
--is-full: round(down, calc(var(--fill) + 0.0025), 1);
--is-empty: round(down, calc(1.0025 - var(--fill)), 1);
margin-left: calc(var(--cube) * -0.25);
flex-shrink: 0;
position: relative;
cursor: pointer;
width: var(--cube);
height: var(--cube);
font-size: var(--font-size-sm);
line-height: var(--line-height-sm);
--face-color: var(--border-color);
/* .cube is an <a>; reset the global anchor styles in
elements.css that would clip the iso silhouette
(overflow:hidden) and underline the empty link. */
overflow: visible;
text-decoration: none;
--state-ease: 50ms cubic-bezier(0.4, 0, 0.2, 1);
color: var(--color);
transition-property: color, background-color;
transition-duration: 50ms;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition: color var(--state-ease);
user-select: none;
pointer-events: none;
&:hover {
--face-color: var(--inv-border-color);
color: var(--background-color);
--face-right: var(--cube-hover-right);
--face-left: var(--cube-hover-left);
--face-top: var(--cube-hover-top);
--face-bottom: var(--cube-hover-bottom);
}
&:active,
&.selected {
color: var(--black);
--face-color: var(--orange);
--face-right: var(--cube-selected-right);
--face-left: var(--cube-selected-left);
--face-top: var(--cube-selected-top);
--face-bottom: var(--cube-selected-bottom);
}
> * {
/* Skeleton state (cube painted but data is stale while a new
chunk loads): hide text AND the pool logo. Using visibility
rather than color:transparent so the raw <img> logo hides too. */
&.skeleton .face-text { visibility: hidden; }
/* Shared face-transform template. Each face div sets --orient,
--x, --y, --sx, --sy and its role (liquid/glass/face-text)
supplies --y-offset. Faces extend outside .cube's layout box
— the iso silhouette spans ~2·iso·cube × 2·cube, offset into
what would be the next cube's space. Clicks land only on the
transformed face rectangles, not the .cube's empty corners. */
.face {
position: absolute;
transform-origin: 0 0;
box-sizing: border-box;
width: var(--cube);
height: var(--cube);
transform: var(--orient)
translate(calc(var(--cube) * var(--x)), calc(var(--cube) * var(--y)))
scale(var(--sx, 1), var(--sy));
pointer-events: auto;
}
.face {
transform-origin: 0 0;
position: absolute;
width: var(--cube);
height: var(--cube);
/* Roles:
.liquid opaque liquid (scales with fill)
.glass translucent glass shell
.face-text text overlay spanning the full rhombus
will-change is on the painted roles only (not .face-text,
whose background never changes) so each liquid/glass gets its
own compositor layer for snappy hover/select repaints. */
.liquid, .glass {
will-change: background-color;
transition: background-color var(--state-ease);
}
.liquid {
background: var(--fc);
opacity: calc(1 - var(--is-empty));
--sy: var(--liquid-y);
--y-offset: var(--glass-y);
}
.glass {
background: oklch(from var(--fc) l c h / var(--empty-alpha));
--sy: var(--glass-y);
--y-offset: 0;
}
.glass.top { opacity: calc(1 - var(--is-full)); }
.right,
.left,
.top {
.face-text {
--sy: var(--iso-scale);
--y-offset: 0;
pointer-events: none;
padding: 0.1rem;
backdrop-filter: blur(4px);
font-family: var(--font-mono);
font-size: var(--font-size-xs);
font-weight: 450;
}
.right,
.left,
.rear-right,
.rear-left {
background: linear-gradient(
to top,
var(--fc) var(--fill-pct),
oklch(from var(--fc) l c h / var(--empty-alpha)) var(--fill-pct)
);
}
.bottom {
background-color: var(--face-color);
transform: rotate(30deg) skew(-30deg)
translate(
calc(var(--cube) * (var(--ox) + 1 + var(--oy) / var(--iso-scale))),
calc(var(--cube) * var(--oy))
)
scaleY(var(--iso-scale));
}
.rear-right {
--fc: var(--face-left-color);
transform: rotate(30deg) skewX(30deg)
translate(
calc(var(--cube) * (var(--ox) + 1)),
calc(var(--cube) * (var(--oy) - var(--iso-scale)))
)
scaleY(var(--iso-scale));
}
.rear-left {
--fc: var(--face-top-color);
transform: rotate(-30deg) skewX(-30deg)
translate(
calc(var(--cube) * (var(--ox) + 1)),
calc(var(--cube) * (var(--ox) * var(--iso-scale) + var(--oy)))
)
scale(-1, var(--iso-scale));
}
.inner-top {
background-color: var(--face-top-color);
transform: rotate(30deg) skew(-30deg)
translate(
calc(var(--cube) * (var(--ox) + 1 + var(--oy) / var(--iso-scale) - var(--fill, 1))),
calc(var(--cube) * (var(--oy) - var(--iso-scale) * var(--fill, 1)))
)
scaleY(var(--iso-scale));
}
.right {
--fc: var(--face-right-color);
transform: rotate(-30deg) skewX(-30deg)
translate(
calc(var(--cube) * (var(--ox) + 1)),
calc(var(--cube) * ((var(--ox) + 1) * var(--iso-scale) + var(--oy)))
)
scaleY(var(--iso-scale));
}
.top {
--is-full: round(down, calc(var(--fill, 1) + 0.0025), 1);
background-color: oklch(
from var(--face-top-color) l c h /
calc(var(--empty-alpha) + var(--is-full) * (1 - var(--empty-alpha)))
);
transform: rotate(30deg) skew(-30deg)
translate(
calc(var(--cube) * (var(--ox) + var(--oy) / var(--iso-scale))),
calc(var(--cube) * (var(--oy) - var(--iso-scale)))
)
scaleY(var(--iso-scale));
justify-content: center;
.face-text.top,
.face-text.right {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
display: flex;
font-weight: 900;
text-transform: uppercase;
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
}
.left {
--fc: var(--face-left-color);
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
transform: rotate(30deg) skewX(30deg)
translate(
calc(var(--cube) * var(--ox)),
calc(var(--cube) * var(--oy))
)
scaleY(var(--iso-scale));
.face-text.top { justify-content: center; text-transform: uppercase; }
.face-text.right { justify-content: space-between; }
.face-text p { margin: 0; }
.face-text .height {
font-size: var(--font-size-sm);
font-weight: normal;
}
&.skeleton {
pointer-events: none;
.face {
color: transparent;
}
}
.fees {
.face-text .fees {
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
align-items: center;
}
/* Pool line: raw (un-tinted) logo + miner name, ellipsis-clipped. */
.face-text .pool {
display: flex;
align-items: center;
justify-content: center;
gap: 0.1em;
width: 100%;
}
.face-text .pool img {
width: 1.25em;
height: 1.25em;
flex-shrink: 0;
}
.face-text .pool span {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1;
}
/* Per-face geometry. The 3 unique orientations each cover two
faces (top+bottom share the iso-squash orient; right/left and
their rear twins share the side-face orients). --sy is only
shared on the top/bottom pair (always full iso rhombus);
side faces inherit --sy from their role (liquid/glass/face-text). */
.top, .bottom {
--orient: rotate(30deg) skewX(-30deg);
--sy: var(--iso-scale);
}
.right, .rear-left { --orient: rotate(-30deg) skewX(-30deg); }
.left, .rear-right { --orient: rotate(30deg) skewX(30deg); }
.bottom {
--fc: var(--face-bottom);
--x: calc(var(--ox) + 1 + var(--oy) / var(--iso-scale));
--y: var(--oy);
}
.top {
--fc: var(--face-top);
--x: calc(var(--ox) + var(--top-x-shift, 0) + var(--oy) / var(--iso-scale));
--y: calc(var(--oy) - var(--iso-scale) + var(--y-offset));
}
.liquid.top { --top-x-shift: calc(1 - var(--fill)); }
.right {
--fc: var(--face-right);
--x: calc(var(--ox) + 1);
--y: calc((var(--ox) + 1) * var(--iso-scale) + var(--oy) + var(--y-offset));
}
.left {
--fc: var(--face-left);
--x: var(--ox);
--y: calc(var(--oy) + var(--y-offset));
}
.rear-right {
--fc: var(--face-left);
--x: calc(var(--ox) + 1);
--y: calc(var(--oy) - var(--iso-scale) + var(--y-offset));
}
.rear-left {
--fc: var(--face-top);
--sx: -1;
--x: calc(var(--ox) + 1);
--y: calc(var(--ox) * var(--iso-scale) + var(--oy) + var(--y-offset));
}
& + & {
margin-bottom: var(--block-gap);