global: snapshot part 3

This commit is contained in:
nym21
2026-03-20 12:54:26 +01:00
parent b8e57f4788
commit 1d671ea41f
32 changed files with 1561 additions and 1249 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ use brk_cohort::{
};
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Indexes, Sats, Version};
use brk_types::{Cents, CentsSquaredSats, Dollars, Height, Indexes, Sats, Version};
use rayon::prelude::*;
use vecdb::{
AnyStoredVec, Database, Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode, WritableVec,
@@ -738,15 +738,9 @@ impl UTXOCohorts<Rw> {
.min()
.unwrap_or_default()
.min(Height::from(self.profitability.min_stateful_len()))
.min(Height::from(
self.all.metrics.realized.min_stateful_len(),
))
.min(Height::from(
self.sth.metrics.realized.min_stateful_len(),
))
.min(Height::from(
self.lth.metrics.realized.min_stateful_len(),
))
.min(Height::from(self.all.min_stateful_len()))
.min(Height::from(self.sth.min_stateful_len()))
.min(Height::from(self.lth.min_stateful_len()))
}
/// Import state for all separate cohorts at or before given height.
@@ -790,7 +784,7 @@ impl UTXOCohorts<Rw> {
/// Aggregate RealizedFull fields from age_range states and push to all/sth/lth.
/// Called during the block loop after separate cohorts' push_state but before reset.
pub(crate) fn push_overlapping_realized_full(&mut self) {
pub(crate) fn push_overlapping(&mut self, height_price: Cents) {
let Self {
all,
sth,
@@ -805,14 +799,26 @@ impl UTXOCohorts<Rw> {
let mut sth_acc = RealizedFullAccum::default();
let mut lth_acc = RealizedFullAccum::default();
for ar in age_range.iter() {
if let Some(state) = ar.state.as_ref() {
let r = &state.realized;
all_acc.add(r);
let mut all_icap = (0u128, 0u128);
let mut sth_icap = (0u128, 0u128);
let mut lth_icap = (0u128, 0u128);
for ar in age_range.iter_mut() {
if let Some(state) = ar.state.as_mut() {
all_acc.add(&state.realized);
let u = state.compute_unrealized_state(height_price);
all_icap.0 += u.investor_cap_in_profit_raw;
all_icap.1 += u.investor_cap_in_loss_raw;
if sth_filter.includes(&ar.metrics.filter) {
sth_acc.add(r);
sth_acc.add(&state.realized);
sth_icap.0 += u.investor_cap_in_profit_raw;
sth_icap.1 += u.investor_cap_in_loss_raw;
} else {
lth_acc.add(r);
lth_acc.add(&state.realized);
lth_icap.0 += u.investor_cap_in_profit_raw;
lth_icap.1 += u.investor_cap_in_loss_raw;
}
}
}
@@ -820,6 +826,13 @@ impl UTXOCohorts<Rw> {
all.metrics.realized.push_accum(&all_acc);
sth.metrics.realized.push_accum(&sth_acc);
lth.metrics.realized.push_accum(&lth_acc);
all.metrics.unrealized.investor_cap_in_profit_raw.push(CentsSquaredSats::new(all_icap.0));
all.metrics.unrealized.investor_cap_in_loss_raw.push(CentsSquaredSats::new(all_icap.1));
sth.metrics.unrealized.investor_cap_in_profit_raw.push(CentsSquaredSats::new(sth_icap.0));
sth.metrics.unrealized.investor_cap_in_loss_raw.push(CentsSquaredSats::new(sth_icap.1));
lth.metrics.unrealized.investor_cap_in_profit_raw.push(CentsSquaredSats::new(lth_icap.0));
lth.metrics.unrealized.investor_cap_in_loss_raw.push(CentsSquaredSats::new(lth_icap.1));
}
}

View File

@@ -551,8 +551,8 @@ fn push_cohort_states(
},
);
// Phase 2: aggregate age_range realized states → push to overlapping cohorts' RealizedFull
utxo_cohorts.push_overlapping_realized_full();
// Phase 2: aggregate age_range states → push to overlapping cohorts
utxo_cohorts.push_overlapping(height_price);
// Phase 3: reset per-block values
utxo_cohorts

View File

@@ -50,11 +50,10 @@ impl CohortMetricsBase for AllCohortMetrics {
}
fn min_stateful_len(&self) -> usize {
self.supply
.min_len()
.min(self.outputs.min_len())
.min(self.activity.min_len())
.min(self.realized.min_stateful_len())
// Only check per-block pushed vecs, not aggregated ones (supply, outputs,
// activity, realized core, unrealized core are summed from age_range).
self.realized
.min_stateful_len()
.min(self.unrealized.min_stateful_len())
.min(self.cost_basis.min_stateful_len())
}
@@ -138,6 +137,7 @@ impl AllCohortMetrics {
self.cost_basis.compute_prices(
starting_indexes,
&prices.spot.cents.height,
&self.unrealized.invested_capital.in_profit.cents.height,
&self.unrealized.invested_capital.in_loss.cents.height,
&self.supply.in_profit.sats.height,

View File

@@ -47,11 +47,10 @@ impl CohortMetricsBase for ExtendedCohortMetrics {
}
fn min_stateful_len(&self) -> usize {
self.supply
.min_len()
.min(self.outputs.min_len())
.min(self.activity.min_len())
.min(self.realized.min_stateful_len())
// Only check per-block pushed vecs, not aggregated ones (supply, outputs,
// activity, realized core, unrealized core are summed from age_range).
self.realized
.min_stateful_len()
.min(self.unrealized.min_stateful_len())
.min(self.cost_basis.min_stateful_len())
}
@@ -116,6 +115,7 @@ impl ExtendedCohortMetrics {
self.cost_basis.compute_prices(
starting_indexes,
&prices.spot.cents.height,
&self.unrealized.invested_capital.in_profit.cents.height,
&self.unrealized.invested_capital.in_loss.cents.height,
&self.supply.in_profit.sats.height,

View File

@@ -1,17 +1,19 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{BasisPoints16, Cents, Height, Indexes, Sats, Version};
use brk_types::CentsSquaredSats;
use brk_types::{BasisPoints16, Cents, Height, Indexes, Sats, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
use crate::internal::{FiatPerBlock, PerBlock, PercentPerBlock, PercentilesVecs, Price, PERCENTILES_LEN};
use crate::internal::{
PERCENTILES_LEN, PerBlock, PercentPerBlock, PercentilesVecs, Price,
};
use super::ImportConfig;
#[derive(Traversable)]
pub struct CostBasisSide<M: StorageMode = Rw> {
pub per_coin: FiatPerBlock<Cents, M>,
pub per_dollar: FiatPerBlock<Cents, M>,
pub per_coin: Price<PerBlock<Cents, M>>,
pub per_dollar: Price<PerBlock<Cents, M>>,
}
/// Cost basis metrics: min/max + profit/loss splits + percentiles + supply density.
@@ -31,12 +33,12 @@ impl CostBasis {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
Ok(Self {
in_profit: CostBasisSide {
per_coin: cfg.import("cost_basis_in_profit_per_coin", Version::ZERO)?,
per_dollar: cfg.import("cost_basis_in_profit_per_dollar", Version::ZERO)?,
per_coin: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_profit_per_coin"), cfg.version + Version::ONE, cfg.indexes)?,
per_dollar: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_profit_per_dollar"), cfg.version + Version::ONE, cfg.indexes)?,
},
in_loss: CostBasisSide {
per_coin: cfg.import("cost_basis_in_loss_per_coin", Version::ZERO)?,
per_dollar: cfg.import("cost_basis_in_loss_per_dollar", Version::ZERO)?,
per_coin: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_loss_per_coin"), cfg.version + Version::ONE, cfg.indexes)?,
per_dollar: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_loss_per_dollar"), cfg.version + Version::ONE, cfg.indexes)?,
},
min: cfg.import("cost_basis_min", Version::ZERO)?,
max: cfg.import("cost_basis_max", Version::ZERO)?,
@@ -124,9 +126,11 @@ impl CostBasis {
vecs
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn compute_prices(
&mut self,
starting_indexes: &Indexes,
spot: &impl ReadableVec<Height, Cents>,
invested_cap_in_profit: &impl ReadableVec<Height, Cents>,
invested_cap_in_loss: &impl ReadableVec<Height, Cents>,
supply_in_profit_sats: &impl ReadableVec<Height, Sats>,
@@ -135,46 +139,64 @@ impl CostBasis {
investor_cap_in_loss_raw: &impl ReadableVec<Height, CentsSquaredSats>,
exit: &Exit,
) -> Result<()> {
self.in_profit.per_coin.cents.height.compute_transform2(
self.in_profit.per_coin.cents.height.compute_transform3(
starting_indexes.height,
invested_cap_in_profit,
supply_in_profit_sats,
|(h, invested_cents, supply_sats, ..)| {
spot,
|(h, invested_cents, supply_sats, spot, ..)| {
let supply = supply_sats.as_u128();
if supply == 0 { return (h, Cents::ZERO); }
(h, Cents::new((invested_cents.as_u128() * Sats::ONE_BTC_U128 / supply) as u64))
if supply == 0 {
return (h, spot);
}
(
h,
Cents::new((invested_cents.as_u128() * Sats::ONE_BTC_U128 / supply) as u64),
)
},
exit,
)?;
self.in_loss.per_coin.cents.height.compute_transform2(
self.in_loss.per_coin.cents.height.compute_transform3(
starting_indexes.height,
invested_cap_in_loss,
supply_in_loss_sats,
|(h, invested_cents, supply_sats, ..)| {
spot,
|(h, invested_cents, supply_sats, spot, ..)| {
let supply = supply_sats.as_u128();
if supply == 0 { return (h, Cents::ZERO); }
(h, Cents::new((invested_cents.as_u128() * Sats::ONE_BTC_U128 / supply) as u64))
if supply == 0 {
return (h, spot);
}
(
h,
Cents::new((invested_cents.as_u128() * Sats::ONE_BTC_U128 / supply) as u64),
)
},
exit,
)?;
self.in_profit.per_dollar.cents.height.compute_transform2(
self.in_profit.per_dollar.cents.height.compute_transform3(
starting_indexes.height,
investor_cap_in_profit_raw,
invested_cap_in_profit,
|(h, investor_cap, invested_cents, ..)| {
spot,
|(h, investor_cap, invested_cents, spot, ..)| {
let invested_raw = invested_cents.as_u128() * Sats::ONE_BTC_U128;
if invested_raw == 0 { return (h, Cents::ZERO); }
if invested_raw == 0 {
return (h, spot);
}
(h, Cents::new((investor_cap.inner() / invested_raw) as u64))
},
exit,
)?;
self.in_loss.per_dollar.cents.height.compute_transform2(
self.in_loss.per_dollar.cents.height.compute_transform3(
starting_indexes.height,
investor_cap_in_loss_raw,
invested_cap_in_loss,
|(h, investor_cap, invested_cents, ..)| {
spot,
|(h, investor_cap, invested_cents, spot, ..)| {
let invested_raw = invested_cents.as_u128() * Sats::ONE_BTC_U128;
if invested_raw == 0 { return (h, Cents::ZERO); }
if invested_raw == 0 {
return (h, spot);
}
(h, Cents::new((investor_cap.inner() / invested_raw) as u64))
},
exit,

View File

@@ -127,7 +127,7 @@ impl RealizedFull {
// Peak regret
let peak_regret = RealizedPeakRegret {
value: cfg.import("realized_peak_regret", Version::new(2))?,
value: cfg.import("realized_peak_regret", Version::new(3))?,
to_rcap: cfg.import("realized_peak_regret_to_rcap", Version::new(2))?,
};

View File

@@ -33,9 +33,7 @@ pub struct UnrealizedFull<M: StorageMode = Rw> {
pub gross_pnl: FiatPerBlock<Cents, M>,
pub invested_capital: UnrealizedInvestedCapital<M>,
#[traversable(hidden)]
pub investor_cap_in_profit_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
#[traversable(hidden)]
pub investor_cap_in_loss_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
pub sentiment: UnrealizedSentiment<M>,
@@ -48,18 +46,20 @@ impl UnrealizedFull {
let gross_pnl = cfg.import("unrealized_gross_pnl", v0)?;
let v1 = Version::ONE;
let invested_capital = UnrealizedInvestedCapital {
in_profit: cfg.import("invested_capital_in_profit", v0)?,
in_loss: cfg.import("invested_capital_in_loss", v0)?,
in_profit: cfg.import("invested_capital_in_profit", v1)?,
in_loss: cfg.import("invested_capital_in_loss", v1)?,
};
let investor_cap_in_profit_raw = cfg.import("investor_cap_in_profit_raw", v0)?;
let investor_cap_in_loss_raw = cfg.import("investor_cap_in_loss_raw", v0)?;
let sentiment = UnrealizedSentiment {
pain_index: cfg.import("pain_index", v0)?,
greed_index: cfg.import("greed_index", v0)?,
net: cfg.import("net_sentiment", Version::ONE)?,
pain_index: cfg.import("pain_index", v1)?,
greed_index: cfg.import("greed_index", v1)?,
net: cfg.import("net_sentiment", Version::new(2))?,
};
Ok(Self {
@@ -73,9 +73,10 @@ impl UnrealizedFull {
}
pub(crate) fn min_stateful_len(&self) -> usize {
self.inner
.min_stateful_len()
.min(self.investor_cap_in_profit_raw.len())
// Only check per-block pushed vecs (investor_cap_raw).
// Core-level vecs (profit/loss) are aggregated from age_range, not stateful.
self.investor_cap_in_profit_raw
.len()
.min(self.investor_cap_in_loss_raw.len())
}
@@ -90,14 +91,8 @@ impl UnrealizedFull {
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
let mut vecs = self.inner.collect_vecs_mut();
vecs.push(&mut self.gross_pnl.cents.height as &mut dyn AnyStoredVec);
vecs.push(&mut self.invested_capital.in_profit.cents.height as &mut dyn AnyStoredVec);
vecs.push(&mut self.invested_capital.in_loss.cents.height as &mut dyn AnyStoredVec);
vecs.push(&mut self.investor_cap_in_profit_raw as &mut dyn AnyStoredVec);
vecs.push(&mut self.investor_cap_in_loss_raw as &mut dyn AnyStoredVec);
vecs.push(&mut self.sentiment.pain_index.cents.height as &mut dyn AnyStoredVec);
vecs.push(&mut self.sentiment.greed_index.cents.height as &mut dyn AnyStoredVec);
vecs.push(&mut self.sentiment.net.cents.height as &mut dyn AnyStoredVec);
vecs
}
@@ -120,30 +115,41 @@ impl UnrealizedFull {
)?;
// invested_capital_in_profit = supply_profit_sats × spot / ONE_BTC - unrealized_profit
self.invested_capital.in_profit.cents.height.compute_transform3(
starting_indexes.height,
supply_in_profit_sats,
&prices.spot.cents.height,
&self.inner.basic.profit.cents.height,
|(h, supply_sats, spot, profit, ..): (_, Sats, Cents, Cents, _)| {
let market_value = supply_sats.as_u128() * spot.as_u128() / Sats::ONE_BTC_U128;
(h, Cents::new(market_value.saturating_sub(profit.as_u128()) as u64))
},
exit,
)?;
self.invested_capital
.in_profit
.cents
.height
.compute_transform3(
starting_indexes.height,
supply_in_profit_sats,
&prices.spot.cents.height,
&self.inner.basic.profit.cents.height,
|(h, supply_sats, spot, profit, ..): (_, Sats, Cents, Cents, _)| {
let market_value = supply_sats.as_u128() * spot.as_u128() / Sats::ONE_BTC_U128;
(
h,
Cents::new(market_value.saturating_sub(profit.as_u128()) as u64),
)
},
exit,
)?;
// invested_capital_in_loss = supply_loss_sats × spot / ONE_BTC + unrealized_loss
self.invested_capital.in_loss.cents.height.compute_transform3(
starting_indexes.height,
supply_in_loss_sats,
&prices.spot.cents.height,
&self.inner.basic.loss.cents.height,
|(h, supply_sats, spot, loss, ..): (_, Sats, Cents, Cents, _)| {
let market_value = supply_sats.as_u128() * spot.as_u128() / Sats::ONE_BTC_U128;
(h, Cents::new((market_value + loss.as_u128()) as u64))
},
exit,
)?;
self.invested_capital
.in_loss
.cents
.height
.compute_transform3(
starting_indexes.height,
supply_in_loss_sats,
&prices.spot.cents.height,
&self.inner.basic.loss.cents.height,
|(h, supply_sats, spot, loss, ..): (_, Sats, Cents, Cents, _)| {
let market_value = supply_sats.as_u128() * spot.as_u128() / Sats::ONE_BTC_U128;
(h, Cents::new((market_value + loss.as_u128()) as u64))
},
exit,
)?;
Ok(())
}
@@ -171,7 +177,10 @@ impl UnrealizedFull {
}
let investor_price = investor_cap.inner() / invested_cap_raw;
let spot_u128 = spot.as_u128();
(h, Cents::new(spot_u128.saturating_sub(investor_price) as u64))
(
h,
Cents::new(spot_u128.saturating_sub(investor_price) as u64),
)
},
exit,
)?;
@@ -189,7 +198,10 @@ impl UnrealizedFull {
}
let investor_price = investor_cap.inner() / invested_cap_raw;
let spot_u128 = spot.as_u128();
(h, Cents::new(investor_price.saturating_sub(spot_u128) as u64))
(
h,
Cents::new(investor_price.saturating_sub(spot_u128) as u64),
)
},
exit,
)?;

View File

@@ -63,7 +63,7 @@ impl UnrealizedLike for UnrealizedFull {
&mut self.inner
}
fn min_stateful_len(&self) -> usize {
self.inner.min_stateful_len()
UnrealizedFull::min_stateful_len(self)
}
#[inline(always)]
fn push_state(&mut self, state: &UnrealizedState) {

View File

@@ -5,7 +5,10 @@ use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
use crate::{
indexes,
internal::{AmountPerBlock, CachedWindowStarts, LazyRollingSumsAmountFromHeight, SatsToCents},
internal::{
AmountPerBlock, CachedWindowStarts, LazyRollingAvgsAmountFromHeight,
LazyRollingSumsAmountFromHeight, SatsToCents,
},
prices,
};
@@ -14,6 +17,7 @@ pub struct AmountPerBlockCumulativeWithSums<M: StorageMode = Rw> {
pub base: AmountPerBlock<M>,
pub cumulative: AmountPerBlock<M>,
pub sum: LazyRollingSumsAmountFromHeight,
pub average: LazyRollingAvgsAmountFromHeight,
}
const VERSION: Version = Version::TWO;
@@ -39,11 +43,20 @@ impl AmountPerBlockCumulativeWithSums {
cached_starts,
indexes,
);
let average = LazyRollingAvgsAmountFromHeight::new(
&format!("{name}_average"),
v,
&cumulative.sats.height,
&cumulative.cents.height,
cached_starts,
indexes,
);
Ok(Self {
base,
cumulative,
sum,
average,
})
}

View File

@@ -6,8 +6,9 @@ use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
use crate::{
indexes,
internal::{
AmountPerBlock, CachedWindowStarts, LazyRollingSumsAmountFromHeight,
RollingDistributionAmountPerBlock, SatsToCents, WindowStarts,
AmountPerBlock, CachedWindowStarts, LazyRollingAvgsAmountFromHeight,
LazyRollingSumsAmountFromHeight, RollingDistributionAmountPerBlock, SatsToCents,
WindowStarts,
},
prices,
};
@@ -17,8 +18,9 @@ pub struct AmountPerBlockFull<M: StorageMode = Rw> {
pub base: AmountPerBlock<M>,
pub cumulative: AmountPerBlock<M>,
pub sum: LazyRollingSumsAmountFromHeight,
pub average: LazyRollingAvgsAmountFromHeight,
#[traversable(flatten)]
pub rolling: RollingDistributionAmountPerBlock<M>,
pub distribution: RollingDistributionAmountPerBlock<M>,
}
const VERSION: Version = Version::TWO;
@@ -34,12 +36,8 @@ impl AmountPerBlockFull {
let v = version + VERSION;
let base = AmountPerBlock::forced_import(db, name, v, indexes)?;
let cumulative = AmountPerBlock::forced_import(
db,
&format!("{name}_cumulative"),
v,
indexes,
)?;
let cumulative =
AmountPerBlock::forced_import(db, &format!("{name}_cumulative"), v, indexes)?;
let sum = LazyRollingSumsAmountFromHeight::new(
&format!("{name}_sum"),
v,
@@ -48,14 +46,22 @@ impl AmountPerBlockFull {
cached_starts,
indexes,
);
let rolling =
RollingDistributionAmountPerBlock::forced_import(db, name, v, indexes)?;
let average = LazyRollingAvgsAmountFromHeight::new(
&format!("{name}_average"),
v,
&cumulative.sats.height,
&cumulative.cents.height,
cached_starts,
indexes,
);
let rolling = RollingDistributionAmountPerBlock::forced_import(db, name, v, indexes)?;
Ok(Self {
base,
cumulative,
sum,
rolling,
average,
distribution: rolling,
})
}
@@ -89,7 +95,7 @@ impl AmountPerBlockFull {
.height
.compute_cumulative(max_from, &self.base.cents.height, exit)?;
self.rolling.compute(
self.distribution.compute(
max_from,
windows,
&self.base.sats.height,

View File

@@ -0,0 +1,124 @@
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{DeltaAvg, LazyDeltaVec, LazyVecFrom1, ReadableCloneableVec};
use crate::{
indexes,
internal::{
CachedWindowStarts, CentsUnsignedToDollars, DerivedResolutions, LazyPerBlock,
LazyRollingAvgFromHeight, Resolutions, SatsToBitcoin, Windows,
},
};
/// Single window slot: lazy rolling average for Amount (sats + btc + cents + usd).
#[derive(Clone, Traversable)]
pub struct LazyRollingAvgAmountFromHeight {
pub btc: LazyPerBlock<Bitcoin, Sats>,
pub sats: LazyRollingAvgFromHeight<Sats>,
pub usd: LazyPerBlock<Dollars, Cents>,
pub cents: LazyRollingAvgFromHeight<Cents>,
}
/// Lazy rolling averages for all 4 windows, for Amount (sats + btc + cents + usd).
#[derive(Clone, Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct LazyRollingAvgsAmountFromHeight(pub Windows<LazyRollingAvgAmountFromHeight>);
impl LazyRollingAvgsAmountFromHeight {
pub fn new(
name: &str,
version: Version,
cumulative_sats: &(impl ReadableCloneableVec<Height, Sats> + 'static),
cumulative_cents: &(impl ReadableCloneableVec<Height, Cents> + 'static),
cached_starts: &CachedWindowStarts,
indexes: &indexes::Vecs,
) -> Self {
let cum_sats = cumulative_sats.read_only_boxed_clone();
let cum_cents = cumulative_cents.read_only_boxed_clone();
let make_slot = |suffix: &str, cached_start: &vecdb::CachedVec<Height, Height>| {
let full_name = format!("{name}_{suffix}");
let cached = cached_start.clone();
let starts_version = cached.version();
// Sats lazy rolling avg
let sats_avg = LazyDeltaVec::<Height, Sats, Sats, DeltaAvg>::new(
&format!("{full_name}_sats"),
version,
cum_sats.clone(),
starts_version,
{
let cached = cached.clone();
move || cached.get()
},
);
let sats_resolutions = Resolutions::forced_import(
&format!("{full_name}_sats"),
sats_avg.read_only_boxed_clone(),
version,
indexes,
);
let sats = LazyRollingAvgFromHeight {
height: sats_avg,
resolutions: Box::new(sats_resolutions),
};
// Btc lazy from sats
let btc = LazyPerBlock {
height: LazyVecFrom1::transformed::<SatsToBitcoin>(
&full_name,
version,
sats.height.read_only_boxed_clone(),
),
resolutions: Box::new(DerivedResolutions::from_derived_computed::<SatsToBitcoin>(
&full_name,
version,
&sats.resolutions,
)),
};
// Cents rolling avg
let cents_avg = LazyDeltaVec::<Height, Cents, Cents, DeltaAvg>::new(
&format!("{full_name}_cents"),
version,
cum_cents.clone(),
starts_version,
move || cached.get(),
);
let cents_resolutions = Resolutions::forced_import(
&format!("{full_name}_cents"),
cents_avg.read_only_boxed_clone(),
version,
indexes,
);
let cents = LazyRollingAvgFromHeight {
height: cents_avg,
resolutions: Box::new(cents_resolutions),
};
// Usd lazy from cents
let usd = LazyPerBlock {
height: LazyVecFrom1::transformed::<CentsUnsignedToDollars>(
&format!("{full_name}_usd"),
version,
cents.height.read_only_boxed_clone(),
),
resolutions: Box::new(DerivedResolutions::from_derived_computed::<
CentsUnsignedToDollars,
>(
&format!("{full_name}_usd"), version, &cents.resolutions
)),
};
LazyRollingAvgAmountFromHeight {
btc,
sats,
usd,
cents,
}
};
Self(cached_starts.0.map_with_suffix(make_slot))
}
}

View File

@@ -4,6 +4,7 @@ mod cumulative_sum;
mod full;
mod lazy;
mod lazy_derived_resolutions;
mod lazy_rolling_avg;
mod lazy_rolling_sum;
mod rolling_distribution;
mod with_deltas;
@@ -14,6 +15,7 @@ pub use cumulative_sum::*;
pub use full::*;
pub use lazy::*;
pub use lazy_derived_resolutions::*;
pub use lazy_rolling_avg::*;
pub use lazy_rolling_sum::*;
pub use rolling_distribution::*;
pub use with_deltas::*;

View File

@@ -9,9 +9,7 @@ use vecdb::{
use crate::{
indexes,
internal::{
CachedWindowStarts, NumericValue, PerBlock, RollingComplete, WindowStarts,
},
internal::{CachedWindowStarts, NumericValue, PerBlock, RollingComplete, WindowStarts},
};
#[derive(Traversable)]
@@ -70,8 +68,7 @@ where
f64: From<T>,
A: VecIndex + VecValue + brk_types::CheckedSub<A>,
{
let combined_version =
source.version() + first_indexes.version() + count_indexes.version();
let combined_version = source.version() + first_indexes.version() + count_indexes.version();
let mut index = max_from;
index = {
@@ -121,7 +118,7 @@ where
);
self.sum.height.push(sum_val);
cumulative_val = cumulative_val + sum_val;
cumulative_val += sum_val;
self.cumulative.height.push(cumulative_val);
Ok(())

View File

@@ -17,7 +17,10 @@ use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
use crate::{
indexes,
internal::{CachedWindowStarts, PerBlock, LazyRollingSumsFromHeight, NumericValue},
internal::{
CachedWindowStarts, LazyRollingAvgsFromHeight, LazyRollingSumsFromHeight, NumericValue,
PerBlock,
},
};
#[derive(Traversable)]
@@ -29,6 +32,7 @@ where
pub base: PerBlock<T, M>,
pub cumulative: PerBlock<C, M>,
pub sum: LazyRollingSumsFromHeight<C>,
pub average: LazyRollingAvgsFromHeight<C>,
}
impl<T, C> PerBlockCumulativeWithSums<T, C>
@@ -53,11 +57,19 @@ where
cached_starts,
indexes,
);
let average = LazyRollingAvgsFromHeight::new(
&format!("{name}_average"),
version,
&cumulative.height,
cached_starts,
indexes,
);
Ok(Self {
base,
cumulative,
sum,
average,
})
}

View File

@@ -21,7 +21,7 @@ where
{
pub base: M::Stored<EagerVec<PcoVec<Height, T>>>,
#[traversable(hidden)]
pub cumulative: M::Stored<EagerVec<PcoVec<Height, f64>>>,
pub cumulative: M::Stored<EagerVec<PcoVec<Height, T>>>,
#[traversable(flatten)]
pub average: LazyRollingAvgsFromHeight<T>,
}
@@ -38,8 +38,8 @@ where
cached_starts: &CachedWindowStarts,
) -> Result<Self> {
let base: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, version)?;
let cumulative: EagerVec<PcoVec<Height, f64>> =
EagerVec::forced_import(db, &format!("{name}_cumulative"), version)?;
let cumulative: EagerVec<PcoVec<Height, T>> =
EagerVec::forced_import(db, &format!("{name}_cumulative"), version + Version::ONE)?;
let average = LazyRollingAvgsFromHeight::new(
&format!("{name}_average"),
version + Version::ONE,

View File

@@ -12,7 +12,7 @@ pub struct LazyRollingAvgFromHeight<T>
where
T: NumericValue + JsonSchema,
{
pub height: LazyDeltaVec<Height, f64, T, DeltaAvg>,
pub height: LazyDeltaVec<Height, T, T, DeltaAvg>,
#[traversable(flatten)]
pub resolutions: Box<Resolutions<T>>,
}

View File

@@ -12,10 +12,11 @@ use crate::{
use super::LazyRollingAvgFromHeight;
/// Lazy rolling averages for all 4 window durations (24h, 1w, 1m, 1y),
/// derived from an f64 cumulative vec + cached window starts.
/// derived from a cumulative vec + cached window starts.
///
/// Nothing is stored on disk — all values are computed on-the-fly via
/// `LazyDeltaVec<Height, f64, T, DeltaAvg>`: `(cum[h] - cum[start-1]) / (h - start + 1)`.
/// `LazyDeltaVec<Height, T, T, DeltaAvg>`: `(cum[h] - cum[start-1]) / (h - start + 1)`.
/// T is converted to f64 internally for division, then back to T.
#[derive(Clone, Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct LazyRollingAvgsFromHeight<T>(pub Windows<LazyRollingAvgFromHeight<T>>)
@@ -29,7 +30,7 @@ where
pub fn new(
name: &str,
version: Version,
cumulative: &(impl ReadableCloneableVec<Height, f64> + 'static),
cumulative: &(impl ReadableCloneableVec<Height, T> + 'static),
cached_starts: &CachedWindowStarts,
indexes: &indexes::Vecs,
) -> Self {
@@ -39,7 +40,7 @@ where
let full_name = format!("{name}_{suffix}");
let cached = cached_start.clone();
let starts_version = cached.version();
let avg = LazyDeltaVec::<Height, f64, T, DeltaAvg>::new(
let avg = LazyDeltaVec::<Height, T, T, DeltaAvg>::new(
&full_name,
version,
cum_source.clone(),

View File

@@ -10,18 +10,19 @@ use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
use crate::{
indexes,
internal::{
CachedWindowStarts, NumericValue, LazyRollingSumsFromHeight, RollingDistribution,
WindowStarts,
CachedWindowStarts, NumericValue, LazyRollingAvgsFromHeight, LazyRollingSumsFromHeight,
RollingDistribution, WindowStarts,
},
};
/// Lazy rolling sums + stored rolling distribution (8 stats × 4 windows).
/// Lazy rolling sums + lazy rolling averages + stored rolling distribution (7 stats × 4 windows).
#[derive(Traversable)]
pub struct RollingComplete<T, M: StorageMode = Rw>
where
T: NumericValue + JsonSchema,
{
pub sum: LazyRollingSumsFromHeight<T>,
pub average: LazyRollingAvgsFromHeight<T>,
#[traversable(flatten)]
pub distribution: RollingDistribution<T, M>,
}
@@ -45,9 +46,20 @@ where
cached_starts,
indexes,
);
let average = LazyRollingAvgsFromHeight::new(
&format!("{name}_average"),
version,
cumulative,
cached_starts,
indexes,
);
let distribution = RollingDistribution::forced_import(db, name, version, indexes)?;
Ok(Self { sum, distribution })
Ok(Self {
sum,
average,
distribution,
})
}
/// Compute rolling distribution stats across all 4 windows.

View File

@@ -6,12 +6,12 @@ use vecdb::{ReadableCloneableVec, UnaryTransform};
use crate::{
indexes,
internal::{
CachedWindowStarts, ComputedVecValue, LazyRollingDistribution, LazyRollingSumsFromHeight,
NumericValue, RollingComplete,
CachedWindowStarts, ComputedVecValue, LazyRollingAvgsFromHeight,
LazyRollingDistribution, LazyRollingSumsFromHeight, NumericValue, RollingComplete,
},
};
/// Lazy analog of `RollingComplete<T>`: lazy rolling sums + lazy rolling distribution.
/// Lazy analog of `RollingComplete<T>`: lazy rolling sums + lazy rolling averages + lazy rolling distribution.
/// Zero stored vecs.
#[derive(Clone, Traversable)]
pub struct LazyRollingComplete<T, S1T>
@@ -20,6 +20,7 @@ where
S1T: ComputedVecValue + JsonSchema,
{
pub sum: LazyRollingSumsFromHeight<T>,
pub average: LazyRollingAvgsFromHeight<T>,
#[traversable(flatten)]
pub distribution: LazyRollingDistribution<T, S1T>,
}
@@ -44,11 +45,22 @@ where
cached_starts,
indexes,
);
let average = LazyRollingAvgsFromHeight::new(
&format!("{name}_average"),
version,
cumulative,
cached_starts,
indexes,
);
let distribution = LazyRollingDistribution::from_rolling_distribution::<F>(
name,
version,
&source.distribution,
);
Self { sum, distribution }
Self {
sum,
average,
distribution,
}
}
}

View File

@@ -12,6 +12,7 @@ impl<T: VecValue> UnaryTransform<T, T> for Identity<T> {
}
}
pub struct HalveSats;
impl UnaryTransform<Sats, Sats> for HalveSats {

View File

@@ -95,7 +95,7 @@ impl Vecs {
},
exit,
)?;
self.subsidy.compute(prices, starting_indexes.height, exit)?;
self.subsidy.compute_rest(starting_indexes.height, prices, exit)?;
self.unclaimed.base.sats.height.compute_transform(
starting_indexes.height,

View File

@@ -36,7 +36,9 @@ impl Vecs {
coinbase: AmountPerBlockCumulativeWithSums::forced_import(
db, "coinbase", version, indexes, cached_starts,
)?,
subsidy: AmountPerBlockCumulative::forced_import(db, "subsidy", version, indexes)?,
subsidy: AmountPerBlockCumulativeWithSums::forced_import(
db, "subsidy", version, indexes, cached_starts,
)?,
fees: AmountPerBlockFull::forced_import(db, "fees", version, indexes, cached_starts)?,
unclaimed: AmountPerBlockCumulative::forced_import(
db,

View File

@@ -11,7 +11,7 @@ use crate::internal::{
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub coinbase: AmountPerBlockCumulativeWithSums<M>,
pub subsidy: AmountPerBlockCumulative<M>,
pub subsidy: AmountPerBlockCumulativeWithSums<M>,
pub fees: AmountPerBlockFull<M>,
pub unclaimed: AmountPerBlockCumulative<M>,
#[traversable(wrap = "fees", rename = "dominance")]

File diff suppressed because it is too large Load Diff

View File

@@ -2292,38 +2292,14 @@ class AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern:
self.pct90: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'pct90'))
self.sum: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'sum'))
class AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2:
class GrossInvestedInvestorLossNetNuplProfitSentimentPattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.average: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'average'))
self.max: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'max'))
self.median: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'median'))
self.min: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'min'))
self.pct10: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct10'))
self.pct25: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct25'))
self.pct75: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct75'))
self.pct90: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct90'))
pass
class BpsCentsPercentilesRatioSatsSmaStdUsdPattern:
"""Pattern struct for repeated tree structure."""
pass
class AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern(Generic[T]):
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.average: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'average'))
self.max: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'max'))
self.median: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'median'))
self.min: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'min'))
self.pct10: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct10'))
self.pct25: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct25'))
self.pct75: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct75'))
self.pct90: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct90'))
class _10y2y3y4y5y6y8yPattern:
"""Pattern struct for repeated tree structure."""
@@ -2360,13 +2336,39 @@ class CapLossMvrvNetPriceProfitSoprPattern:
self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'mvrv'))
self.net_pnl: BaseCumulativeDeltaSumPattern = BaseCumulativeDeltaSumPattern(client, _m(acc, 'net_realized_pnl'))
self.price: BpsCentsRatioSatsUsdPattern = BpsCentsRatioSatsUsdPattern(client, _m(acc, 'realized_price'))
self.profit: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, _m(acc, 'realized_profit'))
self.profit: BaseCumulativeSumPattern = BaseCumulativeSumPattern(client, _m(acc, 'realized_profit'))
self.sopr: RatioValuePattern = RatioValuePattern(client, acc)
class GrossInvestedLossNetNuplProfitSentimentPattern2:
class InInvestedMaxMinPercentilesSupplyPattern:
"""Pattern struct for repeated tree structure."""
pass
class MaxMedianMinPct10Pct25Pct75Pct90Pattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.max: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'max'))
self.median: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'median'))
self.min: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'min'))
self.pct10: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct10'))
self.pct25: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct25'))
self.pct75: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct75'))
self.pct90: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct90'))
class MaxMedianMinPct10Pct25Pct75Pct90Pattern(Generic[T]):
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.max: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'max'))
self.median: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'median'))
self.min: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'min'))
self.pct10: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct10'))
self.pct25: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct25'))
self.pct75: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct75'))
self.pct90: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct90'))
class _1m1w1y2y4yAllPattern:
"""Pattern struct for repeated tree structure."""
@@ -2391,6 +2393,18 @@ class ActivityAddrOutputsRealizedSupplyUnrealizedPattern:
self.supply: DeltaHalfTotalPattern = DeltaHalfTotalPattern(client, _m(acc, 'supply'))
self.unrealized: NuplPattern = NuplPattern(client, _m(acc, 'nupl'))
class AverageBaseCumulativeInSumPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.average: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, _m(acc, 'average'))
self.base: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, acc)
self.cumulative: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'cumulative'))
self.in_loss: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, _m(acc, 'in_loss'))
self.in_profit: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, _m(acc, 'in_profit'))
self.sum: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, _m(acc, 'sum'))
class BaseChangeCumulativeDeltaSumToPattern:
"""Pattern struct for repeated tree structure."""
@@ -2508,17 +2522,6 @@ class ActivityOutputsRealizedSupplyUnrealizedPattern2:
self.supply: DeltaHalfTotalPattern = DeltaHalfTotalPattern(client, _m(acc, 'supply'))
self.unrealized: NuplPattern = NuplPattern(client, _m(acc, 'nupl'))
class BaseCumulativeInSumPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.base: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, acc)
self.cumulative: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'cumulative'))
self.in_loss: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, _m(acc, 'in_loss'))
self.in_profit: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, _m(acc, 'in_profit'))
self.sum: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, _m(acc, 'sum'))
class BaseCumulativeNegativeSumToPattern:
"""Pattern struct for repeated tree structure."""
pass
@@ -2573,10 +2576,10 @@ class CapLossMvrvPriceProfitPattern:
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.cap: CentsDeltaUsdPattern = CentsDeltaUsdPattern(client, _m(acc, 'realized_cap'))
self.loss: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, _m(acc, 'realized_loss'))
self.loss: BaseCumulativeSumPattern = BaseCumulativeSumPattern(client, _m(acc, 'realized_loss'))
self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'mvrv'))
self.price: BpsCentsRatioSatsUsdPattern = BpsCentsRatioSatsUsdPattern(client, _m(acc, 'realized_price'))
self.profit: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, _m(acc, 'realized_profit'))
self.profit: BaseCumulativeSumPattern = BaseCumulativeSumPattern(client, _m(acc, 'realized_profit'))
class CentsToUsdPattern4:
"""Pattern struct for repeated tree structure."""
@@ -2604,10 +2607,6 @@ class EmaHistogramLineSignalPattern:
"""Pattern struct for repeated tree structure."""
pass
class InvestedMaxMinPercentilesSupplyPattern:
"""Pattern struct for repeated tree structure."""
pass
class PhsReboundThsPattern:
"""Pattern struct for repeated tree structure."""
@@ -2694,6 +2693,26 @@ class _1y2y4yAllPattern:
"""Pattern struct for repeated tree structure."""
pass
class AverageBaseCumulativeSumPattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.average: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'average'))
self.base: SeriesPattern1[StoredU32] = SeriesPattern1(client, acc)
self.cumulative: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'cumulative'))
self.sum: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'sum'))
class AverageBaseCumulativeSumPattern3:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.average: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, _m(acc, 'average'))
self.base: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, acc)
self.cumulative: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'cumulative'))
self.sum: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, _m(acc, 'sum'))
class BaseCumulativeDeltaSumPattern:
"""Pattern struct for repeated tree structure."""
@@ -2788,20 +2807,21 @@ class _1m1w1y24hPattern(Generic[T]):
self._1y: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, '1y'))
self._24h: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, '24h'))
class AdjustedRatioValuePattern:
"""Pattern struct for repeated tree structure."""
pass
class BaseCumulativeSumPattern3:
class AverageBaseCumulativeSumPattern(Generic[T]):
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.base: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, acc)
self.cumulative: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'cumulative'))
self.sum: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, _m(acc, 'sum'))
self.average: _1m1w1y24hPattern[T] = _1m1w1y24hPattern(client, _m(acc, 'average'))
self.base: SeriesPattern1[T] = SeriesPattern1(client, acc)
self.cumulative: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'cumulative'))
self.sum: _1m1w1y24hPattern[T] = _1m1w1y24hPattern(client, _m(acc, 'sum'))
class BaseCumulativeSumPattern4:
class AdjustedRatioValuePattern:
"""Pattern struct for repeated tree structure."""
pass
class BaseCumulativeSumPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
@@ -2810,23 +2830,14 @@ class BaseCumulativeSumPattern4:
self.cumulative: CentsUsdPattern2 = CentsUsdPattern2(client, _m(acc, 'cumulative'))
self.sum: _1m1w1y24hPattern5 = _1m1w1y24hPattern5(client, _m(acc, 'sum'))
class BaseCumulativeSumPattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.base: SeriesPattern1[StoredU32] = SeriesPattern1(client, acc)
self.cumulative: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'cumulative'))
self.sum: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'sum'))
class BlocksDominanceRewardsPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.blocks_mined: BaseCumulativeSumPattern2 = BaseCumulativeSumPattern2(client, _m(acc, 'blocks_mined'))
self.blocks_mined: AverageBaseCumulativeSumPattern2 = AverageBaseCumulativeSumPattern2(client, _m(acc, 'blocks_mined'))
self.dominance: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, _m(acc, 'dominance'))
self.rewards: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, _m(acc, 'rewards'))
self.rewards: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, _m(acc, 'rewards'))
class BpsPercentRatioPattern3:
"""Pattern struct for repeated tree structure."""
@@ -2958,19 +2969,10 @@ class _6bBlockTxPattern(Generic[T]):
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self._6b: AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern[T] = AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern(client, _m(acc, '6b'))
self.block: AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern[T] = AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern(client, acc)
self._6b: MaxMedianMinPct10Pct25Pct75Pct90Pattern[T] = MaxMedianMinPct10Pct25Pct75Pct90Pattern(client, _m(acc, '6b'))
self.block: MaxMedianMinPct10Pct25Pct75Pct90Pattern[T] = MaxMedianMinPct10Pct25Pct75Pct90Pattern(client, acc)
self.tx_index: SeriesPattern19[T] = SeriesPattern19(client, acc)
class BaseCumulativeSumPattern(Generic[T]):
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.base: SeriesPattern1[T] = SeriesPattern1(client, acc)
self.cumulative: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'cumulative'))
self.sum: _1m1w1y24hPattern[T] = _1m1w1y24hPattern(client, _m(acc, 'sum'))
class AbsoluteRatePattern:
"""Pattern struct for repeated tree structure."""
@@ -3024,7 +3026,7 @@ class BlocksDominancePattern:
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.blocks_mined: BaseCumulativeSumPattern2 = BaseCumulativeSumPattern2(client, _m(acc, 'blocks_mined'))
self.blocks_mined: AverageBaseCumulativeSumPattern2 = AverageBaseCumulativeSumPattern2(client, _m(acc, 'blocks_mined'))
self.dominance: BpsPercentRatioPattern3 = BpsPercentRatioPattern3(client, _m(acc, 'dominance'))
class BpsRatioPattern2:
@@ -3064,8 +3066,8 @@ class CoindaysTransferPattern:
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.coindays_destroyed: BaseCumulativeSumPattern[StoredF64] = BaseCumulativeSumPattern(client, _m(acc, 'coindays_destroyed'))
self.transfer_volume: BaseCumulativeInSumPattern = BaseCumulativeInSumPattern(client, _m(acc, 'transfer_volume'))
self.coindays_destroyed: AverageBaseCumulativeSumPattern[StoredF64] = AverageBaseCumulativeSumPattern(client, _m(acc, 'coindays_destroyed'))
self.transfer_volume: AverageBaseCumulativeInSumPattern = AverageBaseCumulativeInSumPattern(client, _m(acc, 'transfer_volume'))
class InPattern:
"""Pattern struct for repeated tree structure."""
@@ -3075,6 +3077,14 @@ class InPattern:
self.in_loss: CentsUsdPattern2 = CentsUsdPattern2(client, _m(acc, 'loss'))
self.in_profit: CentsUsdPattern2 = CentsUsdPattern2(client, _m(acc, 'profit'))
class PerPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.per_coin: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'coin'))
self.per_dollar: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'dollar'))
class PriceRatioPattern:
"""Pattern struct for repeated tree structure."""
@@ -3089,7 +3099,7 @@ class RatioValuePattern:
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.ratio: _24hPattern = _24hPattern(client, _m(acc, 'sopr_24h'))
self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, _m(acc, 'value_destroyed'))
self.value_destroyed: AverageBaseCumulativeSumPattern[Cents] = AverageBaseCumulativeSumPattern(client, _m(acc, 'value_destroyed'))
class SdSmaPattern:
"""Pattern struct for repeated tree structure."""
@@ -3129,7 +3139,7 @@ class TransferPattern:
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.transfer_volume: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, acc)
self.transfer_volume: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, acc)
class UnspentPattern:
"""Pattern struct for repeated tree structure."""
@@ -3196,7 +3206,7 @@ class SeriesTree_Blocks_Count:
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.target: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, 'block_count_target')
self.total: BaseCumulativeSumPattern2 = BaseCumulativeSumPattern2(client, 'block_count')
self.total: AverageBaseCumulativeSumPattern2 = AverageBaseCumulativeSumPattern2(client, 'block_count')
class SeriesTree_Blocks_Lookback:
"""Series tree node."""
@@ -3305,8 +3315,8 @@ class SeriesTree_Transactions_Size_Weight:
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.tx_index: SeriesPattern19[Weight] = SeriesPattern19(client, 'tx_weight')
self.block: AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2 = AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2(client, 'tx_weight')
self._6b: AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2 = AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2(client, 'tx_weight_6b')
self.block: MaxMedianMinPct10Pct25Pct75Pct90Pattern2 = MaxMedianMinPct10Pct25Pct75Pct90Pattern2(client, 'tx_weight')
self._6b: MaxMedianMinPct10Pct25Pct75Pct90Pattern2 = MaxMedianMinPct10Pct25Pct75Pct90Pattern2(client, 'tx_weight_6b')
class SeriesTree_Transactions_Size:
"""Series tree node."""
@@ -3328,15 +3338,15 @@ class SeriesTree_Transactions_Versions:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.v1: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'tx_v1')
self.v2: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'tx_v2')
self.v3: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'tx_v3')
self.v1: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'tx_v1')
self.v2: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'tx_v2')
self.v3: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'tx_v3')
class SeriesTree_Transactions_Volume:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.transfer_volume: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'transfer_volume_bis')
self.transfer_volume: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'transfer_volume_bis')
self.tx_per_sec: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, 'tx_per_sec')
self.outputs_per_sec: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, 'outputs_per_sec')
self.inputs_per_sec: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, 'inputs_per_sec')
@@ -3517,15 +3527,15 @@ class SeriesTree_Addrs_New:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.all: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'new_addr_count')
self.p2pk65: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2pk65_new_addr_count')
self.p2pk33: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2pk33_new_addr_count')
self.p2pkh: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2pkh_new_addr_count')
self.p2sh: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2sh_new_addr_count')
self.p2wpkh: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2wpkh_new_addr_count')
self.p2wsh: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2wsh_new_addr_count')
self.p2tr: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2tr_new_addr_count')
self.p2a: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2a_new_addr_count')
self.all: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'new_addr_count')
self.p2pk65: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2pk65_new_addr_count')
self.p2pk33: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2pk33_new_addr_count')
self.p2pkh: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2pkh_new_addr_count')
self.p2sh: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2sh_new_addr_count')
self.p2wpkh: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2wpkh_new_addr_count')
self.p2wsh: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2wsh_new_addr_count')
self.p2tr: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2tr_new_addr_count')
self.p2a: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2a_new_addr_count')
class SeriesTree_Addrs_Delta:
"""Series tree node."""
@@ -3596,24 +3606,24 @@ class SeriesTree_Scripts_Count:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.p2a: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2a_count')
self.p2ms: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2ms_count')
self.p2pk33: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2pk33_count')
self.p2pk65: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2pk65_count')
self.p2pkh: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2pkh_count')
self.p2sh: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2sh_count')
self.p2tr: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2tr_count')
self.p2wpkh: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2wpkh_count')
self.p2wsh: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'p2wsh_count')
self.op_return: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'op_return_count')
self.empty_output: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'empty_output_count')
self.unknown_output: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'unknown_output_count')
self.p2a: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2a_count')
self.p2ms: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2ms_count')
self.p2pk33: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2pk33_count')
self.p2pk65: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2pk65_count')
self.p2pkh: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2pkh_count')
self.p2sh: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2sh_count')
self.p2tr: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2tr_count')
self.p2wpkh: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2wpkh_count')
self.p2wsh: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'p2wsh_count')
self.op_return: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'op_return_count')
self.empty_output: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'empty_output_count')
self.unknown_output: AverageBaseCumulativeSumPattern[StoredU64] = AverageBaseCumulativeSumPattern(client, 'unknown_output_count')
class SeriesTree_Scripts_Value:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.op_return: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'op_return_value')
self.op_return: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'op_return_value')
class SeriesTree_Scripts:
"""Series tree node."""
@@ -3670,7 +3680,7 @@ class SeriesTree_Mining_Rewards:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.coinbase: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'coinbase')
self.coinbase: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'coinbase')
self.subsidy: SeriesTree_Mining_Rewards_Subsidy = SeriesTree_Mining_Rewards_Subsidy(client)
self.fees: SeriesTree_Mining_Rewards_Fees = SeriesTree_Mining_Rewards_Fees(client)
self.unclaimed: SeriesTree_Mining_Rewards_Unclaimed = SeriesTree_Mining_Rewards_Unclaimed(client)
@@ -3719,12 +3729,12 @@ class SeriesTree_Cointime_Activity:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.coinblocks_created: BaseCumulativeSumPattern[StoredF64] = BaseCumulativeSumPattern(client, 'coinblocks_created')
self.coinblocks_stored: BaseCumulativeSumPattern[StoredF64] = BaseCumulativeSumPattern(client, 'coinblocks_stored')
self.coinblocks_created: AverageBaseCumulativeSumPattern[StoredF64] = AverageBaseCumulativeSumPattern(client, 'coinblocks_created')
self.coinblocks_stored: AverageBaseCumulativeSumPattern[StoredF64] = AverageBaseCumulativeSumPattern(client, 'coinblocks_stored')
self.liveliness: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'liveliness')
self.vaultedness: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'vaultedness')
self.ratio: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'activity_to_vaultedness')
self.coinblocks_destroyed: BaseCumulativeSumPattern[StoredF64] = BaseCumulativeSumPattern(client, 'coinblocks_destroyed')
self.coinblocks_destroyed: AverageBaseCumulativeSumPattern[StoredF64] = AverageBaseCumulativeSumPattern(client, 'coinblocks_destroyed')
class SeriesTree_Cointime_Supply:
"""Series tree node."""
@@ -3737,10 +3747,10 @@ class SeriesTree_Cointime_Value:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.destroyed: BaseCumulativeSumPattern[StoredF64] = BaseCumulativeSumPattern(client, 'cointime_value_destroyed')
self.created: BaseCumulativeSumPattern[StoredF64] = BaseCumulativeSumPattern(client, 'cointime_value_created')
self.stored: BaseCumulativeSumPattern[StoredF64] = BaseCumulativeSumPattern(client, 'cointime_value_stored')
self.vocdd: BaseCumulativeSumPattern[StoredF64] = BaseCumulativeSumPattern(client, 'vocdd')
self.destroyed: AverageBaseCumulativeSumPattern[StoredF64] = AverageBaseCumulativeSumPattern(client, 'cointime_value_destroyed')
self.created: AverageBaseCumulativeSumPattern[StoredF64] = AverageBaseCumulativeSumPattern(client, 'cointime_value_created')
self.stored: AverageBaseCumulativeSumPattern[StoredF64] = AverageBaseCumulativeSumPattern(client, 'cointime_value_stored')
self.vocdd: AverageBaseCumulativeSumPattern[StoredF64] = AverageBaseCumulativeSumPattern(client, 'vocdd')
class SeriesTree_Cointime_Cap:
"""Series tree node."""
@@ -4677,7 +4687,7 @@ class SeriesTree_Supply:
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.state: SeriesPattern18[SupplyState] = SeriesPattern18(client, 'supply_state')
self.circulating: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'circulating_supply')
self.burned: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'unspendable_supply')
self.burned: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'unspendable_supply')
self.inflation_rate: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'inflation_rate')
self.velocity: SeriesTree_Supply_Velocity = SeriesTree_Supply_Velocity(client)
self.market_cap: CentsDeltaUsdPattern = CentsDeltaUsdPattern(client, 'market_cap')
@@ -4698,8 +4708,8 @@ class SeriesTree_Cohorts_Utxo_All_Activity:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.transfer_volume: BaseCumulativeInSumPattern = BaseCumulativeInSumPattern(client, 'transfer_volume')
self.coindays_destroyed: BaseCumulativeSumPattern[StoredF64] = BaseCumulativeSumPattern(client, 'coindays_destroyed')
self.transfer_volume: AverageBaseCumulativeInSumPattern = AverageBaseCumulativeInSumPattern(client, 'transfer_volume')
self.coindays_destroyed: AverageBaseCumulativeSumPattern[StoredF64] = AverageBaseCumulativeSumPattern(client, 'coindays_destroyed')
self.coinyears_destroyed: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'coinyears_destroyed')
self.dormancy: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, 'dormancy')
@@ -4820,14 +4830,14 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Sopr_Adjusted:
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'asopr')
self.transfer_volume: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'adj_value_created')
self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'adj_value_destroyed')
self.transfer_volume: AverageBaseCumulativeSumPattern[Cents] = AverageBaseCumulativeSumPattern(client, 'adj_value_created')
self.value_destroyed: AverageBaseCumulativeSumPattern[Cents] = AverageBaseCumulativeSumPattern(client, 'adj_value_destroyed')
class SeriesTree_Cohorts_Utxo_All_Realized_Sopr:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'value_destroyed')
self.value_destroyed: AverageBaseCumulativeSumPattern[Cents] = AverageBaseCumulativeSumPattern(client, 'value_destroyed')
self.ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'sopr')
self.adjusted: SeriesTree_Cohorts_Utxo_All_Realized_Sopr_Adjusted = SeriesTree_Cohorts_Utxo_All_Realized_Sopr_Adjusted(client)
@@ -4842,7 +4852,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized:
self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'mvrv')
self.net_pnl: BaseChangeCumulativeDeltaSumToPattern = BaseChangeCumulativeDeltaSumToPattern(client, 'net')
self.sopr: SeriesTree_Cohorts_Utxo_All_Realized_Sopr = SeriesTree_Cohorts_Utxo_All_Realized_Sopr(client)
self.gross_pnl: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'realized_gross_pnl')
self.gross_pnl: BaseCumulativeSumPattern = BaseCumulativeSumPattern(client, 'realized_gross_pnl')
self.sell_side_risk_ratio: _1m1w1y24hPattern6 = _1m1w1y24hPattern6(client, 'sell_side_risk_ratio')
self.peak_regret: BaseCumulativeSumToPattern = BaseCumulativeSumToPattern(client, 'realized_peak_regret')
self.investor: PricePattern = PricePattern(client, 'investor_price')
@@ -4852,6 +4862,8 @@ class SeriesTree_Cohorts_Utxo_All_CostBasis:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.in_profit: PerPattern = PerPattern(client, 'cost_basis_in_profit_per')
self.in_loss: PerPattern = PerPattern(client, 'cost_basis_in_loss_per')
self.min: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'cost_basis_min')
self.max: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'cost_basis_max')
self.percentiles: Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern = Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern(client, 'cost_basis')
@@ -4903,6 +4915,8 @@ class SeriesTree_Cohorts_Utxo_All_Unrealized:
self.net_pnl: SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl = SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl(client)
self.gross_pnl: CentsUsdPattern2 = CentsUsdPattern2(client, 'unrealized_gross_pnl')
self.invested_capital: InPattern = InPattern(client, 'invested_capital_in')
self.investor_cap_in_profit_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, 'investor_cap_in_profit_raw')
self.investor_cap_in_loss_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, 'investor_cap_in_loss_raw')
self.sentiment: SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment = SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment(client)
class SeriesTree_Cohorts_Utxo_All:
@@ -4920,8 +4934,8 @@ class SeriesTree_Cohorts_Utxo_Sth_Activity:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.transfer_volume: BaseCumulativeInSumPattern = BaseCumulativeInSumPattern(client, 'sth_transfer_volume')
self.coindays_destroyed: BaseCumulativeSumPattern[StoredF64] = BaseCumulativeSumPattern(client, 'sth_coindays_destroyed')
self.transfer_volume: AverageBaseCumulativeInSumPattern = AverageBaseCumulativeInSumPattern(client, 'sth_transfer_volume')
self.coindays_destroyed: AverageBaseCumulativeSumPattern[StoredF64] = AverageBaseCumulativeSumPattern(client, 'sth_coindays_destroyed')
self.coinyears_destroyed: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'sth_coinyears_destroyed')
self.dormancy: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, 'sth_dormancy')
@@ -5042,14 +5056,14 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized_Sopr_Adjusted:
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'sth_asopr')
self.transfer_volume: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'sth_adj_value_created')
self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'sth_adj_value_destroyed')
self.transfer_volume: AverageBaseCumulativeSumPattern[Cents] = AverageBaseCumulativeSumPattern(client, 'sth_adj_value_created')
self.value_destroyed: AverageBaseCumulativeSumPattern[Cents] = AverageBaseCumulativeSumPattern(client, 'sth_adj_value_destroyed')
class SeriesTree_Cohorts_Utxo_Sth_Realized_Sopr:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'sth_value_destroyed')
self.value_destroyed: AverageBaseCumulativeSumPattern[Cents] = AverageBaseCumulativeSumPattern(client, 'sth_value_destroyed')
self.ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'sth_sopr')
self.adjusted: SeriesTree_Cohorts_Utxo_Sth_Realized_Sopr_Adjusted = SeriesTree_Cohorts_Utxo_Sth_Realized_Sopr_Adjusted(client)
@@ -5064,7 +5078,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized:
self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_mvrv')
self.net_pnl: BaseChangeCumulativeDeltaSumToPattern = BaseChangeCumulativeDeltaSumToPattern(client, 'sth_net')
self.sopr: SeriesTree_Cohorts_Utxo_Sth_Realized_Sopr = SeriesTree_Cohorts_Utxo_Sth_Realized_Sopr(client)
self.gross_pnl: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'sth_realized_gross_pnl')
self.gross_pnl: BaseCumulativeSumPattern = BaseCumulativeSumPattern(client, 'sth_realized_gross_pnl')
self.sell_side_risk_ratio: _1m1w1y24hPattern6 = _1m1w1y24hPattern6(client, 'sth_sell_side_risk_ratio')
self.peak_regret: BaseCumulativeSumToPattern = BaseCumulativeSumToPattern(client, 'sth_realized_peak_regret')
self.investor: PricePattern = PricePattern(client, 'sth_investor_price')
@@ -5074,6 +5088,8 @@ class SeriesTree_Cohorts_Utxo_Sth_CostBasis:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.in_profit: PerPattern = PerPattern(client, 'sth_cost_basis_in_profit_per')
self.in_loss: PerPattern = PerPattern(client, 'sth_cost_basis_in_loss_per')
self.min: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'sth_cost_basis_min')
self.max: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'sth_cost_basis_max')
self.percentiles: Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern = Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern(client, 'sth_cost_basis')
@@ -5098,6 +5114,8 @@ class SeriesTree_Cohorts_Utxo_Sth_Unrealized:
self.net_pnl: CentsToUsdPattern3 = CentsToUsdPattern3(client, 'sth_net_unrealized_pnl')
self.gross_pnl: CentsUsdPattern2 = CentsUsdPattern2(client, 'sth_unrealized_gross_pnl')
self.invested_capital: InPattern = InPattern(client, 'sth_invested_capital_in')
self.investor_cap_in_profit_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, 'sth_investor_cap_in_profit_raw')
self.investor_cap_in_loss_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, 'sth_investor_cap_in_loss_raw')
self.sentiment: SeriesTree_Cohorts_Utxo_Sth_Unrealized_Sentiment = SeriesTree_Cohorts_Utxo_Sth_Unrealized_Sentiment(client)
class SeriesTree_Cohorts_Utxo_Sth:
@@ -5115,8 +5133,8 @@ class SeriesTree_Cohorts_Utxo_Lth_Activity:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.transfer_volume: BaseCumulativeInSumPattern = BaseCumulativeInSumPattern(client, 'lth_transfer_volume')
self.coindays_destroyed: BaseCumulativeSumPattern[StoredF64] = BaseCumulativeSumPattern(client, 'lth_coindays_destroyed')
self.transfer_volume: AverageBaseCumulativeInSumPattern = AverageBaseCumulativeInSumPattern(client, 'lth_transfer_volume')
self.coindays_destroyed: AverageBaseCumulativeSumPattern[StoredF64] = AverageBaseCumulativeSumPattern(client, 'lth_coindays_destroyed')
self.coinyears_destroyed: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'lth_coinyears_destroyed')
self.dormancy: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, 'lth_dormancy')
@@ -5236,7 +5254,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized_Sopr:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.value_destroyed: BaseCumulativeSumPattern[Cents] = BaseCumulativeSumPattern(client, 'lth_value_destroyed')
self.value_destroyed: AverageBaseCumulativeSumPattern[Cents] = AverageBaseCumulativeSumPattern(client, 'lth_value_destroyed')
self.ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'lth_sopr')
class SeriesTree_Cohorts_Utxo_Lth_Realized:
@@ -5250,7 +5268,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized:
self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_mvrv')
self.net_pnl: BaseChangeCumulativeDeltaSumToPattern = BaseChangeCumulativeDeltaSumToPattern(client, 'lth_net')
self.sopr: SeriesTree_Cohorts_Utxo_Lth_Realized_Sopr = SeriesTree_Cohorts_Utxo_Lth_Realized_Sopr(client)
self.gross_pnl: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'lth_realized_gross_pnl')
self.gross_pnl: BaseCumulativeSumPattern = BaseCumulativeSumPattern(client, 'lth_realized_gross_pnl')
self.sell_side_risk_ratio: _1m1w1y24hPattern6 = _1m1w1y24hPattern6(client, 'lth_sell_side_risk_ratio')
self.peak_regret: BaseCumulativeSumToPattern = BaseCumulativeSumToPattern(client, 'lth_realized_peak_regret')
self.investor: PricePattern = PricePattern(client, 'lth_investor_price')
@@ -5260,6 +5278,8 @@ class SeriesTree_Cohorts_Utxo_Lth_CostBasis:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.in_profit: PerPattern = PerPattern(client, 'lth_cost_basis_in_profit_per')
self.in_loss: PerPattern = PerPattern(client, 'lth_cost_basis_in_loss_per')
self.min: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'lth_cost_basis_min')
self.max: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'lth_cost_basis_max')
self.percentiles: Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern = Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern(client, 'lth_cost_basis')
@@ -5284,6 +5304,8 @@ class SeriesTree_Cohorts_Utxo_Lth_Unrealized:
self.net_pnl: CentsToUsdPattern3 = CentsToUsdPattern3(client, 'lth_net_unrealized_pnl')
self.gross_pnl: CentsUsdPattern2 = CentsUsdPattern2(client, 'lth_unrealized_gross_pnl')
self.invested_capital: InPattern = InPattern(client, 'lth_invested_capital_in')
self.investor_cap_in_profit_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, 'lth_investor_cap_in_profit_raw')
self.investor_cap_in_loss_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, 'lth_investor_cap_in_loss_raw')
self.sentiment: SeriesTree_Cohorts_Utxo_Lth_Unrealized_Sentiment = SeriesTree_Cohorts_Utxo_Lth_Unrealized_Sentiment(client)
class SeriesTree_Cohorts_Utxo_Lth:
@@ -5549,27 +5571,27 @@ class SeriesTree_Cohorts_Utxo_Matured:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.under_1h: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_under_1h_old_matured_supply')
self._1h_to_1d: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_1h_to_1d_old_matured_supply')
self._1d_to_1w: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_1d_to_1w_old_matured_supply')
self._1w_to_1m: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_1w_to_1m_old_matured_supply')
self._1m_to_2m: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_1m_to_2m_old_matured_supply')
self._2m_to_3m: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_2m_to_3m_old_matured_supply')
self._3m_to_4m: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_3m_to_4m_old_matured_supply')
self._4m_to_5m: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_4m_to_5m_old_matured_supply')
self._5m_to_6m: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_5m_to_6m_old_matured_supply')
self._6m_to_1y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_6m_to_1y_old_matured_supply')
self._1y_to_2y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_1y_to_2y_old_matured_supply')
self._2y_to_3y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_2y_to_3y_old_matured_supply')
self._3y_to_4y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_3y_to_4y_old_matured_supply')
self._4y_to_5y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_4y_to_5y_old_matured_supply')
self._5y_to_6y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_5y_to_6y_old_matured_supply')
self._6y_to_7y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_6y_to_7y_old_matured_supply')
self._7y_to_8y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_7y_to_8y_old_matured_supply')
self._8y_to_10y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_8y_to_10y_old_matured_supply')
self._10y_to_12y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_10y_to_12y_old_matured_supply')
self._12y_to_15y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_12y_to_15y_old_matured_supply')
self.over_15y: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, 'utxos_over_15y_old_matured_supply')
self.under_1h: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_under_1h_old_matured_supply')
self._1h_to_1d: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_1h_to_1d_old_matured_supply')
self._1d_to_1w: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_1d_to_1w_old_matured_supply')
self._1w_to_1m: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_1w_to_1m_old_matured_supply')
self._1m_to_2m: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_1m_to_2m_old_matured_supply')
self._2m_to_3m: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_2m_to_3m_old_matured_supply')
self._3m_to_4m: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_3m_to_4m_old_matured_supply')
self._4m_to_5m: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_4m_to_5m_old_matured_supply')
self._5m_to_6m: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_5m_to_6m_old_matured_supply')
self._6m_to_1y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_6m_to_1y_old_matured_supply')
self._1y_to_2y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_1y_to_2y_old_matured_supply')
self._2y_to_3y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_2y_to_3y_old_matured_supply')
self._3y_to_4y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_3y_to_4y_old_matured_supply')
self._4y_to_5y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_4y_to_5y_old_matured_supply')
self._5y_to_6y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_5y_to_6y_old_matured_supply')
self._6y_to_7y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_6y_to_7y_old_matured_supply')
self._7y_to_8y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_7y_to_8y_old_matured_supply')
self._8y_to_10y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_8y_to_10y_old_matured_supply')
self._10y_to_12y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_10y_to_12y_old_matured_supply')
self._12y_to_15y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_12y_to_15y_old_matured_supply')
self.over_15y: AverageBaseCumulativeSumPattern3 = AverageBaseCumulativeSumPattern3(client, 'utxos_over_15y_old_matured_supply')
class SeriesTree_Cohorts_Utxo:
"""Series tree node."""

View File

@@ -284,6 +284,7 @@ export function createCointimeSection() {
name,
color,
base: pattern.base,
average: pattern.average,
rolling: pattern.sum,
cumulative: pattern.cumulative,
})),
@@ -335,6 +336,7 @@ export function createCointimeSection() {
name,
color,
base: pattern.base,
average: pattern.average,
rolling: pattern.sum,
cumulative: pattern.cumulative,
})),
@@ -342,6 +344,7 @@ export function createCointimeSection() {
name: vocdd.name,
color: vocdd.color,
base: vocdd.pattern.base,
average: vocdd.pattern.average,
rolling: vocdd.pattern.sum,
cumulative: vocdd.pattern.cumulative,
},

View File

@@ -2,18 +2,16 @@
* Cost Basis section builders
*
* Structure:
* - Summary: Key stats (avg + median active, quartiles/extremes available)
* - By Coin: BTC-weighted percentiles (IQR active: p25, p50, p75)
* - By Capital: USD-weighted percentiles (IQR active: p25, p50, p75)
* - Supply Density: Cost basis supply density percentage
* - Per Coin: sats-weighted (profitability + distribution)
* - Per Dollar: value-weighted (profitability + distribution)
* - Profitability: cross-cutting (per coin + per dollar on same chart)
* - Supply Density: cost basis supply density percentage
*
* Only for cohorts WITH costBasis (All, STH, LTH)
*/
import { colors } from "../../utils/colors.js";
import { entries } from "../../utils/array.js";
import { Unit } from "../../utils/units.js";
import { priceLines } from "../constants.js";
import { price, percentRatio } from "../series.js";
import { mapCohortsWithAll, flatMapCohortsWithAll } from "../shared.js";
@@ -24,7 +22,7 @@ const ACTIVE_PCTS = new Set(["pct75", "pct50", "pct25"]);
* @param {(name: string) => string} [n]
* @returns {FetchedPriceSeriesBlueprint[]}
*/
function createCorePercentileSeries(p, n = (x) => x) {
function percentileSeries(p, n = (x) => x) {
return entries(p)
.reverse()
.map(([key, s], i, arr) =>
@@ -37,104 +35,46 @@ function createCorePercentileSeries(p, n = (x) => x) {
);
}
/**
* @param {CohortAll | CohortFull | CohortLongTerm} cohort
* @returns {FetchedPriceSeriesBlueprint[]}
*/
function createSingleSummarySeries(cohort) {
const { color, tree } = cohort;
const p = tree.costBasis.percentiles;
return [
price({ series: tree.realized.price, name: "Average", color }),
price({
series: tree.costBasis.max,
name: "Max (p100)",
color: colors.stat.max,
defaultActive: false,
}),
price({
series: p.pct75,
name: "Q3 (p75)",
color: colors.stat.pct75,
defaultActive: false,
}),
price({ series: p.pct50, name: "Median (p50)", color: colors.stat.median }),
price({
series: p.pct25,
name: "Q1 (p25)",
color: colors.stat.pct25,
defaultActive: false,
}),
price({
series: tree.costBasis.min,
name: "Min (p0)",
color: colors.stat.min,
defaultActive: false,
}),
];
}
// ============================================================================
// Single cohort helpers
// ============================================================================
/**
* @param {readonly (CohortAll | CohortFull | CohortLongTerm)[]} list
* @param {CohortAll} all
* @returns {FetchedPriceSeriesBlueprint[]}
* Per Coin or Per Dollar folder for a single cohort
* @param {Object} args
* @param {AnyPricePattern} args.avgPrice - realized price (per coin) or investor price (per dollar)
* @param {string} args.avgName
* @param {AnyPricePattern} args.inProfit
* @param {AnyPricePattern} args.inLoss
* @param {PercentilesPattern} args.percentiles
* @param {Color} args.color
* @param {string} args.weightLabel
* @param {(name: string) => string} args.title
* @param {AnyPricePattern} [args.min]
* @param {AnyPricePattern} [args.max]
* @returns {PartialOptionsTree}
*/
function createGroupedSummarySeries(list, all) {
return mapCohortsWithAll(list, all, ({ name, color, tree }) =>
price({ series: tree.realized.price, name, color }),
);
}
/**
* @param {CohortAll | CohortFull | CohortLongTerm} cohort
* @returns {FetchedPriceSeriesBlueprint[]}
*/
function createSingleByCoinSeries(cohort) {
const { color, tree } = cohort;
const cb = tree.costBasis;
function singleWeightFolder({ avgPrice, avgName, inProfit, inLoss, percentiles, color, weightLabel, title, min, max }) {
return [
price({ series: tree.realized.price, name: "Average", color }),
price({
series: cb.max,
name: "p100",
color: colors.stat.max,
defaultActive: false,
}),
...createCorePercentileSeries(cb.percentiles),
price({
series: cb.min,
name: "p0",
color: colors.stat.min,
defaultActive: false,
}),
];
}
/**
* @param {CohortAll | CohortFull | CohortLongTerm} cohort
* @returns {FetchedPriceSeriesBlueprint[]}
*/
function createSingleByCapitalSeries(cohort) {
const { color, tree } = cohort;
return [
price({ series: tree.realized.investor.price, name: "Average", color }),
...createCorePercentileSeries(tree.costBasis.investedCapital),
];
}
/**
* @param {CohortAll | CohortFull | CohortLongTerm} cohort
* @returns {AnyFetchedSeriesBlueprint[]}
*/
function createSingleSupplyDensitySeries(cohort) {
const { tree } = cohort;
return [
...percentRatio({
pattern: tree.costBasis.supplyDensity,
name: "Supply Density",
color: colors.bitcoin,
}),
...priceLines({ numbers: [100, 50, 0], unit: Unit.percentage }),
{
name: "Profitability",
title: title(`Cost Basis Profitability (${weightLabel})`),
top: [
price({ series: inProfit, name: "In Profit", color: colors.profit }),
price({ series: avgPrice, name: avgName, color }),
price({ series: inLoss, name: "In Loss", color: colors.loss }),
],
},
{
name: "Distribution",
title: title(`Cost Basis Distribution (${weightLabel})`),
top: [
price({ series: avgPrice, name: avgName, color }),
...(max ? [price({ series: max, name: "p100", color: colors.stat.max, defaultActive: false })] : []),
...percentileSeries(percentiles),
...(min ? [price({ series: min, name: "p0", color: colors.stat.min, defaultActive: false })] : []),
],
},
];
}
@@ -143,125 +83,171 @@ function createSingleSupplyDensitySeries(cohort) {
* @returns {PartialOptionsGroup}
*/
export function createCostBasisSectionWithPercentiles({ cohort, title }) {
const { tree, color } = cohort;
const cb = tree.costBasis;
return {
name: "Cost Basis",
tree: [
{
name: "Summary",
title: title("Cost Basis Summary"),
top: createSingleSummarySeries(cohort),
name: "Per Coin",
tree: singleWeightFolder({
avgPrice: tree.realized.price, avgName: "Average",
inProfit: cb.inProfit.perCoin, inLoss: cb.inLoss.perCoin,
percentiles: cb.percentiles, color, weightLabel: "BTC-weighted", title,
min: cb.min, max: cb.max,
}),
},
{
name: "By Coin",
title: title("Cost Basis Distribution (BTC-weighted)"),
top: createSingleByCoinSeries(cohort),
name: "Per Dollar",
tree: singleWeightFolder({
avgPrice: tree.realized.investor.price, avgName: "Average",
inProfit: cb.inProfit.perDollar, inLoss: cb.inLoss.perDollar,
percentiles: cb.investedCapital, color, weightLabel: "USD-weighted", title,
}),
},
{
name: "By Capital",
title: title("Cost Basis Distribution (USD-weighted)"),
top: createSingleByCapitalSeries(cohort),
name: "Profitability",
tree: [
{
name: "In Profit",
title: title("Cost Basis In Profit"),
top: [
price({ series: cb.inProfit.perCoin, name: "Per Coin", color: colors.realized }),
price({ series: cb.inProfit.perDollar, name: "Per Dollar", color: colors.investor }),
],
},
{
name: "In Loss",
title: title("Cost Basis In Loss"),
top: [
price({ series: cb.inLoss.perCoin, name: "Per Coin", color: colors.realized }),
price({ series: cb.inLoss.perDollar, name: "Per Dollar", color: colors.investor }),
],
},
],
},
{
name: "Supply Density",
title: title("Cost Basis Supply Density"),
bottom: createSingleSupplyDensitySeries(cohort),
bottom: percentRatio({ pattern: cb.supplyDensity, name: "Supply Density", color: colors.bitcoin }),
},
],
};
}
// ============================================================================
// Grouped cohort helpers
// ============================================================================
/**
* Per Coin or Per Dollar folder for grouped cohorts
* @param {Object} args
* @param {readonly (CohortAll | CohortFull | CohortLongTerm)[]} args.list
* @param {CohortAll} args.all
* @param {(c: CohortAll | CohortFull | CohortLongTerm) => AnyPricePattern} args.getAvgPrice
* @param {(c: CohortAll | CohortFull | CohortLongTerm) => AnyPricePattern} args.getInProfit
* @param {(c: CohortAll | CohortFull | CohortLongTerm) => AnyPricePattern} args.getInLoss
* @param {(c: CohortAll | CohortFull | CohortLongTerm) => PercentilesPattern} args.getPercentiles
* @param {string} args.avgTitle
* @param {string} args.weightLabel
* @param {(name: string) => string} args.title
* @returns {PartialOptionsTree}
*/
function groupedWeightFolder({ list, all, getAvgPrice, getInProfit, getInLoss, getPercentiles, avgTitle, weightLabel, title }) {
return [
{
name: "Profitability",
tree: [
{
name: "In Profit",
title: title(`Cost Basis In Profit (${weightLabel})`),
top: mapCohortsWithAll(list, all, (c) =>
price({ series: getInProfit(c), name: c.name, color: c.color }),
),
},
{
name: "In Loss",
title: title(`Cost Basis In Loss (${weightLabel})`),
top: mapCohortsWithAll(list, all, (c) =>
price({ series: getInLoss(c), name: c.name, color: c.color }),
),
},
],
},
{
name: "Distribution",
tree: [
{
name: "Average",
title: title(`${avgTitle} Comparison`),
top: mapCohortsWithAll(list, all, (c) =>
price({ series: getAvgPrice(c), name: c.name, color: c.color }),
),
},
...(/** @type {const} */ ([
["pct50", "Median"],
["pct75", "Q3"],
["pct25", "Q1"],
])).map(([pct, label]) => ({
name: label,
title: title(`Cost Basis ${label} (${weightLabel})`),
top: mapCohortsWithAll(list, all, (c) =>
price({ series: getPercentiles(c)[pct], name: c.name, color: c.color }),
),
})),
],
},
];
}
/**
* @param {{ list: readonly (CohortAll | CohortFull | CohortLongTerm)[], all: CohortAll, title: (name: string) => string }} args
* @returns {PartialOptionsGroup}
*/
export function createGroupedCostBasisSectionWithPercentiles({
list,
all,
title,
}) {
export function createGroupedCostBasisSectionWithPercentiles({ list, all, title }) {
return {
name: "Cost Basis",
tree: [
{
name: "Summary",
title: title("Cost Basis Summary"),
top: createGroupedSummarySeries(list, all),
name: "Per Coin",
tree: groupedWeightFolder({
list, all, title,
getAvgPrice: (c) => c.tree.realized.price,
getInProfit: (c) => c.tree.costBasis.inProfit.perCoin,
getInLoss: (c) => c.tree.costBasis.inLoss.perCoin,
getPercentiles: (c) => c.tree.costBasis.percentiles,
avgTitle: "Average", weightLabel: "BTC-weighted",
}),
},
{
name: "By Coin",
tree: [
{
name: "Average",
title: title("Realized Price Comparison"),
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
price({ series: tree.realized.price, name, color }),
),
},
{
name: "Median",
title: title("Cost Basis Median (BTC-weighted)"),
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
price({ series: tree.costBasis.percentiles.pct50, name, color }),
),
},
{
name: "Q3",
title: title("Cost Basis Q3 (BTC-weighted)"),
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
price({ series: tree.costBasis.percentiles.pct75, name, color }),
),
},
{
name: "Q1",
title: title("Cost Basis Q1 (BTC-weighted)"),
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
price({ series: tree.costBasis.percentiles.pct25, name, color }),
),
},
],
name: "Per Dollar",
tree: groupedWeightFolder({
list, all, title,
getAvgPrice: (c) => c.tree.realized.investor.price,
getInProfit: (c) => c.tree.costBasis.inProfit.perDollar,
getInLoss: (c) => c.tree.costBasis.inLoss.perDollar,
getPercentiles: (c) => c.tree.costBasis.investedCapital,
avgTitle: "Average", weightLabel: "USD-weighted",
}),
},
{
name: "By Capital",
name: "Profitability",
tree: [
{
name: "Average",
title: title("Investor Price Comparison"),
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
price({ series: tree.realized.investor.price, name, color }),
),
name: "In Profit",
title: title("Cost Basis In Profit"),
top: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [
price({ series: tree.costBasis.inProfit.perCoin, name: `${name} (coin)`, color }),
price({ series: tree.costBasis.inProfit.perDollar, name: `${name} (dollar)`, color }),
]),
},
{
name: "Median",
title: title("Cost Basis Median (USD-weighted)"),
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
price({
series: tree.costBasis.investedCapital.pct50,
name,
color,
}),
),
},
{
name: "Q3",
title: title("Cost Basis Q3 (USD-weighted)"),
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
price({
series: tree.costBasis.investedCapital.pct75,
name,
color,
}),
),
},
{
name: "Q1",
title: title("Cost Basis Q1 (USD-weighted)"),
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
price({
series: tree.costBasis.investedCapital.pct25,
name,
color,
}),
),
name: "In Loss",
title: title("Cost Basis In Loss"),
top: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [
price({ series: tree.costBasis.inLoss.perCoin, name: `${name} (coin)`, color }),
price({ series: tree.costBasis.inLoss.perDollar, name: `${name} (dollar)`, color }),
]),
},
],
},
@@ -269,11 +255,7 @@ export function createGroupedCostBasisSectionWithPercentiles({
name: "Supply Density",
title: title("Cost Basis Supply Density"),
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
percentRatio({
pattern: tree.costBasis.supplyDensity,
name,
color,
}),
percentRatio({ pattern: tree.costBasis.supplyDensity, name, color }),
),
},
],

View File

@@ -510,6 +510,7 @@ export function createNetworkSection() {
name: "Created",
color: colors.entity.output,
base: outputs.count.total.sum,
average: outputs.count.total.rolling.average,
rolling: outputs.count.total.rolling.sum,
cumulative: outputs.count.total.cumulative,
},
@@ -517,6 +518,7 @@ export function createNetworkSection() {
name: "Spent",
color: colors.entity.input,
base: inputs.count.sum,
average: inputs.count.rolling.average,
rolling: inputs.count.rolling.sum,
cumulative: inputs.count.cumulative,
},

View File

@@ -84,12 +84,13 @@ export function price({
/**
* Create percentile series (max/min/median/pct75/pct25/pct90/pct10) from any stats pattern
* @param {DistributionStats} pattern
* @param {Unit} unit
* @param {string} title
* @param {Object} args
* @param {DistributionStats} args.pattern
* @param {Unit} args.unit
* @param {string} [args.title]
* @returns {AnyFetchedSeriesBlueprint[]}
*/
function percentileSeries(pattern, unit, title) {
function percentileSeries({ pattern, unit, title = "" }) {
const { stat } = colors;
return [
dots({
@@ -418,9 +419,7 @@ export function fromBaseStatsPattern({
unit,
title = "",
baseColor,
avgActive = true,
}) {
const { stat } = colors;
const stats = statsAtWindow(pattern, window);
return [
dots({
@@ -429,46 +428,17 @@ export function fromBaseStatsPattern({
color: baseColor,
unit,
}),
dots({
series: stats.average,
name: `${title} avg`.trim(),
color: stat.avg,
unit,
defaultActive: avgActive,
}),
...percentileSeries(stats, unit, title),
...percentileSeries({ pattern: stats, unit, title }),
];
}
/**
* Create series from a flat stats pattern (average + pct percentiles as single series)
* Use statsAtWindow() to extract from patterns with _1y24h30d7dPattern stats
* @param {Object} args
* @param {{ average: AnySeriesPattern, median: AnySeriesPattern, max: AnySeriesPattern, min: AnySeriesPattern, pct75: AnySeriesPattern, pct25: AnySeriesPattern, pct90: AnySeriesPattern, pct10: AnySeriesPattern }} args.pattern
* @param {Unit} args.unit
* @param {string} [args.title]
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function fromStatsPattern({ pattern, unit, title = "" }) {
return [
{
type: "Dots",
series: pattern.average,
title: `${title} avg`.trim(),
unit,
},
...percentileSeries(pattern, unit, title),
];
}
/**
* Extract stats at a specific rolling window from patterns with _1y24h30d7dPattern stats
* @param {Record<string, any>} pattern - Pattern with pct10/pct25/pct75/pct90 and average/median/max/min as _1y24h30d7dPattern
* Extract stats at a specific rolling window
* @param {Record<string, any>} pattern - Pattern with pct10/pct25/pct75/pct90 and median/max/min as _1y24h30d7dPattern
* @param {string} window
*/
export function statsAtWindow(pattern, window) {
return {
average: pattern.average[window],
median: pattern.median[window],
max: pattern.max[window],
min: pattern.min[window],
@@ -601,10 +571,10 @@ export function distributionWindowsTree({ pattern, base, title, unit }) {
tree: [
{
name: "Compare",
title: `${title} Average`,
title: `${title} Median`,
bottom: ROLLING_WINDOWS.map((w) =>
line({
series: pattern.average[w.key],
series: pattern.median[w.key],
name: w.name,
color: w.color,
unit,
@@ -616,10 +586,7 @@ export function distributionWindowsTree({ pattern, base, title, unit }) {
title: `${title} Distribution (${w.title})`,
bottom: [
...(base ? [line({ series: base, name: "base", unit })] : []),
...fromStatsPattern({
pattern: statsAtWindow(pattern, w.key),
unit,
}),
...percentileSeries({ pattern: statsAtWindow(pattern, w.key), unit }),
],
})),
],
@@ -628,12 +595,11 @@ export function distributionWindowsTree({ pattern, base, title, unit }) {
/**
* Map a rolling window slot's stats to a specific unit, producing a stats-compatible pattern
* @param {{ average: Record<string, AnySeriesPattern>, median: Record<string, AnySeriesPattern>, max: Record<string, AnySeriesPattern>, min: Record<string, AnySeriesPattern>, pct75: Record<string, AnySeriesPattern>, pct25: Record<string, AnySeriesPattern>, pct90: Record<string, AnySeriesPattern>, pct10: Record<string, AnySeriesPattern> }} slot - Rolling window slot with multi-currency stats
* @param {{ median: Record<string, AnySeriesPattern>, max: Record<string, AnySeriesPattern>, min: Record<string, AnySeriesPattern>, pct75: Record<string, AnySeriesPattern>, pct25: Record<string, AnySeriesPattern>, pct90: Record<string, AnySeriesPattern>, pct10: Record<string, AnySeriesPattern> }} slot - Rolling window slot with multi-currency stats
* @param {BtcSatsUsdKey} unitKey
*/
function rollingSlotForUnit(slot, unitKey) {
return {
average: slot.average[unitKey],
median: slot.median[unitKey],
max: slot.max[unitKey],
min: slot.min[unitKey],
@@ -646,19 +612,19 @@ function rollingSlotForUnit(slot, unitKey) {
/**
* Create distribution series for btc/sats/usd from a rolling window slot
* @param {{ average: Record<string, AnySeriesPattern>, median: Record<string, AnySeriesPattern>, max: Record<string, AnySeriesPattern>, min: Record<string, AnySeriesPattern>, pct75: Record<string, AnySeriesPattern>, pct25: Record<string, AnySeriesPattern>, pct90: Record<string, AnySeriesPattern>, pct10: Record<string, AnySeriesPattern> }} slot - Rolling window slot with multi-currency stats
* @param {{ median: Record<string, AnySeriesPattern>, max: Record<string, AnySeriesPattern>, min: Record<string, AnySeriesPattern>, pct75: Record<string, AnySeriesPattern>, pct25: Record<string, AnySeriesPattern>, pct90: Record<string, AnySeriesPattern>, pct10: Record<string, AnySeriesPattern> }} slot - Rolling window slot with multi-currency stats
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export const distributionBtcSatsUsd = (slot) => [
...fromStatsPattern({
...percentileSeries({
pattern: rollingSlotForUnit(slot, "btc"),
unit: Unit.btc,
}),
...fromStatsPattern({
...percentileSeries({
pattern: rollingSlotForUnit(slot, "sats"),
unit: Unit.sats,
}),
...fromStatsPattern({
...percentileSeries({
pattern: rollingSlotForUnit(slot, "usd"),
unit: Unit.usd,
}),
@@ -907,11 +873,7 @@ export function chartsFromFull({
? `${title} ${distributionSuffix}`
: title;
return [
{
name: "Per Block",
title,
bottom: [{ series: pattern.base, title: "base", unit }],
},
averagesTree({ windows: pattern.average, title, unit }),
sumsTree({ windows: pattern.sum, title, unit }),
distributionWindowsTree({ pattern, title: distTitle, unit }),
{
@@ -948,16 +910,11 @@ export function chartsFromAggregated({
unit,
distributionSuffix = "",
}) {
const { stat } = colors;
const distTitle = distributionSuffix
? `${title} ${distributionSuffix}`
: title;
return [
{
name: "Per Block",
title,
bottom: [{ series: pattern.sum, title: "base", color: stat.sum, unit }],
},
averagesTree({ windows: pattern.rolling.average, title, unit }),
sumsTree({ windows: pattern.rolling.sum, title, unit }),
distributionWindowsTree({
pattern: pattern.rolling,
@@ -996,12 +953,12 @@ export function chartsFromBlockAnd6b({ pattern, title, unit }) {
{
name: "Block",
title: `${title} (Block)`,
bottom: fromStatsPattern({ pattern: pattern.block, unit }),
bottom: percentileSeries({ pattern: pattern.block, unit }),
},
{
name: "Hourly",
title: `${title} (Hourly)`,
bottom: fromStatsPattern({ pattern: pattern._6b, unit }),
bottom: percentileSeries({ pattern: pattern._6b, unit }),
},
];
}
@@ -1037,11 +994,7 @@ export function chartsFromSumsCumulative({ pattern, title, unit, color }) {
*/
export function chartsFromCount({ pattern, title, unit, color }) {
return [
{
name: "Per Block",
title,
bottom: [{ series: pattern.base, title: "base", color, unit }],
},
averagesTree({ windows: pattern.average, title, unit }),
...chartsFromSumsCumulative({ pattern, title, unit, color }),
];
}
@@ -1060,6 +1013,7 @@ export function chartsFromCountEntries({ entries, title, unit }) {
name,
color: colors.at(i, arr.length),
base: data.base,
average: data.average,
rolling: data.sum,
cumulative: data.cumulative,
})),
@@ -1071,7 +1025,7 @@ export function chartsFromCountEntries({ entries, title, unit }) {
/**
* Per Block + Sums + Cumulative tree for multiple named series shown side-by-side
* @param {Object} args
* @param {Array<{ name: string, color: Color, base: AnySeriesPattern, rolling: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, cumulative: AnySeriesPattern }>} args.entries
* @param {Array<{ name: string, color: Color, base: AnySeriesPattern, average: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, rolling: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, cumulative: AnySeriesPattern }>} args.entries
* @param {string} args.title
* @param {Unit} args.unit
* @returns {PartialOptionsTree}
@@ -1079,10 +1033,17 @@ export function chartsFromCountEntries({ entries, title, unit }) {
export function multiSeriesTree({ entries, title, unit }) {
return [
{
name: "Per Block",
title,
bottom: entries.map((e) =>
line({ series: e.base, name: e.name, color: e.color, unit }),
name: "Compare",
title: `${title} Average`,
bottom: ROLLING_WINDOWS.flatMap((w) =>
entries.map((e) =>
line({
series: e.average[w.key],
name: `${e.name} ${w.name}`,
color: e.color,
unit,
}),
),
),
},
...ROLLING_WINDOWS.map((w) => ({

View File

@@ -224,21 +224,35 @@ export function satsBtcUsdRolling({ pattern, name, color, defaultActive }) {
* @param {Color} [args.color]
* @returns {PartialOptionsTree}
*/
export function satsBtcUsdFullTree({ pattern, name, title, color }) {
export function satsBtcUsdFullTree({ pattern, title, color }) {
return [
{
name: "Per Block",
title,
bottom: satsBtcUsd({ pattern: pattern.base, name, color }),
name: "Compare",
title: `${title} Average`,
bottom: ROLLING_WINDOWS.flatMap((w) =>
satsBtcUsd({
pattern: pattern.average[w.key],
name: w.name,
color: w.color,
}),
),
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} ${w.title} Sum`,
bottom: satsBtcUsd({
pattern: pattern.sum[w.key],
name: w.name,
color: w.color,
}),
title: `${title} ${w.title}`,
bottom: [
...satsBtcUsd({
pattern: pattern.sum[w.key],
name: "Sum",
color: w.color,
}),
...satsBtcUsd({
pattern: pattern.average[w.key],
name: "Avg",
color: w.color,
defaultActive: false,
}),
],
})),
{
name: "Cumulative",

View File

@@ -58,10 +58,10 @@
* @typedef {Brk.BpsCentsPercentilesRatioSatsUsdPattern} PriceRatioPercentilesPattern
* AnyRatioPattern: full ratio pattern with percentiles, SMAs, and std dev bands
* @typedef {Brk.BpsCentsPercentilesRatioSatsSmaStdUsdPattern} AnyRatioPattern
* FullValuePattern: base + cumulative + rolling windows (sats/btc/cents/usd)
* @typedef {Brk.BaseCumulativeSumPattern3} FullValuePattern
* RollingWindowSlot: a single rolling window with stats (average, pct10, pct25, median, pct75, pct90, max, min) per unit
* @typedef {Brk.AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern<number>} RollingWindowSlot
* FullValuePattern: base + cumulative + sum + average rolling windows (sats/btc/cents/usd)
* @typedef {Brk.AverageBaseCumulativeSumPattern3} FullValuePattern
* RollingWindowSlot: a single rolling window with stats (pct10, pct25, median, pct75, pct90, max, min) per unit
* @typedef {Brk.MaxMedianMinPct10Pct25Pct75Pct90Pattern<number>} RollingWindowSlot
* @typedef {Brk.AnySeriesPattern} AnySeriesPattern
* @typedef {Brk.CentsSatsUsdPattern} ActivePricePattern
* @typedef {Brk.AnySeriesEndpoint} AnySeriesEndpoint
@@ -73,9 +73,9 @@
* - FullRelativePattern: has BOTH RelToMarketCap AND RelToOwnMarketCap
* @typedef {Brk.LossNetNuplProfitPattern} BasicRelativePattern
* @typedef {Brk.LossNetNuplProfitPattern} GlobalRelativePattern
* @typedef {Brk.GrossInvestedLossNetNuplProfitSentimentPattern2} OwnRelativePattern
* @typedef {Brk.GrossInvestedLossNetNuplProfitSentimentPattern2} FullRelativePattern
* @typedef {Brk.GrossInvestedLossNetNuplProfitSentimentPattern2} UnrealizedPattern
* @typedef {Brk.GrossInvestedInvestorLossNetNuplProfitSentimentPattern2} OwnRelativePattern
* @typedef {Brk.GrossInvestedInvestorLossNetNuplProfitSentimentPattern2} FullRelativePattern
* @typedef {Brk.GrossInvestedInvestorLossNetNuplProfitSentimentPattern2} UnrealizedPattern
*
* Profitability bucket pattern (supply + realized_cap + nupl)
* @typedef {Brk.NuplRealizedSupplyPattern} RealizedSupplyPattern
@@ -87,10 +87,10 @@
* @typedef {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern4
*
* Transfer volume pattern (base + cumulative + inProfit/inLoss + sum windows)
* @typedef {Brk.BaseCumulativeInSumPattern} TransferVolumePattern
* @typedef {Brk.AverageBaseCumulativeInSumPattern} TransferVolumePattern
*
* Realized profit/loss pattern (base + cumulative + sum windows, cents/usd)
* @typedef {Brk.BaseCumulativeSumPattern4} RealizedProfitLossPattern
* @typedef {Brk.BaseCumulativeSumPattern} RealizedProfitLossPattern
*
* Full activity pattern (coindays, coinyears, dormancy, transfer volume)
* @typedef {Brk.CoindaysCoinyearsDormancyTransferPattern} FullActivityPattern
@@ -136,11 +136,11 @@
*/
/**
* Stats pattern: average, min, max, percentiles (height-only indexes, NO base)
* @typedef {Brk.AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern<number>} StatsPattern
* @typedef {Brk.MaxMedianMinPct10Pct25Pct75Pct90Pattern<number>} StatsPattern
*/
/**
* Base stats pattern: average, min, max, percentiles
* @typedef {Brk.AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern<number>} BaseStatsPattern
* @typedef {Brk.MaxMedianMinPct10Pct25Pct75Pct90Pattern<number>} BaseStatsPattern
*/
/**
* Full stats pattern: cumulative, sum, average, min, max, percentiles + rolling
@@ -161,7 +161,7 @@
/**
* Count pattern: height, cumulative, and rolling sum windows
* @template T
* @typedef {Brk.BaseCumulativeSumPattern<T>} CountPattern
* @typedef {Brk.AverageBaseCumulativeSumPattern<T>} CountPattern
*/
/**
* Full per-block pattern: height, cumulative, sum, and distribution stats (all flat)
@@ -172,8 +172,8 @@
* @typedef {FullStatsPattern | BtcFullStatsPattern} AnyStatsPattern
*/
/**
* Distribution stats: 8 series fields (average, min, max, median, pct10/25/75/90)
* @typedef {{ average: AnySeriesPattern, min: AnySeriesPattern, max: AnySeriesPattern, median: AnySeriesPattern, pct10: AnySeriesPattern, pct25: AnySeriesPattern, pct75: AnySeriesPattern, pct90: AnySeriesPattern }} DistributionStats
* Distribution stats: min, max, median, pct10/25/75/90
* @typedef {{ min: AnySeriesPattern, max: AnySeriesPattern, median: AnySeriesPattern, pct10: AnySeriesPattern, pct25: AnySeriesPattern, pct75: AnySeriesPattern, pct90: AnySeriesPattern }} DistributionStats
*/
/**