global: snapshot

This commit is contained in:
nym21
2026-03-19 16:35:54 +01:00
parent 1e68c160a1
commit 8910c0988e
29 changed files with 387 additions and 1336 deletions

View File

@@ -542,9 +542,9 @@ impl UTXOCohorts<Rw> {
.metrics
.realized
.minimal
.sopr
.value_created
.transfer_volume
.base
.cents
.height
.read_only_clone();
let under_1h_value_destroyed = self
@@ -552,7 +552,6 @@ impl UTXOCohorts<Rw> {
.under_1h
.metrics
.realized
.minimal
.sopr
.value_destroyed
.base

View File

@@ -128,8 +128,8 @@ impl AllCohortMetrics {
self.asopr.compute_rest_part2(
starting_indexes,
&self.realized.minimal.sopr.value_created.base.height,
&self.realized.minimal.sopr.value_destroyed.base.height,
&self.realized.minimal.transfer_volume.base.cents.height,
&self.realized.core.sopr.value_destroyed.base.height,
under_1h_value_created,
under_1h_value_destroyed,
exit,

View File

@@ -114,7 +114,7 @@ impl CoreCohortMetrics {
.compute_sent_profitability(prices, starting_indexes, exit)?;
self.realized
.compute_rest_part1(starting_indexes, exit)?;
.compute_rest_part1(prices, starting_indexes, exit)?;
self.unrealized.compute_rest(starting_indexes, exit)?;

View File

@@ -80,8 +80,8 @@ impl ExtendedAdjustedCohortMetrics {
self.asopr.compute_rest_part2(
starting_indexes,
&self.inner.realized.minimal.sopr.value_created.base.height,
&self.inner.realized.minimal.sopr.value_destroyed.base.height,
&self.inner.realized.minimal.transfer_volume.base.cents.height,
&self.inner.realized.core.sopr.value_destroyed.base.height,
under_1h_value_created,
under_1h_value_destroyed,
exit,

View File

@@ -90,7 +90,7 @@ impl MinimalCohortMetrics {
) -> Result<()> {
self.supply.compute(prices, starting_indexes.height, exit)?;
self.realized
.compute_rest_part1(starting_indexes, exit)?;
.compute_rest_part1(prices, starting_indexes, exit)?;
Ok(())
}

View File

@@ -60,7 +60,7 @@ impl TypeCohortMetrics {
) -> Result<()> {
self.supply.compute(prices, starting_indexes.height, exit)?;
self.realized
.compute_rest_part1(starting_indexes, exit)?;
.compute_rest_part1(prices, starting_indexes, exit)?;
Ok(())
}

View File

@@ -8,10 +8,8 @@ use crate::{
indexes,
internal::{
AmountPerBlock, AmountPerBlockCumulative, AmountPerBlockCumulativeWithSums,
CachedWindowStarts, CentsType, PerBlock,
PerBlockCumulative, PerBlockCumulativeWithSums,
FiatPerBlock, FiatPerBlockCumulativeWithSums, NumericValue,
PercentPerBlock, PercentRollingWindows, Price,
CachedWindowStarts, CentsType, FiatPerBlock, FiatPerBlockCumulativeWithSums, NumericValue,
PerBlock, PerBlockCumulativeWithSums, PercentPerBlock, PercentRollingWindows, Price,
PriceWithRatioExtendedPerBlock, PriceWithRatioPerBlock, RatioPerBlock,
RollingWindow24hPerBlock, RollingWindows, RollingWindowsFrom1w,
},
@@ -56,11 +54,6 @@ impl<T: NumericValue + JsonSchema> ConfigImport for PerBlock<T> {
Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes)
}
}
impl<T: NumericValue + JsonSchema> ConfigImport for PerBlockCumulative<T> {
fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result<Self> {
Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes)
}
}
impl<T, C> ConfigImport for PerBlockCumulativeWithSums<T, C>
where
T: NumericValue + JsonSchema + Into<C>,

View File

@@ -228,7 +228,7 @@ pub trait CohortMetricsBase:
.compute_sent_profitability(prices, starting_indexes, exit)?;
self.realized_mut()
.compute_rest_part1(starting_indexes, exit)?;
.compute_rest_part1(prices, starting_indexes, exit)?;
self.unrealized_mut()
.compute_rest(prices, starting_indexes, exit)?;

View File

@@ -11,7 +11,7 @@ use crate::{
#[derive(Traversable)]
pub struct AdjustedSopr<M: StorageMode = Rw> {
pub ratio: RollingWindows<StoredF64, M>,
pub value_created: PerBlockCumulativeWithSums<Cents, Cents, M>,
pub transfer_volume: PerBlockCumulativeWithSums<Cents, Cents, M>,
pub value_destroyed: PerBlockCumulativeWithSums<Cents, Cents, M>,
}
@@ -19,7 +19,7 @@ impl AdjustedSopr {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
Ok(Self {
ratio: cfg.import("asopr", Version::ONE)?,
value_created: cfg.import("adj_value_created", Version::ONE)?,
transfer_volume: cfg.import("adj_value_created", Version::ONE)?,
value_destroyed: cfg.import("adj_value_destroyed", Version::ONE)?,
})
}
@@ -28,17 +28,16 @@ impl AdjustedSopr {
pub(crate) fn compute_rest_part2(
&mut self,
starting_indexes: &Indexes,
base_value_created: &impl ReadableVec<Height, Cents>,
base_transfer_volume: &impl ReadableVec<Height, Cents>,
base_value_destroyed: &impl ReadableVec<Height, Cents>,
under_1h_value_created: &impl ReadableVec<Height, Cents>,
under_1h_transfer_volume: &impl ReadableVec<Height, Cents>,
under_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
// Compute value_created = base.value_created - under_1h.value_created
self.value_created.base.height.compute_subtract(
self.transfer_volume.base.height.compute_subtract(
starting_indexes.height,
base_value_created,
under_1h_value_created,
base_transfer_volume,
under_1h_transfer_volume,
exit,
)?;
self.value_destroyed.base.height.compute_subtract(
@@ -48,23 +47,21 @@ impl AdjustedSopr {
exit,
)?;
// Cumulatives (rolling sums are lazy)
self.value_created
self.transfer_volume
.compute_rest(starting_indexes.height, exit)?;
self.value_destroyed
.compute_rest(starting_indexes.height, exit)?;
// SOPR ratios from lazy rolling sums
for ((sopr, vc), vd) in self
for ((sopr, tv), vd) in self
.ratio
.as_mut_array()
.into_iter()
.zip(self.value_created.sum.as_array())
.zip(self.transfer_volume.sum.as_array())
.zip(self.value_destroyed.sum.as_array())
{
sopr.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&vc.height,
&tv.height,
&vd.height,
exit,
)?;

View File

@@ -3,14 +3,14 @@ use brk_traversable::Traversable;
use brk_types::{BasisPointsSigned32, Bitcoin, Cents, CentsSigned, Dollars, Height, Indexes, StoredF64, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{
AnyStoredVec, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode,
AnyStoredVec, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, WritableVec,
};
use crate::{
distribution::state::{CohortState, CostBasisOps, RealizedOps},
internal::{
FiatPerBlockCumulativeWithSumsAndDeltas, LazyPerBlock, NegCentsUnsignedToDollars,
RatioCents64, RollingWindow24hPerBlock, Windows,
PerBlockCumulativeWithSums, RatioCents64, RollingWindow24hPerBlock, Windows,
},
prices,
};
@@ -28,6 +28,7 @@ pub struct NegRealizedLoss {
#[derive(Traversable)]
pub struct RealizedSoprCore<M: StorageMode = Rw> {
pub value_destroyed: PerBlockCumulativeWithSums<Cents, Cents, M>,
pub ratio: RollingWindow24hPerBlock<StoredF64, M>,
}
@@ -80,11 +81,20 @@ impl RealizedCore {
cfg.cached_starts,
)?;
let value_destroyed = PerBlockCumulativeWithSums::forced_import(
cfg.db,
&cfg.name("value_destroyed"),
cfg.version + v1,
cfg.indexes,
cfg.cached_starts,
)?;
Ok(Self {
minimal,
neg_loss,
net_pnl,
sopr: RealizedSoprCore {
value_destroyed,
ratio: cfg.import("sopr", v1)?,
},
})
@@ -97,10 +107,13 @@ impl RealizedCore {
#[inline(always)]
pub(crate) fn push_state(&mut self, state: &CohortState<impl RealizedOps, impl CostBasisOps>) {
self.minimal.push_state(state);
self.sopr.value_destroyed.base.height.push(state.realized.value_destroyed());
}
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
self.minimal.collect_vecs_mut()
let mut vecs = self.minimal.collect_vecs_mut();
vecs.push(&mut self.sopr.value_destroyed.base.height);
vecs
}
pub(crate) fn compute_from_stateful(
@@ -113,16 +126,22 @@ impl RealizedCore {
self.minimal
.compute_from_stateful(starting_indexes, &minimal_refs, exit)?;
sum_others!(self, starting_indexes, others, exit; sopr.value_destroyed.base.height);
Ok(())
}
pub(crate) fn compute_rest_part1(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.minimal
.compute_rest_part1(starting_indexes, exit)?;
.compute_rest_part1(prices, starting_indexes, exit)?;
self.sopr
.value_destroyed
.compute_rest(starting_indexes.height, exit)?;
self.net_pnl.base.cents.height.compute_transform2(
starting_indexes.height,
@@ -158,8 +177,8 @@ impl RealizedCore {
._24h
.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.minimal.sopr.value_created.sum._24h.height,
&self.minimal.sopr.value_destroyed.sum._24h.height,
&self.minimal.transfer_volume.sum._24h.cents.height,
&self.sopr.value_destroyed.sum._24h.height,
exit,
)?;

View File

@@ -6,7 +6,7 @@ use brk_types::{
};
use derive_more::{Deref, DerefMut};
use vecdb::{
AnyStoredVec, AnyVec, BytesVec, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode,
AnyStoredVec, AnyVec, BytesVec, Exit, ReadableVec, Rw, StorageMode,
WritableVec,
};
@@ -14,9 +14,8 @@ use crate::{
blocks,
distribution::state::{WithCapital, CohortState, CostBasisData, RealizedState},
internal::{
CentsUnsignedToDollars,
PerBlockCumulativeWithSums, FiatPerBlockCumulativeWithSums,
LazyPerBlock, PercentPerBlock, PercentRollingWindows,
FiatPerBlockCumulativeWithSums,
PercentPerBlock, PercentRollingWindows,
PriceWithRatioExtendedPerBlock, RatioCents64, RatioCentsBp32,
RatioCentsSignedCentsBps32, RatioCentsSignedDollarsBps32, RatioDollarsBp32,
RatioPerBlockPercentiles, RatioPerBlockStdDevBands, RatioSma, RollingWindows,
@@ -30,19 +29,13 @@ use crate::distribution::metrics::ImportConfig;
use super::RealizedCore;
#[derive(Traversable)]
pub struct RealizedProfit<M: StorageMode = Rw> {
pub struct RealizedProfitFull<M: StorageMode = Rw> {
pub to_rcap: PercentPerBlock<BasisPoints32, M>,
pub value_created: PerBlockCumulativeWithSums<Cents, Cents, M>,
pub value_destroyed: PerBlockCumulativeWithSums<Cents, Cents, M>,
pub distribution_flow: LazyPerBlock<Dollars, Cents>,
}
#[derive(Traversable)]
pub struct RealizedLoss<M: StorageMode = Rw> {
pub struct RealizedLossFull<M: StorageMode = Rw> {
pub to_rcap: PercentPerBlock<BasisPoints32, M>,
pub value_created: PerBlockCumulativeWithSums<Cents, Cents, M>,
pub value_destroyed: PerBlockCumulativeWithSums<Cents, Cents, M>,
pub capitulation_flow: LazyPerBlock<Dollars, Cents>,
}
#[derive(Traversable)]
@@ -81,8 +74,8 @@ pub struct RealizedFull<M: StorageMode = Rw> {
#[traversable(flatten)]
pub core: RealizedCore<M>,
pub profit: RealizedProfit<M>,
pub loss: RealizedLoss<M>,
pub profit: RealizedProfitFull<M>,
pub loss: RealizedLossFull<M>,
pub gross_pnl: FiatPerBlockCumulativeWithSums<Cents, M>,
pub sell_side_risk_ratio: PercentRollingWindows<BasisPoints32, M>,
pub net_pnl: RealizedNetPnl<M>,
@@ -112,36 +105,11 @@ impl RealizedFull {
let core = RealizedCore::forced_import(cfg)?;
// Profit
let profit_value_destroyed: PerBlockCumulativeWithSums<Cents, Cents> =
cfg.import("profit_value_destroyed", v1)?;
let profit_flow = LazyPerBlock::from_computed::<CentsUnsignedToDollars>(
&cfg.name("distribution_flow"),
cfg.version,
profit_value_destroyed.base.height.read_only_boxed_clone(),
&profit_value_destroyed.base,
);
let profit = RealizedProfit {
let profit = RealizedProfitFull {
to_rcap: cfg.import("realized_profit_to_rcap", Version::new(2))?,
value_created: cfg.import("profit_value_created", v1)?,
value_destroyed: profit_value_destroyed,
distribution_flow: profit_flow,
};
// Loss
let loss_value_destroyed: PerBlockCumulativeWithSums<Cents, Cents> =
cfg.import("loss_value_destroyed", v1)?;
let capitulation_flow = LazyPerBlock::from_computed::<CentsUnsignedToDollars>(
&cfg.name("capitulation_flow"),
cfg.version,
loss_value_destroyed.base.height.read_only_boxed_clone(),
&loss_value_destroyed.base,
);
let loss = RealizedLoss {
let loss = RealizedLossFull {
to_rcap: cfg.import("realized_loss_to_rcap", Version::new(2))?,
value_created: cfg.import("loss_value_created", v1)?,
value_destroyed: loss_value_destroyed,
capitulation_flow,
};
// Gross PnL
@@ -216,15 +184,7 @@ impl RealizedFull {
}
pub(crate) fn min_stateful_len(&self) -> usize {
self.profit
.value_created
.base
.height
.len()
.min(self.profit.value_destroyed.base.height.len())
.min(self.loss.value_created.base.height.len())
.min(self.loss.value_destroyed.base.height.len())
.min(self.investor.price.cents.height.len())
self.investor.price.cents.height.len()
.min(self.cap_raw.len())
.min(self.investor.cap_raw.len())
.min(self.peak_regret.value.base.cents.height.len())
@@ -236,26 +196,6 @@ impl RealizedFull {
state: &CohortState<RealizedState, CostBasisData<WithCapital>>,
) {
self.core.push_state(state);
self.profit
.value_created
.base
.height
.push(state.realized.profit_value_created());
self.profit
.value_destroyed
.base
.height
.push(state.realized.profit_value_destroyed());
self.loss
.value_created
.base
.height
.push(state.realized.loss_value_created());
self.loss
.value_destroyed
.base
.height
.push(state.realized.loss_value_destroyed());
self.investor
.price
.cents
@@ -276,10 +216,6 @@ impl RealizedFull {
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
let mut vecs = self.core.collect_vecs_mut();
vecs.push(&mut self.profit.value_created.base.height as &mut dyn AnyStoredVec);
vecs.push(&mut self.profit.value_destroyed.base.height);
vecs.push(&mut self.loss.value_created.base.height);
vecs.push(&mut self.loss.value_destroyed.base.height);
vecs.push(&mut self.investor.price.cents.height);
vecs.push(&mut self.cap_raw as &mut dyn AnyStoredVec);
vecs.push(&mut self.investor.cap_raw as &mut dyn AnyStoredVec);
@@ -304,26 +240,6 @@ impl RealizedFull {
&mut self,
accum: &RealizedFullAccum,
) {
self.profit
.value_created
.base
.height
.push(accum.profit_value_created());
self.profit
.value_destroyed
.base
.height
.push(accum.profit_value_destroyed());
self.loss
.value_created
.base
.height
.push(accum.loss_value_created());
self.loss
.value_destroyed
.base
.height
.push(accum.loss_value_destroyed());
self.cap_raw
.push(accum.cap_raw);
self.investor
@@ -354,11 +270,12 @@ impl RealizedFull {
pub(crate) fn compute_rest_part1(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.core
.compute_rest_part1(starting_indexes, exit)?;
.compute_rest_part1(prices, starting_indexes, exit)?;
self.peak_regret
.value
@@ -388,12 +305,12 @@ impl RealizedFull {
.ratio_extended
.as_mut_array()
.into_iter()
.zip(self.core.minimal.sopr.value_created.sum.as_array()[1..].iter())
.zip(self.core.minimal.sopr.value_destroyed.sum.as_array()[1..].iter())
.zip(self.core.minimal.transfer_volume.sum.0.as_array()[1..].iter())
.zip(self.core.sopr.value_destroyed.sum.as_array()[1..].iter())
{
sopr.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&vc.height,
&vc.cents.height,
&vd.height,
exit,
)?;
@@ -425,20 +342,6 @@ impl RealizedFull {
exit,
)?;
// Profit/loss value created/destroyed cumulatives (rolling sums are lazy)
self.profit
.value_created
.compute_rest(starting_indexes.height, exit)?;
self.profit
.value_destroyed
.compute_rest(starting_indexes.height, exit)?;
self.loss
.value_created
.compute_rest(starting_indexes.height, exit)?;
self.loss
.value_destroyed
.compute_rest(starting_indexes.height, exit)?;
// Gross PnL
self.gross_pnl.base.cents.height.compute_add(
starting_indexes.height,
@@ -554,10 +457,6 @@ impl RealizedFull {
#[derive(Default)]
pub struct RealizedFullAccum {
profit_value_created: CentsSats,
profit_value_destroyed: CentsSats,
loss_value_created: CentsSats,
loss_value_destroyed: CentsSats,
pub(crate) cap_raw: CentsSats,
pub(crate) investor_cap_raw: CentsSquaredSats,
peak_regret: CentsSats,
@@ -565,31 +464,11 @@ pub struct RealizedFullAccum {
impl RealizedFullAccum {
pub(crate) fn add(&mut self, state: &RealizedState) {
self.profit_value_created += CentsSats::new(state.profit_value_created_raw());
self.profit_value_destroyed += CentsSats::new(state.profit_value_destroyed_raw());
self.loss_value_created += CentsSats::new(state.loss_value_created_raw());
self.loss_value_destroyed += CentsSats::new(state.loss_value_destroyed_raw());
self.cap_raw += state.cap_raw();
self.investor_cap_raw += state.investor_cap_raw();
self.peak_regret += CentsSats::new(state.peak_regret_raw());
}
pub(crate) fn profit_value_created(&self) -> Cents {
self.profit_value_created.to_cents()
}
pub(crate) fn profit_value_destroyed(&self) -> Cents {
self.profit_value_destroyed.to_cents()
}
pub(crate) fn loss_value_created(&self) -> Cents {
self.loss_value_created.to_cents()
}
pub(crate) fn loss_value_destroyed(&self) -> Cents {
self.loss_value_destroyed.to_cents()
}
pub(crate) fn peak_regret(&self) -> Cents {
self.peak_regret.to_cents()
}

View File

@@ -11,7 +11,7 @@ use vecdb::{
use crate::{
distribution::state::{CohortState, CostBasisOps, RealizedOps},
internal::{
PerBlockCumulativeWithSums, FiatPerBlockCumulativeWithSums,
AmountPerBlockCumulativeWithSums, FiatPerBlockCumulativeWithSums,
FiatPerBlockWithDeltas, Identity, LazyPerBlock, PriceWithRatioPerBlock,
},
prices,
@@ -19,12 +19,6 @@ use crate::{
use crate::distribution::metrics::ImportConfig;
#[derive(Traversable)]
pub struct RealizedSoprMinimal<M: StorageMode = Rw> {
pub value_created: PerBlockCumulativeWithSums<Cents, Cents, M>,
pub value_destroyed: PerBlockCumulativeWithSums<Cents, Cents, M>,
}
#[derive(Traversable)]
pub struct RealizedMinimal<M: StorageMode = Rw> {
pub cap: FiatPerBlockWithDeltas<Cents, CentsSigned, BasisPointsSigned32, M>,
@@ -33,7 +27,7 @@ pub struct RealizedMinimal<M: StorageMode = Rw> {
pub price: PriceWithRatioPerBlock<M>,
pub mvrv: LazyPerBlock<StoredF32>,
pub sopr: RealizedSoprMinimal<M>,
pub transfer_volume: AmountPerBlockCumulativeWithSums<M>,
}
impl RealizedMinimal {
@@ -56,16 +50,21 @@ impl RealizedMinimal {
&price.ratio,
);
let transfer_volume = AmountPerBlockCumulativeWithSums::forced_import(
cfg.db,
&cfg.name("transfer_volume"),
cfg.version,
cfg.indexes,
cfg.cached_starts,
)?;
Ok(Self {
cap,
profit: cfg.import("realized_profit", v1)?,
loss: cfg.import("realized_loss", v1)?,
price,
mvrv,
sopr: RealizedSoprMinimal {
value_created: cfg.import("value_created", v1)?,
value_destroyed: cfg.import("value_destroyed", v1)?,
},
transfer_volume,
})
}
@@ -76,8 +75,7 @@ impl RealizedMinimal {
.len()
.min(self.profit.base.cents.height.len())
.min(self.loss.base.cents.height.len())
.min(self.sopr.value_created.base.height.len())
.min(self.sopr.value_destroyed.base.height.len())
.min(self.transfer_volume.base.sats.height.len())
}
#[inline(always)]
@@ -85,16 +83,7 @@ impl RealizedMinimal {
self.cap.cents.height.push(state.realized.cap());
self.profit.base.cents.height.push(state.realized.profit());
self.loss.base.cents.height.push(state.realized.loss());
self.sopr
.value_created
.base
.height
.push(state.realized.value_created());
self.sopr
.value_destroyed
.base
.height
.push(state.realized.value_destroyed());
self.transfer_volume.base.sats.height.push(state.sent);
}
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
@@ -102,8 +91,7 @@ impl RealizedMinimal {
&mut self.cap.cents.height as &mut dyn AnyStoredVec,
&mut self.profit.base.cents.height,
&mut self.loss.base.cents.height,
&mut self.sopr.value_created.base.height,
&mut self.sopr.value_destroyed.base.height,
&mut self.transfer_volume.base.sats.height,
]
}
@@ -116,24 +104,20 @@ impl RealizedMinimal {
sum_others!(self, starting_indexes, others, exit; cap.cents.height);
sum_others!(self, starting_indexes, others, exit; profit.base.cents.height);
sum_others!(self, starting_indexes, others, exit; loss.base.cents.height);
sum_others!(self, starting_indexes, others, exit; sopr.value_created.base.height);
sum_others!(self, starting_indexes, others, exit; sopr.value_destroyed.base.height);
sum_others!(self, starting_indexes, others, exit; transfer_volume.base.sats.height);
Ok(())
}
pub(crate) fn compute_rest_part1(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.profit.compute_rest(starting_indexes.height, exit)?;
self.loss.compute_rest(starting_indexes.height, exit)?;
self.sopr
.value_created
.compute_rest(starting_indexes.height, exit)?;
self.sopr
.value_destroyed
.compute_rest(starting_indexes.height, exit)?;
self.transfer_volume
.compute_rest(starting_indexes.height, prices, exit)?;
Ok(())
}

View File

@@ -12,14 +12,14 @@ use brk_error::Result;
use brk_types::Indexes;
use vecdb::Exit;
use crate::distribution::state::{WithCapital, CohortState, CostBasisData, RealizedState};
use crate::{distribution::state::{WithCapital, CohortState, CostBasisData, RealizedState}, prices};
pub trait RealizedLike: Send + Sync {
fn as_core(&self) -> &RealizedCore;
fn as_core_mut(&mut self) -> &mut RealizedCore;
fn min_stateful_len(&self) -> usize;
fn push_state(&mut self, state: &CohortState<RealizedState, CostBasisData<WithCapital>>);
fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()>;
fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()>;
fn compute_from_stateful(
&mut self,
starting_indexes: &Indexes,
@@ -36,8 +36,8 @@ impl RealizedLike for RealizedCore {
fn push_state(&mut self, state: &CohortState<RealizedState, CostBasisData<WithCapital>>) {
self.push_state(state)
}
fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
self.compute_rest_part1(starting_indexes, exit)
fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
self.compute_rest_part1(prices, starting_indexes, exit)
}
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedCore], exit: &Exit) -> Result<()> {
self.compute_from_stateful(starting_indexes, others, exit)
@@ -52,8 +52,8 @@ impl RealizedLike for RealizedFull {
fn push_state(&mut self, state: &CohortState<RealizedState, CostBasisData<WithCapital>>) {
self.push_state(state)
}
fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
self.compute_rest_part1(starting_indexes, exit)
fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
self.compute_rest_part1(prices, starting_indexes, exit)
}
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedCore], exit: &Exit) -> Result<()> {
self.compute_from_stateful(starting_indexes, others, exit)

View File

@@ -96,8 +96,8 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
}
pub(crate) fn reset_single_iteration_values(&mut self) {
self.sent = Sats::ZERO;
if R::TRACK_ACTIVITY {
self.sent = Sats::ZERO;
self.satdays_destroyed = Sats::ZERO;
}
self.realized.reset_single_iteration_values();
@@ -196,8 +196,8 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
pre: &SendPrecomputed,
) {
self.supply -= supply;
self.sent += pre.sats;
if R::TRACK_ACTIVITY {
self.sent += pre.sats;
self.satdays_destroyed += pre.age.satdays_destroyed(pre.sats);
}
@@ -241,8 +241,8 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
self.supply -= supply;
if supply.value > Sats::ZERO {
self.sent += supply.value;
if R::TRACK_ACTIVITY {
self.sent += supply.value;
self.satdays_destroyed += age.satdays_destroyed(supply.value);
}

View File

@@ -8,9 +8,6 @@ pub trait RealizedOps: Default + Clone + Send + Sync + 'static {
fn cap(&self) -> Cents;
fn profit(&self) -> Cents;
fn loss(&self) -> Cents;
fn value_created(&self) -> Cents {
Cents::ZERO
}
fn value_destroyed(&self) -> Cents {
Cents::ZERO
}
@@ -46,7 +43,6 @@ pub struct MinimalRealizedState {
cap_raw: u128,
profit_raw: u128,
loss_raw: u128,
value_created_raw: u128,
value_destroyed_raw: u128,
}
@@ -76,13 +72,6 @@ impl RealizedOps for MinimalRealizedState {
}
#[inline]
fn value_created(&self) -> Cents {
if self.value_created_raw == 0 {
return Cents::ZERO;
}
Cents::new((self.value_created_raw / Sats::ONE_BTC_U128) as u64)
}
#[inline]
fn value_destroyed(&self) -> Cents {
if self.value_destroyed_raw == 0 {
@@ -103,7 +92,6 @@ impl RealizedOps for MinimalRealizedState {
fn reset_single_iteration_values(&mut self) {
self.profit_raw = 0;
self.loss_raw = 0;
self.value_created_raw = 0;
self.value_destroyed_raw = 0;
}
@@ -145,7 +133,6 @@ impl RealizedOps for MinimalRealizedState {
Ordering::Equal => {}
}
self.cap_raw -= prev_ps.as_u128();
self.value_created_raw += current_ps.as_u128();
self.value_destroyed_raw += prev_ps.as_u128();
}
}
@@ -177,11 +164,6 @@ impl RealizedOps for CoreRealizedState {
self.minimal.loss()
}
#[inline]
fn value_created(&self) -> Cents {
self.minimal.value_created()
}
#[inline]
fn value_destroyed(&self) -> Cents {
self.minimal.value_destroyed()
@@ -263,14 +245,6 @@ pub struct RealizedState {
core: CoreRealizedState,
/// Raw investor cap: Σ(price² × sats)
investor_cap_raw: CentsSquaredSats,
/// sell_price × sats for profit cases
profit_value_created_raw: u128,
/// cost_basis × sats for profit cases
profit_value_destroyed_raw: u128,
/// sell_price × sats for loss cases
loss_value_created_raw: u128,
/// cost_basis × sats for loss cases (= capitulation_flow)
loss_value_destroyed_raw: u128,
/// Raw realized peak regret: Σ((peak - sell_price) × sats)
peak_regret_raw: u128,
}
@@ -293,22 +267,9 @@ impl RealizedOps for RealizedState {
self.core.loss()
}
#[inline]
fn value_created(&self) -> Cents {
let raw = self.profit_value_created_raw + self.loss_value_created_raw;
if raw == 0 {
return Cents::ZERO;
}
Cents::new((raw / Sats::ONE_BTC_U128) as u64)
}
#[inline]
fn value_destroyed(&self) -> Cents {
let raw = self.profit_value_destroyed_raw + self.loss_value_destroyed_raw;
if raw == 0 {
return Cents::ZERO;
}
Cents::new((raw / Sats::ONE_BTC_U128) as u64)
self.core.value_destroyed()
}
#[inline]
@@ -334,10 +295,6 @@ impl RealizedOps for RealizedState {
#[inline]
fn reset_single_iteration_values(&mut self) {
self.core.reset_single_iteration_values();
self.profit_value_created_raw = 0;
self.profit_value_destroyed_raw = 0;
self.loss_value_created_raw = 0;
self.loss_value_destroyed_raw = 0;
self.peak_regret_raw = 0;
}
@@ -370,24 +327,9 @@ impl RealizedOps for RealizedState {
ath_ps: CentsSats,
prev_investor_cap: CentsSquaredSats,
) {
// Delegate cap/profit/loss + value_created/destroyed + sent tracking to core
self.core
.send(sats, current_ps, prev_ps, ath_ps, prev_investor_cap);
// Per-component value flow tracking
let current = current_ps.as_u128();
let prev = prev_ps.as_u128();
match current_ps.cmp(&prev_ps) {
Ordering::Greater | Ordering::Equal => {
self.profit_value_created_raw += current;
self.profit_value_destroyed_raw += prev;
}
Ordering::Less => {
self.loss_value_created_raw += current;
self.loss_value_destroyed_raw += prev;
}
}
self.peak_regret_raw += (ath_ps - current_ps).as_u128();
self.investor_cap_raw -= prev_investor_cap;
}
@@ -417,42 +359,6 @@ impl RealizedState {
self.investor_cap_raw
}
/// Get profit value created as CentsUnsigned (sell_price × sats for profit cases).
#[inline]
pub(crate) fn profit_value_created(&self) -> Cents {
if self.profit_value_created_raw == 0 {
return Cents::ZERO;
}
Cents::new((self.profit_value_created_raw / Sats::ONE_BTC_U128) as u64)
}
/// Get profit value destroyed as CentsUnsigned (cost_basis × sats for profit cases).
#[inline]
pub(crate) fn profit_value_destroyed(&self) -> Cents {
if self.profit_value_destroyed_raw == 0 {
return Cents::ZERO;
}
Cents::new((self.profit_value_destroyed_raw / Sats::ONE_BTC_U128) as u64)
}
/// Get loss value created as CentsUnsigned (sell_price × sats for loss cases).
#[inline]
pub(crate) fn loss_value_created(&self) -> Cents {
if self.loss_value_created_raw == 0 {
return Cents::ZERO;
}
Cents::new((self.loss_value_created_raw / Sats::ONE_BTC_U128) as u64)
}
/// Get loss value destroyed as CentsUnsigned (cost_basis × sats for loss cases).
#[inline]
pub(crate) fn loss_value_destroyed(&self) -> Cents {
if self.loss_value_destroyed_raw == 0 {
return Cents::ZERO;
}
Cents::new((self.loss_value_destroyed_raw / Sats::ONE_BTC_U128) as u64)
}
/// Get realized peak regret as CentsUnsigned.
#[inline]
pub(crate) fn peak_regret(&self) -> Cents {
@@ -462,30 +368,6 @@ impl RealizedState {
Cents::new((self.peak_regret_raw / Sats::ONE_BTC_U128) as u64)
}
/// Raw profit value created for lossless aggregation.
#[inline]
pub(crate) fn profit_value_created_raw(&self) -> u128 {
self.profit_value_created_raw
}
/// Raw profit value destroyed for lossless aggregation.
#[inline]
pub(crate) fn profit_value_destroyed_raw(&self) -> u128 {
self.profit_value_destroyed_raw
}
/// Raw loss value created for lossless aggregation.
#[inline]
pub(crate) fn loss_value_created_raw(&self) -> u128 {
self.loss_value_created_raw
}
/// Raw loss value destroyed for lossless aggregation.
#[inline]
pub(crate) fn loss_value_destroyed_raw(&self) -> u128 {
self.loss_value_destroyed_raw
}
/// Raw peak regret for lossless aggregation.
#[inline]
pub(crate) fn peak_regret_raw(&self) -> u128 {

View File

@@ -1,53 +0,0 @@
//! PerBlockCumulative - base PerBlock + cumulative PerBlock.
//!
//! Like PerBlockCumulativeWithSums but without RollingWindows.
//! Used for distribution metrics where rolling is optional per cohort.
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Version};
use schemars::JsonSchema;
use vecdb::{Database, Exit, Rw, StorageMode};
use crate::{
indexes,
internal::{PerBlock, NumericValue},
};
#[derive(Traversable)]
pub struct PerBlockCumulative<T, M: StorageMode = Rw>
where
T: NumericValue + JsonSchema,
{
pub base: PerBlock<T, M>,
pub cumulative: PerBlock<T, M>,
}
impl<T> PerBlockCumulative<T>
where
T: NumericValue + JsonSchema,
{
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let base = PerBlock::forced_import(db, name, version, indexes)?;
let cumulative =
PerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?;
Ok(Self { base, cumulative })
}
/// Compute cumulative from already-filled base vec.
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()>
where
T: Default,
{
self.cumulative
.height
.compute_cumulative(max_from, &self.base.height, exit)?;
Ok(())
}
}

View File

@@ -1,6 +1,5 @@
mod aggregated;
mod base;
mod cumulative;
mod cumulative_sum;
mod distribution;
mod full;
@@ -13,7 +12,6 @@ mod with_deltas;
pub use aggregated::*;
pub use base::*;
pub use cumulative::*;
pub use cumulative_sum::*;
pub use distribution::*;
pub use full::*;

View File

@@ -44,7 +44,7 @@ pub struct Bp16ToPercent;
impl UnaryTransform<BasisPoints16, StoredF32> for Bp16ToPercent {
#[inline(always)]
fn apply(bp: BasisPoints16) -> StoredF32 {
StoredF32::from(bp.inner() as f32 / 100.0)
StoredF32::from(bp.to_f32() * 100.0)
}
}
@@ -53,7 +53,7 @@ pub struct Bp32ToPercent;
impl UnaryTransform<BasisPoints32, StoredF32> for Bp32ToPercent {
#[inline(always)]
fn apply(bp: BasisPoints32) -> StoredF32 {
StoredF32::from(bp.inner() as f32 / 100.0)
StoredF32::from(bp.to_f32() * 100.0)
}
}
@@ -62,7 +62,7 @@ pub struct Bps16ToPercent;
impl UnaryTransform<BasisPointsSigned16, StoredF32> for Bps16ToPercent {
#[inline(always)]
fn apply(bp: BasisPointsSigned16) -> StoredF32 {
StoredF32::from(bp.inner() as f32 / 100.0)
StoredF32::from(bp.to_f32() * 100.0)
}
}
@@ -71,6 +71,6 @@ pub struct Bps32ToPercent;
impl UnaryTransform<BasisPointsSigned32, StoredF32> for Bps32ToPercent {
#[inline(always)]
fn apply(bp: BasisPointsSigned32) -> StoredF32 {
StoredF32::from(bp.inner() as f32 / 100.0)
StoredF32::from(bp.to_f32() * 100.0)
}
}

View File

@@ -1,7 +1,10 @@
use brk_error::Result;
use brk_types::Indexes;
use brk_types::{Indexes, Sats};
use vecdb::Exit;
/// Initial block subsidy (50 BTC) in sats, as f64 for floating-point comparisons.
const INITIAL_SUBSIDY: f64 = Sats::ONE_BTC_U64 as f64 * 50.0;
use super::Vecs;
use crate::{blocks, distribution, mining, prices, scripts, transactions};
@@ -28,15 +31,24 @@ impl Vecs {
)?;
// 2. Compute inflation rate: (supply[h] / supply[1y_ago]) - 1
// Skip when lookback supply <= first block (50 BTC = 5B sats),
// i.e. the lookback points to block 0 or 1 in the genesis era.
let circulating_supply = &distribution.utxo_cohorts.all.metrics.supply.total.sats;
self.inflation_rate
.bps
.height
.compute_rolling_ratio_change(
.compute_rolling_from_window_starts(
starting_indexes.height,
&blocks.lookback._1y,
&circulating_supply.height,
exit,
|current, previous| {
if previous.is_nan() || previous <= INITIAL_SUBSIDY {
f64::NAN
} else {
current / previous - 1.0
}
},
)?;
// 3. Compute velocity at height level

View File

@@ -37,8 +37,12 @@ impl Vecs {
let burned = burned::Vecs::forced_import(&db, version, indexes, cached_starts)?;
// Inflation rate
let inflation_rate =
PercentPerBlock::forced_import(&db, "inflation_rate", version, indexes)?;
let inflation_rate = PercentPerBlock::forced_import(
&db,
"inflation_rate",
version + Version::ONE,
indexes,
)?;
// Velocity
let velocity = super::velocity::Vecs::forced_import(&db, version, indexes)?;