mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-12 19:48:15 -07:00
global: big snapshot
This commit is contained in:
@@ -1,67 +0,0 @@
|
||||
//! Amount type with height-level data only (no period-derived views).
|
||||
//!
|
||||
//! Stores sats and cents per index, plus lazy btc and usd transforms.
|
||||
//! Use when period views are unnecessary (e.g., rolling windows provide windowed data).
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, Version};
|
||||
use vecdb::{
|
||||
Database, EagerVec, Exit, ImportableVec, LazyVecFrom1, PcoVec, ReadableCloneableVec, Rw,
|
||||
StorageMode, VecIndex,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
internal::{CentsUnsignedToDollars, SatsToBitcoin, SatsToCents},
|
||||
prices,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::TWO; // Match AmountPerBlock versioning
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Amount<I: VecIndex, M: StorageMode = Rw> {
|
||||
pub sats: M::Stored<EagerVec<PcoVec<I, Sats>>>,
|
||||
pub btc: LazyVecFrom1<I, Bitcoin, I, Sats>,
|
||||
pub cents: M::Stored<EagerVec<PcoVec<I, Cents>>>,
|
||||
pub usd: LazyVecFrom1<I, Dollars, I, Cents>,
|
||||
}
|
||||
|
||||
impl Amount<Height> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats: EagerVec<PcoVec<Height, Sats>> =
|
||||
EagerVec::forced_import(db, &format!("{name}_sats"), v)?;
|
||||
let btc = LazyVecFrom1::transformed::<SatsToBitcoin>(name, v, sats.read_only_boxed_clone());
|
||||
let cents: EagerVec<PcoVec<Height, Cents>> =
|
||||
EagerVec::forced_import(db, &format!("{name}_cents"), v)?;
|
||||
let usd = LazyVecFrom1::transformed::<CentsUnsignedToDollars>(
|
||||
&format!("{name}_usd"),
|
||||
v,
|
||||
cents.read_only_boxed_clone(),
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
sats,
|
||||
btc,
|
||||
cents,
|
||||
usd,
|
||||
})
|
||||
}
|
||||
|
||||
/// Eagerly compute cents height values: sats[h] * price_cents[h] / 1e8.
|
||||
pub(crate) fn compute_cents(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.cents.compute_binary::<Sats, Cents, SatsToCents>(
|
||||
max_from,
|
||||
&self.sats,
|
||||
&prices.spot.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
mod base;
|
||||
mod lazy;
|
||||
|
||||
pub use base::*;
|
||||
pub use lazy::*;
|
||||
|
||||
@@ -2,12 +2,10 @@ mod distribution_stats;
|
||||
mod per_resolution;
|
||||
mod window_24h;
|
||||
mod windows;
|
||||
mod windows_except_1m;
|
||||
mod windows_from_1w;
|
||||
|
||||
pub use distribution_stats::*;
|
||||
pub use per_resolution::*;
|
||||
pub use window_24h::*;
|
||||
pub use windows::*;
|
||||
pub use windows_except_1m::*;
|
||||
pub use windows_from_1w::*;
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Height;
|
||||
use vecdb::CachedVec;
|
||||
|
||||
/// Cached window starts for lazy rolling computations.
|
||||
/// Clone-cheap (all fields are Arc-backed). Shared across all metrics.
|
||||
#[derive(Clone)]
|
||||
pub struct CachedWindowStarts(pub Windows<CachedVec<Height, Height>>);
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Windows<A> {
|
||||
@@ -33,4 +40,32 @@ impl<A> Windows<A> {
|
||||
pub fn as_mut_array_from_1w(&mut self) -> [&mut A; 3] {
|
||||
[&mut self._1w, &mut self._1m, &mut self._1y]
|
||||
}
|
||||
|
||||
pub fn map_with_suffix<B>(&self, mut f: impl FnMut(&str, &A) -> B) -> Windows<B> {
|
||||
Windows {
|
||||
_24h: f(Self::SUFFIXES[0], &self._24h),
|
||||
_1w: f(Self::SUFFIXES[1], &self._1w),
|
||||
_1m: f(Self::SUFFIXES[2], &self._1m),
|
||||
_1y: f(Self::SUFFIXES[3], &self._1y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Windows<(A, B)> {
|
||||
pub fn unzip(self) -> (Windows<A>, Windows<B>) {
|
||||
(
|
||||
Windows {
|
||||
_24h: self._24h.0,
|
||||
_1w: self._1w.0,
|
||||
_1m: self._1m.0,
|
||||
_1y: self._1y.0,
|
||||
},
|
||||
Windows {
|
||||
_24h: self._24h.1,
|
||||
_1w: self._1w.1,
|
||||
_1m: self._1m.1,
|
||||
_1y: self._1y.1,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
use brk_traversable::Traversable;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct WindowsExcept1m<A> {
|
||||
pub _24h: A,
|
||||
pub _1w: A,
|
||||
pub _1y: A,
|
||||
}
|
||||
|
||||
impl<A> WindowsExcept1m<A> {
|
||||
pub const SUFFIXES: [&'static str; 3] = ["24h", "1w", "1y"];
|
||||
|
||||
pub fn try_from_fn<E>(
|
||||
mut f: impl FnMut(&str) -> std::result::Result<A, E>,
|
||||
) -> std::result::Result<Self, E> {
|
||||
Ok(Self {
|
||||
_24h: f(Self::SUFFIXES[0])?,
|
||||
_1w: f(Self::SUFFIXES[1])?,
|
||||
_1y: f(Self::SUFFIXES[2])?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_array(&self) -> [&A; 3] {
|
||||
[&self._24h, &self._1w, &self._1y]
|
||||
}
|
||||
|
||||
pub fn as_mut_array(&mut self) -> [&mut A; 3] {
|
||||
[&mut self._24h, &mut self._1w, &mut self._1y]
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, Version};
|
||||
use vecdb::{AnyVec, Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
use vecdb::{AnyVec, Database, Exit, ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
CentsUnsignedToDollars, ComputedPerBlock, LazyPerBlock, SatsToBitcoin, SatsToCents,
|
||||
Windows,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
@@ -74,33 +73,5 @@ impl AmountPerBlock {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
window_starts: &impl ReadableVec<Height, Height>,
|
||||
sats_source: &impl ReadableVec<Height, Sats>,
|
||||
cents_source: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sats
|
||||
.height
|
||||
.compute_rolling_sum(max_from, window_starts, sats_source, exit)?;
|
||||
self.cents
|
||||
.height
|
||||
.compute_rolling_sum(max_from, window_starts, cents_source, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Windows<AmountPerBlock> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Windows::try_from_fn(|suffix| {
|
||||
AmountPerBlock::forced_import(db, &format!("{name}_{suffix}"), version, indexes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,61 +5,76 @@ use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{AmountPerBlock, RollingSumAmountPerBlock, SatsToCents, WindowStarts},
|
||||
internal::{AmountPerBlock, CachedWindowStarts, LazyRollingSumsAmountFromHeight, SatsToCents},
|
||||
prices,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct AmountPerBlockCumulativeSum<M: StorageMode = Rw> {
|
||||
pub base: AmountPerBlock<M>,
|
||||
pub struct AmountPerBlockCumulativeWithSums<M: StorageMode = Rw> {
|
||||
pub raw: AmountPerBlock<M>,
|
||||
pub cumulative: AmountPerBlock<M>,
|
||||
pub sum: RollingSumAmountPerBlock<M>,
|
||||
pub sum: LazyRollingSumsAmountFromHeight,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::TWO;
|
||||
|
||||
impl AmountPerBlockCumulativeSum {
|
||||
impl AmountPerBlockCumulativeWithSums {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let raw = AmountPerBlock::forced_import(db, name, v, indexes)?;
|
||||
let cumulative =
|
||||
AmountPerBlock::forced_import(db, &format!("{name}_cumulative"), v, indexes)?;
|
||||
let sum = LazyRollingSumsAmountFromHeight::new(
|
||||
&format!("{name}_sum"),
|
||||
v,
|
||||
&cumulative.sats.height,
|
||||
&cumulative.cents.height,
|
||||
cached_starts,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
base: AmountPerBlock::forced_import(db, name, v, indexes)?,
|
||||
cumulative: AmountPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_cumulative"),
|
||||
v,
|
||||
indexes,
|
||||
)?,
|
||||
sum: RollingSumAmountPerBlock::forced_import(db, name, v, indexes)?,
|
||||
raw,
|
||||
cumulative,
|
||||
sum,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
prices: &prices::Vecs,
|
||||
exit: &Exit,
|
||||
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
compute_sats(&mut self.base.sats.height)?;
|
||||
compute_sats(&mut self.raw.sats.height)?;
|
||||
self.compute_rest(max_from, prices, exit)
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
prices: &prices::Vecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.cumulative
|
||||
.sats
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.base.sats.height, exit)?;
|
||||
.compute_cumulative(max_from, &self.raw.sats.height, exit)?;
|
||||
|
||||
self.base
|
||||
self.raw
|
||||
.cents
|
||||
.height
|
||||
.compute_binary::<Sats, Cents, SatsToCents>(
|
||||
max_from,
|
||||
&self.base.sats.height,
|
||||
&self.raw.sats.height,
|
||||
&prices.spot.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
@@ -67,15 +82,7 @@ impl AmountPerBlockCumulativeSum {
|
||||
self.cumulative
|
||||
.cents
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.base.cents.height, exit)?;
|
||||
|
||||
self.sum.compute_rolling_sum(
|
||||
max_from,
|
||||
windows,
|
||||
&self.base.sats.height,
|
||||
&self.base.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
.compute_cumulative(max_from, &self.raw.cents.height, exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@ use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{AmountPerBlock, RollingFullAmountPerBlock, SatsToCents, WindowStarts},
|
||||
internal::{
|
||||
AmountPerBlock, CachedWindowStarts, LazyRollingSumsAmountFromHeight,
|
||||
RollingDistributionAmountPerBlock, SatsToCents, WindowStarts,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
|
||||
@@ -13,8 +16,9 @@ use crate::{
|
||||
pub struct AmountPerBlockFull<M: StorageMode = Rw> {
|
||||
pub base: AmountPerBlock<M>,
|
||||
pub cumulative: AmountPerBlock<M>,
|
||||
pub sum: LazyRollingSumsAmountFromHeight,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: RollingFullAmountPerBlock<M>,
|
||||
pub rolling: RollingDistributionAmountPerBlock<M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::TWO;
|
||||
@@ -25,18 +29,33 @@ impl AmountPerBlockFull {
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
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 sum = LazyRollingSumsAmountFromHeight::new(
|
||||
&format!("{name}_sum"),
|
||||
v,
|
||||
&cumulative.sats.height,
|
||||
&cumulative.cents.height,
|
||||
cached_starts,
|
||||
indexes,
|
||||
);
|
||||
let rolling =
|
||||
RollingDistributionAmountPerBlock::forced_import(db, name, v, indexes)?;
|
||||
|
||||
Ok(Self {
|
||||
base: AmountPerBlock::forced_import(db, name, v, indexes)?,
|
||||
cumulative: AmountPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_cumulative"),
|
||||
v,
|
||||
indexes,
|
||||
)?,
|
||||
rolling: RollingFullAmountPerBlock::forced_import(db, name, v, indexes)?,
|
||||
base,
|
||||
cumulative,
|
||||
sum,
|
||||
rolling,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{DeltaSub, LazyDeltaVec, LazyVecFrom1, ReadableCloneableVec};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
CachedWindowStarts, CentsUnsignedToDollars, DerivedResolutions, LazyPerBlock,
|
||||
LazyRollingSumFromHeight, Resolutions, SatsToBitcoin, Windows,
|
||||
},
|
||||
};
|
||||
|
||||
/// Single window slot: lazy rolling sum for Amount (sats + btc + cents + usd).
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyRollingSumAmountFromHeight {
|
||||
pub sats: LazyRollingSumFromHeight<Sats>,
|
||||
pub btc: LazyPerBlock<Bitcoin, Sats>,
|
||||
pub cents: LazyRollingSumFromHeight<Cents>,
|
||||
pub usd: LazyPerBlock<Dollars, Cents>,
|
||||
}
|
||||
|
||||
/// Lazy rolling sums for all 4 windows, for Amount (sats + btc + cents + usd).
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyRollingSumsAmountFromHeight(pub Windows<LazyRollingSumAmountFromHeight>);
|
||||
|
||||
impl LazyRollingSumsAmountFromHeight {
|
||||
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 sum
|
||||
let sats_sum = LazyDeltaVec::<Height, Sats, Sats, DeltaSub>::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_sum.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
let sats = LazyRollingSumFromHeight {
|
||||
height: sats_sum,
|
||||
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 sum
|
||||
let cents_sum = LazyDeltaVec::<Height, Cents, Cents, DeltaSub>::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_sum.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
let cents = LazyRollingSumFromHeight {
|
||||
height: cents_sum,
|
||||
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, ¢s.resolutions
|
||||
)),
|
||||
};
|
||||
|
||||
LazyRollingSumAmountFromHeight {
|
||||
sats,
|
||||
btc,
|
||||
cents,
|
||||
usd,
|
||||
}
|
||||
};
|
||||
|
||||
Self(cached_starts.0.map_with_suffix(make_slot))
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,8 @@ mod cumulative_sum;
|
||||
mod full;
|
||||
mod lazy;
|
||||
mod lazy_derived_resolutions;
|
||||
mod rolling;
|
||||
mod rolling_full;
|
||||
mod rolling_sum;
|
||||
mod windows;
|
||||
mod with_sum_24h;
|
||||
mod lazy_rolling_sum;
|
||||
mod rolling_distribution;
|
||||
|
||||
pub use base::*;
|
||||
pub use cumulative::*;
|
||||
@@ -16,8 +13,5 @@ pub use cumulative_sum::*;
|
||||
pub use full::*;
|
||||
pub use lazy::*;
|
||||
pub use lazy_derived_resolutions::*;
|
||||
pub use rolling::*;
|
||||
pub use rolling_full::*;
|
||||
pub use rolling_sum::*;
|
||||
pub use windows::*;
|
||||
pub use with_sum_24h::*;
|
||||
pub use lazy_rolling_sum::*;
|
||||
pub use rolling_distribution::*;
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
//! Value type for Height + Rolling pattern.
|
||||
//!
|
||||
//! Combines Value (sats/btc/usd per height, no period views) with
|
||||
//! AmountPerBlockWindows (rolling sums across 4 windows).
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{Amount, AmountPerBlockWindows, WindowStarts},
|
||||
prices,
|
||||
};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct AmountPerBlockRolling<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub amount: Amount<Height, M>,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: AmountPerBlockWindows<M>,
|
||||
}
|
||||
|
||||
impl AmountPerBlockRolling {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
amount: Amount::forced_import(db, name, version)?,
|
||||
rolling: AmountPerBlockWindows::forced_import(db, name, version, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute sats height via closure, then cents from price, then rolling windows.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
prices: &prices::Vecs,
|
||||
exit: &Exit,
|
||||
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
compute_sats(&mut self.amount.sats)?;
|
||||
self.amount.compute_cents(prices, max_from, exit)?;
|
||||
self.rolling.compute_rolling_sum(
|
||||
max_from,
|
||||
windows,
|
||||
&self.amount.sats,
|
||||
&self.amount.cents,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+18
-21
@@ -7,21 +7,21 @@ use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
AmountPerBlock, DistributionStats, WindowStarts, Windows, compute_rolling_distribution_from_starts,
|
||||
AmountPerBlock, DistributionStats, WindowStarts, Windows,
|
||||
compute_rolling_distribution_from_starts,
|
||||
},
|
||||
};
|
||||
|
||||
/// One window slot: sum + 8 distribution stats, each a AmountPerBlock.
|
||||
/// One window slot: 8 distribution stats, each a AmountPerBlock.
|
||||
///
|
||||
/// Tree: `sum.sats.height`, `average.sats.height`, etc.
|
||||
/// Tree: `average.sats.height`, `min.sats.height`, etc.
|
||||
#[derive(Traversable)]
|
||||
pub struct RollingFullSlot<M: StorageMode = Rw> {
|
||||
pub sum: AmountPerBlock<M>,
|
||||
pub struct RollingDistributionSlot<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub distribution: DistributionStats<AmountPerBlock<M>>,
|
||||
}
|
||||
|
||||
impl RollingFullSlot {
|
||||
impl RollingDistributionSlot {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
@@ -29,7 +29,6 @@ impl RollingFullSlot {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sum: AmountPerBlock::forced_import(db, &format!("{name}_sum"), version, indexes)?,
|
||||
distribution: DistributionStats::try_from_fn(|suffix| {
|
||||
AmountPerBlock::forced_import(db, &format!("{name}_{suffix}"), version, indexes)
|
||||
})?,
|
||||
@@ -44,15 +43,6 @@ impl RollingFullSlot {
|
||||
cents_source: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sum
|
||||
.sats
|
||||
.height
|
||||
.compute_rolling_sum(max_from, starts, sats_source, exit)?;
|
||||
self.sum
|
||||
.cents
|
||||
.height
|
||||
.compute_rolling_sum(max_from, starts, cents_source, exit)?;
|
||||
|
||||
let d = &mut self.distribution;
|
||||
|
||||
macro_rules! compute_unit {
|
||||
@@ -80,14 +70,16 @@ impl RollingFullSlot {
|
||||
}
|
||||
}
|
||||
|
||||
/// Rolling sum + distribution across 4 windows, window-first.
|
||||
/// Rolling distribution across 4 windows, window-first.
|
||||
///
|
||||
/// Tree: `_24h.sum.sats.height`, `_24h.average.sats.height`, etc.
|
||||
/// Tree: `_24h.average.sats.height`, `_24h.min.sats.height`, etc.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct RollingFullAmountPerBlock<M: StorageMode = Rw>(pub Windows<RollingFullSlot<M>>);
|
||||
pub struct RollingDistributionAmountPerBlock<M: StorageMode = Rw>(
|
||||
pub Windows<RollingDistributionSlot<M>>,
|
||||
);
|
||||
|
||||
impl RollingFullAmountPerBlock {
|
||||
impl RollingDistributionAmountPerBlock {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
@@ -95,7 +87,12 @@ impl RollingFullAmountPerBlock {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(Windows::try_from_fn(|suffix| {
|
||||
RollingFullSlot::forced_import(db, &format!("{name}_{suffix}"), version, indexes)
|
||||
RollingDistributionSlot::forced_import(
|
||||
db,
|
||||
&format!("{name}_{suffix}"),
|
||||
version,
|
||||
indexes,
|
||||
)
|
||||
})?))
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{AmountPerBlock, RollingWindow24h, WindowStarts, Windows},
|
||||
};
|
||||
|
||||
/// Single 24h rolling sum as amount (sats + btc + cents + usd).
|
||||
///
|
||||
/// Tree: `_24h.sats.height`, `_24h.btc.height`, etc.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct RollingWindow24hAmountPerBlock<M: StorageMode = Rw>(
|
||||
pub RollingWindow24h<AmountPerBlock<M>>,
|
||||
);
|
||||
|
||||
impl RollingWindow24hAmountPerBlock {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(RollingWindow24h {
|
||||
_24h: AmountPerBlock::forced_import(db, &format!("{name}_24h"), version, indexes)?,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
height_24h_ago: &impl ReadableVec<Height, Height>,
|
||||
sats_source: &impl ReadableVec<Height, Sats>,
|
||||
cents_source: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self._24h
|
||||
.compute_rolling_sum(max_from, height_24h_ago, sats_source, cents_source, exit)
|
||||
}
|
||||
}
|
||||
|
||||
/// Rolling sum only, window-first then unit.
|
||||
///
|
||||
/// Tree: `_24h.sats.height`, `_24h.btc.height`, etc.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct RollingSumAmountPerBlock<M: StorageMode = Rw>(pub Windows<AmountPerBlock<M>>);
|
||||
|
||||
impl RollingSumAmountPerBlock {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(Windows::<AmountPerBlock>::forced_import(
|
||||
db,
|
||||
&format!("{name}_sum"),
|
||||
version,
|
||||
indexes,
|
||||
)?))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
sats_source: &impl ReadableVec<Height, Sats>,
|
||||
cents_source: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
for (w, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) {
|
||||
w.sats
|
||||
.height
|
||||
.compute_rolling_sum(max_from, *starts, sats_source, exit)?;
|
||||
w.cents
|
||||
.height
|
||||
.compute_rolling_sum(max_from, *starts, cents_source, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
//! AmountPerBlockWindows - window-first ordering.
|
||||
//!
|
||||
//! Access pattern: `coinbase_sum._24h.sats.height`
|
||||
//! Each window (24h, 7d, 30d, 1y) contains sats (stored) + btc (lazy) + usd (stored).
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use brk_types::{Cents, Sats};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{AmountPerBlock, WindowStarts, Windows},
|
||||
};
|
||||
|
||||
/// Value rolling windows — window-first, currency-last.
|
||||
///
|
||||
/// Each window contains `AmountPerBlock` (sats + btc lazy + usd).
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct AmountPerBlockWindows<M: StorageMode = Rw>(pub Windows<AmountPerBlock<M>>);
|
||||
|
||||
impl AmountPerBlockWindows {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(Windows::try_from_fn(|suffix| {
|
||||
AmountPerBlock::forced_import(db, &format!("{name}_{suffix}"), version, indexes)
|
||||
})?))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
sats_source: &impl ReadableVec<Height, Sats>,
|
||||
cents_source: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
for (w, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) {
|
||||
w.compute_rolling_sum(max_from, *starts, sats_source, cents_source, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
//! AmountPerBlockWithSum24h - AmountPerBlock raw + RollingWindow24hAmountPerBlock sum.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{AmountPerBlock, RollingWindow24hAmountPerBlock};
|
||||
|
||||
/// Amount per-block value (sats + cents) with 24h rolling sum (also amount).
|
||||
#[derive(Traversable)]
|
||||
pub struct AmountPerBlockWithSum24h<M: StorageMode = Rw> {
|
||||
pub raw: AmountPerBlock<M>,
|
||||
pub sum: RollingWindow24hAmountPerBlock<M>,
|
||||
}
|
||||
@@ -3,8 +3,6 @@
|
||||
//! For metrics aggregated per-block from finer-grained sources (e.g., per-tx data),
|
||||
//! where we want full per-block stats plus rolling window stats.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
@@ -13,7 +11,7 @@ use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{Full, NumericValue, RollingFull, WindowStarts},
|
||||
internal::{CachedWindowStarts, Full, NumericValue, RollingFull, WindowStarts},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
@@ -35,17 +33,22 @@ where
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let height = Full::forced_import(db, name, version)?;
|
||||
let rolling = RollingFull::forced_import(db, name, version, indexes)?;
|
||||
let full = Full::forced_import(db, name, version)?;
|
||||
let rolling = RollingFull::forced_import(
|
||||
db,
|
||||
name,
|
||||
version,
|
||||
indexes,
|
||||
&full.cumulative,
|
||||
cached_starts,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
full: height,
|
||||
rolling,
|
||||
})
|
||||
Ok(Self { full, rolling })
|
||||
}
|
||||
|
||||
/// Compute Full stats via closure, then rolling windows from the per-block sum.
|
||||
/// Compute Full stats via closure, then rolling distribution from the per-block sum.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
@@ -54,7 +57,7 @@ where
|
||||
compute_full: impl FnOnce(&mut Full<Height, T>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: From<f64> + Default + SubAssign + Copy + Ord,
|
||||
T: From<f64> + Default + Copy + Ord,
|
||||
f64: From<T>,
|
||||
{
|
||||
compute_full(&mut self.full)?;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
//! ComputedPerBlockCumulative - raw ComputedPerBlock + cumulative ComputedPerBlock.
|
||||
//!
|
||||
//! Like ComputedPerBlockCumulativeSum but without RollingWindows.
|
||||
//! Like ComputedPerBlockCumulativeWithSums 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, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -40,20 +40,6 @@ where
|
||||
Ok(Self { raw, cumulative })
|
||||
}
|
||||
|
||||
/// Compute raw data via closure, then cumulative only (no rolling).
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
compute_raw: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
compute_raw(&mut self.raw.height)?;
|
||||
self.compute_rest(max_from, exit)
|
||||
}
|
||||
|
||||
/// Compute cumulative from already-filled raw vec.
|
||||
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()>
|
||||
where
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
//! ComputedPerBlockCumulativeSum - raw ComputedPerBlock + cumulative ComputedPerBlock + RollingWindows (sum).
|
||||
//! ComputedPerBlockCumulativeWithSums - raw ComputedPerBlock + cumulative ComputedPerBlock + lazy rolling sums.
|
||||
//!
|
||||
//! Like ComputedPerBlockFull but with rolling sum only (no distribution).
|
||||
//! Used for count metrics where distribution stats aren't meaningful.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
//! Rolling sums are derived lazily from the cumulative vec via LazyDeltaVec.
|
||||
//! No rolling sum vecs are stored on disk.
|
||||
//!
|
||||
//! Type parameters:
|
||||
//! - `T`: per-block value type (e.g., `StoredU32` for tx counts)
|
||||
//! - `M`: storage mode (`Rw` or `Ro`)
|
||||
//! - `C`: cumulative type, defaults to `T`. Use a wider type (e.g., `StoredU64`)
|
||||
//! when the prefix sum of `T` values could overflow `T`.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
@@ -13,33 +17,42 @@ use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedPerBlock, NumericValue, RollingWindows, WindowStarts},
|
||||
internal::{CachedWindowStarts, ComputedPerBlock, LazyRollingSumsFromHeight, NumericValue},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ComputedPerBlockCumulativeSum<T, M: StorageMode = Rw>
|
||||
pub struct ComputedPerBlockCumulativeWithSums<T, C, M: StorageMode = Rw>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
pub raw: ComputedPerBlock<T, M>,
|
||||
pub cumulative: ComputedPerBlock<T, M>,
|
||||
pub sum: RollingWindows<T, M>,
|
||||
pub cumulative: ComputedPerBlock<C, M>,
|
||||
pub sum: LazyRollingSumsFromHeight<C>,
|
||||
}
|
||||
|
||||
impl<T> ComputedPerBlockCumulativeSum<T>
|
||||
impl<T, C> ComputedPerBlockCumulativeWithSums<T, C>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
T: NumericValue + JsonSchema + Into<C>,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let raw = ComputedPerBlock::forced_import(db, name, version, indexes)?;
|
||||
let cumulative =
|
||||
ComputedPerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?;
|
||||
let sum = RollingWindows::forced_import(db, name, version, indexes)?;
|
||||
let sum = LazyRollingSumsFromHeight::new(
|
||||
&format!("{name}_sum"),
|
||||
version,
|
||||
&cumulative.height,
|
||||
cached_starts,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
raw,
|
||||
@@ -48,36 +61,28 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute raw data via closure, then cumulative + rolling sum.
|
||||
/// Compute raw data via closure, then cumulative. Rolling sums are lazy.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_raw: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Default + SubAssign,
|
||||
C: Default,
|
||||
{
|
||||
compute_raw(&mut self.raw.height)?;
|
||||
self.compute_rest(max_from, windows, exit)
|
||||
self.compute_rest(max_from, exit)
|
||||
}
|
||||
|
||||
/// Compute cumulative + rolling sum from already-populated raw data.
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
/// Compute cumulative from already-populated raw data. Rolling sums are lazy.
|
||||
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()>
|
||||
where
|
||||
T: Default + SubAssign,
|
||||
C: Default,
|
||||
{
|
||||
self.cumulative
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.raw.height, exit)?;
|
||||
self.sum
|
||||
.compute_rolling_sum(max_from, windows, &self.raw.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,292 +0,0 @@
|
||||
//! RollingDelta - raw change + growth rate (%) across time windows.
|
||||
//!
|
||||
//! Three tiers:
|
||||
//! - `RollingDelta1m` — 1m window only (2 stored vecs: change + rate). Default for all cohorts.
|
||||
//! - `RollingDeltaExcept1m` — 24h + 1w + 1y windows (6 stored vecs). Extended tier only.
|
||||
//! - `RollingDelta` — all 4 windows (8 stored vecs). Used for standalone global metrics.
|
||||
//!
|
||||
//! For a monotonic source (e.g., cumulative address count):
|
||||
//! - `change._24h` = count_now - count_24h_ago
|
||||
//! - `rate._24h` = (count_now - count_24h_ago) / count_24h_ago in BPS
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints32, BasisPointsSigned32, Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedPerBlock, NumericValue, PercentPerBlock, PercentRollingWindows,
|
||||
RollingWindows, WindowStarts, WindowsExcept1m,
|
||||
},
|
||||
};
|
||||
|
||||
/// Pre-collect source data from the earliest needed offset.
|
||||
/// Returns (source_data, offset) for use in compute_delta_window.
|
||||
fn collect_source<S: NumericValue>(
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
skip: usize,
|
||||
earliest_starts: &impl ReadableVec<Height, Height>,
|
||||
) -> (Vec<S>, usize) {
|
||||
let source_len = source.len();
|
||||
let offset = if skip > 0 && skip < earliest_starts.len() {
|
||||
earliest_starts.collect_one_at(skip).unwrap().to_usize()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
(source.collect_range_at(offset, source_len), offset)
|
||||
}
|
||||
|
||||
/// Shared computation: change = current - ago, rate = change / ago.
|
||||
pub(super) fn compute_delta_window<S, C, B>(
|
||||
change_h: &mut EagerVec<PcoVec<Height, C>>,
|
||||
rate_bps_h: &mut EagerVec<PcoVec<Height, B>>,
|
||||
max_from: Height,
|
||||
starts: &impl ReadableVec<Height, Height>,
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
S: NumericValue,
|
||||
C: NumericValue,
|
||||
B: NumericValue,
|
||||
{
|
||||
let skip = change_h.len();
|
||||
let (mut source_data, mut offset) = collect_source(source, skip, starts);
|
||||
|
||||
change_h.compute_transform(
|
||||
max_from,
|
||||
starts,
|
||||
|(h, ago_h, ..)| {
|
||||
if h.to_usize() < offset || ago_h.to_usize() < offset {
|
||||
// Version reset cleared the vec — re-collect from scratch
|
||||
source_data = source.collect();
|
||||
offset = 0;
|
||||
}
|
||||
let current: f64 = source_data[h.to_usize() - offset].into();
|
||||
let ago: f64 = source_data[ago_h.to_usize() - offset].into();
|
||||
(h, C::from(current - ago))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
rate_bps_h.compute_transform(
|
||||
max_from,
|
||||
&*change_h,
|
||||
|(h, change, ..)| {
|
||||
if h.to_usize() < offset {
|
||||
// Version reset cleared the vec — re-collect from scratch
|
||||
source_data = source.collect();
|
||||
offset = 0;
|
||||
}
|
||||
let current_f: f64 = source_data[h.to_usize() - offset].into();
|
||||
let change_f: f64 = change.into();
|
||||
let ago = current_f - change_f;
|
||||
let rate = if ago == 0.0 { 0.0 } else { change_f / ago };
|
||||
(h, B::from(rate))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RollingDelta<S, C = S, M: StorageMode = Rw>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
pub change: RollingWindows<C, M>,
|
||||
pub rate: PercentRollingWindows<BasisPoints32, M>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S, C> RollingDelta<S, C>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
change: RollingWindows::forced_import(
|
||||
db,
|
||||
&format!("{name}_change"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
rate: PercentRollingWindows::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
for ((change_w, rate_w), starts) in self
|
||||
.change
|
||||
.0
|
||||
.as_mut_array()
|
||||
.into_iter()
|
||||
.zip(self.rate.0.as_mut_array())
|
||||
.zip(windows.as_array())
|
||||
{
|
||||
compute_delta_window(
|
||||
&mut change_w.height,
|
||||
&mut rate_w.bps.height,
|
||||
max_from,
|
||||
*starts,
|
||||
source,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// 1m-only delta: change + growth rate for the 1-month window.
|
||||
/// Default tier for all cohorts (2 stored vecs).
|
||||
#[derive(Traversable)]
|
||||
pub struct RollingDelta1m<S, C = S, M: StorageMode = Rw>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
#[traversable(wrap = "change", rename = "1m")]
|
||||
pub change_1m: ComputedPerBlock<C, M>,
|
||||
#[traversable(wrap = "rate", rename = "1m")]
|
||||
pub rate_1m: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S, C> RollingDelta1m<S, C>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
change_1m: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_1m"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
rate_1m: PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate_1m"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
height_1m_ago: &impl ReadableVec<Height, Height>,
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
compute_delta_window(
|
||||
&mut self.change_1m.height,
|
||||
&mut self.rate_1m.bps.height,
|
||||
max_from,
|
||||
height_1m_ago,
|
||||
source,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended delta: 24h + 1w + 1y windows (6 stored vecs).
|
||||
/// Only for All/LTH/STH cohorts (Extended tier).
|
||||
#[derive(Traversable)]
|
||||
pub struct RollingDeltaExcept1m<S, C = S, M: StorageMode = Rw>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
pub change: WindowsExcept1m<ComputedPerBlock<C, M>>,
|
||||
pub rate: WindowsExcept1m<PercentPerBlock<BasisPointsSigned32, M>>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S, C> RollingDeltaExcept1m<S, C>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
change: WindowsExcept1m::try_from_fn(|suffix| {
|
||||
ComputedPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_{suffix}"),
|
||||
version,
|
||||
indexes,
|
||||
)
|
||||
})?,
|
||||
rate: WindowsExcept1m::try_from_fn(|suffix| {
|
||||
PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate_{suffix}"),
|
||||
version,
|
||||
indexes,
|
||||
)
|
||||
})?,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let changes = self.change.as_mut_array();
|
||||
let rates = self.rate.as_mut_array();
|
||||
let starts = [windows._24h, windows._1w, windows._1y];
|
||||
|
||||
for ((change_w, rate_w), starts) in changes.into_iter().zip(rates).zip(starts) {
|
||||
compute_delta_window(
|
||||
&mut change_w.height,
|
||||
&mut rate_w.bps.height,
|
||||
max_from,
|
||||
starts,
|
||||
source,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
//! Fiat delta variants — same as RollingDelta* but change is FiatPerBlock<C>
|
||||
//! (stored cents + lazy USD) instead of ComputedPerBlock<C> (stored cents only).
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPointsSigned32, Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
CentsType, FiatPerBlock, NumericValue, PercentPerBlock, PercentRollingWindows,
|
||||
Windows, WindowStarts, WindowsExcept1m,
|
||||
},
|
||||
};
|
||||
|
||||
use super::delta::compute_delta_window;
|
||||
|
||||
/// Fiat 1m-only delta: fiat change (cents + usd) + rate for the 1-month window.
|
||||
#[derive(Traversable)]
|
||||
pub struct FiatRollingDelta1m<S, C, M: StorageMode = Rw>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: CentsType,
|
||||
{
|
||||
#[traversable(wrap = "change", rename = "1m")]
|
||||
pub change_1m: FiatPerBlock<C, M>,
|
||||
#[traversable(wrap = "rate", rename = "1m")]
|
||||
pub rate_1m: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S, C> FiatRollingDelta1m<S, C>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: CentsType,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
change_1m: FiatPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_1m"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
rate_1m: PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate_1m"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
height_1m_ago: &impl ReadableVec<Height, Height>,
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
compute_delta_window(
|
||||
&mut self.change_1m.cents.height,
|
||||
&mut self.rate_1m.bps.height,
|
||||
max_from,
|
||||
height_1m_ago,
|
||||
source,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Fiat extended delta: 24h + 1w + 1y windows, fiat change (cents + usd) + rate.
|
||||
#[derive(Traversable)]
|
||||
pub struct FiatRollingDeltaExcept1m<S, C, M: StorageMode = Rw>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: CentsType,
|
||||
{
|
||||
pub change: WindowsExcept1m<FiatPerBlock<C, M>>,
|
||||
pub rate: WindowsExcept1m<PercentPerBlock<BasisPointsSigned32, M>>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S, C> FiatRollingDeltaExcept1m<S, C>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: CentsType,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
change: WindowsExcept1m::try_from_fn(|suffix| {
|
||||
FiatPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_{suffix}"),
|
||||
version,
|
||||
indexes,
|
||||
)
|
||||
})?,
|
||||
rate: WindowsExcept1m::try_from_fn(|suffix| {
|
||||
PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate_{suffix}"),
|
||||
version,
|
||||
indexes,
|
||||
)
|
||||
})?,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let changes = self.change.as_mut_array();
|
||||
let rates = self.rate.as_mut_array();
|
||||
let starts = [windows._24h, windows._1w, windows._1y];
|
||||
|
||||
for ((change_w, rate_w), starts) in changes.into_iter().zip(rates).zip(starts) {
|
||||
compute_delta_window(
|
||||
&mut change_w.cents.height,
|
||||
&mut rate_w.bps.height,
|
||||
max_from,
|
||||
starts,
|
||||
source,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Fiat rolling delta: all 4 windows, fiat change (cents + usd) + rate.
|
||||
#[derive(Traversable)]
|
||||
pub struct FiatRollingDelta<S, C, M: StorageMode = Rw>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: CentsType,
|
||||
{
|
||||
pub change: Windows<FiatPerBlock<C, M>>,
|
||||
pub rate: PercentRollingWindows<BasisPointsSigned32, M>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S, C> FiatRollingDelta<S, C>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: CentsType,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
change: Windows::try_from_fn(|suffix| {
|
||||
FiatPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_{suffix}"),
|
||||
version,
|
||||
indexes,
|
||||
)
|
||||
})?,
|
||||
rate: PercentRollingWindows::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let changes = self.change.as_mut_array();
|
||||
let rates = self.rate.0.as_mut_array();
|
||||
let starts = windows.as_array();
|
||||
|
||||
for ((change_w, rate_w), starts) in changes.into_iter().zip(rates).zip(starts) {
|
||||
compute_delta_window(
|
||||
&mut change_w.cents.height,
|
||||
&mut rate_w.bps.height,
|
||||
max_from,
|
||||
*starts,
|
||||
source,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,6 @@
|
||||
//!
|
||||
//! For metrics with stored per-block data, cumulative sums, and rolling windows.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
@@ -12,7 +10,7 @@ use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedPerBlock, NumericValue, RollingFull, WindowStarts},
|
||||
internal::{CachedWindowStarts, ComputedPerBlock, NumericValue, RollingFull, WindowStarts},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
@@ -35,11 +33,19 @@ where
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let raw = ComputedPerBlock::forced_import(db, name, version, indexes)?;
|
||||
let cumulative =
|
||||
ComputedPerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?;
|
||||
let rolling = RollingFull::forced_import(db, name, version, indexes)?;
|
||||
let rolling = RollingFull::forced_import(
|
||||
db,
|
||||
name,
|
||||
version,
|
||||
indexes,
|
||||
&cumulative.height,
|
||||
cached_starts,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
raw,
|
||||
@@ -48,7 +54,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute raw data via closure, then cumulative + rolling.
|
||||
/// Compute raw data via closure, then cumulative + rolling distribution.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
@@ -57,7 +63,7 @@ where
|
||||
compute_raw: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: From<f64> + Default + SubAssign + Copy + Ord,
|
||||
T: From<f64> + Default + Copy + Ord,
|
||||
f64: From<T>,
|
||||
{
|
||||
compute_raw(&mut self.raw.height)?;
|
||||
|
||||
@@ -3,25 +3,19 @@ mod base;
|
||||
mod constant;
|
||||
mod cumulative;
|
||||
mod cumulative_sum;
|
||||
mod delta;
|
||||
mod resolutions;
|
||||
mod resolutions_full;
|
||||
mod fiat_delta;
|
||||
mod full;
|
||||
mod rolling_average;
|
||||
mod sum;
|
||||
mod with_sum_24h;
|
||||
mod with_deltas;
|
||||
|
||||
pub use aggregated::*;
|
||||
pub use base::*;
|
||||
pub use constant::*;
|
||||
pub use cumulative::*;
|
||||
pub use cumulative_sum::*;
|
||||
pub use delta::*;
|
||||
pub use resolutions::*;
|
||||
pub use resolutions_full::*;
|
||||
pub use fiat_delta::*;
|
||||
pub use full::*;
|
||||
pub use rolling_average::*;
|
||||
pub use sum::*;
|
||||
pub use with_sum_24h::*;
|
||||
pub use with_deltas::*;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
//! For metrics derived from indexer sources (no stored height vec).
|
||||
//! Cumulative gets its own ComputedPerBlock so it has LazyAggVec index views too.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
@@ -13,7 +11,7 @@ use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedPerBlock, NumericValue, RollingFull, WindowStarts},
|
||||
internal::{CachedWindowStarts, ComputedPerBlock, NumericValue, RollingFull, WindowStarts},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
@@ -35,10 +33,18 @@ where
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let cumulative =
|
||||
ComputedPerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?;
|
||||
let rolling = RollingFull::forced_import(db, name, version, indexes)?;
|
||||
let rolling = RollingFull::forced_import(
|
||||
db,
|
||||
name,
|
||||
version,
|
||||
indexes,
|
||||
&cumulative.height,
|
||||
cached_starts,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
cumulative,
|
||||
@@ -54,7 +60,7 @@ where
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: From<f64> + Default + SubAssign + Copy + Ord,
|
||||
T: From<f64> + Default + Copy + Ord,
|
||||
f64: From<T>,
|
||||
{
|
||||
self.cumulative
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
//! ComputedPerBlock with rolling average (no distribution stats).
|
||||
//!
|
||||
//! Stored height data + 4-window rolling averages (24h, 1w, 1m, 1y).
|
||||
//! Use instead of ComputedPerBlockDistribution when only the average
|
||||
//! is analytically useful (e.g., block interval, activity counts).
|
||||
//! Stored height data + f64 cumulative + lazy 4-window rolling averages.
|
||||
//! Rolling averages are computed on-the-fly from the cumulative via DeltaAvg.
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
@@ -13,7 +12,7 @@ use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::indexes;
|
||||
|
||||
use crate::internal::{NumericValue, RollingWindows, WindowStarts};
|
||||
use crate::internal::{CachedWindowStarts, LazyRollingAvgsFromHeight, NumericValue};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ComputedPerBlockRollingAverage<T, M: StorageMode = Rw>
|
||||
@@ -21,8 +20,10 @@ where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
#[traversable(hidden)]
|
||||
pub cumulative: M::Stored<EagerVec<PcoVec<Height, f64>>>,
|
||||
#[traversable(flatten)]
|
||||
pub average: RollingWindows<T, M>,
|
||||
pub average: LazyRollingAvgsFromHeight<T>,
|
||||
}
|
||||
|
||||
impl<T> ComputedPerBlockRollingAverage<T>
|
||||
@@ -34,45 +35,41 @@ where
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, version)?;
|
||||
let average =
|
||||
RollingWindows::forced_import(db, &format!("{name}_average"), version + Version::ONE, indexes)?;
|
||||
let cumulative: EagerVec<PcoVec<Height, f64>> =
|
||||
EagerVec::forced_import(db, &format!("{name}_cumulative"), version)?;
|
||||
let average = LazyRollingAvgsFromHeight::new(
|
||||
&format!("{name}_average"),
|
||||
version + Version::ONE,
|
||||
&cumulative,
|
||||
cached_starts,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self { height, average })
|
||||
Ok(Self {
|
||||
height,
|
||||
cumulative,
|
||||
average,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute height data via closure, then rolling averages.
|
||||
/// Compute height data via closure, then cumulative. Rolling averages are lazy.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_height: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Default,
|
||||
f64: From<T>,
|
||||
{
|
||||
) -> Result<()> {
|
||||
compute_height(&mut self.height)?;
|
||||
self.compute_rest(max_from, windows, exit)
|
||||
self.compute_rest(max_from, exit)
|
||||
}
|
||||
|
||||
/// Compute rolling averages from already-populated height data.
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Default,
|
||||
f64: From<T>,
|
||||
{
|
||||
for (w, starts) in self.average.0.as_mut_array().into_iter().zip(windows.as_array()) {
|
||||
w.height
|
||||
.compute_rolling_average(max_from, *starts, &self.height, exit)?;
|
||||
}
|
||||
/// Compute cumulative from already-populated height data. Rolling averages are lazy.
|
||||
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> {
|
||||
self.cumulative
|
||||
.compute_cumulative(max_from, &self.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
//! ComputedPerBlockSum - raw ComputedPerBlock + RollingWindows (sum only).
|
||||
//!
|
||||
//! Like ComputedPerBlockCumulativeSum but without the cumulative vec.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedPerBlock, NumericValue, RollingWindows, WindowStarts},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ComputedPerBlockSum<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub raw: ComputedPerBlock<T, M>,
|
||||
pub sum: RollingWindows<T, M>,
|
||||
}
|
||||
|
||||
impl<T> ComputedPerBlockSum<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let raw = ComputedPerBlock::forced_import(db, name, version, indexes)?;
|
||||
let sum = RollingWindows::forced_import(db, &format!("{name}_sum"), version, indexes)?;
|
||||
|
||||
Ok(Self { raw, sum })
|
||||
}
|
||||
|
||||
/// Compute raw data via closure, then rolling sum.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_raw: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Default + SubAssign,
|
||||
{
|
||||
compute_raw(&mut self.raw.height)?;
|
||||
self.sum
|
||||
.compute_rolling_sum(max_from, windows, &self.raw.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
BpsType, CachedWindowStarts, ComputedPerBlock, LazyRollingDeltasFromHeight, NumericValue,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct ComputedPerBlockWithDeltas<S, C, B, M: StorageMode = Rw>
|
||||
where
|
||||
S: NumericValue + JsonSchema + Into<f64>,
|
||||
C: NumericValue + JsonSchema + From<f64>,
|
||||
B: BpsType + From<f64>,
|
||||
{
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub inner: ComputedPerBlock<S, M>,
|
||||
pub delta: LazyRollingDeltasFromHeight<S, C, B>,
|
||||
}
|
||||
|
||||
impl<S, C, B> ComputedPerBlockWithDeltas<S, C, B>
|
||||
where
|
||||
S: NumericValue + JsonSchema + Into<f64>,
|
||||
C: NumericValue + JsonSchema + From<f64>,
|
||||
B: BpsType + From<f64>,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &vecdb::Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
delta_version_offset: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let inner = ComputedPerBlock::forced_import(db, name, version, indexes)?;
|
||||
|
||||
let delta = LazyRollingDeltasFromHeight::new(
|
||||
&format!("{name}_delta"),
|
||||
version + delta_version_offset,
|
||||
&inner.height,
|
||||
cached_starts,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self { inner, delta })
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//! PerBlockWithSum24h - ComputedPerBlock + RollingWindow24hPerBlock rolling sum.
|
||||
//!
|
||||
//! Generic building block for metrics that store a per-block value
|
||||
//! plus its 24h rolling sum. Used across activity and realized metrics.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedPerBlock, ComputedVecValue, RollingWindow24hPerBlock};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct PerBlockWithSum24h<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub raw: ComputedPerBlock<T, M>,
|
||||
pub sum: RollingWindow24hPerBlock<T, M>,
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{CachedWindowStarts, CentsType, FiatPerBlock, LazyRollingSumsFiatFromHeight},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct FiatPerBlockCumulativeWithSums<C: CentsType, M: StorageMode = Rw> {
|
||||
pub raw: FiatPerBlock<C, M>,
|
||||
pub cumulative: FiatPerBlock<C, M>,
|
||||
pub sum: LazyRollingSumsFiatFromHeight<C>,
|
||||
}
|
||||
|
||||
impl<C: CentsType> FiatPerBlockCumulativeWithSums<C> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let raw = FiatPerBlock::forced_import(db, name, version, indexes)?;
|
||||
let cumulative = FiatPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_cumulative"),
|
||||
version,
|
||||
indexes,
|
||||
)?;
|
||||
let sum = LazyRollingSumsFiatFromHeight::new(
|
||||
&format!("{name}_sum"),
|
||||
version,
|
||||
&cumulative.cents.height,
|
||||
cached_starts,
|
||||
indexes,
|
||||
);
|
||||
Ok(Self { raw, cumulative, sum })
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()>
|
||||
where
|
||||
C: Default,
|
||||
{
|
||||
self.cumulative
|
||||
.cents
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.raw.cents.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{BpsType, CachedWindowStarts, LazyRollingDeltasFiatFromHeight},
|
||||
};
|
||||
|
||||
use super::{CentsType, FiatPerBlockCumulativeWithSums};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct FiatPerBlockCumulativeWithSumsAndDeltas<C, CS, B, M: StorageMode = Rw>
|
||||
where
|
||||
C: CentsType + Into<f64>,
|
||||
CS: CentsType + From<f64>,
|
||||
B: BpsType + From<f64>,
|
||||
{
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub inner: FiatPerBlockCumulativeWithSums<C, M>,
|
||||
pub delta: LazyRollingDeltasFiatFromHeight<C, CS, B>,
|
||||
}
|
||||
|
||||
impl<C, CS, B> FiatPerBlockCumulativeWithSumsAndDeltas<C, CS, B>
|
||||
where
|
||||
C: CentsType + Into<f64>,
|
||||
CS: CentsType + From<f64>,
|
||||
B: BpsType + From<f64>,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
delta_version_offset: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let inner =
|
||||
FiatPerBlockCumulativeWithSums::forced_import(db, name, version, indexes, cached_starts)?;
|
||||
|
||||
let delta = LazyRollingDeltasFiatFromHeight::new(
|
||||
&format!("{name}_delta"),
|
||||
version + delta_version_offset,
|
||||
&inner.cumulative.cents.height,
|
||||
cached_starts,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self { inner, delta })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{DeltaSub, LazyDeltaVec, LazyVecFrom1, ReadableCloneableVec};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
CachedWindowStarts, CentsType, DerivedResolutions, LazyPerBlock, LazyRollingSumFromHeight,
|
||||
Resolutions, Windows,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyRollingSumFiatFromHeight<C: CentsType> {
|
||||
pub cents: LazyRollingSumFromHeight<C>,
|
||||
pub usd: LazyPerBlock<Dollars, C>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyRollingSumsFiatFromHeight<C: CentsType>(
|
||||
pub Windows<LazyRollingSumFiatFromHeight<C>>,
|
||||
);
|
||||
|
||||
impl<C: CentsType> LazyRollingSumsFiatFromHeight<C> {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
version: Version,
|
||||
cumulative_cents: &(impl ReadableCloneableVec<Height, C> + 'static),
|
||||
cached_starts: &CachedWindowStarts,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Self {
|
||||
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();
|
||||
|
||||
let cents_sum = LazyDeltaVec::<Height, C, C, DeltaSub>::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_sum.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
let cents = LazyRollingSumFromHeight {
|
||||
height: cents_sum,
|
||||
resolutions: Box::new(cents_resolutions),
|
||||
};
|
||||
|
||||
let usd = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<C::ToDollars>(
|
||||
&full_name,
|
||||
version,
|
||||
cents.height.read_only_boxed_clone(),
|
||||
),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<C::ToDollars>(
|
||||
&full_name,
|
||||
version,
|
||||
¢s.resolutions,
|
||||
)),
|
||||
};
|
||||
|
||||
LazyRollingSumFiatFromHeight { cents, usd }
|
||||
};
|
||||
|
||||
Self(cached_starts.0.map_with_suffix(make_slot))
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
mod base;
|
||||
mod cumulative_sum;
|
||||
mod cumulative_sum_with_deltas;
|
||||
mod lazy;
|
||||
mod with_sum_24h;
|
||||
|
||||
mod lazy_rolling_sum;
|
||||
mod with_deltas;
|
||||
pub use base::*;
|
||||
pub use cumulative_sum::*;
|
||||
pub use cumulative_sum_with_deltas::*;
|
||||
pub use lazy::*;
|
||||
pub use with_sum_24h::*;
|
||||
pub use lazy_rolling_sum::*;
|
||||
pub use with_deltas::*;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{BpsType, CachedWindowStarts, LazyRollingDeltasFiatFromHeight},
|
||||
};
|
||||
|
||||
use super::{CentsType, FiatPerBlock};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct FiatPerBlockWithDeltas<C, CS, B, M: StorageMode = Rw>
|
||||
where
|
||||
C: CentsType + Into<f64>,
|
||||
CS: CentsType + From<f64>,
|
||||
B: BpsType + From<f64>,
|
||||
{
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub inner: FiatPerBlock<C, M>,
|
||||
pub delta: LazyRollingDeltasFiatFromHeight<C, CS, B>,
|
||||
}
|
||||
|
||||
impl<C, CS, B> FiatPerBlockWithDeltas<C, CS, B>
|
||||
where
|
||||
C: CentsType + JsonSchema + Into<f64>,
|
||||
CS: CentsType + From<f64>,
|
||||
B: BpsType + From<f64>,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
delta_version_offset: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let inner = FiatPerBlock::forced_import(db, name, version, indexes)?;
|
||||
|
||||
let delta = LazyRollingDeltasFiatFromHeight::new(
|
||||
&format!("{name}_delta"),
|
||||
version + delta_version_offset,
|
||||
&inner.cents.height,
|
||||
cached_starts,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self { inner, delta })
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
//! FiatPerBlockWithSum24h - FiatPerBlock raw + RollingWindow24hFiatPerBlock sum.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{CentsType, FiatPerBlock, RollingWindow24h},
|
||||
};
|
||||
|
||||
/// Single 24h rolling window backed by FiatPerBlock (cents + lazy usd).
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct RollingWindow24hFiatPerBlock<C: CentsType, M: StorageMode = Rw>(
|
||||
pub RollingWindow24h<FiatPerBlock<C, M>>,
|
||||
);
|
||||
|
||||
impl<C: CentsType> RollingWindow24hFiatPerBlock<C> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(RollingWindow24h {
|
||||
_24h: FiatPerBlock::forced_import(db, &format!("{name}_24h"), version, indexes)?,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
height_24h_ago: &impl ReadableVec<Height, Height>,
|
||||
source: &impl ReadableVec<Height, C>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
C: Default + SubAssign,
|
||||
{
|
||||
self._24h
|
||||
.cents
|
||||
.height
|
||||
.compute_rolling_sum(max_from, height_24h_ago, source, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Fiat per-block value (cents + usd) with 24h rolling sum (also fiat).
|
||||
#[derive(Traversable)]
|
||||
pub struct FiatPerBlockWithSum24h<C: CentsType, M: StorageMode = Rw> {
|
||||
pub raw: FiatPerBlock<C, M>,
|
||||
pub sum: RollingWindow24hFiatPerBlock<C, M>,
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageM
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{BpsType, WindowStarts},
|
||||
internal::{BpsType, CachedWindowStarts},
|
||||
};
|
||||
|
||||
use crate::internal::{ComputedPerBlockRollingAverage, LazyPerBlock};
|
||||
@@ -24,12 +24,14 @@ impl<B: BpsType> PercentPerBlockRollingAverage<B> {
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let bps = ComputedPerBlockRollingAverage::forced_import(
|
||||
db,
|
||||
&format!("{name}_bps"),
|
||||
version,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?;
|
||||
|
||||
let ratio = LazyPerBlock::from_height_source::<B::ToRatio>(
|
||||
@@ -56,14 +58,9 @@ impl<B: BpsType> PercentPerBlockRollingAverage<B> {
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_height: impl FnOnce(&mut EagerVec<PcoVec<Height, B>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
B: Default,
|
||||
f64: From<B>,
|
||||
{
|
||||
self.bps.compute(max_from, windows, exit, compute_height)
|
||||
) -> Result<()> {
|
||||
self.bps.compute(max_from, exit, compute_height)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Height;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{DeltaAvg, LazyDeltaVec};
|
||||
|
||||
use crate::internal::{NumericValue, Resolutions};
|
||||
|
||||
/// A single lazy rolling-average slot from height: the lazy delta vec + its resolution views.
|
||||
#[derive(Clone, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyRollingAvgFromHeight<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub height: LazyDeltaVec<Height, f64, T, DeltaAvg>,
|
||||
#[traversable(flatten)]
|
||||
pub resolutions: Box<Resolutions<T>>,
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{DeltaAvg, LazyDeltaVec, ReadableCloneableVec};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{CachedWindowStarts, NumericValue, Resolutions, Windows},
|
||||
};
|
||||
|
||||
use super::LazyRollingAvgFromHeight;
|
||||
|
||||
/// Lazy rolling averages for all 4 window durations (24h, 1w, 1m, 1y),
|
||||
/// derived from an f64 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)`.
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyRollingAvgsFromHeight<T>(pub Windows<LazyRollingAvgFromHeight<T>>)
|
||||
where
|
||||
T: NumericValue + JsonSchema;
|
||||
|
||||
impl<T> LazyRollingAvgsFromHeight<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub fn new(
|
||||
name: &str,
|
||||
version: Version,
|
||||
cumulative: &(impl ReadableCloneableVec<Height, f64> + 'static),
|
||||
cached_starts: &CachedWindowStarts,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Self {
|
||||
let cum_source = cumulative.read_only_boxed_clone();
|
||||
|
||||
Self(cached_starts.0.map_with_suffix(|suffix, cached_start| {
|
||||
let full_name = format!("{name}_{suffix}");
|
||||
let cached = cached_start.clone();
|
||||
let starts_version = cached.version();
|
||||
let avg = LazyDeltaVec::<Height, f64, T, DeltaAvg>::new(
|
||||
&full_name,
|
||||
version,
|
||||
cum_source.clone(),
|
||||
starts_version,
|
||||
move || cached.get(),
|
||||
);
|
||||
let resolutions = Resolutions::forced_import(
|
||||
&full_name,
|
||||
avg.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
LazyRollingAvgFromHeight {
|
||||
height: avg,
|
||||
resolutions: Box::new(resolutions),
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, StoredF32, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{DeltaChange, DeltaRate, LazyDeltaVec, LazyVecFrom1, ReadableCloneableVec, VecValue};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
BpsType, CachedWindowStarts, CentsType, DerivedResolutions, LazyPerBlock, NumericValue,
|
||||
Resolutions, Windows,
|
||||
},
|
||||
};
|
||||
|
||||
/// Generic single-slot lazy delta: a `LazyDeltaVec` + resolution views.
|
||||
///
|
||||
/// Used as building block for both change and rate deltas.
|
||||
/// - Change: `LazyDeltaFromHeight<S, C, DeltaChange>`
|
||||
/// - Rate BPS: `LazyDeltaFromHeight<S, B, DeltaRate>`
|
||||
#[derive(Clone, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyDeltaFromHeight<S, T, Op: 'static>
|
||||
where
|
||||
S: VecValue,
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub height: LazyDeltaVec<Height, S, T, Op>,
|
||||
#[traversable(flatten)]
|
||||
pub resolutions: Box<Resolutions<T>>,
|
||||
}
|
||||
|
||||
/// Single-slot lazy delta percent: BPS delta + lazy ratio + lazy percent views.
|
||||
///
|
||||
/// Mirrors `PercentPerBlock<B>` but with lazy delta for the BPS source.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyDeltaPercentFromHeight<S, B>
|
||||
where
|
||||
S: VecValue,
|
||||
B: BpsType,
|
||||
{
|
||||
pub bps: LazyDeltaFromHeight<S, B, DeltaRate>,
|
||||
pub ratio: LazyPerBlock<StoredF32, B>,
|
||||
pub percent: LazyPerBlock<StoredF32, B>,
|
||||
}
|
||||
|
||||
/// Lazy rolling deltas for all 4 window durations (24h, 1w, 1m, 1y).
|
||||
///
|
||||
/// Tree shape: `change._24h/...`, `rate._24h/...` — matches old `RollingDelta`.
|
||||
///
|
||||
/// Replaces `RollingDelta`, `RollingDelta1m`, and `RollingDeltaExcept1m` — since
|
||||
/// there is no storage cost, all 4 windows are always available.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyRollingDeltasFromHeight<S, C, B>
|
||||
where
|
||||
S: VecValue,
|
||||
C: NumericValue + JsonSchema,
|
||||
B: BpsType,
|
||||
{
|
||||
pub change: Windows<LazyDeltaFromHeight<S, C, DeltaChange>>,
|
||||
pub rate: Windows<LazyDeltaPercentFromHeight<S, B>>,
|
||||
}
|
||||
|
||||
impl<S, C, B> LazyRollingDeltasFromHeight<S, C, B>
|
||||
where
|
||||
S: VecValue + Into<f64>,
|
||||
C: NumericValue + JsonSchema + From<f64>,
|
||||
B: BpsType + From<f64>,
|
||||
{
|
||||
pub fn new(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: &(impl ReadableCloneableVec<Height, S> + 'static),
|
||||
cached_starts: &CachedWindowStarts,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Self {
|
||||
let src = source.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();
|
||||
|
||||
// Change: source[h] - source[ago] as C (via f64)
|
||||
let change_vec = LazyDeltaVec::<Height, S, C, DeltaChange>::new(
|
||||
&format!("{full_name}_change"),
|
||||
version,
|
||||
src.clone(),
|
||||
starts_version,
|
||||
{
|
||||
let cached = cached.clone();
|
||||
move || cached.get()
|
||||
},
|
||||
);
|
||||
let change_resolutions = Resolutions::forced_import(
|
||||
&format!("{full_name}_change"),
|
||||
change_vec.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
let change = LazyDeltaFromHeight {
|
||||
height: change_vec,
|
||||
resolutions: Box::new(change_resolutions),
|
||||
};
|
||||
|
||||
// Rate BPS: (source[h] - source[ago]) / source[ago] as B (via f64)
|
||||
let rate_vec = LazyDeltaVec::<Height, S, B, DeltaRate>::new(
|
||||
&format!("{full_name}_rate_bps"),
|
||||
version,
|
||||
src.clone(),
|
||||
starts_version,
|
||||
move || cached.get(),
|
||||
);
|
||||
let rate_resolutions = Resolutions::forced_import(
|
||||
&format!("{full_name}_rate_bps"),
|
||||
rate_vec.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
let bps = LazyDeltaFromHeight {
|
||||
height: rate_vec,
|
||||
resolutions: Box::new(rate_resolutions),
|
||||
};
|
||||
|
||||
// Ratio: bps / 10000
|
||||
let ratio = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToRatio>(
|
||||
&format!("{full_name}_rate_ratio"),
|
||||
version,
|
||||
bps.height.read_only_boxed_clone(),
|
||||
),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<B::ToRatio>(
|
||||
&format!("{full_name}_rate_ratio"),
|
||||
version,
|
||||
&bps.resolutions,
|
||||
)),
|
||||
};
|
||||
|
||||
// Percent: bps / 100
|
||||
let percent = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToPercent>(
|
||||
&format!("{full_name}_rate"),
|
||||
version,
|
||||
bps.height.read_only_boxed_clone(),
|
||||
),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<B::ToPercent>(
|
||||
&format!("{full_name}_rate"),
|
||||
version,
|
||||
&bps.resolutions,
|
||||
)),
|
||||
};
|
||||
|
||||
let rate = LazyDeltaPercentFromHeight {
|
||||
bps,
|
||||
ratio,
|
||||
percent,
|
||||
};
|
||||
|
||||
(change, rate)
|
||||
};
|
||||
|
||||
let (change, rate) = cached_starts.0.map_with_suffix(make_slot).unzip();
|
||||
|
||||
Self { change, rate }
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fiat delta types (cents change + lazy USD + rate)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Single-slot fiat delta change: cents delta + lazy USD.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyDeltaFiatFromHeight<S, C>
|
||||
where
|
||||
S: VecValue,
|
||||
C: CentsType,
|
||||
{
|
||||
pub cents: LazyDeltaFromHeight<S, C, DeltaChange>,
|
||||
pub usd: LazyPerBlock<Dollars, C>,
|
||||
}
|
||||
|
||||
/// Lazy fiat rolling deltas for all 4 windows.
|
||||
///
|
||||
/// Tree shape: `change._24h.{cents,usd}/...`, `rate._24h/...` — matches old `FiatRollingDelta`.
|
||||
///
|
||||
/// Replaces `FiatRollingDelta`, `FiatRollingDelta1m`, and `FiatRollingDeltaExcept1m`.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyRollingDeltasFiatFromHeight<S, C, B>
|
||||
where
|
||||
S: VecValue,
|
||||
C: CentsType,
|
||||
B: BpsType,
|
||||
{
|
||||
pub change: Windows<LazyDeltaFiatFromHeight<S, C>>,
|
||||
pub rate: Windows<LazyDeltaPercentFromHeight<S, B>>,
|
||||
}
|
||||
|
||||
impl<S, C, B> LazyRollingDeltasFiatFromHeight<S, C, B>
|
||||
where
|
||||
S: VecValue + Into<f64>,
|
||||
C: CentsType + From<f64>,
|
||||
B: BpsType + From<f64>,
|
||||
{
|
||||
pub fn new(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: &(impl ReadableCloneableVec<Height, S> + 'static),
|
||||
cached_starts: &CachedWindowStarts,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Self {
|
||||
let src = source.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();
|
||||
|
||||
// Change cents: source[h] - source[ago] as C (via f64)
|
||||
let change_vec = LazyDeltaVec::<Height, S, C, DeltaChange>::new(
|
||||
&format!("{full_name}_change"),
|
||||
version,
|
||||
src.clone(),
|
||||
starts_version,
|
||||
{
|
||||
let cached = cached.clone();
|
||||
move || cached.get()
|
||||
},
|
||||
);
|
||||
let change_resolutions = Resolutions::forced_import(
|
||||
&format!("{full_name}_change"),
|
||||
change_vec.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
let cents = LazyDeltaFromHeight {
|
||||
height: change_vec,
|
||||
resolutions: Box::new(change_resolutions),
|
||||
};
|
||||
|
||||
// Change USD: lazy from cents delta
|
||||
let usd = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<C::ToDollars>(
|
||||
&format!("{full_name}_change_usd"),
|
||||
version,
|
||||
cents.height.read_only_boxed_clone(),
|
||||
),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<C::ToDollars>(
|
||||
&format!("{full_name}_change_usd"),
|
||||
version,
|
||||
¢s.resolutions,
|
||||
)),
|
||||
};
|
||||
|
||||
let change = LazyDeltaFiatFromHeight { cents, usd };
|
||||
|
||||
// Rate BPS: (source[h] - source[ago]) / source[ago] as B (via f64)
|
||||
let rate_vec = LazyDeltaVec::<Height, S, B, DeltaRate>::new(
|
||||
&format!("{full_name}_rate_bps"),
|
||||
version,
|
||||
src.clone(),
|
||||
starts_version,
|
||||
move || cached.get(),
|
||||
);
|
||||
let rate_resolutions = Resolutions::forced_import(
|
||||
&format!("{full_name}_rate_bps"),
|
||||
rate_vec.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
let bps = LazyDeltaFromHeight {
|
||||
height: rate_vec,
|
||||
resolutions: Box::new(rate_resolutions),
|
||||
};
|
||||
|
||||
let ratio = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToRatio>(
|
||||
&format!("{full_name}_rate_ratio"),
|
||||
version,
|
||||
bps.height.read_only_boxed_clone(),
|
||||
),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<B::ToRatio>(
|
||||
&format!("{full_name}_rate_ratio"),
|
||||
version,
|
||||
&bps.resolutions,
|
||||
)),
|
||||
};
|
||||
|
||||
let percent = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToPercent>(
|
||||
&format!("{full_name}_rate"),
|
||||
version,
|
||||
bps.height.read_only_boxed_clone(),
|
||||
),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<B::ToPercent>(
|
||||
&format!("{full_name}_rate"),
|
||||
version,
|
||||
&bps.resolutions,
|
||||
)),
|
||||
};
|
||||
|
||||
let rate = LazyDeltaPercentFromHeight {
|
||||
bps,
|
||||
ratio,
|
||||
percent,
|
||||
};
|
||||
|
||||
(change, rate)
|
||||
};
|
||||
|
||||
let (change, rate) = cached_starts.0.map_with_suffix(make_slot).unzip();
|
||||
|
||||
Self { change, rate }
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,27 @@
|
||||
//! RollingFull - Sum + Distribution per rolling window.
|
||||
//!
|
||||
//! 36 stored height vecs per metric (4 sum + 32 distribution), each with 17 index views.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
//! RollingFull - Lazy rolling sums + stored rolling distribution per window.
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedVecValue, NumericValue, RollingDistribution, RollingWindows, WindowStarts},
|
||||
internal::{
|
||||
CachedWindowStarts, NumericValue, LazyRollingSumsFromHeight, RollingDistribution,
|
||||
WindowStarts,
|
||||
},
|
||||
};
|
||||
|
||||
/// Sum (4 windows) + Distribution (8 stats × 4 windows) = 36 stored height vecs.
|
||||
/// Lazy rolling sums + stored rolling distribution (8 stats × 4 windows).
|
||||
#[derive(Traversable)]
|
||||
pub struct RollingFull<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub sum: RollingWindows<T, M>,
|
||||
pub sum: LazyRollingSumsFromHeight<T>,
|
||||
#[traversable(flatten)]
|
||||
pub distribution: RollingDistribution<T, M>,
|
||||
}
|
||||
@@ -36,14 +35,22 @@ where
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cumulative: &(impl ReadableCloneableVec<Height, T> + 'static),
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sum: RollingWindows::forced_import(db, &format!("{name}_sum"), version, indexes)?,
|
||||
distribution: RollingDistribution::forced_import(db, name, version, indexes)?,
|
||||
})
|
||||
let sum = LazyRollingSumsFromHeight::new(
|
||||
&format!("{name}_sum"),
|
||||
version,
|
||||
cumulative,
|
||||
cached_starts,
|
||||
indexes,
|
||||
);
|
||||
let distribution = RollingDistribution::forced_import(db, name, version, indexes)?;
|
||||
|
||||
Ok(Self { sum, distribution })
|
||||
}
|
||||
|
||||
/// Compute rolling sum + all 8 distribution stats across all 4 windows.
|
||||
/// Compute rolling distribution stats across all 4 windows.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
@@ -52,13 +59,10 @@ where
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: From<f64> + Default + SubAssign + Copy + Ord,
|
||||
T: From<f64> + Default + Copy + Ord,
|
||||
f64: From<T>,
|
||||
{
|
||||
self.sum
|
||||
.compute_rolling_sum(max_from, windows, source, exit)?;
|
||||
self.distribution
|
||||
.compute_distribution(max_from, windows, source, exit)?;
|
||||
Ok(())
|
||||
.compute_distribution(max_from, windows, source, exit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
mod avg;
|
||||
mod avgs;
|
||||
mod delta;
|
||||
mod distribution;
|
||||
mod full;
|
||||
mod sum;
|
||||
mod sums;
|
||||
mod windows;
|
||||
|
||||
pub use avg::*;
|
||||
pub use avgs::*;
|
||||
pub use delta::*;
|
||||
pub use distribution::*;
|
||||
pub use full::*;
|
||||
pub use sum::*;
|
||||
pub use sums::*;
|
||||
pub use windows::*;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Height;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{DeltaSub, LazyDeltaVec};
|
||||
|
||||
use crate::internal::{NumericValue, Resolutions};
|
||||
|
||||
/// A single lazy rolling-sum slot from height: the lazy delta vec + its resolution views.
|
||||
#[derive(Clone, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyRollingSumFromHeight<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub height: LazyDeltaVec<Height, T, T, DeltaSub>,
|
||||
#[traversable(flatten)]
|
||||
pub resolutions: Box<Resolutions<T>>,
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{DeltaSub, LazyDeltaVec, ReadableCloneableVec};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{CachedWindowStarts, NumericValue, Resolutions, Windows},
|
||||
};
|
||||
|
||||
use super::LazyRollingSumFromHeight;
|
||||
|
||||
/// Lazy rolling sums for all 4 window durations (24h, 1w, 1m, 1y),
|
||||
/// derived from a cumulative vec + cached window starts.
|
||||
///
|
||||
/// Nothing is stored on disk — all values are computed on-the-fly via
|
||||
/// `LazyDeltaVec<Height, T, T, DeltaSub>`: `cum[h] - cum[window_start[h]]`.
|
||||
///
|
||||
/// Implements `Traversable` to expose `_24h`, `_1w`, `_1m`, `_1y` with
|
||||
/// the same tree structure as the old `RollingWindows<T>`.
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyRollingSumsFromHeight<T>(pub Windows<LazyRollingSumFromHeight<T>>)
|
||||
where
|
||||
T: NumericValue + JsonSchema;
|
||||
|
||||
impl<T> LazyRollingSumsFromHeight<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub fn new(
|
||||
name: &str,
|
||||
version: Version,
|
||||
cumulative: &(impl ReadableCloneableVec<Height, T> + 'static),
|
||||
cached_starts: &CachedWindowStarts,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Self {
|
||||
let cum_source = cumulative.read_only_boxed_clone();
|
||||
|
||||
Self(cached_starts.0.map_with_suffix(|suffix, cached_start| {
|
||||
let full_name = format!("{name}_{suffix}");
|
||||
let cached = cached_start.clone();
|
||||
let starts_version = cached.version();
|
||||
let sum = LazyDeltaVec::<Height, T, T, DeltaSub>::new(
|
||||
&full_name,
|
||||
version,
|
||||
cum_source.clone(),
|
||||
starts_version,
|
||||
move || cached.get(),
|
||||
);
|
||||
let resolutions = Resolutions::forced_import(
|
||||
&full_name,
|
||||
sum.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
LazyRollingSumFromHeight {
|
||||
height: sum,
|
||||
resolutions: Box::new(resolutions),
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,13 @@
|
||||
//! Each of the 4 windows (24h, 1w, 1m, 1y) contains a height-level stored vec
|
||||
//! plus all 17 LazyAggVec index views.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode};
|
||||
use vecdb::{Database, EagerVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -42,23 +40,6 @@ where
|
||||
ComputedPerBlock::forced_import(db, &format!("{name}_{suffix}"), version, indexes)
|
||||
})?))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
source: &impl ReadableVec<Height, T>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Default + SubAssign,
|
||||
{
|
||||
for (w, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) {
|
||||
w.height
|
||||
.compute_rolling_sum(max_from, *starts, source, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Single 24h rolling window backed by ComputedPerBlock (1 stored vec).
|
||||
@@ -89,22 +70,6 @@ where
|
||||
)?,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
height_24h_ago: &impl ReadableVec<Height, Height>,
|
||||
source: &impl ReadableVec<Height, T>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Default + SubAssign,
|
||||
{
|
||||
self._24h
|
||||
.height
|
||||
.compute_rolling_sum(max_from, height_24h_ago, source, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended rolling windows: 1w + 1m + 1y (3 stored vecs).
|
||||
@@ -128,22 +93,4 @@ where
|
||||
ComputedPerBlock::forced_import(db, &format!("{name}_{suffix}"), version, indexes)
|
||||
})?))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
source: &impl ReadableVec<Height, T>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Default + SubAssign,
|
||||
{
|
||||
let starts = [windows._1w, windows._1m, windows._1y];
|
||||
for (w, starts) in self.0.as_mut_array().into_iter().zip(starts) {
|
||||
w.height
|
||||
.compute_rolling_sum(max_from, starts, source, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::ops::{Add, AddAssign, Div};
|
||||
use brk_types::{BasisPoints16, BasisPoints32, BasisPointsSigned16, BasisPointsSigned32, StoredF32};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
use vecdb::{Formattable, PcoVecValue, UnaryTransform};
|
||||
use vecdb::{CheckedSub, Formattable, PcoVecValue, UnaryTransform};
|
||||
|
||||
use crate::internal::{
|
||||
Bp16ToFloat, Bp16ToPercent, Bp32ToFloat, Bp32ToPercent, Bps16ToFloat, Bps16ToPercent,
|
||||
@@ -34,9 +34,9 @@ impl<T> ComputedVecValue for T where
|
||||
{
|
||||
}
|
||||
|
||||
pub trait NumericValue: ComputedVecValue + From<f64> + Into<f64> {}
|
||||
pub trait NumericValue: ComputedVecValue + CheckedSub + Default + From<f64> + Into<f64> {}
|
||||
|
||||
impl<T> NumericValue for T where T: ComputedVecValue + From<f64> + Into<f64> {}
|
||||
impl<T> NumericValue for T where T: ComputedVecValue + CheckedSub + Default + From<f64> + Into<f64> {}
|
||||
|
||||
/// Trait that associates a basis-point type with its transforms to ratio and percent.
|
||||
pub trait BpsType: NumericValue + JsonSchema {
|
||||
|
||||
@@ -25,7 +25,6 @@ pub use derived::{
|
||||
pub use ratio::{
|
||||
RatioCentsBp32, RatioCentsSignedCentsBps32,
|
||||
RatioCentsSignedDollarsBps32, RatioDiffCentsBps32, RatioDiffDollarsBps32, RatioDiffF32Bps32,
|
||||
RatioDollarsBp16, RatioDollarsBp32, RatioDollarsBps32, RatioSatsBp16, RatioU32Bp16,
|
||||
RatioU64Bp16,
|
||||
RatioDollarsBp16, RatioDollarsBp32, RatioDollarsBps32, RatioSatsBp16, RatioU64Bp16,
|
||||
};
|
||||
pub use specialized::{BlockCountTarget, OhlcCentsToDollars, OhlcCentsToSats};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_types::{
|
||||
BasisPoints16, BasisPoints32, BasisPointsSigned32, Cents, CentsSigned, Dollars, Sats, StoredF32,
|
||||
StoredU32, StoredU64,
|
||||
StoredU64,
|
||||
};
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
@@ -43,19 +43,6 @@ impl BinaryTransform<Cents, Cents, BasisPoints32> for RatioCentsBp32 {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RatioU32Bp16;
|
||||
|
||||
impl BinaryTransform<StoredU32, StoredU32, BasisPoints16> for RatioU32Bp16 {
|
||||
#[inline(always)]
|
||||
fn apply(numerator: StoredU32, denominator: StoredU32) -> BasisPoints16 {
|
||||
if *denominator > 0 {
|
||||
BasisPoints16::from(*numerator as f64 / *denominator as f64)
|
||||
} else {
|
||||
BasisPoints16::ZERO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RatioDollarsBp16;
|
||||
|
||||
impl BinaryTransform<Dollars, Dollars, BasisPoints16> for RatioDollarsBp16 {
|
||||
|
||||
Reference in New Issue
Block a user