global: snapshot

This commit is contained in:
nym21
2026-03-01 11:39:02 +01:00
parent a6664bbb93
commit e10013fd2c
23 changed files with 367 additions and 467 deletions
@@ -20,9 +20,9 @@ use brk_traversable::Traversable;
use brk_types::{Height, StoredU32, Version};
use derive_more::{Deref, DerefMut};
use rayon::prelude::*;
use vecdb::{AnyStoredVec, AnyVec, Database, Rw, StorageMode, WritableVec};
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, Rw, StorageMode, WritableVec};
use crate::{indexes, internal::ComputedFromHeightDistribution};
use crate::{indexes, internal::{ComputedFromHeightDistribution, WindowStarts}};
/// Per-block activity counts - reset each block.
///
@@ -187,6 +187,20 @@ impl ActivityCountVecs {
Ok(())
}
pub(crate) fn compute_rest(
&mut self,
max_from: Height,
windows: &WindowStarts<'_>,
exit: &Exit,
) -> Result<()> {
self.reactivated.compute_rest(max_from, windows, exit)?;
self.sending.compute_rest(max_from, windows, exit)?;
self.receiving.compute_rest(max_from, windows, exit)?;
self.balance_increased.compute_rest(max_from, windows, exit)?;
self.balance_decreased.compute_rest(max_from, windows, exit)?;
self.both.compute_rest(max_from, windows, exit)?;
Ok(())
}
}
/// Per-address-type activity count vecs.
@@ -253,6 +267,18 @@ impl AddressTypeToActivityCountVecs {
Ok(())
}
pub(crate) fn compute_rest(
&mut self,
max_from: Height,
windows: &WindowStarts<'_>,
exit: &Exit,
) -> Result<()> {
for type_vecs in self.0.values_mut() {
type_vecs.compute_rest(max_from, windows, exit)?;
}
Ok(())
}
pub(crate) fn truncate_push_height(
&mut self,
height: Height,
@@ -315,6 +341,17 @@ impl AddressActivityVecs {
Ok(())
}
pub(crate) fn compute_rest(
&mut self,
max_from: Height,
windows: &WindowStarts<'_>,
exit: &Exit,
) -> Result<()> {
self.all.compute_rest(max_from, windows, exit)?;
self.by_addresstype.compute_rest(max_from, windows, exit)?;
Ok(())
}
pub(crate) fn truncate_push_height(
&mut self,
height: Height,
@@ -1,23 +1,21 @@
//! New address count: delta of total_addr_count (global + per-type)
//! New address count: per-block delta of total_addr_count (global + per-type)
//! New address count: delta of total_addr_count (global + per-type)
use brk_cohort::{ByAddressType, zip_by_addresstype};
use brk_cohort::ByAddressType;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredU64, Version};
use vecdb::{Database, Exit, Ident, Rw, StorageMode};
use vecdb::{Database, Exit, Rw, StorageMode};
use crate::{indexes, internal::{LazyComputedFromHeightFull, WindowStarts}};
use crate::{indexes, internal::{ComputedFromHeightCumulativeFull, WindowStarts}};
use super::TotalAddrCountVecs;
/// New address count per block (global + per-type)
#[derive(Traversable)]
pub struct NewAddrCountVecs<M: StorageMode = Rw> {
pub all: LazyComputedFromHeightFull<StoredU64, StoredU64, M>,
pub all: ComputedFromHeightCumulativeFull<StoredU64, M>,
#[traversable(flatten)]
pub by_addresstype: ByAddressType<LazyComputedFromHeightFull<StoredU64, StoredU64, M>>,
pub by_addresstype: ByAddressType<ComputedFromHeightCumulativeFull<StoredU64, M>>,
}
impl NewAddrCountVecs {
@@ -25,23 +23,20 @@ impl NewAddrCountVecs {
db: &Database,
version: Version,
indexes: &indexes::Vecs,
total_addr_count: &TotalAddrCountVecs,
) -> Result<Self> {
let all = LazyComputedFromHeightFull::forced_import::<Ident>(
let all = ComputedFromHeightCumulativeFull::forced_import(
db,
"new_addr_count",
version,
&total_addr_count.all.height,
indexes,
)?;
let by_addresstype: ByAddressType<LazyComputedFromHeightFull<StoredU64, StoredU64>> =
zip_by_addresstype(&total_addr_count.by_addresstype, |name, total| {
LazyComputedFromHeightFull::forced_import::<Ident>(
let by_addresstype: ByAddressType<ComputedFromHeightCumulativeFull<StoredU64>> =
ByAddressType::new_with_name(|name| {
ComputedFromHeightCumulativeFull::forced_import(
db,
&format!("{name}_new_addr_count"),
version,
&total.height,
indexes,
)
})?;
@@ -56,12 +51,23 @@ impl NewAddrCountVecs {
&mut self,
max_from: Height,
windows: &WindowStarts<'_>,
total_addr_count: &TotalAddrCountVecs,
exit: &Exit,
) -> Result<()> {
self.all.compute(max_from, windows, exit)?;
for vecs in self.by_addresstype.values_mut() {
vecs.compute(max_from, windows, exit)?;
self.all.compute(max_from, windows, exit, |height_vec| {
Ok(height_vec.compute_change(max_from, &total_addr_count.all.height, 1, exit)?)
})?;
for ((_, new), (_, total)) in self
.by_addresstype
.iter_mut()
.zip(total_addr_count.by_addresstype.iter())
{
new.compute(max_from, windows, exit, |height_vec| {
Ok(height_vec.compute_change(max_from, &total.height, 1, exit)?)
})?;
}
Ok(())
}
}
@@ -6,7 +6,7 @@ use vecdb::{AnyStoredVec, AnyVec, EagerVec, Exit, ImportableVec, PcoVec, Rw, Sto
use crate::{
ComputeIndexes, blocks,
internal::{ComputedFromHeightCumulativeSum, LazyComputedValueFromHeightCumulative, ValueEmaFromHeight},
internal::{ComputedFromHeightCumulativeSum, ValueFromHeightCumulative, ValueFromHeightLast},
};
use super::ImportConfig;
@@ -15,10 +15,10 @@ use super::ImportConfig;
#[derive(Traversable)]
pub struct ActivityMetrics<M: StorageMode = Rw> {
/// Total satoshis sent at each height + derived indexes
pub sent: LazyComputedValueFromHeightCumulative<M>,
pub sent: ValueFromHeightCumulative<M>,
/// 14-day EMA of sent supply (sats, btc, usd)
pub sent_14d_ema: ValueEmaFromHeight<M>,
pub sent_14d_ema: ValueFromHeightLast<M>,
/// Satoshi-blocks destroyed (supply * blocks_old when spent)
pub satblocks_destroyed: M::Stored<EagerVec<PcoVec<Height, Sats>>>,
@@ -37,14 +37,14 @@ impl ActivityMetrics {
/// Import activity metrics from database.
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
Ok(Self {
sent: LazyComputedValueFromHeightCumulative::forced_import(
sent: ValueFromHeightCumulative::forced_import(
cfg.db,
&cfg.name("sent"),
cfg.version,
cfg.indexes,
)?,
sent_14d_ema: ValueEmaFromHeight::forced_import(
sent_14d_ema: ValueFromHeightLast::forced_import(
cfg.db,
&cfg.name("sent_14d_ema"),
cfg.version,
@@ -165,8 +165,8 @@ impl ActivityMetrics {
) -> Result<()> {
let window_starts = blocks.count.window_starts();
// 14-day rolling average of sent (sats and dollars)
self.sent_14d_ema.compute_rolling_average(
// 14-day EMA of sent (sats and dollars)
self.sent_14d_ema.compute_ema(
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent.base.sats.height,
@@ -262,7 +262,7 @@ impl RealizedAdjusted {
// Adjusted SOPR EMAs
self.adjusted_sopr_24h_7d_ema
.height
.compute_rolling_average(
.compute_rolling_ema(
starting_indexes.height,
&blocks.count.height_1w_ago,
&self.adjusted_sopr.height,
@@ -270,7 +270,7 @@ impl RealizedAdjusted {
)?;
self.adjusted_sopr_24h_30d_ema
.height
.compute_rolling_average(
.compute_rolling_ema(
starting_indexes.height,
&blocks.count.height_1m_ago,
&self.adjusted_sopr.height,
@@ -13,9 +13,9 @@ use crate::{
distribution::state::RealizedState,
internal::{
CentsUnsignedToDollars, ComputedFromHeightCumulative, ComputedFromHeightLast,
ComputedFromHeightRatio, DollarsPlus, LazyComputedValueFromHeightCumulative, LazyFromHeightLast,
ComputedFromHeightRatio, DollarsPlus, ValueFromHeightCumulative, LazyFromHeightLast,
PercentageDollarsF32, Price, Ratio64,
StoredF32Identity, ValueEmaFromHeight,
StoredF32Identity, ValueFromHeightLast,
},
prices,
};
@@ -130,10 +130,10 @@ pub struct RealizedBase<M: StorageMode = Rw> {
pub peak_regret_rel_to_realized_cap: ComputedFromHeightLast<StoredF32, M>,
// === Sent in Profit/Loss ===
pub sent_in_profit: LazyComputedValueFromHeightCumulative<M>,
pub sent_in_profit_14d_ema: ValueEmaFromHeight<M>,
pub sent_in_loss: LazyComputedValueFromHeightCumulative<M>,
pub sent_in_loss_14d_ema: ValueEmaFromHeight<M>,
pub sent_in_profit: ValueFromHeightCumulative<M>,
pub sent_in_profit_14d_ema: ValueFromHeightLast<M>,
pub sent_in_loss: ValueFromHeightCumulative<M>,
pub sent_in_loss_14d_ema: ValueFromHeightLast<M>,
}
impl RealizedBase {
@@ -540,25 +540,25 @@ impl RealizedBase {
)?,
peak_regret,
peak_regret_rel_to_realized_cap,
sent_in_profit: LazyComputedValueFromHeightCumulative::forced_import(
sent_in_profit: ValueFromHeightCumulative::forced_import(
cfg.db,
&cfg.name("sent_in_profit"),
cfg.version,
cfg.indexes,
)?,
sent_in_profit_14d_ema: ValueEmaFromHeight::forced_import(
sent_in_profit_14d_ema: ValueFromHeightLast::forced_import(
cfg.db,
&cfg.name("sent_in_profit_14d_ema"),
cfg.version,
cfg.indexes,
)?,
sent_in_loss: LazyComputedValueFromHeightCumulative::forced_import(
sent_in_loss: ValueFromHeightCumulative::forced_import(
cfg.db,
&cfg.name("sent_in_loss"),
cfg.version,
cfg.indexes,
)?,
sent_in_loss_14d_ema: ValueEmaFromHeight::forced_import(
sent_in_loss_14d_ema: ValueFromHeightLast::forced_import(
cfg.db,
&cfg.name("sent_in_loss_14d_ema"),
cfg.version,
@@ -1021,14 +1021,14 @@ impl RealizedBase {
exit,
)?;
// 7d rolling averages
self.realized_profit_7d_ema.height.compute_rolling_average(
// 7d EMAs
self.realized_profit_7d_ema.height.compute_rolling_ema(
starting_indexes.height,
&blocks.count.height_1w_ago,
&self.realized_profit.height,
exit,
)?;
self.realized_loss_7d_ema.height.compute_rolling_average(
self.realized_loss_7d_ema.height.compute_rolling_ema(
starting_indexes.height,
&blocks.count.height_1w_ago,
&self.realized_loss.height,
@@ -1036,22 +1036,22 @@ impl RealizedBase {
)?;
self.net_realized_pnl_7d_ema
.height
.compute_rolling_average(
.compute_rolling_ema(
starting_indexes.height,
&blocks.count.height_1w_ago,
&self.net_realized_pnl.height,
exit,
)?;
// 14-day rolling average of sent in profit/loss
self.sent_in_profit_14d_ema.compute_rolling_average(
// 14-day EMA of sent in profit/loss
self.sent_in_profit_14d_ema.compute_ema(
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent_in_profit.base.sats.height,
&self.sent_in_profit.base.usd.height,
exit,
)?;
self.sent_in_loss_14d_ema.compute_rolling_average(
self.sent_in_loss_14d_ema.compute_ema(
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent_in_loss.base.sats.height,
@@ -1060,13 +1060,13 @@ impl RealizedBase {
)?;
// SOPR EMAs
self.sopr_24h_7d_ema.height.compute_rolling_average(
self.sopr_24h_7d_ema.height.compute_rolling_ema(
starting_indexes.height,
&blocks.count.height_1w_ago,
&self.sopr.height,
exit,
)?;
self.sopr_24h_30d_ema.height.compute_rolling_average(
self.sopr_24h_30d_ema.height.compute_rolling_ema(
starting_indexes.height,
&blocks.count.height_1m_ago,
&self.sopr.height,
@@ -1076,7 +1076,7 @@ impl RealizedBase {
// Sell side risk EMAs
self.sell_side_risk_ratio_24h_7d_ema
.height
.compute_rolling_average(
.compute_rolling_ema(
starting_indexes.height,
&blocks.count.height_1w_ago,
&self.sell_side_risk_ratio.height,
@@ -1084,7 +1084,7 @@ impl RealizedBase {
)?;
self.sell_side_risk_ratio_24h_30d_ema
.height
.compute_rolling_average(
.compute_rolling_ema(
starting_indexes.height,
&blocks.count.height_1m_ago,
&self.sell_side_risk_ratio.height,
@@ -8,7 +8,7 @@ use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
use crate::internal::{
HalveDollars, HalveSats, HalveSatsToBitcoin,
LazyValueFromHeightLast, ValueChangeFromHeight, ValueFromHeightLast,
LazyValueFromHeightLast, ValueFromHeightChange, ValueFromHeightLast,
};
use super::ImportConfig;
@@ -19,7 +19,7 @@ pub struct SupplyMetrics<M: StorageMode = Rw> {
pub total: ValueFromHeightLast<M>,
pub halved: LazyValueFromHeightLast,
/// 30-day change in supply (net position change) - sats, btc, usd
pub _30d_change: ValueChangeFromHeight<M>,
pub _30d_change: ValueFromHeightChange<M>,
}
impl SupplyMetrics {
@@ -38,7 +38,7 @@ impl SupplyMetrics {
HalveDollars,
>(&cfg.name("supply_halved"), &supply, cfg.version);
let _30d_change = ValueChangeFromHeight::forced_import(
let _30d_change = ValueFromHeightChange::forced_import(
cfg.db,
&cfg.name("_30d_change"),
cfg.version,
+7 -5
View File
@@ -52,7 +52,7 @@ pub struct Vecs<M: StorageMode = Rw> {
/// Total addresses ever seen (addr_count + empty_addr_count) - stored, global + per-type
pub total_addr_count: TotalAddrCountVecs<M>,
/// New addresses per block (delta of total) - lazy height, stored day1 stats, global + per-type
/// New addresses per block (delta of total) - stored height + cumulative + rolling, global + per-type
pub new_addr_count: NewAddrCountVecs<M>,
/// Growth rate (new / addr_count) - stored ratio with distribution stats, global + per-type
pub growth_rate: GrowthRateVecs<M>,
@@ -117,9 +117,9 @@ impl Vecs {
// Stored total = addr_count + empty_addr_count (global + per-type, with all derived indexes)
let total_addr_count = TotalAddrCountVecs::forced_import(&db, version, indexes)?;
// Lazy delta of total (global + per-type)
// Per-block delta of total (global + per-type)
let new_addr_count =
NewAddrCountVecs::forced_import(&db, version, indexes, &total_addr_count)?;
NewAddrCountVecs::forced_import(&db, version, indexes)?;
// Growth rate: new / addr_count (global + per-type)
let growth_rate = GrowthRateVecs::forced_import(&db, version, indexes)?;
@@ -352,10 +352,12 @@ impl Vecs {
exit,
)?;
// 6d. Compute new_addr_count cumulative + rolling (height is lazy delta)
let window_starts = blocks.count.window_starts();
self.address_activity
.compute_rest(starting_indexes.height, &window_starts, exit)?;
self.new_addr_count
.compute(starting_indexes.height, &window_starts, exit)?;
.compute(starting_indexes.height, &window_starts, &self.total_addr_count, exit)?;
// 6e. Compute growth_rate = new_addr_count / addr_count
self.growth_rate.compute(