mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-31 10:13:39 -07:00
global: snapshot
This commit is contained in:
@@ -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::*;
|
||||
|
||||
+21
-6
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user