global: snapshot

This commit is contained in:
nym21
2026-03-04 10:25:41 +01:00
parent 269c1d5fdf
commit 0d63724903
91 changed files with 972 additions and 972 deletions

View File

@@ -6,7 +6,7 @@ use vecdb::{Database, ReadableCloneableVec};
use super::Vecs;
use crate::{
indexes,
internal::{Bps32ToFloat, Bps32ToPercent, ComputedFromHeight, ComputedHeightDerived, PercentFromHeight},
internal::{ComputedFromHeight, ComputedHeightDerived, PercentFromHeight},
};
impl Vecs {
@@ -26,7 +26,7 @@ impl Vecs {
indexes,
),
as_hash: ComputedFromHeight::forced_import(db, "difficulty_as_hash", version, indexes)?,
adjustment: PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(db, "difficulty_adjustment", version, indexes)?,
adjustment: PercentFromHeight::forced_import_bps32(db, "difficulty_adjustment", version, indexes)?,
epoch: ComputedFromHeight::forced_import(db, "difficulty_epoch", version, indexes)?,
blocks_before_next_adjustment: ComputedFromHeight::forced_import(
db,

View File

@@ -16,7 +16,7 @@ impl Vecs {
) -> Result<()> {
let window_starts = count_vecs.window_starts();
// vbytes = ceil(weight / 4), stored at height level
// vbytes = floor(weight / 4), stored at height level
self.vbytes.compute(
starting_indexes.height,
&window_starts,

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::StoredF32;
use brk_types::BasisPoints16;
use vecdb::Exit;
use super::Vecs;
@@ -31,7 +31,7 @@ impl Vecs {
vec.compute_transform(
starting_indexes.height,
&indexer.vecs.blocks.weight,
|(h, weight, ..)| (h, StoredF32::from(weight.fullness())),
|(h, weight, ..)| (h, BasisPoints16::from(weight.fullness())),
exit,
)?;
Ok(())

View File

@@ -5,7 +5,7 @@ use vecdb::Database;
use super::Vecs;
use crate::{
indexes,
internal::{ComputedFromHeightDistribution, ComputedHeightDerivedFull},
internal::{ComputedHeightDerivedFull, PercentFromHeightDistribution},
};
impl Vecs {
@@ -22,7 +22,7 @@ impl Vecs {
)?;
let fullness =
ComputedFromHeightDistribution::forced_import(db, "block_fullness", version, indexes)?;
PercentFromHeightDistribution::forced_import_bp16(db, "block_fullness", version, indexes)?;
Ok(Self { weight, fullness })
}

View File

@@ -1,11 +1,11 @@
use brk_traversable::Traversable;
use brk_types::{StoredF32, Weight};
use brk_types::{BasisPoints16, Weight};
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeightDistribution, ComputedHeightDerivedFull};
use crate::internal::{ComputedHeightDerivedFull, PercentFromHeightDistribution};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub weight: ComputedHeightDerivedFull<Weight, M>,
pub fullness: ComputedFromHeightDistribution<StoredF32, M>,
pub fullness: PercentFromHeightDistribution<BasisPoints16, M>,
}

View File

@@ -44,11 +44,10 @@ impl Vecs {
self.coinblocks_stored
.compute(starting_indexes.height, &window_starts, exit, |vec| {
vec.compute_transform2(
vec.compute_subtract(
starting_indexes.height,
&self.coinblocks_created.height,
&coinblocks_destroyed.height,
|(i, created, destroyed, ..)| (i, created.checked_sub(destroyed).unwrap()),
exit,
)?;
Ok(())

View File

@@ -1,5 +1,5 @@
use brk_error::Result;
use brk_types::{BasisPointsSigned32, StoredF64};
use brk_types::BasisPointsSigned32;
use vecdb::Exit;
use super::super::activity;
@@ -16,29 +16,27 @@ impl Vecs {
) -> Result<()> {
self.cointime_adj_inflation_rate.bps.height.compute_transform2(
starting_indexes.height,
&activity.activity_to_vaultedness_ratio.height,
&activity.liveliness.height,
&supply.inflation_rate.bps.height,
|(h, ratio, inflation, ..)| (h, BasisPointsSigned32::from((*ratio) * f64::from(inflation))),
|(h, liveliness, inflation, ..)| (h, BasisPointsSigned32::from(f64::from(liveliness) * f64::from(inflation))),
exit,
)?;
self.cointime_adj_tx_velocity_btc
.height
.compute_transform2(
.compute_multiply(
starting_indexes.height,
&activity.activity_to_vaultedness_ratio.height,
&supply.velocity.btc.height,
|(h, ratio, vel, ..)| (h, StoredF64::from(*ratio * *vel)),
exit,
)?;
self.cointime_adj_tx_velocity_usd
.height
.compute_transform2(
.compute_multiply(
starting_indexes.height,
&activity.activity_to_vaultedness_ratio.height,
&supply.velocity.usd.height,
|(h, ratio, vel, ..)| (h, StoredF64::from(*ratio * *vel)),
exit,
)?;

View File

@@ -5,13 +5,13 @@ use vecdb::Database;
use super::Vecs;
use crate::{
indexes,
internal::{Bps32ToFloat, Bps32ToPercent, ComputedFromHeight, PercentFromHeight},
internal::{ComputedFromHeight, PercentFromHeight},
};
impl Vecs {
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
Ok(Self {
cointime_adj_inflation_rate: PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
cointime_adj_inflation_rate: PercentFromHeight::forced_import_bps32(
db,
"cointime_adj_inflation_rate",
version,

View File

@@ -48,23 +48,17 @@ impl Vecs {
exit,
)?;
self.vaulted_cap.cents.height.compute_transform2(
self.vaulted_cap.cents.height.compute_multiply(
starting_indexes.height,
realized_cap_cents,
&activity.vaultedness.height,
|(i, cap, vaultedness, ..)| {
(i, Cents::from(f64::from(cap) / f64::from(vaultedness)))
},
exit,
)?;
self.active_cap.cents.height.compute_transform2(
self.active_cap.cents.height.compute_multiply(
starting_indexes.height,
realized_cap_cents,
&activity.liveliness.height,
|(i, cap, liveliness, ..)| {
(i, Cents::from(f64::from(cap) * f64::from(liveliness)))
},
exit,
)?;

View File

@@ -54,13 +54,10 @@ impl Vecs {
&self.vaulted_price.cents.height,
)?;
self.active_price.cents.height.compute_transform2(
self.active_price.cents.height.compute_multiply(
starting_indexes.height,
realized_price,
&activity.liveliness.height,
|(i, price, liveliness, ..)| {
(i, Cents::from(f64::from(price) * f64::from(liveliness)))
},
exit,
)?;

View File

@@ -3,12 +3,12 @@
use brk_cohort::ByAddressType;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredF32, StoredU64, Version};
use brk_types::{BasisPoints16, Height, StoredU64, Version};
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode};
use crate::{
indexes,
internal::{ComputedFromHeightDistribution, WindowStarts},
internal::{PercentFromHeightDistribution, WindowStarts},
};
use super::{AddrCountsVecs, NewAddrCountVecs};
@@ -16,9 +16,9 @@ use super::{AddrCountsVecs, NewAddrCountVecs};
/// Growth rate: new_addr_count / addr_count (global + per-type)
#[derive(Traversable)]
pub struct GrowthRateVecs<M: StorageMode = Rw> {
pub all: ComputedFromHeightDistribution<StoredF32, M>,
pub all: PercentFromHeightDistribution<BasisPoints16, M>,
#[traversable(flatten)]
pub by_addresstype: ByAddressType<ComputedFromHeightDistribution<StoredF32, M>>,
pub by_addresstype: ByAddressType<PercentFromHeightDistribution<BasisPoints16, M>>,
}
impl GrowthRateVecs {
@@ -27,22 +27,21 @@ impl GrowthRateVecs {
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let all = ComputedFromHeightDistribution::forced_import(
let all = PercentFromHeightDistribution::forced_import_bp16(
db,
"growth_rate",
version,
indexes,
)?;
let by_addresstype: ByAddressType<ComputedFromHeightDistribution<StoredF32>> =
ByAddressType::new_with_name(|name| {
ComputedFromHeightDistribution::forced_import(
db,
&format!("{name}_growth_rate"),
version,
indexes,
)
})?;
let by_addresstype = ByAddressType::new_with_name(|name| {
PercentFromHeightDistribution::forced_import_bp16(
db,
&format!("{name}_growth_rate"),
version,
indexes,
)
})?;
Ok(Self { all, by_addresstype })
}
@@ -91,7 +90,7 @@ impl GrowthRateVecs {
}
fn compute_ratio(
target: &mut EagerVec<PcoVec<Height, StoredF32>>,
target: &mut EagerVec<PcoVec<Height, BasisPoints16>>,
max_from: Height,
numerator: &impl ReadableVec<Height, StoredU64>,
denominator: &impl ReadableVec<Height, StoredU64>,
@@ -105,7 +104,7 @@ fn compute_ratio(
let n = *num as f64;
let d = *den as f64;
let ratio = if d == 0.0 { 0.0 } else { n / d };
(h, StoredF32::from(ratio))
(h, BasisPoints16::from(ratio))
},
exit,
)?;

View File

@@ -7,11 +7,11 @@ use brk_cohort::{
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
Cents, CentsCompact, CostBasisDistribution, Date, Dollars, Height, Sats,
StoredF32, Version,
BasisPoints16, Cents, CentsCompact, CostBasisDistribution, Date, Dollars, Height, Sats,
Version,
};
use rayon::prelude::*;
use vecdb::{AnyStoredVec, Database, Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode, VecIndex, WritableVec};
use vecdb::{AnyStoredVec, Database, Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode, WritableVec};
use crate::{
ComputeIndexes, blocks,
@@ -859,13 +859,15 @@ impl UTXOCohorts<Rw> {
target
.extended
.spot_cost_basis_percentile
.bps
.height
.truncate_push(height, StoredF32::NAN)?;
.truncate_push(height, BasisPoints16::ZERO)?;
target
.extended
.spot_invested_capital_percentile
.bps
.height
.truncate_push(height, StoredF32::NAN)?;
.truncate_push(height, BasisPoints16::ZERO)?;
continue;
}
@@ -969,12 +971,14 @@ impl UTXOCohorts<Rw> {
target
.extended
.spot_cost_basis_percentile
.bps
.height
.truncate_push(height, rank)?;
let rank = compute_spot_percentile_rank(&usd_result, spot);
target
.extended
.spot_invested_capital_percentile
.bps
.height
.truncate_push(height, rank)?;

View File

@@ -1,12 +1,13 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Cents, Height, StoredF32, Version};
use brk_types::{BasisPoints16, Cents, Height, Version};
use vecdb::{AnyStoredVec, Rw, StorageMode, WritableVec};
use crate::{
distribution::state::CohortState,
internal::{
ComputedFromHeight, PERCENTILES_LEN, PercentilesVecs, compute_spot_percentile_rank,
PERCENTILES_LEN, PercentFromHeight, PercentilesVecs,
compute_spot_percentile_rank,
},
};
@@ -22,10 +23,10 @@ pub struct CostBasisExtended<M: StorageMode = Rw> {
pub invested_capital: PercentilesVecs<M>,
/// What percentile of cost basis is below spot (sat-weighted)
pub spot_cost_basis_percentile: ComputedFromHeight<StoredF32, M>,
pub spot_cost_basis_percentile: PercentFromHeight<BasisPoints16, M>,
/// What percentile of invested capital is below spot (USD-weighted)
pub spot_invested_capital_percentile: ComputedFromHeight<StoredF32, M>,
pub spot_invested_capital_percentile: PercentFromHeight<BasisPoints16, M>,
}
impl CostBasisExtended {
@@ -43,13 +44,13 @@ impl CostBasisExtended {
cfg.version,
cfg.indexes,
)?,
spot_cost_basis_percentile: ComputedFromHeight::forced_import(
spot_cost_basis_percentile: PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("spot_cost_basis_percentile"),
cfg.version,
cfg.indexes,
)?,
spot_invested_capital_percentile: ComputedFromHeight::forced_import(
spot_invested_capital_percentile: PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("spot_invested_capital_percentile"),
cfg.version,
@@ -74,6 +75,7 @@ impl CostBasisExtended {
self.percentiles.truncate_push(height, &sat_prices)?;
let rank = compute_spot_percentile_rank(&sat_prices, spot);
self.spot_cost_basis_percentile
.bps
.height
.truncate_push(height, rank)?;
@@ -85,6 +87,7 @@ impl CostBasisExtended {
self.invested_capital.truncate_push(height, &usd_prices)?;
let rank = compute_spot_percentile_rank(&usd_prices, spot);
self.spot_invested_capital_percentile
.bps
.height
.truncate_push(height, rank)?;
@@ -105,8 +108,8 @@ impl CostBasisExtended {
.iter_mut()
.map(|v| &mut v.cents.height as &mut dyn AnyStoredVec),
);
vecs.push(&mut self.spot_cost_basis_percentile.height);
vecs.push(&mut self.spot_invested_capital_percentile.height);
vecs.push(&mut self.spot_cost_basis_percentile.bps.height);
vecs.push(&mut self.spot_invested_capital_percentile.bps.height);
vecs
}
@@ -116,9 +119,11 @@ impl CostBasisExtended {
self.invested_capital
.validate_computed_version_or_reset(base_version)?;
self.spot_cost_basis_percentile
.bps
.height
.validate_computed_version_or_reset(base_version)?;
self.spot_invested_capital_percentile
.bps
.height
.validate_computed_version_or_reset(base_version)?;
Ok(())

View File

@@ -1,7 +1,7 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
BasisPoints16, BasisPointsSigned16,
BasisPoints16, BasisPoints32, BasisPointsSigned16,
Bitcoin, Cents, CentsSats, CentsSigned, CentsSquaredSats, Dollars, Height, Sats, StoredF32, StoredF64, Version,
};
use vecdb::{
@@ -13,7 +13,6 @@ use crate::{
ComputeIndexes, blocks,
distribution::state::RealizedState,
internal::{
Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent,
CentsPlus, CentsUnsignedToDollars, ComputedFromHeightCumulative, ComputedFromHeight,
ComputedFromHeightRatio, FiatFromHeight, NegCentsUnsignedToDollars, PercentFromHeight,
PercentRollingEmas1w1m, PercentRollingWindows, ValueFromHeightCumulative, LazyFromHeight,
@@ -196,7 +195,7 @@ impl RealizedBase {
)?;
let realized_profit_rel_to_realized_cap =
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("realized_profit_rel_to_realized_cap"),
cfg.version + v1,
@@ -204,7 +203,7 @@ impl RealizedBase {
)?;
let realized_loss_rel_to_realized_cap =
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("realized_loss_rel_to_realized_cap"),
cfg.version + v1,
@@ -212,7 +211,7 @@ impl RealizedBase {
)?;
let net_realized_pnl_rel_to_realized_cap =
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
PercentFromHeight::forced_import_bps16(
cfg.db,
&cfg.name("net_realized_pnl_rel_to_realized_cap"),
cfg.version + v1,
@@ -316,10 +315,9 @@ impl RealizedBase {
cfg.indexes,
)?;
let mvrv = LazyFromHeight::from_computed::<Identity<StoredF32>>(
let mvrv = LazyFromHeight::from_lazy::<Identity<StoredF32>, BasisPoints32>(
&cfg.name("mvrv"),
cfg.version,
realized_price_ratio.ratio.height.read_only_boxed_clone(),
&realized_price_ratio.ratio,
);
@@ -336,7 +334,7 @@ impl RealizedBase {
let sopr = RollingWindows::forced_import(
cfg.db, &cfg.name("sopr"), cfg.version + v1, cfg.indexes,
)?;
let sell_side_risk_ratio = PercentRollingWindows::forced_import::<Bp16ToFloat, Bp16ToPercent>(
let sell_side_risk_ratio = PercentRollingWindows::forced_import_bp16(
cfg.db, &cfg.name("sell_side_risk_ratio"), cfg.version + v1, cfg.indexes,
)?;
@@ -344,12 +342,12 @@ impl RealizedBase {
let sopr_24h_ema = RollingEmas1w1m::forced_import(
cfg.db, &cfg.name("sopr_24h"), cfg.version + v1, cfg.indexes,
)?;
let sell_side_risk_ratio_24h_ema = PercentRollingEmas1w1m::forced_import::<Bp16ToFloat, Bp16ToPercent>(
let sell_side_risk_ratio_24h_ema = PercentRollingEmas1w1m::forced_import_bp16(
cfg.db, &cfg.name("sell_side_risk_ratio_24h"), cfg.version + v1, cfg.indexes,
)?;
let peak_regret_rel_to_realized_cap =
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("realized_peak_regret_rel_to_realized_cap"),
cfg.version + v1,
@@ -407,14 +405,14 @@ impl RealizedBase {
cfg.indexes,
)?,
net_pnl_change_1m_rel_to_realized_cap:
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
PercentFromHeight::forced_import_bps16(
cfg.db,
&cfg.name("net_pnl_change_1m_rel_to_realized_cap"),
cfg.version + v3,
cfg.indexes,
)?,
net_pnl_change_1m_rel_to_market_cap:
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
PercentFromHeight::forced_import_bps16(
cfg.db,
&cfg.name("net_pnl_change_1m_rel_to_market_cap"),
cfg.version + v3,

View File

@@ -6,7 +6,7 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use crate::{
ComputeIndexes, blocks,
internal::{
Bp16ToFloat, Bp16ToPercent, ComputedFromHeightRatioExtension, PercentFromHeight,
ComputedFromHeightRatioExtension, PercentFromHeight,
RatioCents64, RatioDollarsBp16, RollingWindows,
},
};
@@ -37,7 +37,7 @@ impl RealizedExtended {
let v1 = Version::ONE;
Ok(RealizedExtended {
realized_cap_rel_to_own_market_cap: PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
realized_cap_rel_to_own_market_cap: PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("realized_cap_rel_to_own_market_cap"),
cfg.version,

View File

@@ -4,7 +4,7 @@ use brk_types::{BasisPoints16, BasisPointsSigned16, Dollars, Height, Sats, Store
use vecdb::{Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
use crate::internal::{
Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent, LazyFromHeight,
Bps16ToFloat, LazyFromHeight,
NegRatioDollarsBps16, PercentFromHeight, RatioDollarsBp16, RatioDollarsBps16, RatioSatsBp16,
};
@@ -37,7 +37,7 @@ impl RelativeBase {
let v2 = Version::new(2);
let net_unrealized_pnl_rel_to_market_cap =
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
PercentFromHeight::forced_import_bps16(
cfg.db, &cfg.name("net_unrealized_pnl_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
)?;
@@ -50,33 +50,33 @@ impl RelativeBase {
Ok(Self {
supply_in_profit_rel_to_own_supply:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db, &cfg.name("supply_in_profit_rel_to_own_supply"), cfg.version + v1, cfg.indexes,
)?,
supply_in_loss_rel_to_own_supply:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db, &cfg.name("supply_in_loss_rel_to_own_supply"), cfg.version + v1, cfg.indexes,
)?,
unrealized_profit_rel_to_market_cap:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db, &cfg.name("unrealized_profit_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
)?,
unrealized_loss_rel_to_market_cap:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db, &cfg.name("unrealized_loss_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
)?,
neg_unrealized_loss_rel_to_market_cap:
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
PercentFromHeight::forced_import_bps16(
cfg.db, &cfg.name("neg_unrealized_loss_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
)?,
net_unrealized_pnl_rel_to_market_cap,
nupl,
invested_capital_in_profit_rel_to_realized_cap:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db, &cfg.name("invested_capital_in_profit_rel_to_realized_cap"), cfg.version, cfg.indexes,
)?,
invested_capital_in_loss_rel_to_realized_cap:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db, &cfg.name("invested_capital_in_loss_rel_to_realized_cap"), cfg.version, cfg.indexes,
)?,
})

View File

@@ -4,7 +4,6 @@ use brk_types::{BasisPoints16, BasisPointsSigned16, Dollars, Height};
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use crate::internal::{
Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent,
NegRatioDollarsBps16, PercentFromHeight, RatioDollarsBp16, RatioDollarsBps16,
};
@@ -31,28 +30,28 @@ impl RelativeExtendedOwnMarketCap {
Ok(Self {
unrealized_profit_rel_to_own_market_cap:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("unrealized_profit_rel_to_own_market_cap"),
cfg.version + v2,
cfg.indexes,
)?,
unrealized_loss_rel_to_own_market_cap:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
cfg.version + v2,
cfg.indexes,
)?,
neg_unrealized_loss_rel_to_own_market_cap:
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
PercentFromHeight::forced_import_bps16(
cfg.db,
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
cfg.version + v2,
cfg.indexes,
)?,
net_unrealized_pnl_rel_to_own_market_cap:
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
PercentFromHeight::forced_import_bps16(
cfg.db,
&cfg.name("net_unrealized_pnl_rel_to_own_market_cap"),
cfg.version + v2,

View File

@@ -4,7 +4,6 @@ use brk_types::{BasisPoints16, BasisPointsSigned16, Dollars, Height};
use vecdb::{Exit, Rw, StorageMode};
use crate::internal::{
Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent,
NegRatioDollarsBps16, PercentFromHeight, RatioDollarsBp16, RatioDollarsBps16,
};
@@ -32,28 +31,28 @@ impl RelativeExtendedOwnPnl {
Ok(Self {
unrealized_profit_rel_to_own_gross_pnl:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("unrealized_profit_rel_to_own_gross_pnl"),
cfg.version + v1,
cfg.indexes,
)?,
unrealized_loss_rel_to_own_gross_pnl:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("unrealized_loss_rel_to_own_gross_pnl"),
cfg.version + v1,
cfg.indexes,
)?,
neg_unrealized_loss_rel_to_own_gross_pnl:
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
PercentFromHeight::forced_import_bps16(
cfg.db,
&cfg.name("neg_unrealized_loss_rel_to_own_gross_pnl"),
cfg.version + v1,
cfg.indexes,
)?,
net_unrealized_pnl_rel_to_own_gross_pnl:
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
PercentFromHeight::forced_import_bps16(
cfg.db,
&cfg.name("net_unrealized_pnl_rel_to_own_gross_pnl"),
cfg.version + v2,

View File

@@ -3,7 +3,7 @@ use brk_traversable::Traversable;
use brk_types::{BasisPoints16, Height, Sats};
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use crate::internal::{Bp16ToFloat, Bp16ToPercent, PercentFromHeight, RatioSatsBp16};
use crate::internal::{PercentFromHeight, RatioSatsBp16};
use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
@@ -24,21 +24,21 @@ impl RelativeToAll {
) -> Result<Self> {
Ok(Self {
supply_rel_to_circulating_supply:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("supply_rel_to_circulating_supply"),
cfg.version + brk_types::Version::ONE,
cfg.indexes,
)?,
supply_in_profit_rel_to_circulating_supply:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("supply_in_profit_rel_to_circulating_supply"),
cfg.version + brk_types::Version::ONE,
cfg.indexes,
)?,
supply_in_loss_rel_to_circulating_supply:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
PercentFromHeight::forced_import_bp16(
cfg.db,
&cfg.name("supply_in_loss_rel_to_circulating_supply"),
cfg.version + brk_types::Version::ONE,

View File

@@ -165,33 +165,14 @@ where
if values.is_empty() {
// Handle edge case where all items were skipped
if let Some(ref mut max_vec) = max {
max_vec.truncate_push_at(idx, T::from(0_usize))?;
}
if let Some(ref mut pct90_vec) = pct90 {
pct90_vec.truncate_push_at(idx, T::from(0_usize))?;
}
if let Some(ref mut pct75_vec) = pct75 {
pct75_vec.truncate_push_at(idx, T::from(0_usize))?;
}
if let Some(ref mut median_vec) = median {
median_vec.truncate_push_at(idx, T::from(0_usize))?;
}
if let Some(ref mut pct25_vec) = pct25 {
pct25_vec.truncate_push_at(idx, T::from(0_usize))?;
}
if let Some(ref mut pct10_vec) = pct10 {
pct10_vec.truncate_push_at(idx, T::from(0_usize))?;
}
if let Some(ref mut min_vec) = min {
min_vec.truncate_push_at(idx, T::from(0_usize))?;
}
if let Some(ref mut average_vec) = average {
average_vec.truncate_push_at(idx, T::from(0_usize))?;
}
if let Some(ref mut sum_vec) = sum {
sum_vec.truncate_push_at(idx, T::from(0_usize))?;
macro_rules! push_zero {
($($vec:ident),*) => {
$(if let Some(ref mut v) = $vec {
v.truncate_push_at(idx, T::from(0_usize))?;
})*
};
}
push_zero!(max, pct90, pct75, median, pct25, pct10, min, average, sum);
if let Some(ref mut cumulative_vec) = cumulative {
let t = cumulative_val.unwrap();
cumulative_vec.truncate_push_at(idx, t)?;

View File

@@ -1,61 +0,0 @@
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;
use super::{ComputedFromHeight, LazyFromHeight};
use crate::internal::NumericValue;
/// Basis-point storage with lazy ratio float view (÷10000).
///
/// Stores integer basis points on disk (Pco-compressed),
/// exposes a lazy StoredF32 ratio (e.g., 25000 bps → 2.5).
#[derive(Traversable)]
pub struct Float32FromHeight<B, M: StorageMode = Rw>
where
B: NumericValue + JsonSchema,
{
pub bps: ComputedFromHeight<B, M>,
pub float: LazyFromHeight<StoredF32, B>,
}
impl<B> Float32FromHeight<B>
where
B: NumericValue + JsonSchema,
{
pub(crate) fn forced_import<F: UnaryTransform<B, StoredF32>>(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let bps = ComputedFromHeight::forced_import(db, name, version, indexes)?;
let float = LazyFromHeight::from_computed::<F>(
&format!("{name}_float"),
version,
bps.height.read_only_boxed_clone(),
&bps,
);
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)
}
}

View File

@@ -6,10 +6,10 @@ mod cumulative;
mod cumulative_sum;
mod distribution;
mod fiat;
mod float32;
mod full;
mod lazy_base;
mod percent;
mod percent_distribution;
mod percentiles;
mod price;
mod ratio;
@@ -24,10 +24,10 @@ 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 percent_distribution::*;
pub use percentiles::*;
pub use price::*;
pub use ratio::*;

View File

@@ -1,12 +1,17 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredF32, Version};
use brk_types::{
BasisPoints16, BasisPointsSigned16, BasisPointsSigned32, Height, StoredF32, Version,
};
use schemars::JsonSchema;
use vecdb::{BinaryTransform, Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, UnaryTransform, VecValue};
use crate::{
indexes,
internal::NumericValue,
internal::{
Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent, Bps32ToFloat, Bps32ToPercent,
NumericValue,
},
traits::ComputeDrawdown,
};
@@ -44,7 +49,7 @@ where
RatioTransform: UnaryTransform<B, StoredF32>,
PercentTransform: UnaryTransform<B, StoredF32>,
{
let bps = ComputedFromHeight::forced_import(db, name, version, indexes)?;
let bps = ComputedFromHeight::forced_import(db, &format!("{name}_bps"), version, indexes)?;
let ratio = LazyFromHeight::from_computed::<RatioTransform>(
&format!("{name}_ratio"),
@@ -54,7 +59,7 @@ where
);
let percent = LazyFromHeight::from_computed::<PercentTransform>(
&format!("{name}_percent"),
name,
version,
bps.height.read_only_boxed_clone(),
&bps,
@@ -63,6 +68,45 @@ where
Ok(Self { bps, ratio, percent })
}
}
impl PercentFromHeight<BasisPoints16> {
pub(crate) fn forced_import_bp16(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Self::forced_import::<Bp16ToFloat, Bp16ToPercent>(db, name, version, indexes)
}
}
impl PercentFromHeight<BasisPointsSigned16> {
pub(crate) fn forced_import_bps16(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Self::forced_import::<Bps16ToFloat, Bps16ToPercent>(db, name, version, indexes)
}
}
impl PercentFromHeight<BasisPointsSigned32> {
pub(crate) fn forced_import_bps32(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Self::forced_import::<Bps32ToFloat, Bps32ToPercent>(db, name, version, indexes)
}
}
impl<B> PercentFromHeight<B>
where
B: NumericValue + JsonSchema,
{
pub(crate) fn compute_binary<S1T, S2T, F>(
&mut self,
max_from: Height,

View File

@@ -0,0 +1,85 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{BasisPoints16, Height, StoredF32, Version};
use schemars::JsonSchema;
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageMode, UnaryTransform};
use crate::{indexes, internal::{Bp16ToFloat, Bp16ToPercent, NumericValue, WindowStarts}};
use super::{ComputedFromHeightDistribution, LazyFromHeight};
/// Like PercentFromHeight but with rolling distribution stats on the bps data.
#[derive(Traversable)]
pub struct PercentFromHeightDistribution<B, M: StorageMode = Rw>
where
B: NumericValue + JsonSchema,
{
pub bps: ComputedFromHeightDistribution<B, M>,
pub ratio: LazyFromHeight<StoredF32, B>,
pub percent: LazyFromHeight<StoredF32, B>,
}
impl<B> PercentFromHeightDistribution<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 = ComputedFromHeightDistribution::forced_import(db, &format!("{name}_bps"), version, indexes)?;
let ratio = LazyFromHeight::from_height_source::<RatioTransform>(
&format!("{name}_ratio"),
version,
bps.height.read_only_boxed_clone(),
indexes,
);
let percent = LazyFromHeight::from_height_source::<PercentTransform>(
name,
version,
bps.height.read_only_boxed_clone(),
indexes,
);
Ok(Self { bps, ratio, percent })
}
}
impl PercentFromHeightDistribution<BasisPoints16> {
pub(crate) fn forced_import_bp16(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Self::forced_import::<Bp16ToFloat, Bp16ToPercent>(db, name, version, indexes)
}
}
impl<B> PercentFromHeightDistribution<B>
where
B: NumericValue + JsonSchema,
{
pub(crate) fn compute(
&mut self,
max_from: Height,
windows: &WindowStarts<'_>,
exit: &Exit,
compute_height: impl FnOnce(&mut EagerVec<PcoVec<Height, B>>) -> Result<()>,
) -> Result<()>
where
B: Copy + Ord + From<f64> + Default,
f64: From<B>,
{
self.bps.compute(max_from, windows, exit, compute_height)
}
}

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::{Traversable, TreeNode};
use brk_types::{Cents, Height, StoredF32, Version};
use brk_types::{BasisPoints16, Cents, Height, Version};
use vecdb::{AnyExportableVec, Database, ReadOnlyClone, Ro, Rw, StorageMode, WritableVec};
use crate::indexes;
@@ -16,9 +16,9 @@ pub const PERCENTILES_LEN: usize = PERCENTILES.len();
pub(crate) fn compute_spot_percentile_rank(
percentile_prices: &[Cents; PERCENTILES_LEN],
spot: Cents,
) -> StoredF32 {
) -> BasisPoints16 {
if spot == Cents::ZERO && percentile_prices[0] == Cents::ZERO {
return StoredF32::NAN;
return BasisPoints16::ZERO;
}
let spot_f64 = f64::from(spot);
@@ -27,10 +27,10 @@ pub(crate) fn compute_spot_percentile_rank(
let p5 = f64::from(percentile_prices[0]);
if spot_f64 <= p5 {
if p5 == 0.0 {
return StoredF32::from(0.0);
return BasisPoints16::ZERO;
}
// Linear extrapolation: rank = 5 * (spot / p5)
return StoredF32::from((5.0 * spot_f64 / p5).max(0.0));
// Linear extrapolation: rank = 5% * (spot / p5)
return BasisPoints16::from((0.05 * spot_f64 / p5).max(0.0));
}
// Above highest percentile (p95) - extrapolate towards 100
@@ -38,11 +38,11 @@ pub(crate) fn compute_spot_percentile_rank(
let p90 = f64::from(percentile_prices[PERCENTILES_LEN - 2]);
if spot_f64 >= p95 {
if p95 == p90 {
return StoredF32::from(100.0);
return BasisPoints16::ONE;
}
// Linear extrapolation using p90-p95 slope
let slope = 5.0 / (p95 - p90);
return StoredF32::from((95.0 + (spot_f64 - p95) * slope).min(100.0));
let slope = 0.05 / (p95 - p90);
return BasisPoints16::from((0.95 + (spot_f64 - p95) * slope).min(1.0));
}
// Find the band containing spot and interpolate
@@ -51,20 +51,20 @@ pub(crate) fn compute_spot_percentile_rank(
let upper = f64::from(percentile_prices[i + 1]);
if spot_f64 >= lower && spot_f64 <= upper {
let lower_pct = f64::from(PERCENTILES[i]);
let upper_pct = f64::from(PERCENTILES[i + 1]);
let lower_pct = f64::from(PERCENTILES[i]) / 100.0;
let upper_pct = f64::from(PERCENTILES[i + 1]) / 100.0;
if upper == lower {
return StoredF32::from(lower_pct);
return BasisPoints16::from(lower_pct);
}
// Linear interpolation
let ratio = (spot_f64 - lower) / (upper - lower);
return StoredF32::from(lower_pct + ratio * (upper_pct - lower_pct));
return BasisPoints16::from(lower_pct + ratio * (upper_pct - lower_pct));
}
}
StoredF32::NAN
BasisPoints16::ZERO
}
pub struct PercentilesVecs<M: StorageMode = Rw> {

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Cents, Height, StoredF32, Version};
use brk_types::{BasisPoints32, Cents, Height, StoredF32, Version};
use vecdb::{AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex, WritableVec};
use crate::{
@@ -8,18 +8,18 @@ use crate::{
internal::{ComputedFromHeightStdDevExtended, Price, TDigest},
};
use super::super::ComputedFromHeight;
use super::{ComputedFromHeightRatio, super::ComputedFromHeight};
#[derive(Traversable)]
pub struct ComputedFromHeightRatioExtension<M: StorageMode = Rw> {
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>,
pub ratio_pct5: ComputedFromHeight<StoredF32, M>,
pub ratio_pct2: ComputedFromHeight<StoredF32, M>,
pub ratio_pct1: ComputedFromHeight<StoredF32, M>,
pub ratio_sma_1w: ComputedFromHeightRatio<M>,
pub ratio_sma_1m: ComputedFromHeightRatio<M>,
pub ratio_pct99: ComputedFromHeightRatio<M>,
pub ratio_pct98: ComputedFromHeightRatio<M>,
pub ratio_pct95: ComputedFromHeightRatio<M>,
pub ratio_pct5: ComputedFromHeightRatio<M>,
pub ratio_pct2: ComputedFromHeightRatio<M>,
pub ratio_pct1: ComputedFromHeightRatio<M>,
pub ratio_pct99_price: Price<ComputedFromHeight<Cents, M>>,
pub ratio_pct98_price: Price<ComputedFromHeight<Cents, M>>,
pub ratio_pct95_price: Price<ComputedFromHeight<Cents, M>>,
@@ -47,9 +47,9 @@ impl ComputedFromHeightRatioExtension {
) -> Result<Self> {
let v = version + VERSION;
macro_rules! import {
macro_rules! import_ratio {
($suffix:expr) => {
ComputedFromHeight::forced_import(
ComputedFromHeightRatio::forced_import_raw(
db,
&format!("{name}_{}", $suffix),
v,
@@ -78,18 +78,18 @@ impl ComputedFromHeightRatioExtension {
}
Ok(Self {
ratio_sma_1w: import!("ratio_sma_1w"),
ratio_sma_1m: import!("ratio_sma_1m"),
ratio_sma_1w: import_ratio!("ratio_sma_1w"),
ratio_sma_1m: import_ratio!("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"),
ratio_pct5: import!("ratio_pct5"),
ratio_pct2: import!("ratio_pct2"),
ratio_pct1: import!("ratio_pct1"),
ratio_pct99: import_ratio!("ratio_pct99"),
ratio_pct98: import_ratio!("ratio_pct98"),
ratio_pct95: import_ratio!("ratio_pct95"),
ratio_pct5: import_ratio!("ratio_pct5"),
ratio_pct2: import_ratio!("ratio_pct2"),
ratio_pct1: import_ratio!("ratio_pct1"),
ratio_pct99_price: import_price!("ratio_pct99"),
ratio_pct98_price: import_price!("ratio_pct98"),
ratio_pct95_price: import_price!("ratio_pct95"),
@@ -109,14 +109,14 @@ impl ComputedFromHeightRatioExtension {
ratio_source: &impl ReadableVec<Height, StoredF32>,
) -> Result<()> {
// SMA using lookback vecs
self.ratio_sma_1w.height.compute_rolling_average(
self.ratio_sma_1w.bps.height.compute_rolling_average(
starting_indexes.height,
&blocks.count.height_1w_ago,
ratio_source,
exit,
)?;
self.ratio_sma_1m.height.compute_rolling_average(
self.ratio_sma_1m.bps.height.compute_rolling_average(
starting_indexes.height,
&blocks.count.height_1m_ago,
ratio_source,
@@ -124,14 +124,14 @@ impl ComputedFromHeightRatioExtension {
)?;
let ratio_version = ratio_source.version();
self.mut_ratio_vecs()
self.mut_pct_vecs()
.try_for_each(|v| -> Result<()> {
v.validate_computed_version_or_reset(ratio_version)?;
Ok(())
})?;
let starting_height = self
.mut_ratio_vecs()
.mut_pct_vecs()
.map(|v| Height::from(v.len()))
.min()
.unwrap()
@@ -154,13 +154,13 @@ impl ComputedFromHeightRatioExtension {
// Process new blocks [start, ratio_len)
let new_ratios = ratio_source.collect_range_at(start, ratio_len);
let mut pct_vecs: [&mut EagerVec<PcoVec<Height, StoredF32>>; 6] = [
&mut self.ratio_pct1.height,
&mut self.ratio_pct2.height,
&mut self.ratio_pct5.height,
&mut self.ratio_pct95.height,
&mut self.ratio_pct98.height,
&mut self.ratio_pct99.height,
let mut pct_vecs: [&mut EagerVec<PcoVec<Height, BasisPoints32>>; 6] = [
&mut self.ratio_pct1.bps.height,
&mut self.ratio_pct2.bps.height,
&mut self.ratio_pct5.bps.height,
&mut self.ratio_pct95.bps.height,
&mut self.ratio_pct98.bps.height,
&mut self.ratio_pct99.bps.height,
];
const PCTS: [f64; 6] = [0.01, 0.02, 0.05, 0.95, 0.98, 0.99];
let mut out = [0.0f64; 6];
@@ -170,14 +170,14 @@ impl ComputedFromHeightRatioExtension {
self.tdigest.quantiles(&PCTS, &mut out);
let idx = start + offset;
for (vec, &val) in pct_vecs.iter_mut().zip(out.iter()) {
vec.truncate_push_at(idx, StoredF32::from(val as f32))?;
vec.truncate_push_at(idx, BasisPoints32::from(val))?;
}
}
}
{
let _lock = exit.lock();
self.mut_ratio_vecs()
self.mut_pct_vecs()
.try_for_each(|v| v.flush())?;
}
@@ -201,13 +201,13 @@ impl ComputedFromHeightRatioExtension {
metric_price: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
use crate::internal::PriceTimesRatioCents;
use crate::internal::PriceTimesRatioBp32Cents;
macro_rules! compute_band {
($usd_field:ident, $band_source:expr) => {
self.$usd_field
.cents
.compute_binary::<Cents, StoredF32, PriceTimesRatioCents>(
.compute_binary::<Cents, BasisPoints32, PriceTimesRatioBp32Cents>(
starting_indexes.height,
metric_price,
$band_source,
@@ -216,12 +216,12 @@ impl ComputedFromHeightRatioExtension {
};
}
compute_band!(ratio_pct99_price, &self.ratio_pct99.height);
compute_band!(ratio_pct98_price, &self.ratio_pct98.height);
compute_band!(ratio_pct95_price, &self.ratio_pct95.height);
compute_band!(ratio_pct5_price, &self.ratio_pct5.height);
compute_band!(ratio_pct2_price, &self.ratio_pct2.height);
compute_band!(ratio_pct1_price, &self.ratio_pct1.height);
compute_band!(ratio_pct99_price, &self.ratio_pct99.bps.height);
compute_band!(ratio_pct98_price, &self.ratio_pct98.bps.height);
compute_band!(ratio_pct95_price, &self.ratio_pct95.bps.height);
compute_band!(ratio_pct5_price, &self.ratio_pct5.bps.height);
compute_band!(ratio_pct2_price, &self.ratio_pct2.bps.height);
compute_band!(ratio_pct1_price, &self.ratio_pct1.bps.height);
// Stddev cents bands
self.ratio_sd
@@ -236,16 +236,16 @@ impl ComputedFromHeightRatioExtension {
Ok(())
}
fn mut_ratio_vecs(
fn mut_pct_vecs(
&mut self,
) -> impl Iterator<Item = &mut EagerVec<PcoVec<Height, StoredF32>>> {
) -> impl Iterator<Item = &mut EagerVec<PcoVec<Height, BasisPoints32>>> {
[
&mut self.ratio_pct1.height,
&mut self.ratio_pct2.height,
&mut self.ratio_pct5.height,
&mut self.ratio_pct95.height,
&mut self.ratio_pct98.height,
&mut self.ratio_pct99.height,
&mut self.ratio_pct1.bps.height,
&mut self.ratio_pct2.bps.height,
&mut self.ratio_pct5.bps.height,
&mut self.ratio_pct95.bps.height,
&mut self.ratio_pct98.bps.height,
&mut self.ratio_pct99.bps.height,
]
.into_iter()
}

View File

@@ -8,16 +8,17 @@ pub use price_extended::*;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Cents, Height, StoredF32, Version};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
use brk_types::{BasisPoints32, Cents, Height, StoredF32, Version};
use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
use crate::{ComputeIndexes, indexes};
use crate::{ComputeIndexes, indexes, internal::Bp32ToFloat};
use super::ComputedFromHeight;
use super::{ComputedFromHeight, LazyFromHeight};
#[derive(Traversable)]
pub struct ComputedFromHeightRatio<M: StorageMode = Rw> {
pub ratio: ComputedFromHeight<StoredF32, M>,
pub bps: ComputedFromHeight<BasisPoints32, M>,
pub ratio: LazyFromHeight<StoredF32, BasisPoints32>,
}
const VERSION: Version = Version::TWO;
@@ -28,12 +29,28 @@ impl ComputedFromHeightRatio {
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Self::forced_import_raw(db, &format!("{name}_ratio"), version, indexes)
}
pub(crate) fn forced_import_raw(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let v = version + VERSION;
Ok(Self {
ratio: ComputedFromHeight::forced_import(db, &format!("{name}_ratio"), v, indexes)?,
})
let bps = ComputedFromHeight::forced_import(db, &format!("{name}_bps"), v, indexes)?;
let ratio = LazyFromHeight::from_computed::<Bp32ToFloat>(
name,
v,
bps.height.read_only_boxed_clone(),
&bps,
);
Ok(Self { bps, ratio })
}
/// Compute ratio = close_price / metric_price at height level (both in cents)
@@ -44,15 +61,15 @@ impl ComputedFromHeightRatio {
metric_price: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
self.ratio.height.compute_transform2(
self.bps.height.compute_transform2(
starting_indexes.height,
close_price,
metric_price,
|(i, close, price, ..)| {
if price == Cents::ZERO {
(i, StoredF32::from(1.0))
(i, BasisPoints32::from(1.0))
} else {
(i, StoredF32::from(f64::from(close) / f64::from(price)))
(i, BasisPoints32::from(f64::from(close) / f64::from(price)))
}
},
exit,

View File

@@ -1,13 +1,13 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredF32, Version};
use brk_types::{BasisPoints16, 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},
internal::{Bp16ToFloat, Bp16ToPercent, Emas1w1m, NumericValue, PercentFromHeight},
};
const VERSION: Version = Version::ZERO;
@@ -45,6 +45,23 @@ where
})?))
}
}
impl PercentRollingEmas1w1m<BasisPoints16> {
pub(crate) fn forced_import_bp16(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Self::forced_import::<Bp16ToFloat, Bp16ToPercent>(db, name, version, indexes)
}
}
impl<B> PercentRollingEmas1w1m<B>
where
B: NumericValue + JsonSchema,
{
pub(crate) fn compute_from_24h(
&mut self,
max_from: Height,

View File

@@ -1,13 +1,13 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{StoredF32, Version};
use brk_types::{BasisPoints16, StoredF32, Version};
use derive_more::{Deref, DerefMut};
use schemars::JsonSchema;
use vecdb::{Database, Rw, StorageMode, UnaryTransform};
use crate::{
indexes,
internal::{NumericValue, PercentFromHeight, Windows},
internal::{Bp16ToFloat, Bp16ToPercent, NumericValue, PercentFromHeight, Windows},
};
const VERSION: Version = Version::ZERO;
@@ -45,3 +45,14 @@ where
})?))
}
}
impl PercentRollingWindows<BasisPoints16> {
pub(crate) fn forced_import_bp16(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Self::forced_import::<Bp16ToFloat, Bp16ToPercent>(db, name, version, indexes)
}
}

View File

@@ -1,11 +0,0 @@
use brk_types::{BasisPoints16, StoredF32};
use vecdb::UnaryTransform;
pub struct Bp16ToFloat;
impl UnaryTransform<BasisPoints16, StoredF32> for Bp16ToFloat {
#[inline(always)]
fn apply(bp: BasisPoints16) -> StoredF32 {
StoredF32::from(bp.to_f32())
}
}

View File

@@ -1,11 +0,0 @@
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)
}
}

View File

@@ -1,11 +0,0 @@
use brk_types::{BasisPoints32, StoredF32};
use vecdb::UnaryTransform;
pub struct Bp32ToFloat;
impl UnaryTransform<BasisPoints32, StoredF32> for Bp32ToFloat {
#[inline(always)]
fn apply(bp: BasisPoints32) -> StoredF32 {
StoredF32::from(bp.to_f32())
}
}

View File

@@ -1,11 +0,0 @@
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)
}
}

View File

@@ -1,11 +0,0 @@
use brk_types::{BasisPointsSigned16, StoredF32};
use vecdb::UnaryTransform;
pub struct Bps16ToFloat;
impl UnaryTransform<BasisPointsSigned16, StoredF32> for Bps16ToFloat {
#[inline(always)]
fn apply(bp: BasisPointsSigned16) -> StoredF32 {
StoredF32::from(bp.to_f32())
}
}

View File

@@ -1,11 +0,0 @@
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)
}
}

View File

@@ -1,11 +0,0 @@
use brk_types::{BasisPointsSigned32, StoredF32};
use vecdb::UnaryTransform;
pub struct Bps32ToFloat;
impl UnaryTransform<BasisPointsSigned32, StoredF32> for Bps32ToFloat {
#[inline(always)]
fn apply(bp: BasisPointsSigned32) -> StoredF32 {
StoredF32::from(bp.to_f32())
}
}

View File

@@ -1,11 +0,0 @@
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)
}
}

View File

@@ -0,0 +1,38 @@
use brk_types::{BasisPoints16, BasisPoints32, BasisPointsSigned16, BasisPointsSigned32, StoredF32};
use vecdb::UnaryTransform;
pub struct Bp16ToFloat;
impl UnaryTransform<BasisPoints16, StoredF32> for Bp16ToFloat {
#[inline(always)]
fn apply(bp: BasisPoints16) -> StoredF32 {
StoredF32::from(bp.to_f32())
}
}
pub struct Bp32ToFloat;
impl UnaryTransform<BasisPoints32, StoredF32> for Bp32ToFloat {
#[inline(always)]
fn apply(bp: BasisPoints32) -> StoredF32 {
StoredF32::from(bp.to_f32())
}
}
pub struct Bps16ToFloat;
impl UnaryTransform<BasisPointsSigned16, StoredF32> for Bps16ToFloat {
#[inline(always)]
fn apply(bp: BasisPointsSigned16) -> StoredF32 {
StoredF32::from(bp.to_f32())
}
}
pub struct Bps32ToFloat;
impl UnaryTransform<BasisPointsSigned32, StoredF32> for Bps32ToFloat {
#[inline(always)]
fn apply(bp: BasisPointsSigned32) -> StoredF32 {
StoredF32::from(bp.to_f32())
}
}

View File

@@ -0,0 +1,29 @@
use brk_types::{BasisPoints16, BasisPointsSigned16, BasisPointsSigned32, 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)
}
}
pub struct Bps16ToPercent;
impl UnaryTransform<BasisPointsSigned16, StoredF32> for Bps16ToPercent {
#[inline(always)]
fn apply(bp: BasisPointsSigned16) -> StoredF32 {
StoredF32::from(bp.inner() as f32 / 100.0)
}
}
pub struct Bps32ToPercent;
impl UnaryTransform<BasisPointsSigned32, StoredF32> for Bps32ToPercent {
#[inline(always)]
fn apply(bp: BasisPointsSigned32) -> StoredF32 {
StoredF32::from(bp.inner() as f32 / 100.0)
}
}

View File

@@ -0,0 +1,48 @@
use brk_types::{Cents, CentsSigned, Dollars, Sats};
use vecdb::UnaryTransform;
/// CentsUnsigned -> Dollars (convert cents to dollars for display)
pub struct CentsUnsignedToDollars;
impl UnaryTransform<Cents, Dollars> for CentsUnsignedToDollars {
#[inline(always)]
fn apply(cents: Cents) -> Dollars {
cents.into()
}
}
/// Cents -> -Dollars (negate after converting to dollars)
/// Avoids lazy-from-lazy by combining both transforms.
pub struct NegCentsUnsignedToDollars;
impl UnaryTransform<Cents, Dollars> for NegCentsUnsignedToDollars {
#[inline(always)]
fn apply(cents: Cents) -> Dollars {
-Dollars::from(cents)
}
}
/// CentsSigned -> Dollars (convert signed cents to dollars for display)
pub struct CentsSignedToDollars;
impl UnaryTransform<CentsSigned, Dollars> for CentsSignedToDollars {
#[inline(always)]
fn apply(cents: CentsSigned) -> Dollars {
cents.into()
}
}
/// CentsUnsigned -> Sats (sats per dollar: 1 BTC / price)
pub struct CentsUnsignedToSats;
impl UnaryTransform<Cents, Sats> for CentsUnsignedToSats {
#[inline(always)]
fn apply(cents: Cents) -> Sats {
let dollars = Dollars::from(cents);
if dollars == Dollars::ZERO {
Sats::ZERO
} else {
Sats::ONE_BTC / dollars
}
}
}

View File

@@ -1,12 +0,0 @@
use brk_types::Cents;
use vecdb::UnaryTransform;
/// Cents -> Cents/2 (for supply_halved_cents)
pub struct HalveCents;
impl UnaryTransform<Cents, Cents> for HalveCents {
#[inline(always)]
fn apply(cents: Cents) -> Cents {
cents / 2u64
}
}

View File

@@ -1,12 +0,0 @@
use brk_types::{CentsSigned, Dollars};
use vecdb::UnaryTransform;
/// CentsSigned -> Dollars (convert signed cents to dollars for display)
pub struct CentsSignedToDollars;
impl UnaryTransform<CentsSigned, Dollars> for CentsSignedToDollars {
#[inline(always)]
fn apply(cents: CentsSigned) -> Dollars {
cents.into()
}
}

View File

@@ -1,12 +0,0 @@
use brk_types::{Cents, Dollars};
use vecdb::UnaryTransform;
/// CentsUnsigned -> Dollars (convert cents to dollars for display)
pub struct CentsUnsignedToDollars;
impl UnaryTransform<Cents, Dollars> for CentsUnsignedToDollars {
#[inline(always)]
fn apply(cents: Cents) -> Dollars {
cents.into()
}
}

View File

@@ -1,17 +0,0 @@
use brk_types::{Cents, Dollars, Sats};
use vecdb::UnaryTransform;
/// CentsUnsigned -> Sats (sats per dollar: 1 BTC / price)
pub struct CentsUnsignedToSats;
impl UnaryTransform<Cents, Sats> for CentsUnsignedToSats {
#[inline(always)]
fn apply(cents: Cents) -> Sats {
let dollars = Dollars::from(cents);
if dollars == Dollars::ZERO {
Sats::ZERO
} else {
Sats::ONE_BTC / dollars
}
}
}

View File

@@ -1,12 +0,0 @@
use brk_types::Dollars;
use vecdb::UnaryTransform;
/// Dollars -> Dollars/2 (for supply_halved_usd)
pub struct HalveDollars;
impl UnaryTransform<Dollars, Dollars> for HalveDollars {
#[inline(always)]
fn apply(dollars: Dollars) -> Dollars {
dollars.halved()
}
}

View File

@@ -0,0 +1,43 @@
use brk_types::{Bitcoin, Cents, Dollars, Sats};
use vecdb::UnaryTransform;
/// Sats -> Sats/2 (for supply_halved)
pub struct HalveSats;
impl UnaryTransform<Sats, Sats> for HalveSats {
#[inline(always)]
fn apply(sats: Sats) -> Sats {
sats / 2
}
}
/// Sats -> Bitcoin/2 (halve then convert to bitcoin)
/// Avoids lazy-from-lazy by combining both transforms
pub struct HalveSatsToBitcoin;
impl UnaryTransform<Sats, Bitcoin> for HalveSatsToBitcoin {
#[inline(always)]
fn apply(sats: Sats) -> Bitcoin {
Bitcoin::from(sats / 2)
}
}
/// Cents -> Cents/2 (for supply_halved_cents)
pub struct HalveCents;
impl UnaryTransform<Cents, Cents> for HalveCents {
#[inline(always)]
fn apply(cents: Cents) -> Cents {
cents / 2u64
}
}
/// Dollars -> Dollars/2 (for supply_halved_usd)
pub struct HalveDollars;
impl UnaryTransform<Dollars, Dollars> for HalveDollars {
#[inline(always)]
fn apply(dollars: Dollars) -> Dollars {
dollars.halved()
}
}

View File

@@ -1,86 +1,43 @@
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;
mod bps_to_float;
mod bps_to_percent;
mod cents_convert;
mod cents_plus;
mod cents_signed_to_dollars;
mod cents_subtract_to_cents_signed;
mod cents_times_tenths;
mod cents_to_dollars;
mod cents_to_sats;
mod dollar_halve;
mod days_to_years;
mod dollars_to_sats_fract;
mod neg_cents_to_dollars;
mod ohlc_cents_to_dollars;
mod ohlc_cents_to_sats;
mod percentage_diff_close_cents;
mod percentage_diff_close_dollars;
mod price_times_ratio_cents;
mod ratio32;
mod ratio_cents64;
mod halve;
mod identity;
mod ohlc;
mod per_sec;
mod ratio_bp16;
mod ratio_bps16;
mod ratio_bps32;
mod ratio_u64_f32;
mod return_f32_tenths;
mod return_i8;
mod return_u16;
mod sats_to_cents;
mod sat_halve;
mod sat_halve_to_bitcoin;
mod price_times_ratio_cents;
mod ratio;
mod ratio_cents64;
mod return_const;
mod sat_mask;
mod sat_to_bitcoin;
mod days_to_years;
mod sats_to_cents;
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::*;
pub use bps_to_float::*;
pub use bps_to_percent::*;
pub use cents_convert::*;
pub use cents_plus::*;
pub use cents_signed_to_dollars::*;
pub use cents_subtract_to_cents_signed::*;
pub use cents_times_tenths::*;
pub use cents_to_dollars::*;
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 dollar_halve::*;
pub use days_to_years::*;
pub use dollars_to_sats_fract::*;
pub use percentage_diff_close_cents::*;
pub use percentage_diff_close_dollars::*;
pub use price_times_ratio_cents::*;
pub use ratio32::*;
pub use ratio_cents64::*;
pub use halve::*;
pub use identity::*;
pub use ohlc::*;
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::*;
pub use return_u16::*;
pub use sats_to_cents::*;
pub use sat_halve::*;
pub use sat_halve_to_bitcoin::*;
pub use price_times_ratio_cents::*;
pub use ratio::*;
pub use ratio_cents64::*;
pub use return_const::*;
pub use sat_mask::*;
pub use sat_to_bitcoin::*;
pub use days_to_years::*;
pub use sats_to_cents::*;
pub use volatility::*;

View File

@@ -1,13 +0,0 @@
use brk_types::{Cents, Dollars};
use vecdb::UnaryTransform;
/// Cents -> -Dollars (negate after converting to dollars)
/// Avoids lazy-from-lazy by combining both transforms.
pub struct NegCentsUnsignedToDollars;
impl UnaryTransform<Cents, Dollars> for NegCentsUnsignedToDollars {
#[inline(always)]
fn apply(cents: Cents) -> Dollars {
-Dollars::from(cents)
}
}

View File

@@ -1,8 +1,17 @@
use brk_types::{Close, High, Low, OHLCCents, OHLCSats, Open};
use brk_types::{Close, High, Low, OHLCCents, OHLCDollars, OHLCSats, Open};
use vecdb::UnaryTransform;
use super::CentsUnsignedToSats;
pub struct OhlcCentsToDollars;
impl UnaryTransform<OHLCCents, OHLCDollars> for OhlcCentsToDollars {
#[inline(always)]
fn apply(cents: OHLCCents) -> OHLCDollars {
OHLCDollars::from(cents)
}
}
/// OHLCCents -> OHLCSats with high/low swapped (inverse price relationship).
pub struct OhlcCentsToSats;

View File

@@ -1,11 +0,0 @@
use brk_types::{OHLCCents, OHLCDollars};
use vecdb::UnaryTransform;
pub struct OhlcCentsToDollars;
impl UnaryTransform<OHLCCents, OHLCDollars> for OhlcCentsToDollars {
#[inline(always)]
fn apply(cents: OHLCCents) -> OHLCDollars {
OHLCDollars::from(cents)
}
}

View File

@@ -1,17 +0,0 @@
use brk_types::{Cents, StoredF32};
use vecdb::BinaryTransform;
/// (Cents, Cents) -> StoredF32 percentage difference ((a/b - 1) * 100)
pub struct PercentageDiffCents;
impl BinaryTransform<Cents, Cents, StoredF32> for PercentageDiffCents {
#[inline(always)]
fn apply(close: Cents, base: Cents) -> StoredF32 {
let base_f64 = f64::from(base);
if base_f64 == 0.0 {
StoredF32::default()
} else {
StoredF32::from((f64::from(close) / base_f64 - 1.0) * 100.0)
}
}
}

View File

@@ -1,16 +0,0 @@
use brk_types::{Dollars, StoredF32};
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> StoredF32 percentage difference ((a/b - 1) × 100)
pub struct PercentageDiffDollars;
impl BinaryTransform<Dollars, Dollars, StoredF32> for PercentageDiffDollars {
#[inline(always)]
fn apply(close: Dollars, base: Dollars) -> StoredF32 {
if base == Dollars::ZERO {
StoredF32::default()
} else {
StoredF32::from((*close / *base - 1.0) * 100.0)
}
}
}

View File

@@ -1,4 +1,4 @@
use brk_types::{Cents, StoredF32};
use brk_types::{BasisPoints32, Cents, StoredF32};
use vecdb::BinaryTransform;
pub struct PriceTimesRatioCents;
@@ -9,3 +9,12 @@ impl BinaryTransform<Cents, StoredF32, Cents> for PriceTimesRatioCents {
Cents::from(f64::from(price) * f64::from(ratio))
}
}
pub struct PriceTimesRatioBp32Cents;
impl BinaryTransform<Cents, BasisPoints32, Cents> for PriceTimesRatioBp32Cents {
#[inline(always)]
fn apply(price: Cents, ratio: BasisPoints32) -> Cents {
Cents::from(f64::from(price) * f64::from(ratio))
}
}

View File

@@ -0,0 +1,197 @@
use brk_types::{
BasisPoints16, BasisPoints32, BasisPointsSigned16, BasisPointsSigned32, Cents, CentsSigned,
Dollars, Sats, StoredF32, StoredU32, StoredU64,
};
use vecdb::BinaryTransform;
// === BasisPoints16 (unsigned) ratios ===
/// (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
}
}
}
// === BasisPointsSigned16 (signed) ratios ===
/// (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 {
BasisPointsSigned16::from(numerator.inner() as f64 / 100.0 / d)
} else {
BasisPointsSigned16::ZERO
}
}
}
// === BasisPoints32 (unsigned) ratios ===
/// (Dollars, Dollars) -> BasisPoints32 ratio (a / b × 10000)
pub struct RatioDollarsBp32;
impl BinaryTransform<Dollars, Dollars, BasisPoints32> for RatioDollarsBp32 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> BasisPoints32 {
BasisPoints32::from(f64::from(numerator) / f64::from(denominator))
}
}
// === BasisPointsSigned32 (signed) ratio diffs ===
/// (StoredF32, StoredF32) -> BasisPointsSigned32 ratio diff ((a/b - 1) × 10000)
pub struct RatioDiffF32Bps32;
impl BinaryTransform<StoredF32, StoredF32, BasisPointsSigned32> for RatioDiffF32Bps32 {
#[inline(always)]
fn apply(value: StoredF32, base: StoredF32) -> BasisPointsSigned32 {
if base.is_nan() || *base == 0.0 {
BasisPointsSigned32::ZERO
} else {
BasisPointsSigned32::from((*value / *base - 1.0) as f64)
}
}
}
/// (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)
}
}
}

View File

@@ -1,13 +0,0 @@
use brk_types::{Dollars, StoredF32};
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> StoredF32 ratio
/// Used for computing percentage ratios like profit/total, loss/total, etc.
pub struct Ratio32;
impl BinaryTransform<Dollars, Dollars, StoredF32> for Ratio32 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> StoredF32 {
StoredF32::from(numerator / denominator)
}
}

View File

@@ -1,73 +0,0 @@
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
}
}
}

View File

@@ -1,62 +0,0 @@
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
}
}
}

View File

@@ -1,32 +0,0 @@
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)
}
}
}

View File

@@ -1,17 +0,0 @@
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)
}
}
}

View File

@@ -0,0 +1,32 @@
use brk_types::{StoredF32, StoredI8, StoredU16};
use vecdb::UnaryTransform;
/// Returns a constant f32 value from tenths (V=382 -> 38.2), ignoring the input.
pub struct ReturnF32Tenths<const V: u16>;
impl<S, const V: u16> UnaryTransform<S, StoredF32> for ReturnF32Tenths<V> {
#[inline(always)]
fn apply(_: S) -> StoredF32 {
StoredF32::from(V as f32 / 10.0)
}
}
/// Returns a constant u16 value, ignoring the input.
pub struct ReturnU16<const V: u16>;
impl<S, const V: u16> UnaryTransform<S, StoredU16> for ReturnU16<V> {
#[inline(always)]
fn apply(_: S) -> StoredU16 {
StoredU16::new(V)
}
}
/// Returns a constant i8 value, ignoring the input.
pub struct ReturnI8<const V: i8>;
impl<S, const V: i8> UnaryTransform<S, StoredI8> for ReturnI8<V> {
#[inline(always)]
fn apply(_: S) -> StoredI8 {
StoredI8::new(V)
}
}

View File

@@ -1,12 +0,0 @@
use brk_types::StoredF32;
use vecdb::UnaryTransform;
/// Returns a constant f32 value from tenths (V=382 -> 38.2), ignoring the input.
pub struct ReturnF32Tenths<const V: u16>;
impl<S, const V: u16> UnaryTransform<S, StoredF32> for ReturnF32Tenths<V> {
#[inline(always)]
fn apply(_: S) -> StoredF32 {
StoredF32::from(V as f32 / 10.0)
}
}

View File

@@ -1,12 +0,0 @@
use brk_types::StoredI8;
use vecdb::UnaryTransform;
/// Returns a constant i8 value, ignoring the input.
pub struct ReturnI8<const V: i8>;
impl<S, const V: i8> UnaryTransform<S, StoredI8> for ReturnI8<V> {
#[inline(always)]
fn apply(_: S) -> StoredI8 {
StoredI8::new(V)
}
}

View File

@@ -1,12 +0,0 @@
use brk_types::StoredU16;
use vecdb::UnaryTransform;
/// Returns a constant u16 value, ignoring the input.
pub struct ReturnU16<const V: u16>;
impl<S, const V: u16> UnaryTransform<S, StoredU16> for ReturnU16<V> {
#[inline(always)]
fn apply(_: S) -> StoredU16 {
StoredU16::new(V)
}
}

View File

@@ -1,12 +0,0 @@
use brk_types::Sats;
use vecdb::UnaryTransform;
/// Sats -> Sats/2 (for supply_halved)
pub struct HalveSats;
impl UnaryTransform<Sats, Sats> for HalveSats {
#[inline(always)]
fn apply(sats: Sats) -> Sats {
sats / 2
}
}

View File

@@ -1,13 +0,0 @@
use brk_types::{Bitcoin, Sats};
use vecdb::UnaryTransform;
/// Sats -> Bitcoin/2 (halve then convert to bitcoin)
/// Avoids lazy-from-lazy by combining both transforms
pub struct HalveSatsToBitcoin;
impl UnaryTransform<Sats, Bitcoin> for HalveSatsToBitcoin {
#[inline(always)]
fn apply(sats: Sats) -> Bitcoin {
Bitcoin::from(sats / 2)
}
}

View File

@@ -3,7 +3,7 @@ use brk_types::{StoredF32, Timestamp};
use vecdb::{Exit, ReadableVec, VecIndex};
use super::Vecs;
use crate::{blocks, ComputeIndexes, prices, traits::ComputeDrawdown};
use crate::{blocks, ComputeIndexes, prices};
impl Vecs {
pub(crate) fn compute(

View File

@@ -5,7 +5,7 @@ use vecdb::Database;
use super::Vecs;
use crate::{
indexes,
internal::{Bps16ToFloat, Bps16ToPercent, ComputedFromHeight, DaysToYears, LazyHeightDerived, PercentFromHeight, Price},
internal::{ComputedFromHeight, DaysToYears, LazyHeightDerived, PercentFromHeight, Price},
};
const VERSION: Version = Version::ONE;
@@ -40,7 +40,7 @@ impl Vecs {
);
let price_drawdown =
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(db, "price_drawdown", v, indexes)?;
PercentFromHeight::forced_import_bps16(db, "price_drawdown", v, indexes)?;
Ok(Self {
price_ath,

View File

@@ -5,7 +5,7 @@ use vecdb::{Database, ImportableVec};
use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod, Vecs};
use crate::{
indexes,
internal::{Bps32ToFloat, Bps32ToPercent, PercentFromHeight, Price, ValueFromHeight},
internal::{PercentFromHeight, Price, ValueFromHeight},
};
impl Vecs {
@@ -23,7 +23,7 @@ impl Vecs {
})?;
let period_return = ByDcaPeriod::try_new(|name, _days| {
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
PercentFromHeight::forced_import_bps32(
db,
&format!("dca_return_{name}"),
version,
@@ -32,7 +32,7 @@ impl Vecs {
})?;
let period_cagr = ByDcaCagr::try_new(|name, _days| {
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
PercentFromHeight::forced_import_bps32(
db,
&format!("dca_cagr_{name}"),
version,
@@ -45,7 +45,7 @@ impl Vecs {
})?;
let period_lump_sum_return = ByDcaPeriod::try_new(|name, _days| {
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
PercentFromHeight::forced_import_bps32(
db,
&format!("lump_sum_return_{name}"),
version,
@@ -62,7 +62,7 @@ impl Vecs {
})?;
let class_return = ByDcaClass::try_new(|name, _year, _day1| {
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
PercentFromHeight::forced_import_bps32(
db,
&format!("dca_return_{name}"),
version,

View File

@@ -1,11 +1,11 @@
use brk_error::Result;
use brk_types::{Dollars, StoredF32};
use brk_types::{BasisPoints16, Dollars};
use vecdb::Exit;
use super::{super::range, Vecs};
use crate::{
ComputeIndexes, blocks, distribution,
internal::{Ratio32, Windows},
internal::{RatioDollarsBp32, Windows},
mining, prices, transactions,
};
@@ -34,17 +34,17 @@ impl Vecs {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self.puell_multiple.height.compute_divide(
self.puell_multiple.bps.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
starting_indexes.height,
&rewards.subsidy.base.usd.height,
&rewards.subsidy_sma_1y.usd.height,
exit,
)?;
// Stochastic Oscillator: K = (close - low_2w) / (high_2w - low_2w) * 100
// Stochastic Oscillator: K = (close - low_2w) / (high_2w - low_2w), stored as ratio (01)
{
let price = &prices.price.usd.height;
self.stoch_k.height.compute_transform3(
self.stoch_k.bps.height.compute_transform3(
starting_indexes.height,
price,
&range.price_min_2w.usd.height,
@@ -52,19 +52,19 @@ impl Vecs {
|(h, close, low, high, ..)| {
let range = *high - *low;
let stoch = if range == 0.0 {
StoredF32::NAN
BasisPoints16::ZERO
} else {
StoredF32::from(((*close - *low) / range * 100.0) as f32)
BasisPoints16::from(((*close - *low) / range) as f64)
};
(h, stoch)
},
exit,
)?;
self.stoch_d.height.compute_rolling_average(
self.stoch_d.bps.height.compute_rolling_average(
starting_indexes.height,
&blocks.count.height_3d_ago,
&self.stoch_k.height,
&self.stoch_k.bps.height,
exit,
)?;
}
@@ -75,10 +75,10 @@ impl Vecs {
{
let m = tf_multiplier(tf);
let returns_source = match tf {
"24h" => &returns.price_return._24h.height,
"1w" => &returns.price_return._1w.height,
"1m" => &returns.price_return._1m.height,
"1y" => &returns.price_return._1y.height,
"24h" => &returns.price_return._24h.ratio.height,
"1w" => &returns.price_return._1w.ratio.height,
"1m" => &returns.price_return._1m.ratio.height,
"1y" => &returns.price_return._1y.ratio.height,
_ => unreachable!(),
};
super::rsi::compute(
@@ -118,7 +118,7 @@ impl Vecs {
)?;
// NVT: market_cap / tx_volume_24h
self.nvt.compute_binary::<Dollars, Dollars, Ratio32>(
self.nvt.bps.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
starting_indexes.height,
&distribution.utxo_cohorts.all.metrics.supply.total.usd.height,
&transactions.volume.sent_sum.rolling._24h.usd.height,
@@ -126,7 +126,7 @@ impl Vecs {
)?;
// Pi Cycle: sma_111d / sma_350d_x2
self.pi_cycle.compute_binary::<Dollars, Dollars, Ratio32>(
self.pi_cycle.bps.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
starting_indexes.height,
&moving_average.price_sma_111d.price.usd.height,
&moving_average.price_sma_350d_x2.usd.height,

View File

@@ -5,7 +5,10 @@ use vecdb::Database;
use super::{MacdChain, RsiChain, Vecs};
use crate::{
indexes,
internal::{Bp16ToFloat, Bp16ToPercent, ComputedFromHeight, PercentFromHeight, Windows},
internal::{
ComputedFromHeight, ComputedFromHeightRatio,
PercentFromHeight, Windows,
},
};
const VERSION: Version = Version::ONE;
@@ -28,10 +31,21 @@ impl RsiChain {
};
}
macro_rules! percent_import {
($name:expr) => {
PercentFromHeight::forced_import_bp16(
db,
&format!("rsi_{}_{}", $name, tf),
version,
indexes,
)?
};
}
let average_gain = import!("average_gain");
let average_loss = import!("average_loss");
let rsi = ComputedFromHeight::forced_import(
let rsi = PercentFromHeight::forced_import_bp16(
db,
&format!("rsi_{tf}"),
version,
@@ -44,11 +58,11 @@ impl RsiChain {
average_gain,
average_loss,
rsi,
rsi_min: import!("min"),
rsi_max: import!("max"),
stoch_rsi: import!("stoch"),
stoch_rsi_k: import!("stoch_k"),
stoch_rsi_d: import!("stoch_d"),
rsi_min: percent_import!("min"),
rsi_max: percent_import!("max"),
stoch_rsi: percent_import!("stoch"),
stoch_rsi_k: percent_import!("stoch_k"),
stoch_rsi_d: percent_import!("stoch_d"),
})
}
}
@@ -108,19 +122,19 @@ impl Vecs {
) -> Result<Self> {
let v = version + VERSION;
let nvt = ComputedFromHeight::forced_import(db, "nvt", v, indexes)?;
let nvt = ComputedFromHeightRatio::forced_import_raw(db, "nvt", v, indexes)?;
let rsi = Windows::try_from_fn(|tf| RsiChain::forced_import(db, tf, v, indexes))?;
let macd = Windows::try_from_fn(|tf| MacdChain::forced_import(db, tf, v, indexes))?;
let stoch_k = ComputedFromHeight::forced_import(db, "stoch_k", v, indexes)?;
let stoch_d = ComputedFromHeight::forced_import(db, "stoch_d", v, indexes)?;
let gini = PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(db, "gini", v, indexes)?;
let stoch_k = PercentFromHeight::forced_import_bp16(db, "stoch_k", v, indexes)?;
let stoch_d = PercentFromHeight::forced_import_bp16(db, "stoch_d", v, indexes)?;
let gini = PercentFromHeight::forced_import_bp16(db, "gini", v, indexes)?;
let pi_cycle = ComputedFromHeight::forced_import(db, "pi_cycle", v, indexes)?;
let pi_cycle = ComputedFromHeightRatio::forced_import_raw(db, "pi_cycle", v, indexes)?;
Ok(Self {
puell_multiple: ComputedFromHeight::forced_import(db, "puell_multiple", v, indexes)?,
puell_multiple: ComputedFromHeightRatio::forced_import_raw(db, "puell_multiple", v, indexes)?,
nvt,
rsi,
stoch_k,

View File

@@ -1,5 +1,5 @@
use brk_error::Result;
use brk_types::{Height, StoredF32};
use brk_types::{BasisPoints16, Height, StoredF32};
use vecdb::{Exit, ReadableVec};
use super::RsiChain;
@@ -49,46 +49,46 @@ pub(super) fn compute(
exit,
)?;
// RSI = 100 * avg_gain / (avg_gain + avg_loss)
chain.rsi.height.compute_transform2(
// RSI = avg_gain / (avg_gain + avg_loss), stored as ratio (01)
chain.rsi.bps.height.compute_transform2(
starting_indexes.height,
&chain.average_gain.height,
&chain.average_loss.height,
|(h, g, l, ..)| {
let sum = *g + *l;
let rsi = if sum == 0.0 { 50.0 } else { 100.0 * *g / sum };
(h, StoredF32::from(rsi))
let rsi = if sum == 0.0 { 0.5 } else { *g / sum };
(h, BasisPoints16::from(rsi as f64))
},
exit,
)?;
// Rolling min/max of RSI over rma_days window
chain.rsi_min.height.compute_rolling_min_from_starts(
chain.rsi_min.bps.height.compute_rolling_min_from_starts(
starting_indexes.height,
ws_rma,
&chain.rsi.height,
&chain.rsi.bps.height,
exit,
)?;
chain.rsi_max.height.compute_rolling_max_from_starts(
chain.rsi_max.bps.height.compute_rolling_max_from_starts(
starting_indexes.height,
ws_rma,
&chain.rsi.height,
&chain.rsi.bps.height,
exit,
)?;
// StochRSI = (rsi - rsi_min) / (rsi_max - rsi_min) * 100
chain.stoch_rsi.height.compute_transform3(
// StochRSI = (rsi - rsi_min) / (rsi_max - rsi_min), stored as ratio (01)
chain.stoch_rsi.bps.height.compute_transform3(
starting_indexes.height,
&chain.rsi.height,
&chain.rsi_min.height,
&chain.rsi_max.height,
&chain.rsi.bps.height,
&chain.rsi_min.bps.height,
&chain.rsi_max.bps.height,
|(h, r, mn, mx, ..)| {
let range = *mx - *mn;
let range = f64::from(*mx) - f64::from(*mn);
let stoch = if range == 0.0 {
StoredF32::NAN
BasisPoints16::ZERO
} else {
StoredF32::from((*r - *mn) / range * 100.0)
BasisPoints16::from((f64::from(*r) - f64::from(*mn)) / range)
};
(h, stoch)
},
@@ -96,18 +96,18 @@ pub(super) fn compute(
)?;
// StochRSI K = SMA of StochRSI
chain.stoch_rsi_k.height.compute_rolling_average(
chain.stoch_rsi_k.bps.height.compute_rolling_average(
starting_indexes.height,
ws_sma,
&chain.stoch_rsi.height,
&chain.stoch_rsi.bps.height,
exit,
)?;
// StochRSI D = SMA of K
chain.stoch_rsi_d.height.compute_rolling_average(
chain.stoch_rsi_d.bps.height.compute_rolling_average(
starting_indexes.height,
ws_sma,
&chain.stoch_rsi_k.height,
&chain.stoch_rsi_k.bps.height,
exit,
)?;

View File

@@ -2,7 +2,7 @@ use brk_traversable::Traversable;
use brk_types::{BasisPoints16, StoredF32};
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeight, PercentFromHeight, Windows};
use crate::internal::{ComputedFromHeight, ComputedFromHeightRatio, PercentFromHeight, Windows};
#[derive(Traversable)]
pub struct RsiChain<M: StorageMode = Rw> {
@@ -10,12 +10,12 @@ pub struct RsiChain<M: StorageMode = Rw> {
pub losses: ComputedFromHeight<StoredF32, M>,
pub average_gain: ComputedFromHeight<StoredF32, M>,
pub average_loss: ComputedFromHeight<StoredF32, M>,
pub rsi: ComputedFromHeight<StoredF32, M>,
pub rsi_min: ComputedFromHeight<StoredF32, M>,
pub rsi_max: ComputedFromHeight<StoredF32, M>,
pub stoch_rsi: ComputedFromHeight<StoredF32, M>,
pub stoch_rsi_k: ComputedFromHeight<StoredF32, M>,
pub stoch_rsi_d: ComputedFromHeight<StoredF32, M>,
pub rsi: PercentFromHeight<BasisPoints16, M>,
pub rsi_min: PercentFromHeight<BasisPoints16, M>,
pub rsi_max: PercentFromHeight<BasisPoints16, M>,
pub stoch_rsi: PercentFromHeight<BasisPoints16, M>,
pub stoch_rsi_k: PercentFromHeight<BasisPoints16, M>,
pub stoch_rsi_d: PercentFromHeight<BasisPoints16, M>,
}
#[derive(Traversable)]
@@ -29,15 +29,15 @@ pub struct MacdChain<M: StorageMode = Rw> {
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub puell_multiple: ComputedFromHeight<StoredF32, M>,
pub nvt: ComputedFromHeight<StoredF32, M>,
pub puell_multiple: ComputedFromHeightRatio<M>,
pub nvt: ComputedFromHeightRatio<M>,
pub rsi: Windows<RsiChain<M>>,
pub stoch_k: ComputedFromHeight<StoredF32, M>,
pub stoch_d: ComputedFromHeight<StoredF32, M>,
pub stoch_k: PercentFromHeight<BasisPoints16, M>,
pub stoch_d: PercentFromHeight<BasisPoints16, M>,
pub pi_cycle: ComputedFromHeight<StoredF32, M>,
pub pi_cycle: ComputedFromHeightRatio<M>,
pub macd: Windows<MacdChain<M>>,

View File

@@ -1,5 +1,5 @@
use brk_error::Result;
use brk_types::StoredF32;
use brk_types::{BasisPoints16, StoredF32};
use vecdb::{Exit, ReadableVec, VecIndex};
use super::Vecs;
@@ -54,7 +54,7 @@ impl Vecs {
exit,
)?;
self.price_choppiness_index_2w.height.compute_transform4(
self.price_choppiness_index_2w.bps.height.compute_transform4(
starting_indexes.height,
&self.price_true_range_sum_2w.height,
&self.price_max_2w.cents.height,
@@ -64,11 +64,11 @@ impl Vecs {
let range = f64::from(max) - f64::from(min);
let n = (h.to_usize() - window_start.to_usize() + 1) as f32;
let ci = if range > 0.0 && n > 1.0 {
StoredF32::from(
100.0 * (*tr_sum / range as f32).log10() / n.log10(),
BasisPoints16::from(
(*tr_sum / range as f32).log10() as f64 / n.log10() as f64,
)
} else {
StoredF32::NAN
BasisPoints16::ZERO
};
(h, ci)
},

View File

@@ -3,7 +3,7 @@ use brk_types::Version;
use vecdb::Database;
use super::Vecs;
use crate::{indexes, internal::{ComputedFromHeight, Price}};
use crate::{indexes, internal::{ComputedFromHeight, PercentFromHeight, Price}};
impl Vecs {
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
@@ -24,7 +24,7 @@ impl Vecs {
price_true_range_sum_2w: ComputedFromHeight::forced_import(
db, "price_true_range_sum_2w", version + v1, indexes,
)?,
price_choppiness_index_2w: ComputedFromHeight::forced_import(
price_choppiness_index_2w: PercentFromHeight::forced_import_bp16(
db, "price_choppiness_index_2w", version + v1, indexes,
)?,
})

View File

@@ -1,8 +1,8 @@
use brk_traversable::Traversable;
use brk_types::{Cents, StoredF32};
use brk_types::{BasisPoints16, Cents, StoredF32};
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeight, Price};
use crate::internal::{ComputedFromHeight, PercentFromHeight, Price};
/// Price range and choppiness metrics
#[derive(Traversable)]
@@ -17,5 +17,5 @@ pub struct Vecs<M: StorageMode = Rw> {
pub price_max_1y: Price<ComputedFromHeight<Cents, M>>,
pub price_true_range: ComputedFromHeight<StoredF32, M>,
pub price_true_range_sum_2w: ComputedFromHeight<StoredF32, M>,
pub price_choppiness_index_2w: ComputedFromHeight<StoredF32, M>,
pub price_choppiness_index_2w: PercentFromHeight<BasisPoints16, M>,
}

View File

@@ -44,22 +44,22 @@ impl Vecs {
)?;
}
let _24h_price_return_height = &self.price_return._24h.bps.height;
let _24h_price_return_ratio = &self.price_return._24h.ratio.height;
self.price_return_24h_sd_1w
.compute_all(blocks, starting_indexes, exit, _24h_price_return_height)?;
.compute_all(blocks, starting_indexes, exit, _24h_price_return_ratio)?;
self.price_return_24h_sd_1m
.compute_all(blocks, starting_indexes, exit, _24h_price_return_height)?;
.compute_all(blocks, starting_indexes, exit, _24h_price_return_ratio)?;
self.price_return_24h_sd_1y
.compute_all(blocks, starting_indexes, exit, _24h_price_return_height)?;
.compute_all(blocks, starting_indexes, exit, _24h_price_return_ratio)?;
// Downside returns: min(return, 0)
self.price_downside_24h.compute_transform(
starting_indexes.height,
_24h_price_return_height,
_24h_price_return_ratio,
|(i, ret, ..)| {
let v = f64::from(ret).min(0.0);
(i, StoredF32::from(v as f32))
let v = f32::from(ret).min(0.0);
(i, StoredF32::from(v))
},
exit,
)?;

View File

@@ -6,7 +6,7 @@ use super::super::lookback::ByLookbackPeriod;
use super::Vecs;
use crate::{
indexes,
internal::{Bps32ToFloat, Bps32ToPercent, ComputedFromHeightStdDev, PercentFromHeight},
internal::{ComputedFromHeightStdDev, PercentFromHeight},
market::dca::ByDcaCagr,
};
@@ -19,7 +19,7 @@ impl Vecs {
let v1 = Version::ONE;
let price_return = ByLookbackPeriod::try_new(|name, _days| {
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
PercentFromHeight::forced_import_bps32(
db,
&format!("price_return_{name}"),
version,
@@ -29,7 +29,7 @@ impl Vecs {
// CAGR (computed, 2y+ only)
let price_cagr = ByDcaCagr::try_new(|name, _days| {
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
PercentFromHeight::forced_import_bps32(
db,
&format!("price_cagr_{name}"),
version,

View File

@@ -3,7 +3,7 @@ use brk_types::{BasisPointsSigned32, Height, StoredF32};
use vecdb::{EagerVec, PcoVec, Rw, StorageMode};
use crate::{
internal::{ComputedFromHeight, ComputedFromHeightStdDev, PercentFromHeight},
internal::{ComputedFromHeightStdDev, PercentFromHeight},
market::{dca::ByDcaCagr, lookback::ByLookbackPeriod},
};

View File

@@ -14,17 +14,17 @@ impl Vecs {
) -> Result<()> {
// Sharpe ratios: returns / volatility
for (out, ret, vol) in [
(&mut self.price_sharpe_1w, &returns.price_return._1w.height, &self.price_volatility_1w.height),
(&mut self.price_sharpe_1m, &returns.price_return._1m.height, &self.price_volatility_1m.height),
(&mut self.price_sharpe_1y, &returns.price_return._1y.height, &self.price_volatility_1y.height),
(&mut self.price_sharpe_1w, &returns.price_return._1w.ratio.height, &self.price_volatility_1w.height),
(&mut self.price_sharpe_1m, &returns.price_return._1m.ratio.height, &self.price_volatility_1m.height),
(&mut self.price_sharpe_1y, &returns.price_return._1y.ratio.height, &self.price_volatility_1y.height),
] {
compute_ratio(&mut out.height, starting_indexes_height, ret, vol, exit)?;
}
// Sortino ratios: returns / downside volatility
compute_ratio(&mut self.price_sortino_1w.height, starting_indexes_height, &returns.price_return._1w.height, &returns.price_downside_24h_sd_1w.sd.height, exit)?;
compute_ratio(&mut self.price_sortino_1m.height, starting_indexes_height, &returns.price_return._1m.height, &returns.price_downside_24h_sd_1m.sd.height, exit)?;
compute_ratio(&mut self.price_sortino_1y.height, starting_indexes_height, &returns.price_return._1y.height, &returns.price_downside_24h_sd_1y.sd.height, exit)?;
// Sortino ratios: returns / downside volatility (sd * sqrt(days))
compute_sortino(&mut self.price_sortino_1w.height, starting_indexes_height, &returns.price_return._1w.ratio.height, &returns.price_downside_24h_sd_1w.sd.height, 7.0_f32.sqrt(), exit)?;
compute_sortino(&mut self.price_sortino_1m.height, starting_indexes_height, &returns.price_return._1m.ratio.height, &returns.price_downside_24h_sd_1m.sd.height, 30.0_f32.sqrt(), exit)?;
compute_sortino(&mut self.price_sortino_1y.height, starting_indexes_height, &returns.price_return._1y.ratio.height, &returns.price_downside_24h_sd_1y.sd.height, 365.0_f32.sqrt(), exit)?;
Ok(())
}
@@ -49,3 +49,25 @@ fn compute_ratio(
)?;
Ok(())
}
fn compute_sortino(
out: &mut EagerVec<PcoVec<Height, StoredF32>>,
starting_indexes_height: Height,
ret: &impl ReadableVec<Height, StoredF32>,
sd: &impl ReadableVec<Height, StoredF32>,
sqrt_days: f32,
exit: &Exit,
) -> Result<()> {
out.compute_transform2(
starting_indexes_height,
ret,
sd,
|(h, ret, sd, ..)| {
let downside_vol = f32::from(*sd) * sqrt_days;
let ratio = if downside_vol == 0.0 { 0.0 } else { f32::from(*ret) / downside_vol };
(h, StoredF32::from(ratio))
},
exit,
)?;
Ok(())
}

View File

@@ -5,8 +5,8 @@ use vecdb::{Exit, ReadableVec};
use super::Vecs;
use crate::{
blocks::{self, ONE_TERA_HASH, TARGET_BLOCKS_PER_DAY_F64},
internal::RatioDiffF32Bps32,
ComputeIndexes,
traits::ComputeDrawdown,
};
impl Vecs {
@@ -151,8 +151,7 @@ impl Vecs {
)?;
self.hash_price_rebound
.height
.compute_percentage_difference(
.compute_binary::<StoredF32, StoredF32, RatioDiffF32Bps32>(
starting_indexes.height,
&self.hash_price_phs.height,
&self.hash_price_phs_min.height,
@@ -160,8 +159,7 @@ impl Vecs {
)?;
self.hash_value_rebound
.height
.compute_percentage_difference(
.compute_binary::<StoredF32, StoredF32, RatioDiffF32Bps32>(
starting_indexes.height,
&self.hash_value_phs.height,
&self.hash_value_phs_min.height,

View File

@@ -5,7 +5,10 @@ use vecdb::Database;
use super::Vecs;
use crate::{
indexes,
internal::{Bps16ToFloat, Bps16ToPercent, ComputedFromHeight, PercentFromHeight},
internal::{
ComputedFromHeight,
PercentFromHeight,
},
};
impl Vecs {
@@ -49,7 +52,7 @@ impl Vecs {
version,
indexes,
)?,
hash_rate_drawdown: PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
hash_rate_drawdown: PercentFromHeight::forced_import_bps16(
db,
"hash_rate_drawdown",
version,
@@ -79,7 +82,7 @@ impl Vecs {
version + v4,
indexes,
)?,
hash_price_rebound: ComputedFromHeight::forced_import(
hash_price_rebound: PercentFromHeight::forced_import_bps32(
db,
"hash_price_rebound",
version + v4,
@@ -109,7 +112,7 @@ impl Vecs {
version + v4,
indexes,
)?,
hash_value_rebound: ComputedFromHeight::forced_import(
hash_value_rebound: PercentFromHeight::forced_import_bps32(
db,
"hash_value_rebound",
version + v4,

View File

@@ -1,5 +1,5 @@
use brk_traversable::Traversable;
use brk_types::{BasisPointsSigned16, StoredF32, StoredF64};
use brk_types::{BasisPointsSigned16, BasisPointsSigned32, StoredF32, StoredF64};
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeight, PercentFromHeight};
@@ -18,10 +18,10 @@ pub struct Vecs<M: StorageMode = Rw> {
pub hash_price_ths_min: ComputedFromHeight<StoredF32, M>,
pub hash_price_phs: ComputedFromHeight<StoredF32, M>,
pub hash_price_phs_min: ComputedFromHeight<StoredF32, M>,
pub hash_price_rebound: ComputedFromHeight<StoredF32, M>,
pub hash_price_rebound: PercentFromHeight<BasisPointsSigned32, M>,
pub hash_value_ths: ComputedFromHeight<StoredF32, M>,
pub hash_value_ths_min: ComputedFromHeight<StoredF32, M>,
pub hash_value_phs: ComputedFromHeight<StoredF32, M>,
pub hash_value_phs_min: ComputedFromHeight<StoredF32, M>,
pub hash_value_rebound: ComputedFromHeight<StoredF32, M>,
pub hash_value_rebound: PercentFromHeight<BasisPointsSigned32, M>,
}

View File

@@ -6,7 +6,7 @@ use super::Vecs;
use crate::{
indexes,
internal::{
Bp16ToFloat, Bp16ToPercent, FiatFromHeight, PercentFromHeight, PercentRollingWindows,
FiatFromHeight, PercentFromHeight, PercentRollingWindows,
ValueFromHeightFull, ValueFromHeightCumulativeSum,
},
};
@@ -27,25 +27,25 @@ impl Vecs {
version,
indexes,
)?,
fee_dominance: PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
fee_dominance: PercentFromHeight::forced_import_bp16(
db,
"fee_dominance",
version,
indexes,
)?,
fee_dominance_rolling: PercentRollingWindows::forced_import::<Bp16ToFloat, Bp16ToPercent>(
fee_dominance_rolling: PercentRollingWindows::forced_import_bp16(
db,
"fee_dominance",
version,
indexes,
)?,
subsidy_dominance: PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
subsidy_dominance: PercentFromHeight::forced_import_bp16(
db,
"subsidy_dominance",
version,
indexes,
)?,
subsidy_dominance_rolling: PercentRollingWindows::forced_import::<Bp16ToFloat, Bp16ToPercent>(
subsidy_dominance_rolling: PercentRollingWindows::forced_import_bp16(
db,
"subsidy_dominance",
version,

View File

@@ -3,7 +3,7 @@ use brk_types::{BasisPoints16, Cents};
use vecdb::{Rw, StorageMode};
use crate::internal::{
FiatFromHeight, PercentFromHeight, PercentRollingWindows, RollingWindows,
FiatFromHeight, PercentFromHeight, PercentRollingWindows,
ValueFromHeightFull, ValueFromHeightCumulativeSum,
};

View File

@@ -7,7 +7,7 @@ use crate::{
blocks,
indexes::{self, ComputeIndexes},
internal::{
Bp16ToFloat, Bp16ToPercent, ComputedFromHeightCumulativeSum, ComputedFromHeight, MaskSats,
ComputedFromHeightCumulativeSum, ComputedFromHeight, MaskSats,
PercentFromHeight, PercentRollingWindows, RatioU32Bp16, RollingWindows,
ValueFromHeightCumulativeSum,
},
@@ -58,9 +58,9 @@ impl Vecs {
ValueFromHeightCumulativeSum::forced_import(db, &suffix("coinbase"), version, indexes)?;
let dominance =
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(db, &suffix("dominance"), version, indexes)?;
PercentFromHeight::forced_import_bp16(db, &suffix("dominance"), version, indexes)?;
let dominance_rolling =
PercentRollingWindows::forced_import::<Bp16ToFloat, Bp16ToPercent>(db, &suffix("dominance"), version, indexes)?;
PercentRollingWindows::forced_import_bp16(db, &suffix("dominance"), version, indexes)?;
Ok(Self {
dominance,

View File

@@ -5,7 +5,7 @@ use vecdb::{Database, Exit, Rw, StorageMode};
use crate::{
ComputeIndexes, indexes,
internal::{Bp16ToFloat, Bp16ToPercent, PercentFromHeight, RatioU64Bp16},
internal::{PercentFromHeight, RatioU64Bp16},
outputs,
};
@@ -24,13 +24,13 @@ impl Vecs {
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self {
taproot: PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
taproot: PercentFromHeight::forced_import_bp16(
db,
"taproot_adoption",
version,
indexes,
)?,
segwit: PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
segwit: PercentFromHeight::forced_import_bp16(
db,
"segwit_adoption",
version,

View File

@@ -9,7 +9,7 @@ use super::Vecs;
use crate::{
distribution, indexes,
internal::{
Bps32ToFloat, Bps32ToPercent, ComputedFromHeight, Identity, LazyFromHeight,
ComputedFromHeight, Identity, LazyFromHeight,
LazyValueFromHeight, PercentFromHeight, SatsToBitcoin,
},
};
@@ -42,7 +42,7 @@ impl Vecs {
// Inflation rate
let inflation_rate =
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(&db, "inflation_rate", version, indexes)?;
PercentFromHeight::forced_import_bps32(&db, "inflation_rate", version, indexes)?;
// Velocity
let velocity = super::velocity::Vecs::forced_import(&db, version, indexes)?;
@@ -55,13 +55,13 @@ impl Vecs {
);
// Growth rates
let market_cap_growth_rate = PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
let market_cap_growth_rate = PercentFromHeight::forced_import_bps32(
&db,
"market_cap_growth_rate",
version + Version::ONE,
indexes,
)?;
let realized_cap_growth_rate = PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
let realized_cap_growth_rate = PercentFromHeight::forced_import_bps32(
&db,
"realized_cap_growth_rate",
version + Version::ONE,

View File

@@ -1,5 +1,5 @@
use brk_error::Result;
use brk_types::{BasisPointsSigned16, StoredF32};
use brk_types::BasisPointsSigned16;
use vecdb::{
AnyStoredVec, AnyVec, EagerVec, Exit, PcoVec, PcoVecValue, ReadableVec, VecIndex, VecValue,
WritableVec,
@@ -209,41 +209,6 @@ pub trait ComputeDrawdown<I: VecIndex> {
f64: From<C> + From<A>;
}
impl<I> ComputeDrawdown<I> for EagerVec<PcoVec<I, StoredF32>>
where
I: VecIndex,
{
fn compute_drawdown<C, A>(
&mut self,
max_from: I,
current: &impl ReadableVec<I, C>,
ath: &impl ReadableVec<I, A>,
exit: &Exit,
) -> Result<()>
where
C: VecValue,
A: VecValue,
f64: From<C> + From<A>,
{
self.compute_transform2(
max_from,
current,
ath,
|(i, current, ath, _)| {
let ath_f64 = f64::from(ath);
let drawdown = if ath_f64 == 0.0 {
StoredF32::default()
} else {
StoredF32::from((f64::from(current) - ath_f64) / ath_f64 * 100.0)
};
(i, drawdown)
},
exit,
)?;
Ok(())
}
}
impl<I> ComputeDrawdown<I> for EagerVec<PcoVec<I, BasisPointsSigned16>>
where
I: VecIndex,