mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-25 23:29:58 -07:00
global: snapshot
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
mod rolling_full;
|
||||
mod rolling_sum;
|
||||
mod windows;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Sats, Version};
|
||||
use vecdb::{Database, ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin},
|
||||
};
|
||||
|
||||
pub use rolling_full::*;
|
||||
pub use rolling_sum::*;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ByUnit<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightLast<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: ComputedFromHeightLast<Dollars, M>,
|
||||
}
|
||||
|
||||
impl ByUnit {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let sats = ComputedFromHeightLast::forced_import(db, name, version, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightLast::from_computed::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
version,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
&sats,
|
||||
);
|
||||
|
||||
let usd =
|
||||
ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), version, indexes)?;
|
||||
|
||||
Ok(Self { sats, btc, usd })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ByUnit, DistributionStats, WindowStarts, Windows},
|
||||
traits::compute_rolling_distribution_from_starts,
|
||||
};
|
||||
|
||||
/// One window slot: sum + 8 distribution stats, each a ByUnit.
|
||||
///
|
||||
/// Tree: `sum.sats.height`, `average.sats.height`, etc.
|
||||
#[derive(Traversable)]
|
||||
pub struct RollingFullSlot<M: StorageMode = Rw> {
|
||||
pub sum: ByUnit<M>,
|
||||
#[traversable(flatten)]
|
||||
pub distribution: DistributionStats<ByUnit<M>>,
|
||||
}
|
||||
|
||||
impl RollingFullSlot {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sum: ByUnit::forced_import(db, &format!("{name}_sum"), version, indexes)?,
|
||||
distribution: DistributionStats {
|
||||
average: ByUnit::forced_import(db, &format!("{name}_average"), version, indexes)?,
|
||||
min: ByUnit::forced_import(db, &format!("{name}_min"), version, indexes)?,
|
||||
max: ByUnit::forced_import(db, &format!("{name}_max"), version, indexes)?,
|
||||
pct10: ByUnit::forced_import(db, &format!("{name}_p10"), version, indexes)?,
|
||||
pct25: ByUnit::forced_import(db, &format!("{name}_p25"), version, indexes)?,
|
||||
median: ByUnit::forced_import(db, &format!("{name}_median"), version, indexes)?,
|
||||
pct75: ByUnit::forced_import(db, &format!("{name}_p75"), version, indexes)?,
|
||||
pct90: ByUnit::forced_import(db, &format!("{name}_p90"), version, indexes)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
starts: &impl ReadableVec<Height, Height>,
|
||||
sats_source: &impl ReadableVec<Height, Sats>,
|
||||
usd_source: &impl ReadableVec<Height, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sum.sats.height.compute_rolling_sum(max_from, starts, sats_source, exit)?;
|
||||
self.sum.usd.height.compute_rolling_sum(max_from, starts, usd_source, exit)?;
|
||||
|
||||
let d = &mut self.distribution;
|
||||
|
||||
compute_rolling_distribution_from_starts(
|
||||
max_from, starts, sats_source,
|
||||
&mut d.average.sats.height, &mut d.min.sats.height,
|
||||
&mut d.max.sats.height, &mut d.pct10.sats.height,
|
||||
&mut d.pct25.sats.height, &mut d.median.sats.height,
|
||||
&mut d.pct75.sats.height, &mut d.pct90.sats.height, exit,
|
||||
)?;
|
||||
|
||||
compute_rolling_distribution_from_starts(
|
||||
max_from, starts, usd_source,
|
||||
&mut d.average.usd.height, &mut d.min.usd.height,
|
||||
&mut d.max.usd.height, &mut d.pct10.usd.height,
|
||||
&mut d.pct25.usd.height, &mut d.median.usd.height,
|
||||
&mut d.pct75.usd.height, &mut d.pct90.usd.height, exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Rolling sum + distribution across 4 windows, window-first.
|
||||
///
|
||||
/// Tree: `_24h.sum.sats.height`, `_24h.average.sats.height`, etc.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct RollingFullByUnit<M: StorageMode = Rw>(pub Windows<RollingFullSlot<M>>);
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl RollingFullByUnit {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
Ok(Self(Windows {
|
||||
_24h: RollingFullSlot::forced_import(db, &format!("{name}_24h"), v, indexes)?,
|
||||
_7d: RollingFullSlot::forced_import(db, &format!("{name}_7d"), v, indexes)?,
|
||||
_30d: RollingFullSlot::forced_import(db, &format!("{name}_30d"), v, indexes)?,
|
||||
_1y: RollingFullSlot::forced_import(db, &format!("{name}_1y"), v, indexes)?,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
sats_source: &impl ReadableVec<Height, Sats>,
|
||||
usd_source: &impl ReadableVec<Height, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
for (slot, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) {
|
||||
slot.compute(max_from, starts, sats_source, usd_source, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ByUnit, WindowStarts, Windows},
|
||||
};
|
||||
|
||||
/// Rolling sum only, window-first then unit.
|
||||
///
|
||||
/// Tree: `_24h.sats.height`, `_24h.btc.height`, etc.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct RollingSumByUnit<M: StorageMode = Rw>(pub Windows<ByUnit<M>>);
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl RollingSumByUnit {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
Ok(Self(Windows::<ByUnit>::forced_import(db, &format!("{name}_sum"), v, indexes)?))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
sats_source: &impl ReadableVec<Height, Sats>,
|
||||
usd_source: &impl ReadableVec<Height, Dollars>,
|
||||
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.usd.height.compute_rolling_sum(max_from, starts, usd_source, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::Version;
|
||||
use vecdb::Database;
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ByUnit, Windows},
|
||||
};
|
||||
|
||||
impl Windows<ByUnit> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
_24h: ByUnit::forced_import(db, &format!("{name}_24h"), version, indexes)?,
|
||||
_7d: ByUnit::forced_import(db, &format!("{name}_7d"), version, indexes)?,
|
||||
_30d: ByUnit::forced_import(db, &format!("{name}_30d"), version, indexes)?,
|
||||
_1y: ByUnit::forced_import(db, &format!("{name}_1y"), version, indexes)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ where
|
||||
{
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
pub cumulative: ComputedFromHeightLast<T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: RollingFull<T, M>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod by_unit;
|
||||
mod constant;
|
||||
mod cumulative;
|
||||
mod cumulative_rolling_full;
|
||||
@@ -21,6 +22,7 @@ mod value_lazy_computed_cumulative;
|
||||
mod value_lazy_last;
|
||||
mod value_sum_cumulative;
|
||||
|
||||
pub use by_unit::*;
|
||||
pub use constant::*;
|
||||
pub use cumulative::*;
|
||||
pub use cumulative_rolling_full::*;
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
//! Stored value type for Last pattern from Height.
|
||||
//!
|
||||
//! Both sats and USD are stored eagerly at the height level.
|
||||
//! Used for rolling-window sums where USD = sum(usd_per_block),
|
||||
//! NOT sats * current_price.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
use brk_types::{Dollars, Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin},
|
||||
internal::ByUnit,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Traversable)]
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct StoredValueFromHeightLast<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightLast<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: ComputedFromHeightLast<Dollars, M>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub base: ByUnit<M>,
|
||||
}
|
||||
|
||||
impl StoredValueFromHeightLast {
|
||||
@@ -31,19 +27,9 @@ impl StoredValueFromHeightLast {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = ComputedFromHeightLast::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightLast::from_computed::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
&sats,
|
||||
);
|
||||
|
||||
let usd = ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), v, indexes)?;
|
||||
|
||||
Ok(Self { sats, btc, usd })
|
||||
Ok(Self {
|
||||
base: ByUnit::forced_import(db, name, v, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
@@ -54,10 +40,12 @@ impl StoredValueFromHeightLast {
|
||||
usd_source: &impl ReadableVec<Height, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sats
|
||||
self.base
|
||||
.sats
|
||||
.height
|
||||
.compute_rolling_sum(max_from, window_starts, sats_source, exit)?;
|
||||
self.usd
|
||||
self.base
|
||||
.usd
|
||||
.height
|
||||
.compute_rolling_sum(max_from, window_starts, usd_source, exit)?;
|
||||
Ok(())
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
//! Rolling average values from Height - stores sats and dollars, btc is lazy.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
use brk_types::{Dollars, Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin},
|
||||
internal::ByUnit,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Rolling average values indexed by height - sats (stored), btc (lazy), usd (stored).
|
||||
#[derive(Traversable)]
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct ValueEmaFromHeight<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightLast<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: ComputedFromHeightLast<Dollars, M>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub base: ByUnit<M>,
|
||||
}
|
||||
|
||||
impl ValueEmaFromHeight {
|
||||
@@ -28,27 +27,11 @@ impl ValueEmaFromHeight {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = ComputedFromHeightLast::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightLast::from_computed::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
&sats,
|
||||
);
|
||||
|
||||
let usd = ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
&format!("{name}_usd"),
|
||||
v,
|
||||
indexes,
|
||||
)?;
|
||||
|
||||
Ok(Self { sats, btc, usd })
|
||||
Ok(Self {
|
||||
base: ByUnit::forced_import(db, name, v, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute rolling average for both sats and dollars in one call.
|
||||
pub(crate) fn compute_rolling_average(
|
||||
&mut self,
|
||||
starting_height: Height,
|
||||
@@ -57,10 +40,12 @@ impl ValueEmaFromHeight {
|
||||
dollars_source: &(impl ReadableVec<Height, Dollars> + Sync),
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sats
|
||||
self.base
|
||||
.sats
|
||||
.height
|
||||
.compute_rolling_average(starting_height, window_starts, sats_source, exit)?;
|
||||
self.usd
|
||||
self.base
|
||||
.usd
|
||||
.height
|
||||
.compute_rolling_average(starting_height, window_starts, dollars_source, exit)?;
|
||||
Ok(())
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
//! Value type for Full pattern from Height.
|
||||
//!
|
||||
//! Height-level USD stats are stored (eagerly computed from sats × price).
|
||||
//! Uses CumFull: stored base + cumulative + rolling windows.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageMode};
|
||||
use brk_types::{Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedFromHeightCumulativeFull, LazyFromHeightLast, SatsToBitcoin, SatsToDollars,
|
||||
WindowStarts,
|
||||
},
|
||||
internal::{ByUnit, RollingFullByUnit, SatsToDollars, WindowStarts},
|
||||
prices,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ValueFromHeightFull<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightCumulativeFull<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: ComputedFromHeightCumulativeFull<Dollars, M>,
|
||||
pub base: ByUnit<M>,
|
||||
pub cumulative: ByUnit<M>,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: RollingFullByUnit<M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::TWO; // Bumped for stored height dollars
|
||||
const VERSION: Version = Version::TWO;
|
||||
|
||||
impl ValueFromHeightFull {
|
||||
pub(crate) fn forced_import(
|
||||
@@ -35,19 +28,11 @@ impl ValueFromHeightFull {
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = ComputedFromHeightCumulativeFull::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightLast::from_height_source::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
indexes,
|
||||
);
|
||||
|
||||
let usd =
|
||||
ComputedFromHeightCumulativeFull::forced_import(db, &format!("{name}_usd"), v, indexes)?;
|
||||
|
||||
Ok(Self { sats, btc, usd })
|
||||
Ok(Self {
|
||||
base: ByUnit::forced_import(db, name, v, indexes)?,
|
||||
cumulative: ByUnit::forced_import(db, &format!("{name}_cumulative"), v, indexes)?,
|
||||
rolling: RollingFullByUnit::forced_import(db, name, v, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
@@ -58,15 +43,36 @@ impl ValueFromHeightFull {
|
||||
exit: &Exit,
|
||||
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
self.sats.compute(max_from, windows, exit, compute_sats)?;
|
||||
compute_sats(&mut self.base.sats.height)?;
|
||||
|
||||
self.usd.compute(max_from, windows, exit, |vec| {
|
||||
Ok(vec.compute_binary::<Sats, Dollars, SatsToDollars>(
|
||||
self.cumulative
|
||||
.sats
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.base.sats.height, exit)?;
|
||||
|
||||
self.base
|
||||
.usd
|
||||
.height
|
||||
.compute_binary::<Sats, Dollars, SatsToDollars>(
|
||||
max_from,
|
||||
&self.sats.height,
|
||||
&self.base.sats.height,
|
||||
&prices.price.usd,
|
||||
exit,
|
||||
)?)
|
||||
})
|
||||
)?;
|
||||
|
||||
self.cumulative
|
||||
.usd
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.base.usd.height, exit)?;
|
||||
|
||||
self.rolling.compute(
|
||||
max_from,
|
||||
windows,
|
||||
&self.base.sats.height,
|
||||
&self.base.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
//! Value type for Last pattern from Height.
|
||||
//!
|
||||
//! Height-level USD value is stored (eagerly computed from sats × price).
|
||||
//! Day1 last is stored since it requires finding the last value within each date.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, Exit, ReadableCloneableVec, Rw, StorageMode};
|
||||
use brk_types::{Dollars, Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes, prices,
|
||||
internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin, SatsToDollars},
|
||||
internal::{ByUnit, SatsToDollars},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct ValueFromHeightLast<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightLast<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: ComputedFromHeightLast<Dollars, M>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub base: ByUnit<M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::TWO; // Bumped for stored height dollars
|
||||
const VERSION: Version = Version::TWO;
|
||||
|
||||
impl ValueFromHeightLast {
|
||||
pub(crate) fn forced_import(
|
||||
@@ -30,35 +27,20 @@ impl ValueFromHeightLast {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = ComputedFromHeightLast::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightLast::from_computed::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
&sats,
|
||||
);
|
||||
|
||||
let usd = ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), v, indexes)?;
|
||||
|
||||
Ok(Self {
|
||||
sats,
|
||||
btc,
|
||||
usd,
|
||||
base: ByUnit::forced_import(db, name, v, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Eagerly compute USD height values: sats[h] * price[h].
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.usd.compute_binary::<Sats, Dollars, SatsToDollars>(
|
||||
self.base.usd.compute_binary::<Sats, Dollars, SatsToDollars>(
|
||||
max_from,
|
||||
&self.sats.height,
|
||||
&self.base.sats.height,
|
||||
&prices.price.usd,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -21,6 +21,7 @@ pub struct ValueFromHeightLastRolling<M: StorageMode = Rw> {
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub value: ValueFromHeight<M>,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: StoredValueRollingWindows<M>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,21 @@
|
||||
//! Value type with stored sats height + cumulative, stored usd, lazy btc.
|
||||
//!
|
||||
//! - Sats: stored height + cumulative (ComputedFromHeightCumulative)
|
||||
//! - BTC: lazy transform from sats (LazyFromHeightLast)
|
||||
//! - USD: stored (eagerly computed from price × sats)
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, Exit, ReadableCloneableVec, Rw, StorageMode};
|
||||
use brk_types::{Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedFromHeightCumulative, ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin,
|
||||
SatsToDollars,
|
||||
},
|
||||
internal::{ByUnit, SatsToDollars},
|
||||
prices,
|
||||
};
|
||||
|
||||
/// Value wrapper with stored sats height + cumulative, lazy btc + stored usd.
|
||||
#[derive(Traversable)]
|
||||
pub struct LazyComputedValueFromHeightCumulative<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightCumulative<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: ComputedFromHeightLast<Dollars, M>,
|
||||
pub base: ByUnit<M>,
|
||||
pub cumulative: ByUnit<M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE; // Bumped for stored height dollars
|
||||
const VERSION: Version = Version::ONE;
|
||||
|
||||
impl LazyComputedValueFromHeightCumulative {
|
||||
pub(crate) fn forced_import(
|
||||
@@ -37,35 +26,37 @@ impl LazyComputedValueFromHeightCumulative {
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = ComputedFromHeightCumulative::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightLast::from_height_source::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
indexes,
|
||||
);
|
||||
|
||||
let usd = ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), v, indexes)?;
|
||||
|
||||
Ok(Self { sats, btc, usd })
|
||||
Ok(Self {
|
||||
base: ByUnit::forced_import(db, name, v, indexes)?,
|
||||
cumulative: ByUnit::forced_import(db, &format!("{name}_cumulative"), v, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute cumulative + USD from already-filled sats height vec.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sats.compute_rest(max_from, exit)?;
|
||||
self.cumulative
|
||||
.sats
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.base.sats.height, exit)?;
|
||||
|
||||
self.base
|
||||
.usd
|
||||
.compute_binary::<Sats, Dollars, SatsToDollars>(
|
||||
max_from,
|
||||
&self.base.sats.height,
|
||||
&prices.price.usd,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.cumulative
|
||||
.usd
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.base.usd.height, exit)?;
|
||||
|
||||
self.usd.compute_binary::<Sats, Dollars, SatsToDollars>(
|
||||
max_from,
|
||||
&self.sats.height,
|
||||
&prices.price.usd,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
//! Value type for SumCumulative pattern from Height.
|
||||
//!
|
||||
//! Height-level USD sum is stored (eagerly computed from sats × price).
|
||||
//! Uses CumSum: stored base + cumulative + rolling sum windows.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageMode};
|
||||
use brk_types::{Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes, prices,
|
||||
internal::{
|
||||
ComputedFromHeightCumulativeSum, LazyFromHeightLast, SatsToBitcoin, SatsToDollars,
|
||||
WindowStarts,
|
||||
},
|
||||
indexes,
|
||||
internal::{ByUnit, RollingSumByUnit, SatsToDollars, WindowStarts},
|
||||
prices,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ValueFromHeightSumCumulative<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightCumulativeSum<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: ComputedFromHeightCumulativeSum<Dollars, M>,
|
||||
pub base: ByUnit<M>,
|
||||
pub cumulative: ByUnit<M>,
|
||||
pub sum: RollingSumByUnit<M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::TWO; // Bumped for stored height dollars
|
||||
const VERSION: Version = Version::TWO;
|
||||
|
||||
impl ValueFromHeightSumCumulative {
|
||||
pub(crate) fn forced_import(
|
||||
@@ -34,19 +27,11 @@ impl ValueFromHeightSumCumulative {
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = ComputedFromHeightCumulativeSum::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightLast::from_height_source::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
indexes,
|
||||
);
|
||||
|
||||
let usd =
|
||||
ComputedFromHeightCumulativeSum::forced_import(db, &format!("{name}_usd"), v, indexes)?;
|
||||
|
||||
Ok(Self { sats, btc, usd })
|
||||
Ok(Self {
|
||||
base: ByUnit::forced_import(db, name, v, indexes)?,
|
||||
cumulative: ByUnit::forced_import(db, &format!("{name}_cumulative"), v, indexes)?,
|
||||
sum: RollingSumByUnit::forced_import(db, name, v, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
@@ -57,15 +42,36 @@ impl ValueFromHeightSumCumulative {
|
||||
exit: &Exit,
|
||||
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
self.sats.compute(max_from, windows, exit, compute_sats)?;
|
||||
compute_sats(&mut self.base.sats.height)?;
|
||||
|
||||
self.usd.compute(max_from, windows, exit, |vec| {
|
||||
Ok(vec.compute_binary::<Sats, Dollars, SatsToDollars>(
|
||||
self.cumulative
|
||||
.sats
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.base.sats.height, exit)?;
|
||||
|
||||
self.base
|
||||
.usd
|
||||
.height
|
||||
.compute_binary::<Sats, Dollars, SatsToDollars>(
|
||||
max_from,
|
||||
&self.sats.height,
|
||||
&self.base.sats.height,
|
||||
&prices.price.usd,
|
||||
exit,
|
||||
)?)
|
||||
})
|
||||
)?;
|
||||
|
||||
self.cumulative
|
||||
.usd
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.base.usd.height, exit)?;
|
||||
|
||||
self.sum.compute_rolling_sum(
|
||||
max_from,
|
||||
windows,
|
||||
&self.base.sats.height,
|
||||
&self.base.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Minute1, Minute5,
|
||||
Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
Day1, Day3, DifficultyEpoch, FromCoarserIndex, HalvingEpoch, Height, Hour1, Hour4, Hour12,
|
||||
Minute1, Minute5, Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{LazyAggVec, ReadOnlyClone, ReadableBoxedVec, ReadableCloneableVec};
|
||||
use vecdb::{
|
||||
Cursor, LazyAggVec, ReadOnlyClone, ReadableBoxedVec, ReadableCloneableVec, VecIndex, VecValue,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
indexes, indexes_from,
|
||||
@@ -77,13 +79,45 @@ where
|
||||
};
|
||||
}
|
||||
|
||||
fn for_each_range_from_coarser<
|
||||
I: VecIndex,
|
||||
O: VecValue,
|
||||
S1I: VecIndex + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<S1I, O>,
|
||||
mapping: &ReadableBoxedVec<I, S2T>,
|
||||
f: &mut dyn FnMut(O),
|
||||
) {
|
||||
let mapping_len = mapping.len();
|
||||
let source_len = source.len();
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
for i in from..to {
|
||||
if i >= mapping_len {
|
||||
break;
|
||||
}
|
||||
let target = S1I::max_from(I::from(i), source_len);
|
||||
if cursor.position() <= target {
|
||||
cursor.advance(target - cursor.position());
|
||||
if let Some(v) = cursor.next() {
|
||||
f(v);
|
||||
}
|
||||
} else if let Some(v) = source.collect_one_at(target) {
|
||||
f(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! epoch {
|
||||
($idx:ident) => {
|
||||
LazyAggVec::from_source(
|
||||
LazyAggVec::new(
|
||||
name,
|
||||
v,
|
||||
height_source.clone(),
|
||||
indexes.$idx.identity.read_only_boxed_clone(),
|
||||
for_each_range_from_coarser,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user