mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-25 07:09:59 -07:00
global: snapshot
This commit is contained in:
@@ -9,11 +9,11 @@ pub struct DistributionStats<A, B = A, C = A, D = A, E = A, F = A, G = A, H = A>
|
||||
pub average: A,
|
||||
pub min: B,
|
||||
pub max: C,
|
||||
pub p10: D,
|
||||
pub p25: E,
|
||||
pub pct10: D,
|
||||
pub pct25: E,
|
||||
pub median: F,
|
||||
pub p75: G,
|
||||
pub p90: H,
|
||||
pub pct75: G,
|
||||
pub pct90: H,
|
||||
}
|
||||
|
||||
impl<A> DistributionStats<A> {
|
||||
@@ -22,11 +22,11 @@ impl<A> DistributionStats<A> {
|
||||
f(&mut self.average)?;
|
||||
f(&mut self.min)?;
|
||||
f(&mut self.max)?;
|
||||
f(&mut self.p10)?;
|
||||
f(&mut self.p25)?;
|
||||
f(&mut self.pct10)?;
|
||||
f(&mut self.pct25)?;
|
||||
f(&mut self.median)?;
|
||||
f(&mut self.p75)?;
|
||||
f(&mut self.p90)?;
|
||||
f(&mut self.pct75)?;
|
||||
f(&mut self.pct90)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -35,11 +35,11 @@ impl<A> DistributionStats<A> {
|
||||
f(&self.average)
|
||||
.min(f(&self.min))
|
||||
.min(f(&self.max))
|
||||
.min(f(&self.p10))
|
||||
.min(f(&self.p25))
|
||||
.min(f(&self.pct10))
|
||||
.min(f(&self.pct25))
|
||||
.min(f(&self.median))
|
||||
.min(f(&self.p75))
|
||||
.min(f(&self.p90))
|
||||
.min(f(&self.pct75))
|
||||
.min(f(&self.pct90))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
Database, Exit, ReadableVec, Ro, Rw, StorageMode, VecIndex, VecValue, Version,
|
||||
Database, Exit, ReadableVec, Ro, Rw, VecIndex, VecValue, Version,
|
||||
};
|
||||
|
||||
use crate::internal::{
|
||||
AverageVec, ComputedVecValue, MaxVec, MedianVec, MinVec, Pct10Vec, Pct25Vec, Pct75Vec,
|
||||
Pct90Vec,
|
||||
AverageVec, ComputedVecValue, DistributionStats, MaxVec, MedianVec, MinVec, Pct10Vec,
|
||||
Pct25Vec, Pct75Vec, Pct90Vec,
|
||||
};
|
||||
|
||||
/// Distribution stats (average + min + max + percentiles) — flat 8-field struct.
|
||||
#[derive(Traversable)]
|
||||
pub struct Distribution<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode = Rw> {
|
||||
pub average: AverageVec<I, T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub min: MinVec<I, T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub max: MaxVec<I, T, M>,
|
||||
pub pct10: Pct10Vec<I, T, M>,
|
||||
pub pct25: Pct25Vec<I, T, M>,
|
||||
pub median: MedianVec<I, T, M>,
|
||||
pub pct75: Pct75Vec<I, T, M>,
|
||||
pub pct90: Pct90Vec<I, T, M>,
|
||||
}
|
||||
/// Distribution stats (average + min + max + percentiles) — concrete vec type alias.
|
||||
pub type Distribution<I, T, M = Rw> = DistributionStats<
|
||||
AverageVec<I, T, M>,
|
||||
MinVec<I, T, M>,
|
||||
MaxVec<I, T, M>,
|
||||
Pct10Vec<I, T, M>,
|
||||
Pct25Vec<I, T, M>,
|
||||
MedianVec<I, T, M>,
|
||||
Pct75Vec<I, T, M>,
|
||||
Pct90Vec<I, T, M>,
|
||||
>;
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> Distribution<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
@@ -111,7 +107,7 @@ impl<I: VecIndex, T: ComputedVecValue + JsonSchema> Distribution<I, T> {
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> Distribution<I, T, Ro> {
|
||||
Distribution {
|
||||
DistributionStats {
|
||||
average: self.average.read_only_clone(),
|
||||
min: self.min.read_only_clone(),
|
||||
max: self.max.read_only_clone(),
|
||||
|
||||
@@ -41,11 +41,11 @@ where
|
||||
average: RollingWindows::forced_import(db, &format!("{name}_average"), v, indexes)?,
|
||||
min: RollingWindows::forced_import(db, &format!("{name}_min"), v, indexes)?,
|
||||
max: RollingWindows::forced_import(db, &format!("{name}_max"), v, indexes)?,
|
||||
p10: RollingWindows::forced_import(db, &format!("{name}_p10"), v, indexes)?,
|
||||
p25: RollingWindows::forced_import(db, &format!("{name}_p25"), v, indexes)?,
|
||||
pct10: RollingWindows::forced_import(db, &format!("{name}_p10"), v, indexes)?,
|
||||
pct25: RollingWindows::forced_import(db, &format!("{name}_p25"), v, indexes)?,
|
||||
median: RollingWindows::forced_import(db, &format!("{name}_median"), v, indexes)?,
|
||||
p75: RollingWindows::forced_import(db, &format!("{name}_p75"), v, indexes)?,
|
||||
p90: RollingWindows::forced_import(db, &format!("{name}_p90"), v, indexes)?,
|
||||
pct75: RollingWindows::forced_import(db, &format!("{name}_p75"), v, indexes)?,
|
||||
pct90: RollingWindows::forced_import(db, &format!("{name}_p90"), v, indexes)?,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -69,30 +69,30 @@ where
|
||||
compute_rolling_distribution_from_starts(
|
||||
max_from, windows._24h, source,
|
||||
&mut self.0.average._24h.height, &mut self.0.min._24h.height,
|
||||
&mut self.0.max._24h.height, &mut self.0.p10._24h.height,
|
||||
&mut self.0.p25._24h.height, &mut self.0.median._24h.height,
|
||||
&mut self.0.p75._24h.height, &mut self.0.p90._24h.height, exit,
|
||||
&mut self.0.max._24h.height, &mut self.0.pct10._24h.height,
|
||||
&mut self.0.pct25._24h.height, &mut self.0.median._24h.height,
|
||||
&mut self.0.pct75._24h.height, &mut self.0.pct90._24h.height, exit,
|
||||
)?;
|
||||
compute_rolling_distribution_from_starts(
|
||||
max_from, windows._7d, source,
|
||||
&mut self.0.average._7d.height, &mut self.0.min._7d.height,
|
||||
&mut self.0.max._7d.height, &mut self.0.p10._7d.height,
|
||||
&mut self.0.p25._7d.height, &mut self.0.median._7d.height,
|
||||
&mut self.0.p75._7d.height, &mut self.0.p90._7d.height, exit,
|
||||
&mut self.0.max._7d.height, &mut self.0.pct10._7d.height,
|
||||
&mut self.0.pct25._7d.height, &mut self.0.median._7d.height,
|
||||
&mut self.0.pct75._7d.height, &mut self.0.pct90._7d.height, exit,
|
||||
)?;
|
||||
compute_rolling_distribution_from_starts(
|
||||
max_from, windows._30d, source,
|
||||
&mut self.0.average._30d.height, &mut self.0.min._30d.height,
|
||||
&mut self.0.max._30d.height, &mut self.0.p10._30d.height,
|
||||
&mut self.0.p25._30d.height, &mut self.0.median._30d.height,
|
||||
&mut self.0.p75._30d.height, &mut self.0.p90._30d.height, exit,
|
||||
&mut self.0.max._30d.height, &mut self.0.pct10._30d.height,
|
||||
&mut self.0.pct25._30d.height, &mut self.0.median._30d.height,
|
||||
&mut self.0.pct75._30d.height, &mut self.0.pct90._30d.height, exit,
|
||||
)?;
|
||||
compute_rolling_distribution_from_starts(
|
||||
max_from, windows._1y, source,
|
||||
&mut self.0.average._1y.height, &mut self.0.min._1y.height,
|
||||
&mut self.0.max._1y.height, &mut self.0.p10._1y.height,
|
||||
&mut self.0.p25._1y.height, &mut self.0.median._1y.height,
|
||||
&mut self.0.p75._1y.height, &mut self.0.p90._1y.height, exit,
|
||||
&mut self.0.max._1y.height, &mut self.0.pct10._1y.height,
|
||||
&mut self.0.pct25._1y.height, &mut self.0.median._1y.height,
|
||||
&mut self.0.pct75._1y.height, &mut self.0.pct90._1y.height, exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
mod block_count_target;
|
||||
mod cents_to_dollars;
|
||||
mod cents_to_sats;
|
||||
mod ohlc_cents_to_dollars;
|
||||
mod ohlc_cents_to_sats;
|
||||
|
||||
mod dollar_halve;
|
||||
mod dollar_identity;
|
||||
mod dollar_plus;
|
||||
mod dollar_times_tenths;
|
||||
mod dollars_to_sats_fract;
|
||||
mod f32_identity;
|
||||
mod ohlc_cents_to_dollars;
|
||||
mod ohlc_cents_to_sats;
|
||||
mod percentage_diff_close_dollars;
|
||||
mod percentage_dollars_f32;
|
||||
mod percentage_dollars_f32_neg;
|
||||
@@ -18,7 +17,7 @@ mod percentage_u32_f32;
|
||||
mod price_times_ratio;
|
||||
mod ratio32;
|
||||
mod ratio64;
|
||||
|
||||
mod ratio_u64_f32;
|
||||
mod return_f32_tenths;
|
||||
mod return_i8;
|
||||
mod return_u16;
|
||||
@@ -52,13 +51,12 @@ pub use percentage_dollars_f32_neg::*;
|
||||
pub use percentage_sats_f64::*;
|
||||
pub use percentage_u32_f32::*;
|
||||
pub use price_times_ratio::*;
|
||||
|
||||
pub use ratio32::*;
|
||||
pub use ratio64::*;
|
||||
pub use ratio_u64_f32::*;
|
||||
pub use return_f32_tenths::*;
|
||||
pub use return_i8::*;
|
||||
pub use return_u16::*;
|
||||
|
||||
pub use sat_halve::*;
|
||||
pub use sat_halve_to_bitcoin::*;
|
||||
pub use sat_identity::*;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
use brk_types::{StoredF32, StoredU64};
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
/// (StoredU64, StoredU64) -> StoredF32 ratio (a/b)
|
||||
/// Used for adoption ratio calculations (script_count / total_outputs)
|
||||
pub struct RatioU64F32;
|
||||
|
||||
impl BinaryTransform<StoredU64, StoredU64, StoredF32> for RatioU64F32 {
|
||||
#[inline(always)]
|
||||
fn apply(numerator: StoredU64, denominator: StoredU64) -> StoredF32 {
|
||||
if *denominator > 0 {
|
||||
StoredF32::from(*numerator as f64 / *denominator as f64)
|
||||
} else {
|
||||
StoredF32::from(0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user