global: snapshot

This commit is contained in:
nym21
2026-03-03 22:10:05 +01:00
parent 28f6b0f18b
commit 269c1d5fdf
99 changed files with 1565 additions and 1146 deletions
+4 -4
View File
@@ -1,5 +1,5 @@
mod _14d;
mod _7d_30d;
mod _1w_1m;
mod _2w;
pub use _14d::*;
pub use _7d_30d::*;
pub use _1w_1m::*;
pub use _2w::*;
@@ -1,20 +1,20 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{StoredF32, Version};
use brk_types::{Height, StoredF32, Version};
use schemars::JsonSchema;
use vecdb::{Database, ReadableCloneableVec, Rw, StorageMode, UnaryTransform};
use vecdb::{BinaryTransform, Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, UnaryTransform, VecValue};
use crate::indexes;
use super::{ComputedFromHeight, LazyFromHeight};
use crate::internal::NumericValue;
/// Basis-point storage with lazy float view.
/// Basis-point storage with lazy ratio float view (÷10000).
///
/// Stores integer basis points on disk (Pco-compressed),
/// exposes a lazy StoredF32 view (bps / 100).
/// exposes a lazy StoredF32 ratio (e.g., 25000 bps → 2.5).
#[derive(Traversable)]
pub struct BpsFromHeight<B, M: StorageMode = Rw>
pub struct Float32FromHeight<B, M: StorageMode = Rw>
where
B: NumericValue + JsonSchema,
{
@@ -22,7 +22,7 @@ where
pub float: LazyFromHeight<StoredF32, B>,
}
impl<B> BpsFromHeight<B>
impl<B> Float32FromHeight<B>
where
B: NumericValue + JsonSchema,
{
@@ -43,4 +43,19 @@ where
Ok(Self { bps, float })
}
pub(crate) fn compute_binary<S1T, S2T, F>(
&mut self,
max_from: Height,
source1: &impl ReadableVec<Height, S1T>,
source2: &impl ReadableVec<Height, S2T>,
exit: &Exit,
) -> Result<()>
where
S1T: VecValue,
S2T: VecValue,
F: BinaryTransform<S1T, S2T, B>,
{
self.bps.compute_binary::<S1T, S2T, F>(max_from, source1, source2, exit)
}
}
@@ -1,14 +1,15 @@
mod aggregated;
mod base;
mod bps;
mod by_unit;
mod constant;
mod cumulative;
mod cumulative_sum;
mod distribution;
mod fiat;
mod float32;
mod full;
mod lazy_base;
mod percent;
mod percentiles;
mod price;
mod ratio;
@@ -17,15 +18,16 @@ mod value;
pub use aggregated::*;
pub use base::*;
pub use bps::*;
pub use by_unit::*;
pub use constant::*;
pub use cumulative::*;
pub use cumulative_sum::*;
pub use distribution::*;
pub use fiat::*;
pub use float32::*;
pub use full::*;
pub use lazy_base::*;
pub use percent::*;
pub use percentiles::*;
pub use price::*;
pub use ratio::*;
@@ -0,0 +1,96 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredF32, Version};
use schemars::JsonSchema;
use vecdb::{BinaryTransform, Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, UnaryTransform, VecValue};
use crate::{
indexes,
internal::NumericValue,
traits::ComputeDrawdown,
};
use super::{ComputedFromHeight, LazyFromHeight};
/// Basis-point storage with both ratio and percentage float views.
///
/// Stores integer basis points on disk (Pco-compressed),
/// exposes two lazy StoredF32 views:
/// - `ratio`: bps ÷ 10000 (e.g., 4523 bps → 0.4523)
/// - `percent`: bps ÷ 100 (e.g., 4523 bps → 45.23%)
///
/// Use for dominance, adoption, RSI, and other percentage-valued metrics.
#[derive(Traversable)]
pub struct PercentFromHeight<B, M: StorageMode = Rw>
where
B: NumericValue + JsonSchema,
{
pub bps: ComputedFromHeight<B, M>,
pub ratio: LazyFromHeight<StoredF32, B>,
pub percent: LazyFromHeight<StoredF32, B>,
}
impl<B> PercentFromHeight<B>
where
B: NumericValue + JsonSchema,
{
pub(crate) fn forced_import<RatioTransform, PercentTransform>(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self>
where
RatioTransform: UnaryTransform<B, StoredF32>,
PercentTransform: UnaryTransform<B, StoredF32>,
{
let bps = ComputedFromHeight::forced_import(db, name, version, indexes)?;
let ratio = LazyFromHeight::from_computed::<RatioTransform>(
&format!("{name}_ratio"),
version,
bps.height.read_only_boxed_clone(),
&bps,
);
let percent = LazyFromHeight::from_computed::<PercentTransform>(
&format!("{name}_percent"),
version,
bps.height.read_only_boxed_clone(),
&bps,
);
Ok(Self { bps, ratio, percent })
}
pub(crate) fn compute_binary<S1T, S2T, F>(
&mut self,
max_from: Height,
source1: &impl ReadableVec<Height, S1T>,
source2: &impl ReadableVec<Height, S2T>,
exit: &Exit,
) -> Result<()>
where
S1T: VecValue,
S2T: VecValue,
F: BinaryTransform<S1T, S2T, B>,
{
self.bps.compute_binary::<S1T, S2T, F>(max_from, source1, source2, exit)
}
pub(crate) fn compute_drawdown<C, A>(
&mut self,
max_from: Height,
current: &impl ReadableVec<Height, C>,
ath: &impl ReadableVec<Height, A>,
exit: &Exit,
) -> Result<()>
where
C: VecValue,
A: VecValue,
f64: From<C> + From<A>,
vecdb::EagerVec<vecdb::PcoVec<Height, B>>: ComputeDrawdown<Height>,
{
self.bps.height.compute_drawdown(max_from, current, ath, exit)
}
}
@@ -12,8 +12,8 @@ use super::super::ComputedFromHeight;
#[derive(Traversable)]
pub struct ComputedFromHeightRatioExtension<M: StorageMode = Rw> {
pub ratio_1w_sma: ComputedFromHeight<StoredF32, M>,
pub ratio_1m_sma: ComputedFromHeight<StoredF32, M>,
pub ratio_sma_1w: ComputedFromHeight<StoredF32, M>,
pub ratio_sma_1m: ComputedFromHeight<StoredF32, M>,
pub ratio_pct99: ComputedFromHeight<StoredF32, M>,
pub ratio_pct98: ComputedFromHeight<StoredF32, M>,
pub ratio_pct95: ComputedFromHeight<StoredF32, M>,
@@ -28,9 +28,9 @@ pub struct ComputedFromHeightRatioExtension<M: StorageMode = Rw> {
pub ratio_pct1_price: Price<ComputedFromHeight<Cents, M>>,
pub ratio_sd: ComputedFromHeightStdDevExtended<M>,
pub ratio_4y_sd: ComputedFromHeightStdDevExtended<M>,
pub ratio_2y_sd: ComputedFromHeightStdDevExtended<M>,
pub ratio_1y_sd: ComputedFromHeightStdDevExtended<M>,
pub ratio_sd_4y: ComputedFromHeightStdDevExtended<M>,
pub ratio_sd_2y: ComputedFromHeightStdDevExtended<M>,
pub ratio_sd_1y: ComputedFromHeightStdDevExtended<M>,
#[traversable(skip)]
tdigest: TDigest,
@@ -59,10 +59,11 @@ impl ComputedFromHeightRatioExtension {
}
macro_rules! import_sd {
($suffix:expr, $days:expr) => {
($suffix:expr, $period:expr, $days:expr) => {
ComputedFromHeightStdDevExtended::forced_import(
db,
&format!("{name}_{}", $suffix),
$period,
$days,
v,
indexes,
@@ -77,12 +78,12 @@ impl ComputedFromHeightRatioExtension {
}
Ok(Self {
ratio_1w_sma: import!("ratio_1w_sma"),
ratio_1m_sma: import!("ratio_1m_sma"),
ratio_sd: import_sd!("ratio", usize::MAX),
ratio_1y_sd: import_sd!("ratio_1y", 365),
ratio_2y_sd: import_sd!("ratio_2y", 2 * 365),
ratio_4y_sd: import_sd!("ratio_4y", 4 * 365),
ratio_sma_1w: import!("ratio_sma_1w"),
ratio_sma_1m: import!("ratio_sma_1m"),
ratio_sd: import_sd!("ratio", "", usize::MAX),
ratio_sd_1y: import_sd!("ratio", "1y", 365),
ratio_sd_2y: import_sd!("ratio", "2y", 2 * 365),
ratio_sd_4y: import_sd!("ratio", "4y", 4 * 365),
ratio_pct99: import!("ratio_pct99"),
ratio_pct98: import!("ratio_pct98"),
ratio_pct95: import!("ratio_pct95"),
@@ -108,14 +109,14 @@ impl ComputedFromHeightRatioExtension {
ratio_source: &impl ReadableVec<Height, StoredF32>,
) -> Result<()> {
// SMA using lookback vecs
self.ratio_1w_sma.height.compute_rolling_average(
self.ratio_sma_1w.height.compute_rolling_average(
starting_indexes.height,
&blocks.count.height_1w_ago,
ratio_source,
exit,
)?;
self.ratio_1m_sma.height.compute_rolling_average(
self.ratio_sma_1m.height.compute_rolling_average(
starting_indexes.height,
&blocks.count.height_1m_ago,
ratio_source,
@@ -183,11 +184,11 @@ impl ComputedFromHeightRatioExtension {
// Compute stddev at height level
self.ratio_sd
.compute_all(blocks, starting_indexes, exit, ratio_source)?;
self.ratio_4y_sd
self.ratio_sd_4y
.compute_all(blocks, starting_indexes, exit, ratio_source)?;
self.ratio_2y_sd
self.ratio_sd_2y
.compute_all(blocks, starting_indexes, exit, ratio_source)?;
self.ratio_1y_sd
self.ratio_sd_1y
.compute_all(blocks, starting_indexes, exit, ratio_source)?;
Ok(())
@@ -225,11 +226,11 @@ impl ComputedFromHeightRatioExtension {
// Stddev cents bands
self.ratio_sd
.compute_cents_bands(starting_indexes, metric_price, exit)?;
self.ratio_4y_sd
self.ratio_sd_4y
.compute_cents_bands(starting_indexes, metric_price, exit)?;
self.ratio_2y_sd
self.ratio_sd_2y
.compute_cents_bands(starting_indexes, metric_price, exit)?;
self.ratio_1y_sd
self.ratio_sd_1y
.compute_cents_bands(starting_indexes, metric_price, exit)?;
Ok(())
@@ -51,17 +51,19 @@ impl ComputedFromHeightStdDevExtended {
pub(crate) fn forced_import(
db: &Database,
name: &str,
period: &str,
days: usize,
parent_version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let version = parent_version + Version::TWO;
let p = super::period_suffix(period);
macro_rules! import {
($suffix:expr) => {
ComputedFromHeight::forced_import(
db,
&format!("{name}_{}", $suffix),
&format!("{name}_{}{p}", $suffix),
version,
indexes,
)?
@@ -70,12 +72,12 @@ impl ComputedFromHeightStdDevExtended {
macro_rules! import_price {
($suffix:expr) => {
Price::forced_import(db, &format!("{name}_{}", $suffix), version, indexes)?
Price::forced_import(db, &format!("{name}_{}{p}", $suffix), version, indexes)?
};
}
Ok(Self {
base: ComputedFromHeightStdDev::forced_import(db, name, days, parent_version, indexes)?,
base: ComputedFromHeightStdDev::forced_import(db, name, period, days, parent_version, indexes)?,
zscore: import!("zscore"),
p0_5sd: import!("p0_5sd"),
p1sd: import!("p1sd"),
@@ -11,6 +11,14 @@ use crate::{ComputeIndexes, blocks, indexes};
use crate::internal::ComputedFromHeight;
fn period_suffix(period: &str) -> String {
if period.is_empty() {
String::new()
} else {
format!("_{period}")
}
}
#[derive(Traversable)]
pub struct ComputedFromHeightStdDev<M: StorageMode = Rw> {
days: usize,
@@ -22,21 +30,23 @@ impl ComputedFromHeightStdDev {
pub(crate) fn forced_import(
db: &Database,
name: &str,
period: &str,
days: usize,
parent_version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let version = parent_version + Version::TWO;
let p = period_suffix(period);
let sma = ComputedFromHeight::forced_import(
db,
&format!("{name}_sma"),
&format!("{name}_sma{p}"),
version,
indexes,
)?;
let sd = ComputedFromHeight::forced_import(
db,
&format!("{name}_sd"),
&format!("{name}_sd{p}"),
version,
indexes,
)?;
@@ -74,18 +74,18 @@ where
&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.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,
max_from, windows._1w, source,
&mut self.0.average._1w.height, &mut self.0.min._1w.height,
&mut self.0.max._1w.height, &mut self.0.pct10._1w.height,
&mut self.0.pct25._1w.height, &mut self.0.median._1w.height,
&mut self.0.pct75._1w.height, &mut self.0.pct90._1w.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.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,
max_from, windows._1m, source,
&mut self.0.average._1m.height, &mut self.0.min._1m.height,
&mut self.0.max._1m.height, &mut self.0.pct10._1m.height,
&mut self.0.pct25._1m.height, &mut self.0.median._1m.height,
&mut self.0.pct75._1m.height, &mut self.0.pct90._1m.height, exit,
)?;
compute_rolling_distribution_from_starts(
max_from, windows._1y, source,
@@ -1,5 +1,7 @@
mod _14d;
mod _7d_30d;
mod _1w_1m;
mod _2w;
mod percent_1w_1m;
pub use _14d::*;
pub use _7d_30d::*;
pub use _1w_1m::*;
pub use _2w::*;
pub use percent_1w_1m::*;
@@ -0,0 +1,70 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredF32, Version};
use derive_more::{Deref, DerefMut};
use schemars::JsonSchema;
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode, UnaryTransform};
use crate::{
indexes,
internal::{Emas1w1m, NumericValue, PercentFromHeight},
};
const VERSION: Version = Version::ZERO;
/// 2 EMA vecs (1w, 1m) sourced from 24h rolling window,
/// each storing basis points with lazy ratio and percent float views.
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct PercentRollingEmas1w1m<B, M: StorageMode = Rw>(pub Emas1w1m<PercentFromHeight<B, M>>)
where
B: NumericValue + JsonSchema;
impl<B> PercentRollingEmas1w1m<B>
where
B: NumericValue + JsonSchema,
{
pub(crate) fn forced_import<RatioTransform, PercentTransform>(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self>
where
RatioTransform: UnaryTransform<B, StoredF32>,
PercentTransform: UnaryTransform<B, StoredF32>,
{
let v = version + VERSION;
Ok(Self(Emas1w1m::try_from_fn(|suffix| {
PercentFromHeight::forced_import::<RatioTransform, PercentTransform>(
db,
&format!("{name}_{suffix}"),
v,
indexes,
)
})?))
}
pub(crate) fn compute_from_24h(
&mut self,
max_from: Height,
height_1w_ago: &impl ReadableVec<Height, Height>,
height_1m_ago: &impl ReadableVec<Height, Height>,
source: &impl ReadableVec<Height, B>,
exit: &Exit,
) -> Result<()>
where
f64: From<B>,
B: From<f64> + Default,
{
self._1w
.bps
.height
.compute_rolling_ema(max_from, height_1w_ago, source, exit)?;
self._1m
.bps
.height
.compute_rolling_ema(max_from, height_1m_ago, source, exit)?;
Ok(())
}
}
@@ -1,11 +1,13 @@
mod distribution;
mod emas;
mod full;
mod percent_windows;
mod value_windows;
mod windows;
pub use distribution::*;
pub use emas::*;
pub use full::*;
pub use percent_windows::*;
pub use value_windows::*;
pub use windows::*;
@@ -0,0 +1,47 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{StoredF32, Version};
use derive_more::{Deref, DerefMut};
use schemars::JsonSchema;
use vecdb::{Database, Rw, StorageMode, UnaryTransform};
use crate::{
indexes,
internal::{NumericValue, PercentFromHeight, Windows},
};
const VERSION: Version = Version::ZERO;
/// 4 rolling window vecs (24h, 1w, 1m, 1y), each storing basis points
/// with lazy ratio and percent float views.
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct PercentRollingWindows<B, M: StorageMode = Rw>(pub Windows<PercentFromHeight<B, M>>)
where
B: NumericValue + JsonSchema;
impl<B> PercentRollingWindows<B>
where
B: NumericValue + JsonSchema,
{
pub(crate) fn forced_import<RatioTransform, PercentTransform>(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self>
where
RatioTransform: UnaryTransform<B, StoredF32>,
PercentTransform: UnaryTransform<B, StoredF32>,
{
let v = version + VERSION;
Ok(Self(Windows::try_from_fn(|suffix| {
PercentFromHeight::forced_import::<RatioTransform, PercentTransform>(
db,
&format!("{name}_{suffix}"),
v,
indexes,
)
})?))
}
}
@@ -1,6 +1,6 @@
//! RollingWindows - newtype on Windows with ComputedFromHeight per window duration.
//!
//! Each of the 4 windows (24h, 7d, 30d, 1y) contains a height-level stored vec
//! 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;
@@ -18,10 +18,10 @@ use crate::{
internal::{ComputedFromHeight, ComputedVecValue, NumericValue, Windows},
};
/// Rolling window start heights — the 4 height-ago vecs (24h, 7d, 30d, 1y).
/// Rolling window start heights — the 4 height-ago vecs (24h, 1w, 1m, 1y).
pub type WindowStarts<'a> = Windows<&'a EagerVec<PcoVec<Height, Height>>>;
/// 4 rolling window vecs (24h, 7d, 30d, 1y), each with height data + all 17 index views.
/// 4 rolling window vecs (24h, 1w, 1m, 1y), each with height data + all 17 index views.
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct RollingWindows<T, M: StorageMode = Rw>(pub Windows<ComputedFromHeight<T, M>>)
@@ -0,0 +1,11 @@
use brk_types::{BasisPoints16, StoredF32};
use vecdb::UnaryTransform;
pub struct Bp16ToPercent;
impl UnaryTransform<BasisPoints16, StoredF32> for Bp16ToPercent {
#[inline(always)]
fn apply(bp: BasisPoints16) -> StoredF32 {
StoredF32::from(bp.inner() as f32 / 100.0)
}
}
@@ -0,0 +1,11 @@
use brk_types::{BasisPoints32, StoredF32};
use vecdb::UnaryTransform;
pub struct Bp32ToPercent;
impl UnaryTransform<BasisPoints32, StoredF32> for Bp32ToPercent {
#[inline(always)]
fn apply(bp: BasisPoints32) -> StoredF32 {
StoredF32::from(bp.inner() as f32 / 100.0)
}
}
@@ -0,0 +1,11 @@
use brk_types::{BasisPointsSigned16, StoredF32};
use vecdb::UnaryTransform;
pub struct Bps16ToPercent;
impl UnaryTransform<BasisPointsSigned16, StoredF32> for Bps16ToPercent {
#[inline(always)]
fn apply(bp: BasisPointsSigned16) -> StoredF32 {
StoredF32::from(bp.inner() as f32 / 100.0)
}
}
@@ -0,0 +1,11 @@
use brk_types::{BasisPointsSigned32, StoredF32};
use vecdb::UnaryTransform;
pub struct Bps32ToPercent;
impl UnaryTransform<BasisPointsSigned32, StoredF32> for Bps32ToPercent {
#[inline(always)]
fn apply(bp: BasisPointsSigned32) -> StoredF32 {
StoredF32::from(bp.inner() as f32 / 100.0)
}
}
@@ -1,7 +1,11 @@
mod bp16_to_float;
mod bp16_to_percent;
mod bp32_to_float;
mod bp32_to_percent;
mod bps16_to_float;
mod bps16_to_percent;
mod bps32_to_float;
mod bps32_to_percent;
mod block_count_target;
mod cents_halve;
mod identity;
@@ -16,19 +20,15 @@ mod dollars_to_sats_fract;
mod neg_cents_to_dollars;
mod ohlc_cents_to_dollars;
mod ohlc_cents_to_sats;
mod percentage_cents_f32;
mod percentage_cents_signed_dollars_f32;
mod percentage_cents_signed_f32;
mod percentage_diff_close_cents;
mod percentage_diff_close_dollars;
mod percentage_dollars_f32;
mod percentage_dollars_f32_neg;
mod percentage_sats_f64;
mod percentage_u32_f32;
mod price_times_ratio_cents;
mod ratio32;
mod ratio_cents64;
mod per_sec;
mod ratio_bp16;
mod ratio_bps16;
mod ratio_bps32;
mod ratio_u64_f32;
mod return_f32_tenths;
mod return_i8;
@@ -43,9 +43,13 @@ mod days_to_years;
mod volatility;
pub use bp16_to_float::*;
pub use bp16_to_percent::*;
pub use bp32_to_float::*;
pub use bp32_to_percent::*;
pub use bps16_to_float::*;
pub use bps16_to_percent::*;
pub use bps32_to_float::*;
pub use bps32_to_percent::*;
pub use block_count_target::*;
pub use cents_halve::*;
pub use identity::*;
@@ -58,22 +62,17 @@ pub use cents_to_sats::*;
pub use neg_cents_to_dollars::*;
pub use ohlc_cents_to_dollars::*;
pub use ohlc_cents_to_sats::*;
pub use percentage_cents_f32::*;
pub use percentage_cents_signed_dollars_f32::*;
pub use percentage_cents_signed_f32::*;
pub use dollar_halve::*;
pub use dollars_to_sats_fract::*;
pub use percentage_diff_close_cents::*;
pub use percentage_diff_close_dollars::*;
pub use percentage_dollars_f32::*;
pub use percentage_dollars_f32_neg::*;
pub use percentage_sats_f64::*;
pub use percentage_u32_f32::*;
pub use price_times_ratio_cents::*;
pub use ratio32::*;
pub use ratio_cents64::*;
pub use per_sec::*;
pub use ratio_bp16::*;
pub use ratio_bps16::*;
pub use ratio_bps32::*;
pub use ratio_u64_f32::*;
pub use return_f32_tenths::*;
pub use return_i8::*;
@@ -1,16 +0,0 @@
use brk_types::{Cents, StoredF32};
use vecdb::BinaryTransform;
/// (Cents, Cents) -> StoredF32 percentage (a/b × 100)
pub struct PercentageCentsF32;
impl BinaryTransform<Cents, Cents, StoredF32> for PercentageCentsF32 {
#[inline(always)]
fn apply(numerator: Cents, denominator: Cents) -> StoredF32 {
if denominator == Cents::ZERO {
StoredF32::default()
} else {
StoredF32::from(numerator.inner() as f64 / denominator.inner() as f64 * 100.0)
}
}
}
@@ -1,17 +0,0 @@
use brk_types::{CentsSigned, Dollars, StoredF32};
use vecdb::BinaryTransform;
/// (CentsSigned, Dollars) -> StoredF32 percentage (a/b × 100)
/// For cross-type percentage when numerator is CentsSigned and denominator is Dollars.
pub struct PercentageCentsSignedDollarsF32;
impl BinaryTransform<CentsSigned, Dollars, StoredF32> for PercentageCentsSignedDollarsF32 {
#[inline(always)]
fn apply(numerator: CentsSigned, denominator: Dollars) -> StoredF32 {
if denominator == Dollars::ZERO {
StoredF32::default()
} else {
StoredF32::from(numerator.inner() as f64 / *denominator * 100.0)
}
}
}
@@ -1,17 +0,0 @@
use brk_types::{Cents, CentsSigned, StoredF32};
use vecdb::BinaryTransform;
/// (CentsSigned, Cents) -> StoredF32 percentage (a/b × 100)
/// For cross-type percentage when numerator is signed.
pub struct PercentageCentsSignedCentsF32;
impl BinaryTransform<CentsSigned, Cents, StoredF32> for PercentageCentsSignedCentsF32 {
#[inline(always)]
fn apply(numerator: CentsSigned, denominator: Cents) -> StoredF32 {
if denominator == Cents::ZERO {
StoredF32::default()
} else {
StoredF32::from(numerator.inner() as f64 / denominator.inner() as f64 * 100.0)
}
}
}
@@ -1,14 +0,0 @@
use brk_types::{Dollars, StoredF32};
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> StoredF32 percentage (a/b × 100)
/// Used for unrealized/realized ratio calculations
pub struct PercentageDollarsF32;
impl BinaryTransform<Dollars, Dollars, StoredF32> for PercentageDollarsF32 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> StoredF32 {
// Dollars / Dollars returns StoredF64, so dereference and multiply
StoredF32::from(*(numerator / denominator) * 100.0)
}
}
@@ -1,14 +0,0 @@
use brk_types::{Dollars, StoredF32};
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> StoredF32 negated percentage (-(a/b × 100))
/// Used for negated loss ratio calculations, avoiding lazy-from-lazy chains.
pub struct NegPercentageDollarsF32;
impl BinaryTransform<Dollars, Dollars, StoredF32> for NegPercentageDollarsF32 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> StoredF32 {
// Dollars / Dollars returns StoredF64, so dereference and multiply
StoredF32::from(-(*(numerator / denominator) * 100.0))
}
}
@@ -1,13 +0,0 @@
use brk_types::{Sats, StoredF64};
use vecdb::BinaryTransform;
/// (Sats, Sats) -> StoredF64 percentage (a/b × 100)
/// Used for supply ratio calculations (equivalent to Bitcoin/Bitcoin since 1e8 cancels)
pub struct PercentageSatsF64;
impl BinaryTransform<Sats, Sats, StoredF64> for PercentageSatsF64 {
#[inline(always)]
fn apply(numerator: Sats, denominator: Sats) -> StoredF64 {
StoredF64::from((*numerator as f64 / *denominator as f64) * 100.0)
}
}
@@ -1,13 +0,0 @@
use brk_types::{StoredF32, StoredU32};
use vecdb::BinaryTransform;
/// (StoredU32, StoredU32) -> StoredF32 percentage (a/b × 100)
/// Used for pool dominance calculations (pool_blocks / total_blocks × 100)
pub struct PercentageU32F32;
impl BinaryTransform<StoredU32, StoredU32, StoredF32> for PercentageU32F32 {
#[inline(always)]
fn apply(numerator: StoredU32, denominator: StoredU32) -> StoredF32 {
StoredF32::from((*numerator as f64 / *denominator as f64) * 100.0)
}
}
@@ -0,0 +1,73 @@
use brk_types::{BasisPoints16, Cents, Dollars, Sats, StoredU32, StoredU64};
use vecdb::BinaryTransform;
/// (StoredU64, StoredU64) -> BasisPoints16 ratio (a/b × 10000)
pub struct RatioU64Bp16;
impl BinaryTransform<StoredU64, StoredU64, BasisPoints16> for RatioU64Bp16 {
#[inline(always)]
fn apply(numerator: StoredU64, denominator: StoredU64) -> BasisPoints16 {
if *denominator > 0 {
BasisPoints16::from(*numerator as f64 / *denominator as f64)
} else {
BasisPoints16::ZERO
}
}
}
/// (Sats, Sats) -> BasisPoints16 ratio (a/b × 10000)
pub struct RatioSatsBp16;
impl BinaryTransform<Sats, Sats, BasisPoints16> for RatioSatsBp16 {
#[inline(always)]
fn apply(numerator: Sats, denominator: Sats) -> BasisPoints16 {
if *denominator > 0 {
BasisPoints16::from(*numerator as f64 / *denominator as f64)
} else {
BasisPoints16::ZERO
}
}
}
/// (Cents, Cents) -> BasisPoints16 ratio (a/b × 10000)
pub struct RatioCentsBp16;
impl BinaryTransform<Cents, Cents, BasisPoints16> for RatioCentsBp16 {
#[inline(always)]
fn apply(numerator: Cents, denominator: Cents) -> BasisPoints16 {
if denominator == Cents::ZERO {
BasisPoints16::ZERO
} else {
BasisPoints16::from(numerator.inner() as f64 / denominator.inner() as f64)
}
}
}
/// (StoredU32, StoredU32) -> BasisPoints16 ratio (a/b × 10000)
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
}
}
}
/// (Dollars, Dollars) -> BasisPoints16 ratio (a/b × 10000)
pub struct RatioDollarsBp16;
impl BinaryTransform<Dollars, Dollars, BasisPoints16> for RatioDollarsBp16 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> BasisPoints16 {
let ratio = *(numerator / denominator);
if ratio.is_finite() {
BasisPoints16::from(ratio)
} else {
BasisPoints16::ZERO
}
}
}
@@ -0,0 +1,62 @@
use brk_types::{BasisPointsSigned16, Cents, CentsSigned, Dollars};
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> BasisPointsSigned16 ratio (a/b × 10000)
pub struct RatioDollarsBps16;
impl BinaryTransform<Dollars, Dollars, BasisPointsSigned16> for RatioDollarsBps16 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> BasisPointsSigned16 {
let ratio = *(numerator / denominator);
if ratio.is_finite() {
BasisPointsSigned16::from(ratio)
} else {
BasisPointsSigned16::ZERO
}
}
}
/// (Dollars, Dollars) -> BasisPointsSigned16 negated ratio (-(a/b) × 10000)
pub struct NegRatioDollarsBps16;
impl BinaryTransform<Dollars, Dollars, BasisPointsSigned16> for NegRatioDollarsBps16 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> BasisPointsSigned16 {
let ratio = *(numerator / denominator);
if ratio.is_finite() {
BasisPointsSigned16::from(-ratio)
} else {
BasisPointsSigned16::ZERO
}
}
}
/// (CentsSigned, Cents) -> BasisPointsSigned16 ratio (a/b × 10000)
pub struct RatioCentsSignedCentsBps16;
impl BinaryTransform<CentsSigned, Cents, BasisPointsSigned16> for RatioCentsSignedCentsBps16 {
#[inline(always)]
fn apply(numerator: CentsSigned, denominator: Cents) -> BasisPointsSigned16 {
if denominator == Cents::ZERO {
BasisPointsSigned16::ZERO
} else {
BasisPointsSigned16::from(numerator.inner() as f64 / denominator.inner() as f64)
}
}
}
/// (CentsSigned, Dollars) -> BasisPointsSigned16 ratio (a/b × 10000)
pub struct RatioCentsSignedDollarsBps16;
impl BinaryTransform<CentsSigned, Dollars, BasisPointsSigned16> for RatioCentsSignedDollarsBps16 {
#[inline(always)]
fn apply(numerator: CentsSigned, denominator: Dollars) -> BasisPointsSigned16 {
let d: f64 = denominator.into();
if d > 0.0 {
// Convert cents to dollars first, then compute ratio
BasisPointsSigned16::from(numerator.inner() as f64 / 100.0 / d)
} else {
BasisPointsSigned16::ZERO
}
}
}
@@ -0,0 +1,32 @@
use brk_types::{BasisPointsSigned32, Cents, Dollars};
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> BasisPointsSigned32 ratio diff ((a/b - 1) × 10000)
pub struct RatioDiffDollarsBps32;
impl BinaryTransform<Dollars, Dollars, BasisPointsSigned32> for RatioDiffDollarsBps32 {
#[inline(always)]
fn apply(close: Dollars, base: Dollars) -> BasisPointsSigned32 {
let base_f64: f64 = base.into();
if base_f64 == 0.0 {
BasisPointsSigned32::ZERO
} else {
BasisPointsSigned32::from(f64::from(close) / base_f64 - 1.0)
}
}
}
/// (Cents, Cents) -> BasisPointsSigned32 ratio diff ((a/b - 1) × 10000)
pub struct RatioDiffCentsBps32;
impl BinaryTransform<Cents, Cents, BasisPointsSigned32> for RatioDiffCentsBps32 {
#[inline(always)]
fn apply(close: Cents, base: Cents) -> BasisPointsSigned32 {
let base_f64 = f64::from(base);
if base_f64 == 0.0 {
BasisPointsSigned32::ZERO
} else {
BasisPointsSigned32::from(f64::from(close) / base_f64 - 1.0)
}
}
}
+10 -10
View File
@@ -1,6 +1,6 @@
//! Base generic struct with 4 type parameters — one per rolling window duration.
//!
//! Foundation for all rolling window types (24h, 7d, 30d, 1y).
//! Foundation for all rolling window types (24h, 1w, 1m, 1y).
use brk_traversable::Traversable;
@@ -8,33 +8,33 @@ use brk_traversable::Traversable;
pub struct Windows<A, B = A, C = A, D = A> {
#[traversable(rename = "24h")]
pub _24h: A,
#[traversable(rename = "7d")]
pub _7d: B,
#[traversable(rename = "30d")]
pub _30d: C,
#[traversable(rename = "1w")]
pub _1w: B,
#[traversable(rename = "1m")]
pub _1m: C,
#[traversable(rename = "1y")]
pub _1y: D,
}
impl<A> Windows<A> {
pub const SUFFIXES: [&'static str; 4] = ["24h", "7d", "30d", "1y"];
pub const SUFFIXES: [&'static str; 4] = ["24h", "1w", "1m", "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])?,
_7d: f(Self::SUFFIXES[1])?,
_30d: f(Self::SUFFIXES[2])?,
_1w: f(Self::SUFFIXES[1])?,
_1m: f(Self::SUFFIXES[2])?,
_1y: f(Self::SUFFIXES[3])?,
})
}
pub fn as_array(&self) -> [&A; 4] {
[&self._24h, &self._7d, &self._30d, &self._1y]
[&self._24h, &self._1w, &self._1m, &self._1y]
}
pub fn as_mut_array(&mut self) -> [&mut A; 4] {
[&mut self._24h, &mut self._7d, &mut self._30d, &mut self._1y]
[&mut self._24h, &mut self._1w, &mut self._1m, &mut self._1y]
}
}