global: snapshot

This commit is contained in:
nym21
2026-03-03 22:10:05 +01:00
parent 28f6b0f18b
commit 269c1d5fdf
99 changed files with 1565 additions and 1146 deletions

View File

@@ -162,8 +162,8 @@ impl Vecs {
// Compute rolling window block counts (both block_count's own rolling + separate block_count_sum)
let ws = WindowStarts {
_24h: &self.height_24h_ago,
_7d: &self.height_1w_ago,
_30d: &self.height_1m_ago,
_1w: &self.height_1w_ago,
_1m: &self.height_1m_ago,
_1y: &self.height_1y_ago,
};
self.block_count.sum.compute_rolling_sum(

View File

@@ -59,12 +59,12 @@ pub struct Vecs<M: StorageMode = Rw> {
}
impl Vecs {
/// Get the standard 4 rolling window start heights (24h, 7d, 30d, 1y).
/// Get the standard 4 rolling window start heights (24h, 1w, 1m, 1y).
pub fn window_starts(&self) -> WindowStarts<'_> {
WindowStarts {
_24h: &self.height_24h_ago,
_7d: &self.height_1w_ago,
_30d: &self.height_1m_ago,
_1w: &self.height_1w_ago,
_1m: &self.height_1m_ago,
_1y: &self.height_1y_ago,
}
}

View File

@@ -26,8 +26,8 @@ impl Vecs {
exit,
)?;
// Compute difficulty adjustment percentage
self.adjustment.height.compute_percentage_change(
// Compute difficulty adjustment ratio: (current - previous) / previous
self.adjustment.bps.height.compute_ratio_change(
starting_indexes.height,
&indexer.vecs.blocks.difficulty,
1,

View File

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

View File

@@ -1,15 +1,15 @@
use brk_traversable::Traversable;
use brk_types::{DifficultyEpoch, StoredF32, StoredF64, StoredU32};
use brk_types::{BasisPointsSigned32, DifficultyEpoch, StoredF32, StoredF64, StoredU32};
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeight, ComputedHeightDerived};
use crate::internal::{ComputedFromHeight, ComputedHeightDerived, PercentFromHeight};
/// Difficulty metrics: raw difficulty, derived stats, adjustment, and countdown
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub raw: ComputedHeightDerived<StoredF64>,
pub as_hash: ComputedFromHeight<StoredF64, M>,
pub adjustment: ComputedFromHeight<StoredF32, M>,
pub adjustment: PercentFromHeight<BasisPointsSigned32, M>,
pub epoch: ComputedFromHeight<DifficultyEpoch, M>,
pub blocks_before_next_adjustment: ComputedFromHeight<StoredU32, M>,
pub days_before_next_adjustment: ComputedFromHeight<StoredF32, M>,

View File

@@ -1,5 +1,5 @@
use brk_error::Result;
use brk_types::{StoredF32, StoredF64};
use brk_types::{BasisPointsSigned32, StoredF64};
use vecdb::Exit;
use super::super::activity;
@@ -14,15 +14,15 @@ impl Vecs {
activity: &activity::Vecs,
exit: &Exit,
) -> Result<()> {
self.cointime_adj_inflation_rate.height.compute_transform2(
self.cointime_adj_inflation_rate.bps.height.compute_transform2(
starting_indexes.height,
&activity.activity_to_vaultedness_ratio.height,
&supply.inflation.height,
|(h, ratio, inflation, ..)| (h, StoredF32::from((*ratio) * f64::from(*inflation))),
&supply.inflation_rate.bps.height,
|(h, ratio, inflation, ..)| (h, BasisPointsSigned32::from((*ratio) * f64::from(inflation))),
exit,
)?;
self.cointime_adj_tx_btc_velocity
self.cointime_adj_tx_velocity_btc
.height
.compute_transform2(
starting_indexes.height,
@@ -32,7 +32,7 @@ impl Vecs {
exit,
)?;
self.cointime_adj_tx_usd_velocity
self.cointime_adj_tx_velocity_usd
.height
.compute_transform2(
starting_indexes.height,

View File

@@ -3,26 +3,29 @@ use brk_types::Version;
use vecdb::Database;
use super::Vecs;
use crate::{indexes, internal::ComputedFromHeight};
use crate::{
indexes,
internal::{Bps32ToFloat, Bps32ToPercent, ComputedFromHeight, PercentFromHeight},
};
impl Vecs {
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
Ok(Self {
cointime_adj_inflation_rate: ComputedFromHeight::forced_import(
cointime_adj_inflation_rate: PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
db,
"cointime_adj_inflation_rate",
version,
indexes,
)?,
cointime_adj_tx_btc_velocity: ComputedFromHeight::forced_import(
cointime_adj_tx_velocity_btc: ComputedFromHeight::forced_import(
db,
"cointime_adj_tx_btc_velocity",
"cointime_adj_tx_velocity_btc",
version,
indexes,
)?,
cointime_adj_tx_usd_velocity: ComputedFromHeight::forced_import(
cointime_adj_tx_velocity_usd: ComputedFromHeight::forced_import(
db,
"cointime_adj_tx_usd_velocity",
"cointime_adj_tx_velocity_usd",
version,
indexes,
)?,

View File

@@ -1,12 +1,12 @@
use brk_traversable::Traversable;
use brk_types::{StoredF32, StoredF64};
use brk_types::{BasisPointsSigned32, StoredF64};
use vecdb::{Rw, StorageMode};
use crate::internal::ComputedFromHeight;
use crate::internal::{ComputedFromHeight, PercentFromHeight};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub cointime_adj_inflation_rate: ComputedFromHeight<StoredF32, M>,
pub cointime_adj_tx_btc_velocity: ComputedFromHeight<StoredF64, M>,
pub cointime_adj_tx_usd_velocity: ComputedFromHeight<StoredF64, M>,
pub cointime_adj_inflation_rate: PercentFromHeight<BasisPointsSigned32, M>,
pub cointime_adj_tx_velocity_btc: ComputedFromHeight<StoredF64, M>,
pub cointime_adj_tx_velocity_usd: ComputedFromHeight<StoredF64, M>,
}

View File

@@ -14,7 +14,7 @@ impl Vecs {
value: &value::Vecs,
exit: &Exit,
) -> Result<()> {
self.vocdd_365d_median.compute_rolling_median_from_starts(
self.vocdd_median_1y.compute_rolling_median_from_starts(
starting_indexes.height,
&blocks.count.height_1y_ago,
&value.vocdd.height,
@@ -24,7 +24,7 @@ impl Vecs {
self.hodl_bank.compute_cumulative_transformed_binary(
starting_indexes.height,
&prices.price.usd.height,
&self.vocdd_365d_median,
&self.vocdd_median_1y,
|price, median| StoredF64::from(f64::from(price) - f64::from(median)),
exit,
)?;

View File

@@ -13,7 +13,7 @@ impl Vecs {
) -> Result<Self> {
let v1 = version + Version::ONE;
Ok(Self {
vocdd_365d_median: EagerVec::forced_import(db, "vocdd_365d_median", v1)?,
vocdd_median_1y: EagerVec::forced_import(db, "vocdd_median_1y", v1)?,
hodl_bank: EagerVec::forced_import(db, "hodl_bank", v1)?,
reserve_risk: ComputedFromHeight::forced_import(db, "reserve_risk", v1, indexes)?,
})

View File

@@ -6,7 +6,7 @@ use crate::internal::ComputedFromHeight;
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub vocdd_365d_median: M::Stored<EagerVec<PcoVec<Height, StoredF64>>>,
pub vocdd_median_1y: M::Stored<EagerVec<PcoVec<Height, StoredF64>>>,
pub hodl_bank: M::Stored<EagerVec<PcoVec<Height, StoredF64>>>,
pub reserve_risk: ComputedFromHeight<StoredF64, M>,
}

View File

@@ -11,12 +11,12 @@ use vecdb::{
use crate::{ComputeIndexes, blocks, indexes, internal::ComputedFromHeight};
/// Address count with 30d change metric for a single type.
/// Address count with 1m change metric for a single type.
#[derive(Traversable)]
pub struct AddrCountVecs<M: StorageMode = Rw> {
#[traversable(flatten)]
pub count: ComputedFromHeight<StoredU64, M>,
pub _30d_change: ComputedFromHeight<StoredF64, M>,
pub change_1m: ComputedFromHeight<StoredF64, M>,
}
impl AddrCountVecs {
@@ -28,9 +28,9 @@ impl AddrCountVecs {
) -> Result<Self> {
Ok(Self {
count: ComputedFromHeight::forced_import(db, name, version, indexes)?,
_30d_change: ComputedFromHeight::forced_import(
change_1m: ComputedFromHeight::forced_import(
db,
&format!("{name}_30d_change"),
&format!("{name}_change_1m"),
version,
indexes,
)?,
@@ -43,7 +43,7 @@ impl AddrCountVecs {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self._30d_change.height.compute_rolling_change(
self.change_1m.height.compute_rolling_change(
starting_indexes.height,
&blocks.count.height_1m_ago,
&self.count.height,
@@ -85,7 +85,7 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount {
}
}
/// Address count per address type, with height + derived indexes + 30d change.
/// Address count per address type, with height + derived indexes + 1m change.
#[derive(Deref, DerefMut, Traversable)]
pub struct AddressTypeToAddrCountVecs<M: StorageMode = Rw>(ByAddressType<AddrCountVecs<M>>);
@@ -290,7 +290,7 @@ impl AddrCountsVecs {
.height
.compute_sum_of_others(starting_indexes.height, &sources, exit)?;
self.all._30d_change.height.compute_rolling_change(
self.all.change_1m.height.compute_rolling_change(
starting_indexes.height,
&blocks.count.height_1m_ago,
&self.all.count.height,

View File

@@ -99,9 +99,9 @@ impl AddressCohorts {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
// 1. Compute addr_count_30d_change using rolling window
// 1. Compute addr_count_change_1m using rolling window
self.par_iter_mut().try_for_each(|v| {
v.addr_count_30d_change.height.compute_rolling_change(
v.addr_count_change_1m.height.compute_rolling_change(
starting_indexes.height,
&blocks.count.height_1m_ago,
&v.addr_count.height,

View File

@@ -36,7 +36,7 @@ pub struct AddressCohortVecs<M: StorageMode = Rw> {
pub metrics: BasicCohortMetrics<M>,
pub addr_count: ComputedFromHeight<StoredU64, M>,
pub addr_count_30d_change: ComputedFromHeight<StoredF64, M>,
pub addr_count_change_1m: ComputedFromHeight<StoredF64, M>,
}
impl AddressCohortVecs {
@@ -73,9 +73,9 @@ impl AddressCohortVecs {
version + VERSION,
indexes,
)?,
addr_count_30d_change: ComputedFromHeight::forced_import(
addr_count_change_1m: ComputedFromHeight::forced_import(
db,
&cfg.name("addr_count_30d_change"),
&cfg.name("addr_count_change_1m"),
version + VERSION,
indexes,
)?,

View File

@@ -6,7 +6,7 @@ use vecdb::{AnyStoredVec, AnyVec, EagerVec, Exit, ImportableVec, PcoVec, Rw, Sto
use crate::{
ComputeIndexes, blocks,
internal::{ComputedFromHeightCumulativeSum, ValueFromHeightCumulative, ValueFromHeight},
internal::{ComputedFromHeightCumulativeSum, RollingEmas2w, ValueFromHeightCumulative},
};
use super::ImportConfig;
@@ -18,7 +18,7 @@ pub struct ActivityMetrics<M: StorageMode = Rw> {
pub sent: ValueFromHeightCumulative<M>,
/// 14-day EMA of sent supply (sats, btc, usd)
pub sent_14d_ema: ValueFromHeight<M>,
pub sent_ema: RollingEmas2w<M>,
/// Satoshi-blocks destroyed (supply * blocks_old when spent)
pub satblocks_destroyed: M::Stored<EagerVec<PcoVec<Height, Sats>>>,
@@ -44,9 +44,9 @@ impl ActivityMetrics {
cfg.indexes,
)?,
sent_14d_ema: ValueFromHeight::forced_import(
sent_ema: RollingEmas2w::forced_import(
cfg.db,
&cfg.name("sent_14d_ema"),
&cfg.name("sent"),
cfg.version,
cfg.indexes,
)?,
@@ -166,7 +166,7 @@ impl ActivityMetrics {
let window_starts = blocks.count.window_starts();
// 14-day EMA of sent (sats and dollars)
self.sent_14d_ema.compute_ema(
self.sent_ema.compute(
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent.base.sats.height,

View File

@@ -26,13 +26,13 @@ impl CostBasisBase {
Ok(Self {
min: Price::forced_import(
cfg.db,
&cfg.name("min_cost_basis"),
&cfg.name("cost_basis_min"),
cfg.version,
cfg.indexes,
)?,
max: Price::forced_import(
cfg.db,
&cfg.name("max_cost_basis"),
&cfg.name("cost_basis_max"),
cfg.version,
cfg.indexes,
)?,

View File

@@ -12,7 +12,7 @@ use super::ImportConfig;
#[derive(Traversable)]
pub struct OutputsMetrics<M: StorageMode = Rw> {
pub utxo_count: ComputedFromHeight<StoredU64, M>,
pub utxo_count_30d_change: ComputedFromHeight<StoredF64, M>,
pub utxo_count_change_1m: ComputedFromHeight<StoredF64, M>,
}
impl OutputsMetrics {
@@ -25,9 +25,9 @@ impl OutputsMetrics {
cfg.version,
cfg.indexes,
)?,
utxo_count_30d_change: ComputedFromHeight::forced_import(
utxo_count_change_1m: ComputedFromHeight::forced_import(
cfg.db,
&cfg.name("utxo_count_30d_change"),
&cfg.name("utxo_count_change_1m"),
cfg.version,
cfg.indexes,
)?,
@@ -77,7 +77,7 @@ impl OutputsMetrics {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self.utxo_count_30d_change.height.compute_rolling_change(
self.utxo_count_change_1m.height.compute_rolling_change(
starting_indexes.height,
&blocks.count.height_1m_ago,
&self.utxo_count.height,

View File

@@ -5,7 +5,7 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use crate::{
ComputeIndexes, blocks,
internal::{ComputedFromHeight, RatioCents64, RollingEmas7d30d, RollingWindows},
internal::{ComputedFromHeight, RatioCents64, RollingEmas1w1m, RollingWindows},
};
use crate::distribution::metrics::ImportConfig;
@@ -23,7 +23,7 @@ pub struct RealizedAdjusted<M: StorageMode = Rw> {
// === Adjusted SOPR (rolling window ratios) ===
pub adjusted_sopr: RollingWindows<StoredF64, M>,
pub adjusted_sopr_ema: RollingEmas7d30d<StoredF64, M>,
pub adjusted_sopr_ema: RollingEmas1w1m<StoredF64, M>,
}
impl RealizedAdjusted {
@@ -52,7 +52,7 @@ impl RealizedAdjusted {
let adjusted_sopr = RollingWindows::forced_import(
cfg.db, &cfg.name("adjusted_sopr"), cfg.version + v1, cfg.indexes,
)?;
let adjusted_sopr_ema = RollingEmas7d30d::forced_import(
let adjusted_sopr_ema = RollingEmas1w1m::forced_import(
cfg.db, &cfg.name("adjusted_sopr_24h"), cfg.version + v1, cfg.indexes,
)?;

View File

@@ -1,6 +1,7 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
BasisPoints16, BasisPointsSigned16,
Bitcoin, Cents, CentsSats, CentsSigned, CentsSquaredSats, Dollars, Height, Sats, StoredF32, StoredF64, Version,
};
use vecdb::{
@@ -12,10 +13,13 @@ use crate::{
ComputeIndexes, blocks,
distribution::state::RealizedState,
internal::{
Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent,
CentsPlus, CentsUnsignedToDollars, ComputedFromHeightCumulative, ComputedFromHeight,
ComputedFromHeightRatio, NegCentsUnsignedToDollars, ValueFromHeightCumulative, LazyFromHeight,
PercentageCentsF32, PercentageCentsSignedCentsF32, PercentageCentsSignedDollarsF32, Price, RatioCents64,
RollingEmas7d30d, RollingWindows, Identity, ValueFromHeight,
ComputedFromHeightRatio, FiatFromHeight, NegCentsUnsignedToDollars, PercentFromHeight,
PercentRollingEmas1w1m, PercentRollingWindows, ValueFromHeightCumulative, LazyFromHeight,
Price,
RatioCentsBp16, RatioCentsSignedCentsBps16, RatioCentsSignedDollarsBps16, RatioCents64,
RollingEmas1w1m, RollingEmas2w, RollingWindows, Identity,
},
prices,
};
@@ -29,12 +33,12 @@ pub struct RealizedBase<M: StorageMode = Rw> {
pub realized_cap_cents: ComputedFromHeight<Cents, M>,
pub realized_cap: LazyFromHeight<Dollars, Cents>,
pub realized_price: Price<ComputedFromHeight<Cents, M>>,
pub realized_price_extra: ComputedFromHeightRatio<M>,
pub realized_cap_30d_delta: ComputedFromHeight<CentsSigned, M>,
pub realized_price_ratio: ComputedFromHeightRatio<M>,
pub realized_cap_change_1m: ComputedFromHeight<CentsSigned, M>,
// === Investor Price ===
pub investor_price: Price<ComputedFromHeight<Cents, M>>,
pub investor_price_extra: ComputedFromHeightRatio<M>,
pub investor_price_ratio: ComputedFromHeightRatio<M>,
// === Floor/Ceiling Price Bands ===
pub lower_price_band: Price<ComputedFromHeight<Cents, M>>,
@@ -49,21 +53,18 @@ pub struct RealizedBase<M: StorageMode = Rw> {
// === Realized Profit/Loss ===
pub realized_profit: ComputedFromHeightCumulative<Cents, M>,
pub realized_profit_7d_ema: ComputedFromHeight<Cents, M>,
pub realized_profit_ema_1w: ComputedFromHeight<Cents, M>,
pub realized_loss: ComputedFromHeightCumulative<Cents, M>,
pub realized_loss_7d_ema: ComputedFromHeight<Cents, M>,
pub realized_loss_ema_1w: ComputedFromHeight<Cents, M>,
pub neg_realized_loss: LazyFromHeight<Dollars, Cents>,
pub net_realized_pnl: ComputedFromHeightCumulative<CentsSigned, M>,
pub net_realized_pnl_7d_ema: ComputedFromHeight<CentsSigned, M>,
pub realized_value: ComputedFromHeight<Cents, M>,
pub net_realized_pnl_ema_1w: ComputedFromHeight<CentsSigned, M>,
pub gross_pnl: FiatFromHeight<Cents, M>,
// === Realized vs Realized Cap Ratios ===
pub realized_profit_rel_to_realized_cap: ComputedFromHeight<StoredF32, M>,
pub realized_loss_rel_to_realized_cap: ComputedFromHeight<StoredF32, M>,
pub net_realized_pnl_rel_to_realized_cap: ComputedFromHeight<StoredF32, M>,
// === Total Realized PnL ===
pub total_realized_pnl: LazyFromHeight<Dollars, Cents>,
pub realized_profit_rel_to_realized_cap: PercentFromHeight<BasisPoints16, M>,
pub realized_loss_rel_to_realized_cap: PercentFromHeight<BasisPoints16, M>,
pub net_realized_pnl_rel_to_realized_cap: PercentFromHeight<BasisPointsSigned16, M>,
// === Value Created/Destroyed Splits (stored) ===
pub profit_value_created: ComputedFromHeight<Cents, M>,
@@ -85,29 +86,29 @@ pub struct RealizedBase<M: StorageMode = Rw> {
// === SOPR (rolling window ratios) ===
pub sopr: RollingWindows<StoredF64, M>,
pub sopr_ema: RollingEmas7d30d<StoredF64, M>,
pub sopr_24h_ema: RollingEmas1w1m<StoredF64, M>,
// === Sell Side Risk ===
pub realized_value_sum: RollingWindows<Cents, M>,
pub sell_side_risk_ratio: RollingWindows<StoredF32, M>,
pub sell_side_risk_ratio_ema: RollingEmas7d30d<StoredF32, M>,
pub gross_pnl_sum: RollingWindows<Cents, M>,
pub sell_side_risk_ratio: PercentRollingWindows<BasisPoints16, M>,
pub sell_side_risk_ratio_24h_ema: PercentRollingEmas1w1m<BasisPoints16, M>,
// === Net Realized PnL Deltas ===
pub net_realized_pnl_cumulative_30d_delta: ComputedFromHeight<CentsSigned, M>,
pub net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap:
ComputedFromHeight<StoredF32, M>,
pub net_realized_pnl_cumulative_30d_delta_rel_to_market_cap:
ComputedFromHeight<StoredF32, M>,
pub net_pnl_change_1m: ComputedFromHeight<CentsSigned, M>,
pub net_pnl_change_1m_rel_to_realized_cap:
PercentFromHeight<BasisPointsSigned16, M>,
pub net_pnl_change_1m_rel_to_market_cap:
PercentFromHeight<BasisPointsSigned16, M>,
// === Peak Regret ===
pub peak_regret: ComputedFromHeightCumulative<Cents, M>,
pub peak_regret_rel_to_realized_cap: ComputedFromHeight<StoredF32, M>,
pub peak_regret_rel_to_realized_cap: PercentFromHeight<BasisPoints16, M>,
// === Sent in Profit/Loss ===
pub sent_in_profit: ValueFromHeightCumulative<M>,
pub sent_in_profit_14d_ema: ValueFromHeight<M>,
pub sent_in_profit_ema: RollingEmas2w<M>,
pub sent_in_loss: ValueFromHeightCumulative<M>,
pub sent_in_loss_14d_ema: ValueFromHeight<M>,
pub sent_in_loss_ema: RollingEmas2w<M>,
}
impl RealizedBase {
@@ -138,9 +139,9 @@ impl RealizedBase {
cfg.indexes,
)?;
let realized_profit_7d_ema = ComputedFromHeight::forced_import(
let realized_profit_ema_1w = ComputedFromHeight::forced_import(
cfg.db,
&cfg.name("realized_profit_7d_ema"),
&cfg.name("realized_profit_ema_1w"),
cfg.version,
cfg.indexes,
)?;
@@ -152,9 +153,9 @@ impl RealizedBase {
cfg.indexes,
)?;
let realized_loss_7d_ema = ComputedFromHeight::forced_import(
let realized_loss_ema_1w = ComputedFromHeight::forced_import(
cfg.db,
&cfg.name("realized_loss_7d_ema"),
&cfg.name("realized_loss_ema_1w"),
cfg.version,
cfg.indexes,
)?;
@@ -173,9 +174,9 @@ impl RealizedBase {
cfg.indexes,
)?;
let net_realized_pnl_7d_ema = ComputedFromHeight::forced_import(
let net_realized_pnl_ema_1w = ComputedFromHeight::forced_import(
cfg.db,
&cfg.name("net_realized_pnl_7d_ema"),
&cfg.name("net_realized_pnl_ema_1w"),
cfg.version,
cfg.indexes,
)?;
@@ -187,40 +188,36 @@ impl RealizedBase {
cfg.indexes,
)?;
let realized_value = ComputedFromHeight::forced_import(
let gross_pnl = FiatFromHeight::forced_import(
cfg.db,
&cfg.name("realized_value"),
&cfg.name("gross_pnl"),
cfg.version,
cfg.indexes,
)?;
let total_realized_pnl = LazyFromHeight::from_computed::<CentsUnsignedToDollars>(
&cfg.name("total_realized_pnl"),
cfg.version + v1,
realized_value.height.read_only_boxed_clone(),
&realized_value,
);
let realized_profit_rel_to_realized_cap =
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db,
&cfg.name("realized_profit_rel_to_realized_cap"),
cfg.version + v1,
cfg.indexes,
)?;
let realized_profit_rel_to_realized_cap = ComputedFromHeight::forced_import(
cfg.db,
&cfg.name("realized_profit_rel_to_realized_cap"),
cfg.version + v1,
cfg.indexes,
)?;
let realized_loss_rel_to_realized_cap =
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db,
&cfg.name("realized_loss_rel_to_realized_cap"),
cfg.version + v1,
cfg.indexes,
)?;
let realized_loss_rel_to_realized_cap = ComputedFromHeight::forced_import(
cfg.db,
&cfg.name("realized_loss_rel_to_realized_cap"),
cfg.version + v1,
cfg.indexes,
)?;
let net_realized_pnl_rel_to_realized_cap = ComputedFromHeight::forced_import(
cfg.db,
&cfg.name("net_realized_pnl_rel_to_realized_cap"),
cfg.version + v1,
cfg.indexes,
)?;
let net_realized_pnl_rel_to_realized_cap =
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
cfg.db,
&cfg.name("net_realized_pnl_rel_to_realized_cap"),
cfg.version + v1,
cfg.indexes,
)?;
let realized_price = Price::forced_import(
cfg.db,
@@ -236,7 +233,7 @@ impl RealizedBase {
cfg.indexes,
)?;
let investor_price_extra = ComputedFromHeightRatio::forced_import(
let investor_price_ratio = ComputedFromHeightRatio::forced_import(
cfg.db,
&cfg.name("investor_price"),
cfg.version,
@@ -312,7 +309,7 @@ impl RealizedBase {
&profit_value_destroyed,
);
let realized_price_extra = ComputedFromHeightRatio::forced_import(
let realized_price_ratio = ComputedFromHeightRatio::forced_import(
cfg.db,
&cfg.name("realized_price"),
cfg.version + v1,
@@ -322,8 +319,8 @@ impl RealizedBase {
let mvrv = LazyFromHeight::from_computed::<Identity<StoredF32>>(
&cfg.name("mvrv"),
cfg.version,
realized_price_extra.ratio.height.read_only_boxed_clone(),
&realized_price_extra.ratio,
realized_price_ratio.ratio.height.read_only_boxed_clone(),
&realized_price_ratio.ratio,
);
// === Rolling windows ===
@@ -333,61 +330,61 @@ impl RealizedBase {
let value_destroyed_sum = RollingWindows::forced_import(
cfg.db, &cfg.name("value_destroyed"), cfg.version + v1, cfg.indexes,
)?;
let realized_value_sum = RollingWindows::forced_import(
cfg.db, &cfg.name("realized_value"), cfg.version + v1, cfg.indexes,
let gross_pnl_sum = RollingWindows::forced_import(
cfg.db, &cfg.name("gross_pnl_sum"), cfg.version + v1, cfg.indexes,
)?;
let sopr = RollingWindows::forced_import(
cfg.db, &cfg.name("sopr"), cfg.version + v1, cfg.indexes,
)?;
let sell_side_risk_ratio = RollingWindows::forced_import(
let sell_side_risk_ratio = PercentRollingWindows::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db, &cfg.name("sell_side_risk_ratio"), cfg.version + v1, cfg.indexes,
)?;
// === EMA imports ===
let sopr_ema = RollingEmas7d30d::forced_import(
let sopr_24h_ema = RollingEmas1w1m::forced_import(
cfg.db, &cfg.name("sopr_24h"), cfg.version + v1, cfg.indexes,
)?;
let sell_side_risk_ratio_ema = RollingEmas7d30d::forced_import(
let sell_side_risk_ratio_24h_ema = PercentRollingEmas1w1m::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db, &cfg.name("sell_side_risk_ratio_24h"), cfg.version + v1, cfg.indexes,
)?;
let peak_regret_rel_to_realized_cap = ComputedFromHeight::forced_import(
cfg.db,
&cfg.name("peak_regret_rel_to_realized_cap"),
cfg.version + v1,
cfg.indexes,
)?;
let peak_regret_rel_to_realized_cap =
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db,
&cfg.name("realized_peak_regret_rel_to_realized_cap"),
cfg.version + v1,
cfg.indexes,
)?;
Ok(Self {
realized_cap_cents,
realized_cap,
realized_price,
realized_price_extra,
realized_cap_30d_delta: ComputedFromHeight::forced_import(
realized_price_ratio,
realized_cap_change_1m: ComputedFromHeight::forced_import(
cfg.db,
&cfg.name("realized_cap_30d_delta"),
&cfg.name("realized_cap_change_1m"),
cfg.version,
cfg.indexes,
)?,
investor_price,
investor_price_extra,
investor_price_ratio,
lower_price_band,
upper_price_band,
cap_raw,
investor_cap_raw,
mvrv,
realized_profit,
realized_profit_7d_ema,
realized_profit_ema_1w,
realized_loss,
realized_loss_7d_ema,
realized_loss_ema_1w,
neg_realized_loss,
net_realized_pnl,
net_realized_pnl_7d_ema,
realized_value,
net_realized_pnl_ema_1w,
gross_pnl,
realized_profit_rel_to_realized_cap,
realized_loss_rel_to_realized_cap,
net_realized_pnl_rel_to_realized_cap,
total_realized_pnl,
profit_value_created,
profit_value_destroyed,
loss_value_created,
@@ -399,27 +396,27 @@ impl RealizedBase {
value_created_sum,
value_destroyed_sum,
sopr,
sopr_ema,
realized_value_sum,
sopr_24h_ema,
gross_pnl_sum,
sell_side_risk_ratio,
sell_side_risk_ratio_ema,
net_realized_pnl_cumulative_30d_delta: ComputedFromHeight::forced_import(
sell_side_risk_ratio_24h_ema,
net_pnl_change_1m: ComputedFromHeight::forced_import(
cfg.db,
&cfg.name("net_realized_pnl_cumulative_30d_delta"),
&cfg.name("net_pnl_change_1m"),
cfg.version + v3,
cfg.indexes,
)?,
net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap:
ComputedFromHeight::forced_import(
net_pnl_change_1m_rel_to_realized_cap:
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
cfg.db,
&cfg.name("net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap"),
&cfg.name("net_pnl_change_1m_rel_to_realized_cap"),
cfg.version + v3,
cfg.indexes,
)?,
net_realized_pnl_cumulative_30d_delta_rel_to_market_cap:
ComputedFromHeight::forced_import(
net_pnl_change_1m_rel_to_market_cap:
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
cfg.db,
&cfg.name("net_realized_pnl_cumulative_30d_delta_rel_to_market_cap"),
&cfg.name("net_pnl_change_1m_rel_to_market_cap"),
cfg.version + v3,
cfg.indexes,
)?,
@@ -431,9 +428,9 @@ impl RealizedBase {
cfg.version,
cfg.indexes,
)?,
sent_in_profit_14d_ema: ValueFromHeight::forced_import(
sent_in_profit_ema: RollingEmas2w::forced_import(
cfg.db,
&cfg.name("sent_in_profit_14d_ema"),
&cfg.name("sent_in_profit"),
cfg.version,
cfg.indexes,
)?,
@@ -443,9 +440,9 @@ impl RealizedBase {
cfg.version,
cfg.indexes,
)?,
sent_in_loss_14d_ema: ValueFromHeight::forced_import(
sent_in_loss_ema: RollingEmas2w::forced_import(
cfg.db,
&cfg.name("sent_in_loss_14d_ema"),
&cfg.name("sent_in_loss"),
cfg.version,
cfg.indexes,
)?,
@@ -710,7 +707,7 @@ impl RealizedBase {
Ok(())
})?;
self.realized_value.height.compute_add(
self.gross_pnl.cents.height.compute_add(
starting_indexes.height,
&self.realized_profit.height,
&self.realized_loss.height,
@@ -750,14 +747,14 @@ impl RealizedBase {
exit,
)?;
self.realized_price_extra.compute_ratio(
self.realized_price_ratio.compute_ratio(
starting_indexes,
&prices.price.cents.height,
&self.realized_price.cents.height,
exit,
)?;
self.investor_price_extra.compute_ratio(
self.investor_price_ratio.compute_ratio(
starting_indexes,
&prices.price.cents.height,
&self.investor_price.cents.height,
@@ -796,7 +793,7 @@ impl RealizedBase {
exit,
)?;
self.realized_cap_30d_delta.height.compute_rolling_change(
self.realized_cap_change_1m.height.compute_rolling_change(
starting_indexes.height,
&blocks.count.height_1m_ago,
&self.realized_cap_cents.height,
@@ -827,8 +824,8 @@ impl RealizedBase {
self.value_destroyed_sum.compute_rolling_sum(
starting_indexes.height, &window_starts, &self.value_destroyed.height, exit,
)?;
self.realized_value_sum.compute_rolling_sum(
starting_indexes.height, &window_starts, &self.realized_value.height, exit,
self.gross_pnl_sum.compute_rolling_sum(
starting_indexes.height, &window_starts, &self.gross_pnl.cents.height, exit,
)?;
// Compute SOPR from rolling sums
@@ -843,27 +840,27 @@ impl RealizedBase {
// Compute sell-side risk ratios
for (ssrr, rv) in self.sell_side_risk_ratio.as_mut_array().into_iter()
.zip(self.realized_value_sum.as_array())
.zip(self.gross_pnl_sum.as_array())
{
ssrr.compute_binary::<Cents, Cents, PercentageCentsF32>(
ssrr.compute_binary::<Cents, Cents, RatioCentsBp16>(
starting_indexes.height, &rv.height, &self.realized_cap_cents.height, exit,
)?;
}
// 7d EMAs
self.realized_profit_7d_ema.height.compute_rolling_ema(
self.realized_profit_ema_1w.height.compute_rolling_ema(
starting_indexes.height,
&blocks.count.height_1w_ago,
&self.realized_profit.height,
exit,
)?;
self.realized_loss_7d_ema.height.compute_rolling_ema(
self.realized_loss_ema_1w.height.compute_rolling_ema(
starting_indexes.height,
&blocks.count.height_1w_ago,
&self.realized_loss.height,
exit,
)?;
self.net_realized_pnl_7d_ema
self.net_realized_pnl_ema_1w
.height
.compute_rolling_ema(
starting_indexes.height,
@@ -873,14 +870,14 @@ impl RealizedBase {
)?;
// 14-day EMA of sent in profit/loss
self.sent_in_profit_14d_ema.compute_ema(
self.sent_in_profit_ema.compute(
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent_in_profit.base.sats.height,
&self.sent_in_profit.base.cents.height,
exit,
)?;
self.sent_in_loss_14d_ema.compute_ema(
self.sent_in_loss_ema.compute(
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent_in_loss.base.sats.height,
@@ -889,7 +886,7 @@ impl RealizedBase {
)?;
// SOPR EMAs (based on 24h window)
self.sopr_ema.compute_from_24h(
self.sopr_24h_ema.compute_from_24h(
starting_indexes.height,
&blocks.count.height_1w_ago,
&blocks.count.height_1m_ago,
@@ -898,38 +895,38 @@ impl RealizedBase {
)?;
// Sell side risk EMAs (based on 24h window)
self.sell_side_risk_ratio_ema.compute_from_24h(
self.sell_side_risk_ratio_24h_ema.compute_from_24h(
starting_indexes.height,
&blocks.count.height_1w_ago,
&blocks.count.height_1m_ago,
&self.sell_side_risk_ratio._24h.height,
&self.sell_side_risk_ratio._24h.bps.height,
exit,
)?;
// Realized profit/loss/net relative to realized cap
self.realized_profit_rel_to_realized_cap
.compute_binary::<Cents, Cents, PercentageCentsF32>(
.compute_binary::<Cents, Cents, RatioCentsBp16>(
starting_indexes.height,
&self.realized_profit.height,
&self.realized_cap_cents.height,
exit,
)?;
self.realized_loss_rel_to_realized_cap
.compute_binary::<Cents, Cents, PercentageCentsF32>(
.compute_binary::<Cents, Cents, RatioCentsBp16>(
starting_indexes.height,
&self.realized_loss.height,
&self.realized_cap_cents.height,
exit,
)?;
self.net_realized_pnl_rel_to_realized_cap
.compute_binary::<CentsSigned, Cents, PercentageCentsSignedCentsF32>(
.compute_binary::<CentsSigned, Cents, RatioCentsSignedCentsBps16>(
starting_indexes.height,
&self.net_realized_pnl.height,
&self.realized_cap_cents.height,
exit,
)?;
self.peak_regret_rel_to_realized_cap
.compute_binary::<Cents, Cents, PercentageCentsF32>(
.compute_binary::<Cents, Cents, RatioCentsBp16>(
starting_indexes.height,
&self.peak_regret.height,
&self.realized_cap_cents.height,
@@ -937,7 +934,7 @@ impl RealizedBase {
)?;
// Net realized PnL cumulative 30d delta
self.net_realized_pnl_cumulative_30d_delta
self.net_pnl_change_1m
.height
.compute_rolling_change(
starting_indexes.height,
@@ -946,18 +943,18 @@ impl RealizedBase {
exit,
)?;
self.net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap
.compute_binary::<CentsSigned, Cents, PercentageCentsSignedCentsF32>(
self.net_pnl_change_1m_rel_to_realized_cap
.compute_binary::<CentsSigned, Cents, RatioCentsSignedCentsBps16>(
starting_indexes.height,
&self.net_realized_pnl_cumulative_30d_delta.height,
&self.net_pnl_change_1m.height,
&self.realized_cap_cents.height,
exit,
)?;
self.net_realized_pnl_cumulative_30d_delta_rel_to_market_cap
.compute_binary::<CentsSigned, Dollars, PercentageCentsSignedDollarsF32>(
self.net_pnl_change_1m_rel_to_market_cap
.compute_binary::<CentsSigned, Dollars, RatioCentsSignedDollarsBps16>(
starting_indexes.height,
&self.net_realized_pnl_cumulative_30d_delta.height,
&self.net_pnl_change_1m.height,
height_to_market_cap,
exit,
)?;

View File

@@ -1,11 +1,14 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Cents, Dollars, Height, StoredF32, StoredF64, Version};
use brk_types::{BasisPoints16, Cents, Dollars, Height, StoredF64, Version};
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use crate::{
ComputeIndexes, blocks,
internal::{ComputedFromHeight, ComputedFromHeightRatioExtension, RatioCents64, RollingWindows},
internal::{
Bp16ToFloat, Bp16ToPercent, ComputedFromHeightRatioExtension, PercentFromHeight,
RatioCents64, RatioDollarsBp16, RollingWindows,
},
};
use crate::distribution::metrics::ImportConfig;
@@ -15,7 +18,7 @@ use super::RealizedBase;
/// Extended realized metrics (only for extended cohorts: all, sth, lth, age_range).
#[derive(Traversable)]
pub struct RealizedExtended<M: StorageMode = Rw> {
pub realized_cap_rel_to_own_market_cap: ComputedFromHeight<StoredF32, M>,
pub realized_cap_rel_to_own_market_cap: PercentFromHeight<BasisPoints16, M>,
// === Realized Profit/Loss Rolling Sums ===
pub realized_profit_sum: RollingWindows<Cents, M>,
@@ -34,7 +37,7 @@ impl RealizedExtended {
let v1 = Version::ONE;
Ok(RealizedExtended {
realized_cap_rel_to_own_market_cap: ComputedFromHeight::forced_import(
realized_cap_rel_to_own_market_cap: PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db,
&cfg.name("realized_cap_rel_to_own_market_cap"),
cfg.version,
@@ -84,8 +87,7 @@ impl RealizedExtended {
// Realized cap relative to own market cap
self.realized_cap_rel_to_own_market_cap
.height
.compute_percentage(
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
starting_indexes.height,
&base.realized_cap.height,
height_to_market_cap,
@@ -107,7 +109,7 @@ impl RealizedExtended {
blocks,
starting_indexes,
exit,
&base.realized_price_extra.ratio.height,
&base.realized_price_ratio.ratio.height,
)?;
self.realized_price_ratio_ext.compute_cents_bands(
starting_indexes,
@@ -119,7 +121,7 @@ impl RealizedExtended {
blocks,
starting_indexes,
exit,
&base.investor_price_extra.ratio.height,
&base.investor_price_ratio.ratio.height,
)?;
self.investor_price_ratio_ext.compute_cents_bands(
starting_indexes,

View File

@@ -1,11 +1,11 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Sats, StoredF32, StoredF64, Version};
use brk_types::{BasisPoints16, BasisPointsSigned16, Dollars, Height, Sats, StoredF32, Version};
use vecdb::{Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
use crate::internal::{
ComputedFromHeight, Identity, LazyFromHeight,
NegPercentageDollarsF32, PercentageDollarsF32, PercentageSatsF64,
Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent, LazyFromHeight,
NegRatioDollarsBps16, PercentFromHeight, RatioDollarsBp16, RatioDollarsBps16, RatioSatsBp16,
};
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase};
@@ -16,19 +16,19 @@ use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase};
#[derive(Traversable)]
pub struct RelativeBase<M: StorageMode = Rw> {
// === Supply in Profit/Loss Relative to Own Supply ===
pub supply_in_profit_rel_to_own_supply: ComputedFromHeight<StoredF64, M>,
pub supply_in_loss_rel_to_own_supply: ComputedFromHeight<StoredF64, M>,
pub supply_in_profit_rel_to_own_supply: PercentFromHeight<BasisPoints16, M>,
pub supply_in_loss_rel_to_own_supply: PercentFromHeight<BasisPoints16, M>,
// === Unrealized vs Market Cap ===
pub unrealized_profit_rel_to_market_cap: ComputedFromHeight<StoredF32, M>,
pub unrealized_loss_rel_to_market_cap: ComputedFromHeight<StoredF32, M>,
pub neg_unrealized_loss_rel_to_market_cap: ComputedFromHeight<StoredF32, M>,
pub net_unrealized_pnl_rel_to_market_cap: ComputedFromHeight<StoredF32, M>,
pub nupl: LazyFromHeight<StoredF32, StoredF32>,
pub unrealized_profit_rel_to_market_cap: PercentFromHeight<BasisPoints16, M>,
pub unrealized_loss_rel_to_market_cap: PercentFromHeight<BasisPoints16, M>,
pub neg_unrealized_loss_rel_to_market_cap: PercentFromHeight<BasisPointsSigned16, M>,
pub net_unrealized_pnl_rel_to_market_cap: PercentFromHeight<BasisPointsSigned16, M>,
pub nupl: LazyFromHeight<StoredF32, BasisPointsSigned16>,
// === Invested Capital in Profit/Loss as % of Realized Cap ===
pub invested_capital_in_profit_pct: ComputedFromHeight<StoredF32, M>,
pub invested_capital_in_loss_pct: ComputedFromHeight<StoredF32, M>,
pub invested_capital_in_profit_rel_to_realized_cap: PercentFromHeight<BasisPoints16, M>,
pub invested_capital_in_loss_rel_to_realized_cap: PercentFromHeight<BasisPoints16, M>,
}
impl RelativeBase {
@@ -36,41 +36,49 @@ impl RelativeBase {
let v1 = Version::ONE;
let v2 = Version::new(2);
let net_unrealized_pnl_rel_to_market_cap = ComputedFromHeight::forced_import(
cfg.db, &cfg.name("net_unrealized_pnl_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
)?;
let net_unrealized_pnl_rel_to_market_cap =
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
cfg.db, &cfg.name("net_unrealized_pnl_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
)?;
let nupl = LazyFromHeight::from_computed::<Identity<StoredF32>>(
let nupl = LazyFromHeight::from_computed::<Bps16ToFloat>(
&cfg.name("nupl"),
cfg.version + v2,
net_unrealized_pnl_rel_to_market_cap.height.read_only_boxed_clone(),
&net_unrealized_pnl_rel_to_market_cap,
net_unrealized_pnl_rel_to_market_cap.bps.height.read_only_boxed_clone(),
&net_unrealized_pnl_rel_to_market_cap.bps,
);
Ok(Self {
supply_in_profit_rel_to_own_supply: ComputedFromHeight::forced_import(
cfg.db, &cfg.name("supply_in_profit_rel_to_own_supply"), cfg.version + v1, cfg.indexes,
)?,
supply_in_loss_rel_to_own_supply: ComputedFromHeight::forced_import(
cfg.db, &cfg.name("supply_in_loss_rel_to_own_supply"), cfg.version + v1, cfg.indexes,
)?,
unrealized_profit_rel_to_market_cap: ComputedFromHeight::forced_import(
cfg.db, &cfg.name("unrealized_profit_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
)?,
unrealized_loss_rel_to_market_cap: ComputedFromHeight::forced_import(
cfg.db, &cfg.name("unrealized_loss_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
)?,
neg_unrealized_loss_rel_to_market_cap: ComputedFromHeight::forced_import(
cfg.db, &cfg.name("neg_unrealized_loss_rel_to_market_cap"), cfg.version + v2, cfg.indexes,
)?,
supply_in_profit_rel_to_own_supply:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
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>(
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>(
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>(
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>(
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_pct: ComputedFromHeight::forced_import(
cfg.db, &cfg.name("invested_capital_in_profit_pct"), cfg.version, cfg.indexes,
)?,
invested_capital_in_loss_pct: ComputedFromHeight::forced_import(
cfg.db, &cfg.name("invested_capital_in_loss_pct"), cfg.version, cfg.indexes,
)?,
invested_capital_in_profit_rel_to_realized_cap:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
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>(
cfg.db, &cfg.name("invested_capital_in_loss_rel_to_realized_cap"), cfg.version, cfg.indexes,
)?,
})
}
@@ -84,35 +92,35 @@ impl RelativeBase {
exit: &Exit,
) -> Result<()> {
self.supply_in_profit_rel_to_own_supply
.compute_binary::<Sats, Sats, PercentageSatsF64>(
.compute_binary::<Sats, Sats, RatioSatsBp16>(
max_from, &unrealized.supply_in_profit.sats.height, supply_total_sats, exit,
)?;
self.supply_in_loss_rel_to_own_supply
.compute_binary::<Sats, Sats, PercentageSatsF64>(
.compute_binary::<Sats, Sats, RatioSatsBp16>(
max_from, &unrealized.supply_in_loss.sats.height, supply_total_sats, exit,
)?;
self.unrealized_profit_rel_to_market_cap
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
max_from, &unrealized.unrealized_profit.usd.height, market_cap, exit,
)?;
self.unrealized_loss_rel_to_market_cap
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
max_from, &unrealized.unrealized_loss.usd.height, market_cap, exit,
)?;
self.neg_unrealized_loss_rel_to_market_cap
.compute_binary::<Dollars, Dollars, NegPercentageDollarsF32>(
.compute_binary::<Dollars, Dollars, NegRatioDollarsBps16>(
max_from, &unrealized.unrealized_loss.usd.height, market_cap, exit,
)?;
self.net_unrealized_pnl_rel_to_market_cap
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Dollars, Dollars, RatioDollarsBps16>(
max_from, &unrealized.net_unrealized_pnl.usd.height, market_cap, exit,
)?;
self.invested_capital_in_profit_pct
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
self.invested_capital_in_profit_rel_to_realized_cap
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
max_from, &unrealized.invested_capital_in_profit.usd.height, &realized.realized_cap.height, exit,
)?;
self.invested_capital_in_loss_pct
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
self.invested_capital_in_loss_rel_to_realized_cap
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
max_from, &unrealized.invested_capital_in_loss.usd.height, &realized.realized_cap.height, exit,
)?;
Ok(())

View File

@@ -1,10 +1,11 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, StoredF32};
use brk_types::{BasisPoints16, BasisPointsSigned16, Dollars, Height};
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use crate::internal::{
ComputedFromHeight, NegPercentageDollarsF32, PercentageDollarsF32,
Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent,
NegRatioDollarsBps16, PercentFromHeight, RatioDollarsBp16, RatioDollarsBps16,
};
use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
@@ -13,13 +14,13 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
#[derive(Traversable)]
pub struct RelativeExtendedOwnMarketCap<M: StorageMode = Rw> {
pub unrealized_profit_rel_to_own_market_cap:
ComputedFromHeight<StoredF32, M>,
PercentFromHeight<BasisPoints16, M>,
pub unrealized_loss_rel_to_own_market_cap:
ComputedFromHeight<StoredF32, M>,
PercentFromHeight<BasisPoints16, M>,
pub neg_unrealized_loss_rel_to_own_market_cap:
ComputedFromHeight<StoredF32, M>,
PercentFromHeight<BasisPointsSigned16, M>,
pub net_unrealized_pnl_rel_to_own_market_cap:
ComputedFromHeight<StoredF32, M>,
PercentFromHeight<BasisPointsSigned16, M>,
}
impl RelativeExtendedOwnMarketCap {
@@ -30,28 +31,28 @@ impl RelativeExtendedOwnMarketCap {
Ok(Self {
unrealized_profit_rel_to_own_market_cap:
ComputedFromHeight::forced_import(
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db,
&cfg.name("unrealized_profit_rel_to_own_market_cap"),
cfg.version + v2,
cfg.indexes,
)?,
unrealized_loss_rel_to_own_market_cap:
ComputedFromHeight::forced_import(
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db,
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
cfg.version + v2,
cfg.indexes,
)?,
neg_unrealized_loss_rel_to_own_market_cap:
ComputedFromHeight::forced_import(
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
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:
ComputedFromHeight::forced_import(
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
cfg.db,
&cfg.name("net_unrealized_pnl_rel_to_own_market_cap"),
cfg.version + v2,
@@ -68,19 +69,19 @@ impl RelativeExtendedOwnMarketCap {
exit: &Exit,
) -> Result<()> {
self.unrealized_profit_rel_to_own_market_cap
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
max_from, &unrealized.unrealized_profit.usd.height, own_market_cap, exit,
)?;
self.unrealized_loss_rel_to_own_market_cap
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
max_from, &unrealized.unrealized_loss.usd.height, own_market_cap, exit,
)?;
self.neg_unrealized_loss_rel_to_own_market_cap
.compute_binary::<Dollars, Dollars, NegPercentageDollarsF32>(
.compute_binary::<Dollars, Dollars, NegRatioDollarsBps16>(
max_from, &unrealized.unrealized_loss.usd.height, own_market_cap, exit,
)?;
self.net_unrealized_pnl_rel_to_own_market_cap
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Dollars, Dollars, RatioDollarsBps16>(
max_from, &unrealized.net_unrealized_pnl.usd.height, own_market_cap, exit,
)?;
Ok(())

View File

@@ -1,10 +1,11 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, StoredF32};
use brk_types::{BasisPoints16, BasisPointsSigned16, Dollars, Height};
use vecdb::{Exit, Rw, StorageMode};
use crate::internal::{
ComputedFromHeight, NegPercentageDollarsF32, PercentageDollarsF32,
Bp16ToFloat, Bp16ToPercent, Bps16ToFloat, Bps16ToPercent,
NegRatioDollarsBps16, PercentFromHeight, RatioDollarsBp16, RatioDollarsBps16,
};
use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
@@ -12,14 +13,14 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
/// Extended relative metrics for own total unrealized PnL (extended only).
#[derive(Traversable)]
pub struct RelativeExtendedOwnPnl<M: StorageMode = Rw> {
pub unrealized_profit_rel_to_own_total_unrealized_pnl:
ComputedFromHeight<StoredF32, M>,
pub unrealized_loss_rel_to_own_total_unrealized_pnl:
ComputedFromHeight<StoredF32, M>,
pub neg_unrealized_loss_rel_to_own_total_unrealized_pnl:
ComputedFromHeight<StoredF32, M>,
pub net_unrealized_pnl_rel_to_own_total_unrealized_pnl:
ComputedFromHeight<StoredF32, M>,
pub unrealized_profit_rel_to_own_gross_pnl:
PercentFromHeight<BasisPoints16, M>,
pub unrealized_loss_rel_to_own_gross_pnl:
PercentFromHeight<BasisPoints16, M>,
pub neg_unrealized_loss_rel_to_own_gross_pnl:
PercentFromHeight<BasisPointsSigned16, M>,
pub net_unrealized_pnl_rel_to_own_gross_pnl:
PercentFromHeight<BasisPointsSigned16, M>,
}
impl RelativeExtendedOwnPnl {
@@ -30,31 +31,31 @@ impl RelativeExtendedOwnPnl {
let v2 = brk_types::Version::new(2);
Ok(Self {
unrealized_profit_rel_to_own_total_unrealized_pnl:
ComputedFromHeight::forced_import(
unrealized_profit_rel_to_own_gross_pnl:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db,
&cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"),
&cfg.name("unrealized_profit_rel_to_own_gross_pnl"),
cfg.version + v1,
cfg.indexes,
)?,
unrealized_loss_rel_to_own_total_unrealized_pnl:
ComputedFromHeight::forced_import(
unrealized_loss_rel_to_own_gross_pnl:
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db,
&cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"),
&cfg.name("unrealized_loss_rel_to_own_gross_pnl"),
cfg.version + v1,
cfg.indexes,
)?,
neg_unrealized_loss_rel_to_own_total_unrealized_pnl:
ComputedFromHeight::forced_import(
neg_unrealized_loss_rel_to_own_gross_pnl:
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
cfg.db,
&cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"),
&cfg.name("neg_unrealized_loss_rel_to_own_gross_pnl"),
cfg.version + v1,
cfg.indexes,
)?,
net_unrealized_pnl_rel_to_own_total_unrealized_pnl:
ComputedFromHeight::forced_import(
net_unrealized_pnl_rel_to_own_gross_pnl:
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
cfg.db,
&cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"),
&cfg.name("net_unrealized_pnl_rel_to_own_gross_pnl"),
cfg.version + v2,
cfg.indexes,
)?,
@@ -67,21 +68,21 @@ impl RelativeExtendedOwnPnl {
unrealized: &UnrealizedBase,
exit: &Exit,
) -> Result<()> {
self.unrealized_profit_rel_to_own_total_unrealized_pnl
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
max_from, &unrealized.unrealized_profit.usd.height, &unrealized.total_unrealized_pnl.usd.height, exit,
self.unrealized_profit_rel_to_own_gross_pnl
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
max_from, &unrealized.unrealized_profit.usd.height, &unrealized.gross_pnl.usd.height, exit,
)?;
self.unrealized_loss_rel_to_own_total_unrealized_pnl
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
max_from, &unrealized.unrealized_loss.usd.height, &unrealized.total_unrealized_pnl.usd.height, exit,
self.unrealized_loss_rel_to_own_gross_pnl
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
max_from, &unrealized.unrealized_loss.usd.height, &unrealized.gross_pnl.usd.height, exit,
)?;
self.neg_unrealized_loss_rel_to_own_total_unrealized_pnl
.compute_binary::<Dollars, Dollars, NegPercentageDollarsF32>(
max_from, &unrealized.unrealized_loss.usd.height, &unrealized.total_unrealized_pnl.usd.height, exit,
self.neg_unrealized_loss_rel_to_own_gross_pnl
.compute_binary::<Dollars, Dollars, NegRatioDollarsBps16>(
max_from, &unrealized.unrealized_loss.usd.height, &unrealized.gross_pnl.usd.height, exit,
)?;
self.net_unrealized_pnl_rel_to_own_total_unrealized_pnl
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
max_from, &unrealized.net_unrealized_pnl.usd.height, &unrealized.total_unrealized_pnl.usd.height, exit,
self.net_unrealized_pnl_rel_to_own_gross_pnl
.compute_binary::<Dollars, Dollars, RatioDollarsBps16>(
max_from, &unrealized.net_unrealized_pnl.usd.height, &unrealized.gross_pnl.usd.height, exit,
)?;
Ok(())
}

View File

@@ -1,9 +1,9 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Sats, StoredF64};
use brk_types::{BasisPoints16, Height, Sats};
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use crate::internal::{ComputedFromHeight, PercentageSatsF64};
use crate::internal::{Bp16ToFloat, Bp16ToPercent, PercentFromHeight, RatioSatsBp16};
use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
@@ -11,11 +11,11 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
#[derive(Traversable)]
pub struct RelativeToAll<M: StorageMode = Rw> {
pub supply_rel_to_circulating_supply:
ComputedFromHeight<StoredF64, M>,
PercentFromHeight<BasisPoints16, M>,
pub supply_in_profit_rel_to_circulating_supply:
ComputedFromHeight<StoredF64, M>,
PercentFromHeight<BasisPoints16, M>,
pub supply_in_loss_rel_to_circulating_supply:
ComputedFromHeight<StoredF64, M>,
PercentFromHeight<BasisPoints16, M>,
}
impl RelativeToAll {
@@ -24,21 +24,21 @@ impl RelativeToAll {
) -> Result<Self> {
Ok(Self {
supply_rel_to_circulating_supply:
ComputedFromHeight::forced_import(
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db,
&cfg.name("supply_rel_to_circulating_supply"),
cfg.version + brk_types::Version::ONE,
cfg.indexes,
)?,
supply_in_profit_rel_to_circulating_supply:
ComputedFromHeight::forced_import(
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
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:
ComputedFromHeight::forced_import(
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
cfg.db,
&cfg.name("supply_in_loss_rel_to_circulating_supply"),
cfg.version + brk_types::Version::ONE,
@@ -56,15 +56,15 @@ impl RelativeToAll {
exit: &Exit,
) -> Result<()> {
self.supply_rel_to_circulating_supply
.compute_binary::<Sats, Sats, PercentageSatsF64>(
.compute_binary::<Sats, Sats, RatioSatsBp16>(
max_from, supply_total_sats, all_supply_sats, exit,
)?;
self.supply_in_profit_rel_to_circulating_supply
.compute_binary::<Sats, Sats, PercentageSatsF64>(
.compute_binary::<Sats, Sats, RatioSatsBp16>(
max_from, &unrealized.supply_in_profit.sats.height, all_supply_sats, exit,
)?;
self.supply_in_loss_rel_to_circulating_supply
.compute_binary::<Sats, Sats, PercentageSatsF64>(
.compute_binary::<Sats, Sats, RatioSatsBp16>(
max_from, &unrealized.supply_in_loss.sats.height, all_supply_sats, exit,
)?;
Ok(())

View File

@@ -18,8 +18,8 @@ use super::ImportConfig;
pub struct SupplyMetrics<M: StorageMode = Rw> {
pub total: ValueFromHeight<M>,
pub halved: LazyValueFromHeight,
/// 30-day change in supply (net position change) - sats, btc, usd
pub _30d_change: ValueFromHeightChange<M>,
/// 1-month change in supply (net position change) - sats, btc, usd
pub change_1m: ValueFromHeightChange<M>,
}
impl SupplyMetrics {
@@ -39,9 +39,9 @@ impl SupplyMetrics {
HalveDollars,
>(&cfg.name("supply_halved"), &supply, cfg.version);
let _30d_change = ValueFromHeightChange::forced_import(
let change_1m = ValueFromHeightChange::forced_import(
cfg.db,
&cfg.name("_30d_change"),
&cfg.name("supply_change_1m"),
cfg.version,
cfg.indexes,
)?;
@@ -49,7 +49,7 @@ impl SupplyMetrics {
Ok(Self {
total: supply,
halved: supply_halved,
_30d_change,
change_1m,
})
}
@@ -114,7 +114,7 @@ impl SupplyMetrics {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self._30d_change.compute_rolling(
self.change_1m.compute_rolling(
starting_indexes.height,
&blocks.count.height_1m_ago,
&self.total.sats.height,

View File

@@ -51,7 +51,7 @@ pub struct UnrealizedBase<M: StorageMode = Rw> {
// === Net and Total ===
pub net_unrealized_pnl: FiatFromHeight<CentsSigned, M>,
pub total_unrealized_pnl: FiatFromHeight<Cents, M>,
pub gross_pnl: FiatFromHeight<Cents, M>,
}
impl UnrealizedBase {
@@ -148,9 +148,9 @@ impl UnrealizedBase {
cfg.version,
cfg.indexes,
)?;
let total_unrealized_pnl = FiatFromHeight::forced_import(
let gross_pnl = FiatFromHeight::forced_import(
cfg.db,
&cfg.name("total_unrealized_pnl"),
&cfg.name("gross_pnl"),
cfg.version,
cfg.indexes,
)?;
@@ -171,7 +171,7 @@ impl UnrealizedBase {
net_sentiment,
neg_unrealized_loss,
net_unrealized_pnl,
total_unrealized_pnl,
gross_pnl,
})
}
@@ -444,7 +444,7 @@ impl UnrealizedBase {
&self.unrealized_loss.cents.height,
exit,
)?;
self.total_unrealized_pnl.cents.height.compute_add(
self.gross_pnl.cents.height.compute_add(
starting_indexes.height,
&self.unrealized_profit.cents.height,
&self.unrealized_loss.cents.height,

View File

@@ -1,5 +1,5 @@
mod _14d;
mod _7d_30d;
mod _1w_1m;
mod _2w;
pub use _14d::*;
pub use _7d_30d::*;
pub use _1w_1m::*;
pub use _2w::*;

View File

@@ -1,20 +1,20 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{StoredF32, Version};
use brk_types::{Height, StoredF32, Version};
use schemars::JsonSchema;
use vecdb::{Database, ReadableCloneableVec, Rw, StorageMode, UnaryTransform};
use vecdb::{BinaryTransform, Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, UnaryTransform, VecValue};
use crate::indexes;
use super::{ComputedFromHeight, LazyFromHeight};
use crate::internal::NumericValue;
/// Basis-point storage with lazy float view.
/// Basis-point storage with lazy ratio float view (÷10000).
///
/// Stores integer basis points on disk (Pco-compressed),
/// exposes a lazy StoredF32 view (bps / 100).
/// exposes a lazy StoredF32 ratio (e.g., 25000 bps → 2.5).
#[derive(Traversable)]
pub struct BpsFromHeight<B, M: StorageMode = Rw>
pub struct Float32FromHeight<B, M: StorageMode = Rw>
where
B: NumericValue + JsonSchema,
{
@@ -22,7 +22,7 @@ where
pub float: LazyFromHeight<StoredF32, B>,
}
impl<B> BpsFromHeight<B>
impl<B> Float32FromHeight<B>
where
B: NumericValue + JsonSchema,
{
@@ -43,4 +43,19 @@ where
Ok(Self { bps, float })
}
pub(crate) fn compute_binary<S1T, S2T, F>(
&mut self,
max_from: Height,
source1: &impl ReadableVec<Height, S1T>,
source2: &impl ReadableVec<Height, S2T>,
exit: &Exit,
) -> Result<()>
where
S1T: VecValue,
S2T: VecValue,
F: BinaryTransform<S1T, S2T, B>,
{
self.bps.compute_binary::<S1T, S2T, F>(max_from, source1, source2, exit)
}
}

View File

@@ -1,14 +1,15 @@
mod aggregated;
mod base;
mod bps;
mod by_unit;
mod constant;
mod cumulative;
mod cumulative_sum;
mod distribution;
mod fiat;
mod float32;
mod full;
mod lazy_base;
mod percent;
mod percentiles;
mod price;
mod ratio;
@@ -17,15 +18,16 @@ mod value;
pub use aggregated::*;
pub use base::*;
pub use bps::*;
pub use by_unit::*;
pub use constant::*;
pub use cumulative::*;
pub use cumulative_sum::*;
pub use distribution::*;
pub use fiat::*;
pub use float32::*;
pub use full::*;
pub use lazy_base::*;
pub use percent::*;
pub use percentiles::*;
pub use price::*;
pub use ratio::*;

View File

@@ -0,0 +1,96 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredF32, Version};
use schemars::JsonSchema;
use vecdb::{BinaryTransform, Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, UnaryTransform, VecValue};
use crate::{
indexes,
internal::NumericValue,
traits::ComputeDrawdown,
};
use super::{ComputedFromHeight, LazyFromHeight};
/// Basis-point storage with both ratio and percentage float views.
///
/// Stores integer basis points on disk (Pco-compressed),
/// exposes two lazy StoredF32 views:
/// - `ratio`: bps ÷ 10000 (e.g., 4523 bps → 0.4523)
/// - `percent`: bps ÷ 100 (e.g., 4523 bps → 45.23%)
///
/// Use for dominance, adoption, RSI, and other percentage-valued metrics.
#[derive(Traversable)]
pub struct PercentFromHeight<B, M: StorageMode = Rw>
where
B: NumericValue + JsonSchema,
{
pub bps: ComputedFromHeight<B, M>,
pub ratio: LazyFromHeight<StoredF32, B>,
pub percent: LazyFromHeight<StoredF32, B>,
}
impl<B> PercentFromHeight<B>
where
B: NumericValue + JsonSchema,
{
pub(crate) fn forced_import<RatioTransform, PercentTransform>(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self>
where
RatioTransform: UnaryTransform<B, StoredF32>,
PercentTransform: UnaryTransform<B, StoredF32>,
{
let bps = ComputedFromHeight::forced_import(db, name, version, indexes)?;
let ratio = LazyFromHeight::from_computed::<RatioTransform>(
&format!("{name}_ratio"),
version,
bps.height.read_only_boxed_clone(),
&bps,
);
let percent = LazyFromHeight::from_computed::<PercentTransform>(
&format!("{name}_percent"),
version,
bps.height.read_only_boxed_clone(),
&bps,
);
Ok(Self { bps, ratio, percent })
}
pub(crate) fn compute_binary<S1T, S2T, F>(
&mut self,
max_from: Height,
source1: &impl ReadableVec<Height, S1T>,
source2: &impl ReadableVec<Height, S2T>,
exit: &Exit,
) -> Result<()>
where
S1T: VecValue,
S2T: VecValue,
F: BinaryTransform<S1T, S2T, B>,
{
self.bps.compute_binary::<S1T, S2T, F>(max_from, source1, source2, exit)
}
pub(crate) fn compute_drawdown<C, A>(
&mut self,
max_from: Height,
current: &impl ReadableVec<Height, C>,
ath: &impl ReadableVec<Height, A>,
exit: &Exit,
) -> Result<()>
where
C: VecValue,
A: VecValue,
f64: From<C> + From<A>,
vecdb::EagerVec<vecdb::PcoVec<Height, B>>: ComputeDrawdown<Height>,
{
self.bps.height.compute_drawdown(max_from, current, ath, exit)
}
}

View File

@@ -12,8 +12,8 @@ use super::super::ComputedFromHeight;
#[derive(Traversable)]
pub struct ComputedFromHeightRatioExtension<M: StorageMode = Rw> {
pub ratio_1w_sma: ComputedFromHeight<StoredF32, M>,
pub ratio_1m_sma: ComputedFromHeight<StoredF32, M>,
pub ratio_sma_1w: ComputedFromHeight<StoredF32, M>,
pub ratio_sma_1m: ComputedFromHeight<StoredF32, M>,
pub ratio_pct99: ComputedFromHeight<StoredF32, M>,
pub ratio_pct98: ComputedFromHeight<StoredF32, M>,
pub ratio_pct95: ComputedFromHeight<StoredF32, M>,
@@ -28,9 +28,9 @@ pub struct ComputedFromHeightRatioExtension<M: StorageMode = Rw> {
pub ratio_pct1_price: Price<ComputedFromHeight<Cents, M>>,
pub ratio_sd: ComputedFromHeightStdDevExtended<M>,
pub ratio_4y_sd: ComputedFromHeightStdDevExtended<M>,
pub ratio_2y_sd: ComputedFromHeightStdDevExtended<M>,
pub ratio_1y_sd: ComputedFromHeightStdDevExtended<M>,
pub ratio_sd_4y: ComputedFromHeightStdDevExtended<M>,
pub ratio_sd_2y: ComputedFromHeightStdDevExtended<M>,
pub ratio_sd_1y: ComputedFromHeightStdDevExtended<M>,
#[traversable(skip)]
tdigest: TDigest,
@@ -59,10 +59,11 @@ impl ComputedFromHeightRatioExtension {
}
macro_rules! import_sd {
($suffix:expr, $days:expr) => {
($suffix:expr, $period:expr, $days:expr) => {
ComputedFromHeightStdDevExtended::forced_import(
db,
&format!("{name}_{}", $suffix),
$period,
$days,
v,
indexes,
@@ -77,12 +78,12 @@ impl ComputedFromHeightRatioExtension {
}
Ok(Self {
ratio_1w_sma: import!("ratio_1w_sma"),
ratio_1m_sma: import!("ratio_1m_sma"),
ratio_sd: import_sd!("ratio", usize::MAX),
ratio_1y_sd: import_sd!("ratio_1y", 365),
ratio_2y_sd: import_sd!("ratio_2y", 2 * 365),
ratio_4y_sd: import_sd!("ratio_4y", 4 * 365),
ratio_sma_1w: import!("ratio_sma_1w"),
ratio_sma_1m: import!("ratio_sma_1m"),
ratio_sd: import_sd!("ratio", "", usize::MAX),
ratio_sd_1y: import_sd!("ratio", "1y", 365),
ratio_sd_2y: import_sd!("ratio", "2y", 2 * 365),
ratio_sd_4y: import_sd!("ratio", "4y", 4 * 365),
ratio_pct99: import!("ratio_pct99"),
ratio_pct98: import!("ratio_pct98"),
ratio_pct95: import!("ratio_pct95"),
@@ -108,14 +109,14 @@ impl ComputedFromHeightRatioExtension {
ratio_source: &impl ReadableVec<Height, StoredF32>,
) -> Result<()> {
// SMA using lookback vecs
self.ratio_1w_sma.height.compute_rolling_average(
self.ratio_sma_1w.height.compute_rolling_average(
starting_indexes.height,
&blocks.count.height_1w_ago,
ratio_source,
exit,
)?;
self.ratio_1m_sma.height.compute_rolling_average(
self.ratio_sma_1m.height.compute_rolling_average(
starting_indexes.height,
&blocks.count.height_1m_ago,
ratio_source,
@@ -183,11 +184,11 @@ impl ComputedFromHeightRatioExtension {
// Compute stddev at height level
self.ratio_sd
.compute_all(blocks, starting_indexes, exit, ratio_source)?;
self.ratio_4y_sd
self.ratio_sd_4y
.compute_all(blocks, starting_indexes, exit, ratio_source)?;
self.ratio_2y_sd
self.ratio_sd_2y
.compute_all(blocks, starting_indexes, exit, ratio_source)?;
self.ratio_1y_sd
self.ratio_sd_1y
.compute_all(blocks, starting_indexes, exit, ratio_source)?;
Ok(())
@@ -225,11 +226,11 @@ impl ComputedFromHeightRatioExtension {
// Stddev cents bands
self.ratio_sd
.compute_cents_bands(starting_indexes, metric_price, exit)?;
self.ratio_4y_sd
self.ratio_sd_4y
.compute_cents_bands(starting_indexes, metric_price, exit)?;
self.ratio_2y_sd
self.ratio_sd_2y
.compute_cents_bands(starting_indexes, metric_price, exit)?;
self.ratio_1y_sd
self.ratio_sd_1y
.compute_cents_bands(starting_indexes, metric_price, exit)?;
Ok(())

View File

@@ -51,17 +51,19 @@ impl ComputedFromHeightStdDevExtended {
pub(crate) fn forced_import(
db: &Database,
name: &str,
period: &str,
days: usize,
parent_version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let version = parent_version + Version::TWO;
let p = super::period_suffix(period);
macro_rules! import {
($suffix:expr) => {
ComputedFromHeight::forced_import(
db,
&format!("{name}_{}", $suffix),
&format!("{name}_{}{p}", $suffix),
version,
indexes,
)?
@@ -70,12 +72,12 @@ impl ComputedFromHeightStdDevExtended {
macro_rules! import_price {
($suffix:expr) => {
Price::forced_import(db, &format!("{name}_{}", $suffix), version, indexes)?
Price::forced_import(db, &format!("{name}_{}{p}", $suffix), version, indexes)?
};
}
Ok(Self {
base: ComputedFromHeightStdDev::forced_import(db, name, days, parent_version, indexes)?,
base: ComputedFromHeightStdDev::forced_import(db, name, period, days, parent_version, indexes)?,
zscore: import!("zscore"),
p0_5sd: import!("p0_5sd"),
p1sd: import!("p1sd"),

View File

@@ -11,6 +11,14 @@ use crate::{ComputeIndexes, blocks, indexes};
use crate::internal::ComputedFromHeight;
fn period_suffix(period: &str) -> String {
if period.is_empty() {
String::new()
} else {
format!("_{period}")
}
}
#[derive(Traversable)]
pub struct ComputedFromHeightStdDev<M: StorageMode = Rw> {
days: usize,
@@ -22,21 +30,23 @@ impl ComputedFromHeightStdDev {
pub(crate) fn forced_import(
db: &Database,
name: &str,
period: &str,
days: usize,
parent_version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let version = parent_version + Version::TWO;
let p = period_suffix(period);
let sma = ComputedFromHeight::forced_import(
db,
&format!("{name}_sma"),
&format!("{name}_sma{p}"),
version,
indexes,
)?;
let sd = ComputedFromHeight::forced_import(
db,
&format!("{name}_sd"),
&format!("{name}_sd{p}"),
version,
indexes,
)?;

View File

@@ -74,18 +74,18 @@ where
&mut self.0.pct75._24h.height, &mut self.0.pct90._24h.height, exit,
)?;
compute_rolling_distribution_from_starts(
max_from, windows._7d, source,
&mut self.0.average._7d.height, &mut self.0.min._7d.height,
&mut self.0.max._7d.height, &mut self.0.pct10._7d.height,
&mut self.0.pct25._7d.height, &mut self.0.median._7d.height,
&mut self.0.pct75._7d.height, &mut self.0.pct90._7d.height, exit,
max_from, windows._1w, source,
&mut self.0.average._1w.height, &mut self.0.min._1w.height,
&mut self.0.max._1w.height, &mut self.0.pct10._1w.height,
&mut self.0.pct25._1w.height, &mut self.0.median._1w.height,
&mut self.0.pct75._1w.height, &mut self.0.pct90._1w.height, exit,
)?;
compute_rolling_distribution_from_starts(
max_from, windows._30d, source,
&mut self.0.average._30d.height, &mut self.0.min._30d.height,
&mut self.0.max._30d.height, &mut self.0.pct10._30d.height,
&mut self.0.pct25._30d.height, &mut self.0.median._30d.height,
&mut self.0.pct75._30d.height, &mut self.0.pct90._30d.height, exit,
max_from, windows._1m, source,
&mut self.0.average._1m.height, &mut self.0.min._1m.height,
&mut self.0.max._1m.height, &mut self.0.pct10._1m.height,
&mut self.0.pct25._1m.height, &mut self.0.median._1m.height,
&mut self.0.pct75._1m.height, &mut self.0.pct90._1m.height, exit,
)?;
compute_rolling_distribution_from_starts(
max_from, windows._1y, source,

View File

@@ -1,5 +1,7 @@
mod _14d;
mod _7d_30d;
mod _1w_1m;
mod _2w;
mod percent_1w_1m;
pub use _14d::*;
pub use _7d_30d::*;
pub use _1w_1m::*;
pub use _2w::*;
pub use percent_1w_1m::*;

View File

@@ -0,0 +1,70 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredF32, Version};
use derive_more::{Deref, DerefMut};
use schemars::JsonSchema;
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode, UnaryTransform};
use crate::{
indexes,
internal::{Emas1w1m, NumericValue, PercentFromHeight},
};
const VERSION: Version = Version::ZERO;
/// 2 EMA vecs (1w, 1m) sourced from 24h rolling window,
/// each storing basis points with lazy ratio and percent float views.
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct PercentRollingEmas1w1m<B, M: StorageMode = Rw>(pub Emas1w1m<PercentFromHeight<B, M>>)
where
B: NumericValue + JsonSchema;
impl<B> PercentRollingEmas1w1m<B>
where
B: NumericValue + JsonSchema,
{
pub(crate) fn forced_import<RatioTransform, PercentTransform>(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self>
where
RatioTransform: UnaryTransform<B, StoredF32>,
PercentTransform: UnaryTransform<B, StoredF32>,
{
let v = version + VERSION;
Ok(Self(Emas1w1m::try_from_fn(|suffix| {
PercentFromHeight::forced_import::<RatioTransform, PercentTransform>(
db,
&format!("{name}_{suffix}"),
v,
indexes,
)
})?))
}
pub(crate) fn compute_from_24h(
&mut self,
max_from: Height,
height_1w_ago: &impl ReadableVec<Height, Height>,
height_1m_ago: &impl ReadableVec<Height, Height>,
source: &impl ReadableVec<Height, B>,
exit: &Exit,
) -> Result<()>
where
f64: From<B>,
B: From<f64> + Default,
{
self._1w
.bps
.height
.compute_rolling_ema(max_from, height_1w_ago, source, exit)?;
self._1m
.bps
.height
.compute_rolling_ema(max_from, height_1m_ago, source, exit)?;
Ok(())
}
}

View File

@@ -1,11 +1,13 @@
mod distribution;
mod emas;
mod full;
mod percent_windows;
mod value_windows;
mod windows;
pub use distribution::*;
pub use emas::*;
pub use full::*;
pub use percent_windows::*;
pub use value_windows::*;
pub use windows::*;

View File

@@ -0,0 +1,47 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{StoredF32, Version};
use derive_more::{Deref, DerefMut};
use schemars::JsonSchema;
use vecdb::{Database, Rw, StorageMode, UnaryTransform};
use crate::{
indexes,
internal::{NumericValue, PercentFromHeight, Windows},
};
const VERSION: Version = Version::ZERO;
/// 4 rolling window vecs (24h, 1w, 1m, 1y), each storing basis points
/// with lazy ratio and percent float views.
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct PercentRollingWindows<B, M: StorageMode = Rw>(pub Windows<PercentFromHeight<B, M>>)
where
B: NumericValue + JsonSchema;
impl<B> PercentRollingWindows<B>
where
B: NumericValue + JsonSchema,
{
pub(crate) fn forced_import<RatioTransform, PercentTransform>(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self>
where
RatioTransform: UnaryTransform<B, StoredF32>,
PercentTransform: UnaryTransform<B, StoredF32>,
{
let v = version + VERSION;
Ok(Self(Windows::try_from_fn(|suffix| {
PercentFromHeight::forced_import::<RatioTransform, PercentTransform>(
db,
&format!("{name}_{suffix}"),
v,
indexes,
)
})?))
}
}

View File

@@ -1,6 +1,6 @@
//! RollingWindows - newtype on Windows with ComputedFromHeight per window duration.
//!
//! Each of the 4 windows (24h, 7d, 30d, 1y) contains a height-level stored vec
//! Each of the 4 windows (24h, 1w, 1m, 1y) contains a height-level stored vec
//! plus all 17 LazyAggVec index views.
use std::ops::SubAssign;
@@ -18,10 +18,10 @@ use crate::{
internal::{ComputedFromHeight, ComputedVecValue, NumericValue, Windows},
};
/// Rolling window start heights — the 4 height-ago vecs (24h, 7d, 30d, 1y).
/// Rolling window start heights — the 4 height-ago vecs (24h, 1w, 1m, 1y).
pub type WindowStarts<'a> = Windows<&'a EagerVec<PcoVec<Height, Height>>>;
/// 4 rolling window vecs (24h, 7d, 30d, 1y), each with height data + all 17 index views.
/// 4 rolling window vecs (24h, 1w, 1m, 1y), each with height data + all 17 index views.
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct RollingWindows<T, M: StorageMode = Rw>(pub Windows<ComputedFromHeight<T, M>>)

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,11 @@
mod bp16_to_float;
mod bp16_to_percent;
mod bp32_to_float;
mod bp32_to_percent;
mod bps16_to_float;
mod bps16_to_percent;
mod bps32_to_float;
mod bps32_to_percent;
mod block_count_target;
mod cents_halve;
mod identity;
@@ -16,19 +20,15 @@ mod dollars_to_sats_fract;
mod neg_cents_to_dollars;
mod ohlc_cents_to_dollars;
mod ohlc_cents_to_sats;
mod percentage_cents_f32;
mod percentage_cents_signed_dollars_f32;
mod percentage_cents_signed_f32;
mod percentage_diff_close_cents;
mod percentage_diff_close_dollars;
mod percentage_dollars_f32;
mod percentage_dollars_f32_neg;
mod percentage_sats_f64;
mod percentage_u32_f32;
mod price_times_ratio_cents;
mod ratio32;
mod ratio_cents64;
mod per_sec;
mod ratio_bp16;
mod ratio_bps16;
mod ratio_bps32;
mod ratio_u64_f32;
mod return_f32_tenths;
mod return_i8;
@@ -43,9 +43,13 @@ mod days_to_years;
mod volatility;
pub use bp16_to_float::*;
pub use bp16_to_percent::*;
pub use bp32_to_float::*;
pub use bp32_to_percent::*;
pub use bps16_to_float::*;
pub use bps16_to_percent::*;
pub use bps32_to_float::*;
pub use bps32_to_percent::*;
pub use block_count_target::*;
pub use cents_halve::*;
pub use identity::*;
@@ -58,22 +62,17 @@ pub use cents_to_sats::*;
pub use neg_cents_to_dollars::*;
pub use ohlc_cents_to_dollars::*;
pub use ohlc_cents_to_sats::*;
pub use percentage_cents_f32::*;
pub use percentage_cents_signed_dollars_f32::*;
pub use percentage_cents_signed_f32::*;
pub use dollar_halve::*;
pub use dollars_to_sats_fract::*;
pub use percentage_diff_close_cents::*;
pub use percentage_diff_close_dollars::*;
pub use percentage_dollars_f32::*;
pub use percentage_dollars_f32_neg::*;
pub use percentage_sats_f64::*;
pub use percentage_u32_f32::*;
pub use price_times_ratio_cents::*;
pub use ratio32::*;
pub use ratio_cents64::*;
pub use per_sec::*;
pub use ratio_bp16::*;
pub use ratio_bps16::*;
pub use ratio_bps32::*;
pub use ratio_u64_f32::*;
pub use return_f32_tenths::*;
pub use return_i8::*;

View File

@@ -1,16 +0,0 @@
use brk_types::{Cents, StoredF32};
use vecdb::BinaryTransform;
/// (Cents, Cents) -> StoredF32 percentage (a/b × 100)
pub struct PercentageCentsF32;
impl BinaryTransform<Cents, Cents, StoredF32> for PercentageCentsF32 {
#[inline(always)]
fn apply(numerator: Cents, denominator: Cents) -> StoredF32 {
if denominator == Cents::ZERO {
StoredF32::default()
} else {
StoredF32::from(numerator.inner() as f64 / denominator.inner() as f64 * 100.0)
}
}
}

View File

@@ -1,17 +0,0 @@
use brk_types::{CentsSigned, Dollars, StoredF32};
use vecdb::BinaryTransform;
/// (CentsSigned, Dollars) -> StoredF32 percentage (a/b × 100)
/// For cross-type percentage when numerator is CentsSigned and denominator is Dollars.
pub struct PercentageCentsSignedDollarsF32;
impl BinaryTransform<CentsSigned, Dollars, StoredF32> for PercentageCentsSignedDollarsF32 {
#[inline(always)]
fn apply(numerator: CentsSigned, denominator: Dollars) -> StoredF32 {
if denominator == Dollars::ZERO {
StoredF32::default()
} else {
StoredF32::from(numerator.inner() as f64 / *denominator * 100.0)
}
}
}

View File

@@ -1,17 +0,0 @@
use brk_types::{Cents, CentsSigned, StoredF32};
use vecdb::BinaryTransform;
/// (CentsSigned, Cents) -> StoredF32 percentage (a/b × 100)
/// For cross-type percentage when numerator is signed.
pub struct PercentageCentsSignedCentsF32;
impl BinaryTransform<CentsSigned, Cents, StoredF32> for PercentageCentsSignedCentsF32 {
#[inline(always)]
fn apply(numerator: CentsSigned, denominator: Cents) -> StoredF32 {
if denominator == Cents::ZERO {
StoredF32::default()
} else {
StoredF32::from(numerator.inner() as f64 / denominator.inner() as f64 * 100.0)
}
}
}

View File

@@ -1,14 +0,0 @@
use brk_types::{Dollars, StoredF32};
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> StoredF32 percentage (a/b × 100)
/// Used for unrealized/realized ratio calculations
pub struct PercentageDollarsF32;
impl BinaryTransform<Dollars, Dollars, StoredF32> for PercentageDollarsF32 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> StoredF32 {
// Dollars / Dollars returns StoredF64, so dereference and multiply
StoredF32::from(*(numerator / denominator) * 100.0)
}
}

View File

@@ -1,14 +0,0 @@
use brk_types::{Dollars, StoredF32};
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> StoredF32 negated percentage (-(a/b × 100))
/// Used for negated loss ratio calculations, avoiding lazy-from-lazy chains.
pub struct NegPercentageDollarsF32;
impl BinaryTransform<Dollars, Dollars, StoredF32> for NegPercentageDollarsF32 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> StoredF32 {
// Dollars / Dollars returns StoredF64, so dereference and multiply
StoredF32::from(-(*(numerator / denominator) * 100.0))
}
}

View File

@@ -1,13 +0,0 @@
use brk_types::{Sats, StoredF64};
use vecdb::BinaryTransform;
/// (Sats, Sats) -> StoredF64 percentage (a/b × 100)
/// Used for supply ratio calculations (equivalent to Bitcoin/Bitcoin since 1e8 cancels)
pub struct PercentageSatsF64;
impl BinaryTransform<Sats, Sats, StoredF64> for PercentageSatsF64 {
#[inline(always)]
fn apply(numerator: Sats, denominator: Sats) -> StoredF64 {
StoredF64::from((*numerator as f64 / *denominator as f64) * 100.0)
}
}

View File

@@ -1,13 +0,0 @@
use brk_types::{StoredF32, StoredU32};
use vecdb::BinaryTransform;
/// (StoredU32, StoredU32) -> StoredF32 percentage (a/b × 100)
/// Used for pool dominance calculations (pool_blocks / total_blocks × 100)
pub struct PercentageU32F32;
impl BinaryTransform<StoredU32, StoredU32, StoredF32> for PercentageU32F32 {
#[inline(always)]
fn apply(numerator: StoredU32, denominator: StoredU32) -> StoredF32 {
StoredF32::from((*numerator as f64 / *denominator as f64) * 100.0)
}
}

View File

@@ -0,0 +1,73 @@
use brk_types::{BasisPoints16, Cents, Dollars, Sats, StoredU32, StoredU64};
use vecdb::BinaryTransform;
/// (StoredU64, StoredU64) -> BasisPoints16 ratio (a/b × 10000)
pub struct RatioU64Bp16;
impl BinaryTransform<StoredU64, StoredU64, BasisPoints16> for RatioU64Bp16 {
#[inline(always)]
fn apply(numerator: StoredU64, denominator: StoredU64) -> BasisPoints16 {
if *denominator > 0 {
BasisPoints16::from(*numerator as f64 / *denominator as f64)
} else {
BasisPoints16::ZERO
}
}
}
/// (Sats, Sats) -> BasisPoints16 ratio (a/b × 10000)
pub struct RatioSatsBp16;
impl BinaryTransform<Sats, Sats, BasisPoints16> for RatioSatsBp16 {
#[inline(always)]
fn apply(numerator: Sats, denominator: Sats) -> BasisPoints16 {
if *denominator > 0 {
BasisPoints16::from(*numerator as f64 / *denominator as f64)
} else {
BasisPoints16::ZERO
}
}
}
/// (Cents, Cents) -> BasisPoints16 ratio (a/b × 10000)
pub struct RatioCentsBp16;
impl BinaryTransform<Cents, Cents, BasisPoints16> for RatioCentsBp16 {
#[inline(always)]
fn apply(numerator: Cents, denominator: Cents) -> BasisPoints16 {
if denominator == Cents::ZERO {
BasisPoints16::ZERO
} else {
BasisPoints16::from(numerator.inner() as f64 / denominator.inner() as f64)
}
}
}
/// (StoredU32, StoredU32) -> BasisPoints16 ratio (a/b × 10000)
pub struct RatioU32Bp16;
impl BinaryTransform<StoredU32, StoredU32, BasisPoints16> for RatioU32Bp16 {
#[inline(always)]
fn apply(numerator: StoredU32, denominator: StoredU32) -> BasisPoints16 {
if *denominator > 0 {
BasisPoints16::from(*numerator as f64 / *denominator as f64)
} else {
BasisPoints16::ZERO
}
}
}
/// (Dollars, Dollars) -> BasisPoints16 ratio (a/b × 10000)
pub struct RatioDollarsBp16;
impl BinaryTransform<Dollars, Dollars, BasisPoints16> for RatioDollarsBp16 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> BasisPoints16 {
let ratio = *(numerator / denominator);
if ratio.is_finite() {
BasisPoints16::from(ratio)
} else {
BasisPoints16::ZERO
}
}
}

View File

@@ -0,0 +1,62 @@
use brk_types::{BasisPointsSigned16, Cents, CentsSigned, Dollars};
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> BasisPointsSigned16 ratio (a/b × 10000)
pub struct RatioDollarsBps16;
impl BinaryTransform<Dollars, Dollars, BasisPointsSigned16> for RatioDollarsBps16 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> BasisPointsSigned16 {
let ratio = *(numerator / denominator);
if ratio.is_finite() {
BasisPointsSigned16::from(ratio)
} else {
BasisPointsSigned16::ZERO
}
}
}
/// (Dollars, Dollars) -> BasisPointsSigned16 negated ratio (-(a/b) × 10000)
pub struct NegRatioDollarsBps16;
impl BinaryTransform<Dollars, Dollars, BasisPointsSigned16> for NegRatioDollarsBps16 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> BasisPointsSigned16 {
let ratio = *(numerator / denominator);
if ratio.is_finite() {
BasisPointsSigned16::from(-ratio)
} else {
BasisPointsSigned16::ZERO
}
}
}
/// (CentsSigned, Cents) -> BasisPointsSigned16 ratio (a/b × 10000)
pub struct RatioCentsSignedCentsBps16;
impl BinaryTransform<CentsSigned, Cents, BasisPointsSigned16> for RatioCentsSignedCentsBps16 {
#[inline(always)]
fn apply(numerator: CentsSigned, denominator: Cents) -> BasisPointsSigned16 {
if denominator == Cents::ZERO {
BasisPointsSigned16::ZERO
} else {
BasisPointsSigned16::from(numerator.inner() as f64 / denominator.inner() as f64)
}
}
}
/// (CentsSigned, Dollars) -> BasisPointsSigned16 ratio (a/b × 10000)
pub struct RatioCentsSignedDollarsBps16;
impl BinaryTransform<CentsSigned, Dollars, BasisPointsSigned16> for RatioCentsSignedDollarsBps16 {
#[inline(always)]
fn apply(numerator: CentsSigned, denominator: Dollars) -> BasisPointsSigned16 {
let d: f64 = denominator.into();
if d > 0.0 {
// Convert cents to dollars first, then compute ratio
BasisPointsSigned16::from(numerator.inner() as f64 / 100.0 / d)
} else {
BasisPointsSigned16::ZERO
}
}
}

View File

@@ -0,0 +1,32 @@
use brk_types::{BasisPointsSigned32, Cents, Dollars};
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> BasisPointsSigned32 ratio diff ((a/b - 1) × 10000)
pub struct RatioDiffDollarsBps32;
impl BinaryTransform<Dollars, Dollars, BasisPointsSigned32> for RatioDiffDollarsBps32 {
#[inline(always)]
fn apply(close: Dollars, base: Dollars) -> BasisPointsSigned32 {
let base_f64: f64 = base.into();
if base_f64 == 0.0 {
BasisPointsSigned32::ZERO
} else {
BasisPointsSigned32::from(f64::from(close) / base_f64 - 1.0)
}
}
}
/// (Cents, Cents) -> BasisPointsSigned32 ratio diff ((a/b - 1) × 10000)
pub struct RatioDiffCentsBps32;
impl BinaryTransform<Cents, Cents, BasisPointsSigned32> for RatioDiffCentsBps32 {
#[inline(always)]
fn apply(close: Cents, base: Cents) -> BasisPointsSigned32 {
let base_f64 = f64::from(base);
if base_f64 == 0.0 {
BasisPointsSigned32::ZERO
} else {
BasisPointsSigned32::from(f64::from(close) / base_f64 - 1.0)
}
}
}

View File

@@ -1,6 +1,6 @@
//! Base generic struct with 4 type parameters — one per rolling window duration.
//!
//! Foundation for all rolling window types (24h, 7d, 30d, 1y).
//! Foundation for all rolling window types (24h, 1w, 1m, 1y).
use brk_traversable::Traversable;
@@ -8,33 +8,33 @@ use brk_traversable::Traversable;
pub struct Windows<A, B = A, C = A, D = A> {
#[traversable(rename = "24h")]
pub _24h: A,
#[traversable(rename = "7d")]
pub _7d: B,
#[traversable(rename = "30d")]
pub _30d: C,
#[traversable(rename = "1w")]
pub _1w: B,
#[traversable(rename = "1m")]
pub _1m: C,
#[traversable(rename = "1y")]
pub _1y: D,
}
impl<A> Windows<A> {
pub const SUFFIXES: [&'static str; 4] = ["24h", "7d", "30d", "1y"];
pub const SUFFIXES: [&'static str; 4] = ["24h", "1w", "1m", "1y"];
pub fn try_from_fn<E>(
mut f: impl FnMut(&str) -> std::result::Result<A, E>,
) -> std::result::Result<Self, E> {
Ok(Self {
_24h: f(Self::SUFFIXES[0])?,
_7d: f(Self::SUFFIXES[1])?,
_30d: f(Self::SUFFIXES[2])?,
_1w: f(Self::SUFFIXES[1])?,
_1m: f(Self::SUFFIXES[2])?,
_1y: f(Self::SUFFIXES[3])?,
})
}
pub fn as_array(&self) -> [&A; 4] {
[&self._24h, &self._7d, &self._30d, &self._1y]
[&self._24h, &self._1w, &self._1m, &self._1y]
}
pub fn as_mut_array(&mut self) -> [&mut A; 4] {
[&mut self._24h, &mut self._7d, &mut self._30d, &mut self._1y]
[&mut self._24h, &mut self._1w, &mut self._1m, &mut self._1y]
}
}

View File

@@ -47,7 +47,7 @@ impl Vecs {
)?;
let mut prev = None;
self.max_days_between_price_aths.height.compute_transform(
self.max_days_between_price_ath.height.compute_transform(
starting_indexes.height,
&self.days_since_price_ath.height,
|(i, days, slf)| {
@@ -66,7 +66,7 @@ impl Vecs {
exit,
)?;
self.price_drawdown.height.compute_drawdown(
self.price_drawdown.compute_drawdown(
starting_indexes.height,
&prices.price.cents.height,
&self.price_ath.cents.height,

View File

@@ -5,7 +5,7 @@ use vecdb::Database;
use super::Vecs;
use crate::{
indexes,
internal::{ComputedFromHeight, DaysToYears, LazyHeightDerived, Price},
internal::{Bps16ToFloat, Bps16ToPercent, ComputedFromHeight, DaysToYears, LazyHeightDerived, PercentFromHeight, Price},
};
const VERSION: Version = Version::ONE;
@@ -20,14 +20,14 @@ impl Vecs {
let price_ath = Price::forced_import(db, "price_ath", v, indexes)?;
let max_days_between_price_aths =
ComputedFromHeight::forced_import(db, "max_days_between_price_aths", v, indexes)?;
let max_days_between_price_ath =
ComputedFromHeight::forced_import(db, "max_days_between_price_ath", v, indexes)?;
let max_years_between_price_aths =
let max_years_between_price_ath =
LazyHeightDerived::from_computed::<DaysToYears>(
"max_years_between_price_aths",
"max_years_between_price_ath",
v,
&max_days_between_price_aths,
&max_days_between_price_ath,
);
let days_since_price_ath =
@@ -40,15 +40,15 @@ impl Vecs {
);
let price_drawdown =
ComputedFromHeight::forced_import(db, "price_drawdown", v, indexes)?;
PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(db, "price_drawdown", v, indexes)?;
Ok(Self {
price_ath,
price_drawdown,
days_since_price_ath,
years_since_price_ath,
max_days_between_price_aths,
max_years_between_price_aths,
max_days_between_price_ath,
max_years_between_price_ath,
})
}
}

View File

@@ -1,15 +1,15 @@
use brk_traversable::Traversable;
use brk_types::{Cents, StoredF32};
use brk_types::{BasisPointsSigned16, Cents, StoredF32};
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeight, LazyHeightDerived, Price};
use crate::internal::{ComputedFromHeight, LazyHeightDerived, PercentFromHeight, Price};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub price_ath: Price<ComputedFromHeight<Cents, M>>,
pub price_drawdown: ComputedFromHeight<StoredF32, M>,
pub price_drawdown: PercentFromHeight<BasisPointsSigned16, M>,
pub days_since_price_ath: ComputedFromHeight<StoredF32, M>,
pub years_since_price_ath: LazyHeightDerived<StoredF32, StoredF32>,
pub max_days_between_price_aths: ComputedFromHeight<StoredF32, M>,
pub max_years_between_price_aths: LazyHeightDerived<StoredF32, StoredF32>,
pub max_days_between_price_ath: ComputedFromHeight<StoredF32, M>,
pub max_years_between_price_ath: LazyHeightDerived<StoredF32, StoredF32>,
}

View File

@@ -3,51 +3,51 @@ use brk_types::{Date, Day1};
/// DCA class years
pub const DCA_CLASS_YEARS: ByDcaClass<u16> = ByDcaClass {
_2015: 2015,
_2016: 2016,
_2017: 2017,
_2018: 2018,
_2019: 2019,
_2020: 2020,
_2021: 2021,
_2022: 2022,
_2023: 2023,
_2024: 2024,
_2025: 2025,
_2026: 2026,
from_2015: 2015,
from_2016: 2016,
from_2017: 2017,
from_2018: 2018,
from_2019: 2019,
from_2020: 2020,
from_2021: 2021,
from_2022: 2022,
from_2023: 2023,
from_2024: 2024,
from_2025: 2025,
from_2026: 2026,
};
/// DCA class names
pub const DCA_CLASS_NAMES: ByDcaClass<&'static str> = ByDcaClass {
_2015: "dca_class_2015",
_2016: "dca_class_2016",
_2017: "dca_class_2017",
_2018: "dca_class_2018",
_2019: "dca_class_2019",
_2020: "dca_class_2020",
_2021: "dca_class_2021",
_2022: "dca_class_2022",
_2023: "dca_class_2023",
_2024: "dca_class_2024",
_2025: "dca_class_2025",
_2026: "dca_class_2026",
from_2015: "from_2015",
from_2016: "from_2016",
from_2017: "from_2017",
from_2018: "from_2018",
from_2019: "from_2019",
from_2020: "from_2020",
from_2021: "from_2021",
from_2022: "from_2022",
from_2023: "from_2023",
from_2024: "from_2024",
from_2025: "from_2025",
from_2026: "from_2026",
};
/// Generic wrapper for DCA year class data
#[derive(Clone, Default, Traversable)]
pub struct ByDcaClass<T> {
pub _2015: T,
pub _2016: T,
pub _2017: T,
pub _2018: T,
pub _2019: T,
pub _2020: T,
pub _2021: T,
pub _2022: T,
pub _2023: T,
pub _2024: T,
pub _2025: T,
pub _2026: T,
pub from_2015: T,
pub from_2016: T,
pub from_2017: T,
pub from_2018: T,
pub from_2019: T,
pub from_2020: T,
pub from_2021: T,
pub from_2022: T,
pub from_2023: T,
pub from_2024: T,
pub from_2025: T,
pub from_2026: T,
}
impl<T> ByDcaClass<T> {
@@ -58,18 +58,18 @@ impl<T> ByDcaClass<T> {
let n = DCA_CLASS_NAMES;
let y = DCA_CLASS_YEARS;
Ok(Self {
_2015: create(n._2015, y._2015, Self::day1(y._2015))?,
_2016: create(n._2016, y._2016, Self::day1(y._2016))?,
_2017: create(n._2017, y._2017, Self::day1(y._2017))?,
_2018: create(n._2018, y._2018, Self::day1(y._2018))?,
_2019: create(n._2019, y._2019, Self::day1(y._2019))?,
_2020: create(n._2020, y._2020, Self::day1(y._2020))?,
_2021: create(n._2021, y._2021, Self::day1(y._2021))?,
_2022: create(n._2022, y._2022, Self::day1(y._2022))?,
_2023: create(n._2023, y._2023, Self::day1(y._2023))?,
_2024: create(n._2024, y._2024, Self::day1(y._2024))?,
_2025: create(n._2025, y._2025, Self::day1(y._2025))?,
_2026: create(n._2026, y._2026, Self::day1(y._2026))?,
from_2015: create(n.from_2015, y.from_2015, Self::day1(y.from_2015))?,
from_2016: create(n.from_2016, y.from_2016, Self::day1(y.from_2016))?,
from_2017: create(n.from_2017, y.from_2017, Self::day1(y.from_2017))?,
from_2018: create(n.from_2018, y.from_2018, Self::day1(y.from_2018))?,
from_2019: create(n.from_2019, y.from_2019, Self::day1(y.from_2019))?,
from_2020: create(n.from_2020, y.from_2020, Self::day1(y.from_2020))?,
from_2021: create(n.from_2021, y.from_2021, Self::day1(y.from_2021))?,
from_2022: create(n.from_2022, y.from_2022, Self::day1(y.from_2022))?,
from_2023: create(n.from_2023, y.from_2023, Self::day1(y.from_2023))?,
from_2024: create(n.from_2024, y.from_2024, Self::day1(y.from_2024))?,
from_2025: create(n.from_2025, y.from_2025, Self::day1(y.from_2025))?,
from_2026: create(n.from_2026, y.from_2026, Self::day1(y.from_2026))?,
})
}
@@ -79,36 +79,36 @@ impl<T> ByDcaClass<T> {
pub(crate) fn iter(&self) -> impl Iterator<Item = &T> {
[
&self._2015,
&self._2016,
&self._2017,
&self._2018,
&self._2019,
&self._2020,
&self._2021,
&self._2022,
&self._2023,
&self._2024,
&self._2025,
&self._2026,
&self.from_2015,
&self.from_2016,
&self.from_2017,
&self.from_2018,
&self.from_2019,
&self.from_2020,
&self.from_2021,
&self.from_2022,
&self.from_2023,
&self.from_2024,
&self.from_2025,
&self.from_2026,
]
.into_iter()
}
pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
[
&mut self._2015,
&mut self._2016,
&mut self._2017,
&mut self._2018,
&mut self._2019,
&mut self._2020,
&mut self._2021,
&mut self._2022,
&mut self._2023,
&mut self._2024,
&mut self._2025,
&mut self._2026,
&mut self.from_2015,
&mut self.from_2016,
&mut self.from_2017,
&mut self.from_2018,
&mut self.from_2019,
&mut self.from_2020,
&mut self.from_2021,
&mut self.from_2022,
&mut self.from_2023,
&mut self.from_2024,
&mut self.from_2025,
&mut self.from_2026,
]
.into_iter()
}
@@ -116,19 +116,18 @@ impl<T> ByDcaClass<T> {
pub(crate) fn start_days() -> [Day1; 12] {
let y = DCA_CLASS_YEARS;
[
Self::day1(y._2015),
Self::day1(y._2016),
Self::day1(y._2017),
Self::day1(y._2018),
Self::day1(y._2019),
Self::day1(y._2020),
Self::day1(y._2021),
Self::day1(y._2022),
Self::day1(y._2023),
Self::day1(y._2024),
Self::day1(y._2025),
Self::day1(y._2026),
Self::day1(y.from_2015),
Self::day1(y.from_2016),
Self::day1(y.from_2017),
Self::day1(y.from_2018),
Self::day1(y.from_2019),
Self::day1(y.from_2020),
Self::day1(y.from_2021),
Self::day1(y.from_2022),
Self::day1(y.from_2023),
Self::day1(y.from_2024),
Self::day1(y.from_2025),
Self::day1(y.from_2026),
]
}
}

View File

@@ -1,10 +1,10 @@
use brk_error::Result;
use brk_types::{Bitcoin, Cents, Date, Day1, Dollars, Sats, StoredF32};
use brk_types::{BasisPointsSigned32, Bitcoin, Cents, Date, Day1, Dollars, Sats};
use vecdb::{AnyVec, Exit, ReadableOptionVec, ReadableVec, VecIndex};
use super::Vecs;
use crate::{
ComputeIndexes, blocks, indexes, internal::PercentageDiffCents, market::lookback, prices,
ComputeIndexes, blocks, indexes, internal::RatioDiffCentsBps32, market::lookback, prices,
};
const DCA_AMOUNT: Dollars = Dollars::mint(100.0);
@@ -65,7 +65,7 @@ impl Vecs {
// DCA by period - average price (derived from stack)
let sh = starting_indexes.height.to_usize();
for (average_price, stack, days) in self
.period_average_price
.period_cost_basis
.zip_mut_with_days(&self.period_stack)
{
let days = days as usize;
@@ -93,11 +93,11 @@ impl Vecs {
// DCA by period - returns (compute from average price)
for (returns, (average_price, _)) in self
.period_returns
.period_return
.iter_mut()
.zip(self.period_average_price.iter_with_days())
.zip(self.period_cost_basis.iter_with_days())
{
returns.compute_binary::<Cents, Cents, PercentageDiffCents>(
returns.compute_binary::<Cents, Cents, RatioDiffCentsBps32>(
starting_indexes.height,
&prices.price.cents.height,
&average_price.cents.height,
@@ -106,21 +106,22 @@ impl Vecs {
}
// DCA by period - CAGR (computed from returns at height level)
for (cagr, returns, days) in self.period_cagr.zip_mut_with_period(&self.period_returns) {
let years = days as f32 / 365.0;
cagr.height.compute_transform(
for (cagr, returns, days) in self.period_cagr.zip_mut_with_period(&self.period_return) {
let years = days as f64 / 365.0;
cagr.bps.height.compute_transform(
starting_indexes.height,
&returns.height,
&returns.bps.height,
|(h, r, ..)| {
let v = ((*r / 100.0 + 1.0).powf(1.0 / years) - 1.0) * 100.0;
(h, StoredF32::from(v))
let ratio = f64::from(r);
let v = (ratio + 1.0).powf(1.0 / years) - 1.0;
(h, BasisPointsSigned32::from(v))
},
exit,
)?;
}
// Lump sum by period - stack
let lookback_dca = lookback.price_ago.as_dca_period();
let lookback_dca = lookback.price_lookback.as_dca_period();
for (stack, lookback_price, days) in
self.period_lump_sum_stack.zip_mut_with_days(&lookback_dca)
{
@@ -146,13 +147,13 @@ impl Vecs {
}
// Lump sum by period - returns (compute from lookback price)
let lookback_dca2 = lookback.price_ago.as_dca_period();
let lookback_dca2 = lookback.price_lookback.as_dca_period();
for (returns, (lookback_price, _)) in self
.period_lump_sum_returns
.period_lump_sum_return
.iter_mut()
.zip(lookback_dca2.iter_with_days())
{
returns.compute_binary::<Cents, Cents, PercentageDiffCents>(
returns.compute_binary::<Cents, Cents, RatioDiffCentsBps32>(
starting_indexes.height,
&prices.price.cents.height,
&lookback_price.cents.height,
@@ -214,7 +215,7 @@ impl Vecs {
// DCA by year class - average price (derived from stack)
let start_days = super::ByDcaClass::<()>::start_days();
for ((average_price, stack), from) in self
.class_average_price
.class_cost_basis
.iter_mut()
.zip(self.class_stack.iter())
.zip(start_days)
@@ -243,11 +244,11 @@ impl Vecs {
// DCA by year class - returns (compute from average price)
for (returns, average_price) in self
.class_returns
.class_return
.iter_mut()
.zip(self.class_average_price.iter())
.zip(self.class_cost_basis.iter())
{
returns.compute_binary::<Cents, Cents, PercentageDiffCents>(
returns.compute_binary::<Cents, Cents, RatioDiffCentsBps32>(
starting_indexes.height,
&prices.price.cents.height,
&average_price.cents.height,

View File

@@ -5,7 +5,7 @@ use vecdb::{Database, ImportableVec};
use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod, Vecs};
use crate::{
indexes,
internal::{ComputedFromHeight, Price, ValueFromHeight},
internal::{Bps32ToFloat, Bps32ToPercent, PercentFromHeight, Price, ValueFromHeight},
};
impl Vecs {
@@ -15,57 +15,72 @@ impl Vecs {
indexes: &indexes::Vecs,
) -> Result<Self> {
let period_stack = ByDcaPeriod::try_new(|name, _days| {
ValueFromHeight::forced_import(db, &format!("{name}_dca_stack"), version, indexes)
ValueFromHeight::forced_import(db, &format!("dca_stack_{name}"), version, indexes)
})?;
let period_average_price = ByDcaPeriod::try_new(|name, _days| {
Price::forced_import(db, &format!("{name}_dca_average_price"), version, indexes)
let period_cost_basis = ByDcaPeriod::try_new(|name, _days| {
Price::forced_import(db, &format!("dca_cost_basis_{name}"), version, indexes)
})?;
let period_returns = ByDcaPeriod::try_new(|name, _days| {
ComputedFromHeight::forced_import(db, &format!("{name}_dca_returns"), version, indexes)
let period_return = ByDcaPeriod::try_new(|name, _days| {
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
db,
&format!("dca_return_{name}"),
version,
indexes,
)
})?;
let period_cagr = ByDcaCagr::try_new(|name, _days| {
ComputedFromHeight::forced_import(db, &format!("{name}_dca_cagr"), version, indexes)
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
db,
&format!("dca_cagr_{name}"),
version,
indexes,
)
})?;
let period_lump_sum_stack = ByDcaPeriod::try_new(|name, _days| {
ValueFromHeight::forced_import(db, &format!("{name}_lump_sum_stack"), version, indexes)
ValueFromHeight::forced_import(db, &format!("lump_sum_stack_{name}"), version, indexes)
})?;
let period_lump_sum_returns = ByDcaPeriod::try_new(|name, _days| {
ComputedFromHeight::forced_import(
let period_lump_sum_return = ByDcaPeriod::try_new(|name, _days| {
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
db,
&format!("{name}_lump_sum_returns"),
&format!("lump_sum_return_{name}"),
version,
indexes,
)
})?;
let class_stack = ByDcaClass::try_new(|name, _year, _day1| {
ValueFromHeight::forced_import(db, &format!("{name}_stack"), version, indexes)
ValueFromHeight::forced_import(db, &format!("dca_stack_{name}"), version, indexes)
})?;
let class_average_price = ByDcaClass::try_new(|name, _year, _day1| {
Price::forced_import(db, &format!("{name}_average_price"), version, indexes)
let class_cost_basis = ByDcaClass::try_new(|name, _year, _day1| {
Price::forced_import(db, &format!("dca_cost_basis_{name}"), version, indexes)
})?;
let class_returns = ByDcaClass::try_new(|name, _year, _day1| {
ComputedFromHeight::forced_import(db, &format!("{name}_returns"), version, indexes)
let class_return = ByDcaClass::try_new(|name, _year, _day1| {
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
db,
&format!("dca_return_{name}"),
version,
indexes,
)
})?;
Ok(Self {
dca_sats_per_day: ImportableVec::forced_import(db, "dca_sats_per_day", version)?,
period_stack,
period_average_price,
period_returns,
period_cost_basis,
period_return,
period_cagr,
period_lump_sum_stack,
period_lump_sum_returns,
period_lump_sum_return,
class_stack,
class_average_price,
class_returns,
class_cost_basis,
class_return,
})
}
}

View File

@@ -1,9 +1,9 @@
use brk_traversable::Traversable;
use brk_types::{Cents, Height, Sats, StoredF32};
use brk_types::{BasisPointsSigned32, Cents, Height, Sats};
use vecdb::{EagerVec, PcoVec, Rw, StorageMode};
use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod};
use crate::internal::{ComputedFromHeight, Price, ValueFromHeight};
use crate::internal::{ComputedFromHeight, PercentFromHeight, Price, ValueFromHeight};
/// Dollar-cost averaging metrics by time period and year class
#[derive(Traversable)]
@@ -14,16 +14,16 @@ pub struct Vecs<M: StorageMode = Rw> {
// DCA by period
pub period_stack: ByDcaPeriod<ValueFromHeight<M>>,
pub period_average_price: ByDcaPeriod<Price<ComputedFromHeight<Cents, M>>>,
pub period_returns: ByDcaPeriod<ComputedFromHeight<StoredF32, M>>,
pub period_cagr: ByDcaCagr<ComputedFromHeight<StoredF32, M>>,
pub period_cost_basis: ByDcaPeriod<Price<ComputedFromHeight<Cents, M>>>,
pub period_return: ByDcaPeriod<PercentFromHeight<BasisPointsSigned32, M>>,
pub period_cagr: ByDcaCagr<PercentFromHeight<BasisPointsSigned32, M>>,
// Lump sum by period (for comparison with DCA)
pub period_lump_sum_stack: ByDcaPeriod<ValueFromHeight<M>>,
pub period_lump_sum_returns: ByDcaPeriod<ComputedFromHeight<StoredF32, M>>,
pub period_lump_sum_return: ByDcaPeriod<PercentFromHeight<BasisPointsSigned32, M>>,
// DCA by year class
pub class_stack: ByDcaClass<ValueFromHeight<M>>,
pub class_average_price: ByDcaClass<Price<ComputedFromHeight<Cents, M>>>,
pub class_returns: ByDcaClass<ComputedFromHeight<StoredF32, M>>,
pub class_cost_basis: ByDcaClass<Price<ComputedFromHeight<Cents, M>>>,
pub class_return: ByDcaClass<PercentFromHeight<BasisPointsSigned32, M>>,
}

View File

@@ -5,13 +5,13 @@ use vecdb::Exit;
use super::{super::range, Vecs};
use crate::{
ComputeIndexes, blocks, distribution,
internal::Ratio32,
internal::{Ratio32, Windows},
mining, prices, transactions,
};
fn tf_multiplier(tf: &str) -> usize {
match tf {
"1d" => 1,
"24h" => 1,
"1w" => 7,
"1m" => 30,
"1y" => 365,
@@ -37,7 +37,7 @@ impl Vecs {
self.puell_multiple.height.compute_divide(
starting_indexes.height,
&rewards.subsidy.base.usd.height,
&rewards.subsidy_usd_1y_sma.usd.height,
&rewards.subsidy_sma_1y.usd.height,
exit,
)?;
@@ -47,8 +47,8 @@ impl Vecs {
self.stoch_k.height.compute_transform3(
starting_indexes.height,
price,
&range.price_2w_min.usd.height,
&range.price_2w_max.usd.height,
&range.price_min_2w.usd.height,
&range.price_max_2w.usd.height,
|(h, close, low, high, ..)| {
let range = *high - *low;
let stoch = if range == 0.0 {
@@ -70,13 +70,15 @@ impl Vecs {
}
// RSI per timeframe
for (tf, rsi_chain) in self.rsi.iter_mut() {
for (tf, rsi_chain) in Windows::<()>::SUFFIXES.into_iter()
.zip(self.rsi.as_mut_array())
{
let m = tf_multiplier(tf);
let returns_source = match tf {
"1d" => &returns.price_returns._24h.height,
"1w" => &returns.price_returns._1w.height,
"1m" => &returns.price_returns._1m.height,
"1y" => &returns.price_returns._1y.height,
"24h" => &returns.price_return._24h.height,
"1w" => &returns.price_return._1w.height,
"1m" => &returns.price_return._1m.height,
"1y" => &returns.price_return._1y.height,
_ => unreachable!(),
};
super::rsi::compute(
@@ -91,7 +93,9 @@ impl Vecs {
}
// MACD per timeframe
for (tf, macd_chain) in self.macd.iter_mut() {
for (tf, macd_chain) in Windows::<()>::SUFFIXES.into_iter()
.zip(self.macd.as_mut_array())
{
let m = tf_multiplier(tf);
super::macd::compute(
macd_chain,
@@ -124,8 +128,8 @@ impl Vecs {
// Pi Cycle: sma_111d / sma_350d_x2
self.pi_cycle.compute_binary::<Dollars, Dollars, Ratio32>(
starting_indexes.height,
&moving_average.price_111d_sma.price.usd.height,
&moving_average.price_350d_sma_x2.usd.height,
&moving_average.price_sma_111d.price.usd.height,
&moving_average.price_sma_350d_x2.usd.height,
exit,
)?;

View File

@@ -1,11 +1,11 @@
use brk_error::Result;
use brk_types::{Sats, StoredF32, StoredU64, Version};
use brk_types::{BasisPoints16, Sats, StoredU64, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec};
use crate::{ComputeIndexes, distribution, internal::ComputedFromHeight};
use crate::{ComputeIndexes, distribution, internal::PercentFromHeight};
pub(super) fn compute(
gini: &mut ComputedFromHeight<StoredF32>,
gini: &mut PercentFromHeight<BasisPoints16>,
distribution: &distribution::Vecs,
starting_indexes: &ComputeIndexes,
exit: &Exit,
@@ -32,10 +32,10 @@ pub(super) fn compute(
.iter()
.fold(Version::ZERO, |acc, v| acc + v.version());
gini.height
gini.bps.height
.validate_computed_version_or_reset(source_version)?;
gini.height
.truncate_if_needed_at(gini.height.len().min(starting_indexes.height.to_usize()))?;
gini.bps.height
.truncate_if_needed_at(gini.bps.height.len().min(starting_indexes.height.to_usize()))?;
let total_heights = supply_vecs
.iter()
@@ -44,7 +44,7 @@ pub(super) fn compute(
.unwrap_or(0)
.min(count_vecs.iter().map(|v| v.len()).min().unwrap_or(0));
let start_height = gini.height.len();
let start_height = gini.bps.height.len();
if start_height >= total_heights {
return Ok(());
}
@@ -68,24 +68,24 @@ pub(super) fn compute(
let count: u64 = count_data[c][offset].into();
buckets.push((count, supply));
}
gini.height
.push(StoredF32::from(gini_from_lorenz(&buckets)));
gini.bps.height
.push(gini_from_lorenz(&buckets));
}
{
let _lock = exit.lock();
gini.height.write()?;
gini.bps.height.write()?;
}
Ok(())
}
fn gini_from_lorenz(buckets: &[(u64, u64)]) -> f32 {
fn gini_from_lorenz(buckets: &[(u64, u64)]) -> BasisPoints16 {
let total_count: u64 = buckets.iter().map(|(c, _)| c).sum();
let total_supply: u64 = buckets.iter().map(|(_, s)| s).sum();
if total_count == 0 || total_supply == 0 {
return f32::NAN;
return BasisPoints16::ZERO;
}
let (mut cumulative_count, mut cumulative_supply, mut area) = (0u64, 0u64, 0.0f64);
@@ -99,5 +99,5 @@ fn gini_from_lorenz(buckets: &[(u64, u64)]) -> f32 {
area += (p1 - p0) * (w0 + w1) / 2.0;
}
(1.0 - 2.0 * area) as f32
BasisPoints16::from(1.0 - 2.0 * area)
}

View File

@@ -2,10 +2,10 @@ use brk_error::Result;
use brk_types::Version;
use vecdb::Database;
use super::{ByIndicatorTimeframe, MacdChain, RsiChain, Vecs};
use super::{MacdChain, RsiChain, Vecs};
use crate::{
indexes,
internal::ComputedFromHeight,
internal::{Bp16ToFloat, Bp16ToPercent, ComputedFromHeight, PercentFromHeight, Windows},
};
const VERSION: Version = Version::ONE;
@@ -28,8 +28,8 @@ impl RsiChain {
};
}
let average_gain = import!("avg_gain");
let average_loss = import!("avg_loss");
let average_gain = import!("average_gain");
let average_loss = import!("average_loss");
let rsi = ComputedFromHeight::forced_import(
db,
@@ -44,11 +44,11 @@ impl RsiChain {
average_gain,
average_loss,
rsi,
rsi_min: import!("rsi_min"),
rsi_max: import!("rsi_max"),
stoch_rsi: import!("stoch_rsi"),
stoch_rsi_k: import!("stoch_rsi_k"),
stoch_rsi_d: import!("stoch_rsi_d"),
rsi_min: import!("min"),
rsi_max: import!("max"),
stoch_rsi: import!("stoch"),
stoch_rsi_k: import!("stoch_k"),
stoch_rsi_d: import!("stoch_d"),
})
}
}
@@ -110,12 +110,12 @@ impl Vecs {
let nvt = ComputedFromHeight::forced_import(db, "nvt", v, indexes)?;
let rsi = ByIndicatorTimeframe::try_new(|tf| RsiChain::forced_import(db, tf, v, indexes))?;
let macd = ByIndicatorTimeframe::try_new(|tf| MacdChain::forced_import(db, tf, 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 = ComputedFromHeight::forced_import(db, "gini", v, indexes)?;
let gini = PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(db, "gini", v, indexes)?;
let pi_cycle = ComputedFromHeight::forced_import(db, "pi_cycle", v, indexes)?;

View File

@@ -5,4 +5,4 @@ mod macd;
mod rsi;
mod vecs;
pub use vecs::{ByIndicatorTimeframe, MacdChain, RsiChain, Vecs};
pub use vecs::{MacdChain, RsiChain, Vecs};

View File

@@ -1,39 +1,8 @@
use brk_traversable::Traversable;
use brk_types::StoredF32;
use brk_types::{BasisPoints16, StoredF32};
use vecdb::{Rw, StorageMode};
use crate::internal::ComputedFromHeight;
pub const TIMEFRAME_NAMES: [&str; 4] = ["1d", "1w", "1m", "1y"];
#[derive(Clone, Traversable)]
pub struct ByIndicatorTimeframe<T> {
pub _1d: T,
pub _1w: T,
pub _1m: T,
pub _1y: T,
}
impl<T> ByIndicatorTimeframe<T> {
pub fn try_new<E>(mut create: impl FnMut(&str) -> Result<T, E>) -> Result<Self, E> {
Ok(Self {
_1d: create(TIMEFRAME_NAMES[0])?,
_1w: create(TIMEFRAME_NAMES[1])?,
_1m: create(TIMEFRAME_NAMES[2])?,
_1y: create(TIMEFRAME_NAMES[3])?,
})
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut T)> {
[
(TIMEFRAME_NAMES[0], &mut self._1d),
(TIMEFRAME_NAMES[1], &mut self._1w),
(TIMEFRAME_NAMES[2], &mut self._1m),
(TIMEFRAME_NAMES[3], &mut self._1y),
]
.into_iter()
}
}
use crate::internal::{ComputedFromHeight, PercentFromHeight, Windows};
#[derive(Traversable)]
pub struct RsiChain<M: StorageMode = Rw> {
@@ -63,14 +32,14 @@ pub struct Vecs<M: StorageMode = Rw> {
pub puell_multiple: ComputedFromHeight<StoredF32, M>,
pub nvt: ComputedFromHeight<StoredF32, M>,
pub rsi: ByIndicatorTimeframe<RsiChain<M>>,
pub rsi: Windows<RsiChain<M>>,
pub stoch_k: ComputedFromHeight<StoredF32, M>,
pub stoch_d: ComputedFromHeight<StoredF32, M>,
pub pi_cycle: ComputedFromHeight<StoredF32, M>,
pub macd: ByIndicatorTimeframe<MacdChain<M>>,
pub macd: Windows<MacdChain<M>>,
pub gini: ComputedFromHeight<StoredF32, M>,
pub gini: PercentFromHeight<BasisPoints16, M>,
}

View File

@@ -14,9 +14,9 @@ impl Vecs {
) -> Result<()> {
let price = &prices.price.cents.height;
for (price_ago, days) in self.price_ago.iter_mut_with_days() {
for (price_lookback, days) in self.price_lookback.iter_mut_with_days() {
let window_starts = blocks.count.start_vec(days as usize);
price_ago.cents.height.compute_lookback(
price_lookback.cents.height.compute_lookback(
starting_indexes.height,
window_starts,
price,

View File

@@ -7,10 +7,10 @@ use crate::{indexes, internal::Price};
impl Vecs {
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
let price_ago = ByLookbackPeriod::try_new(|name, _days| {
Price::forced_import(db, &format!("price_{name}_ago"), version, indexes)
let price_lookback = ByLookbackPeriod::try_new(|name, _days| {
Price::forced_import(db, &format!("price_lookback_{name}"), version, indexes)
})?;
Ok(Self { price_ago })
Ok(Self { price_lookback })
}
}

View File

@@ -9,5 +9,5 @@ use crate::internal::{ComputedFromHeight, Price};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
#[traversable(flatten)]
pub price_ago: ByLookbackPeriod<Price<ComputedFromHeight<Cents, M>>>,
pub price_lookback: ByLookbackPeriod<Price<ComputedFromHeight<Cents, M>>>,
}

View File

@@ -15,22 +15,22 @@ impl Vecs {
let close = &prices.price.cents.height;
for (sma, period) in [
(&mut self.price_1w_sma, 7),
(&mut self.price_8d_sma, 8),
(&mut self.price_13d_sma, 13),
(&mut self.price_21d_sma, 21),
(&mut self.price_1m_sma, 30),
(&mut self.price_34d_sma, 34),
(&mut self.price_55d_sma, 55),
(&mut self.price_89d_sma, 89),
(&mut self.price_111d_sma, 111),
(&mut self.price_144d_sma, 144),
(&mut self.price_200d_sma, 200),
(&mut self.price_350d_sma, 350),
(&mut self.price_1y_sma, 365),
(&mut self.price_2y_sma, 2 * 365),
(&mut self.price_200w_sma, 200 * 7),
(&mut self.price_4y_sma, 4 * 365),
(&mut self.price_sma_1w, 7),
(&mut self.price_sma_8d, 8),
(&mut self.price_sma_13d, 13),
(&mut self.price_sma_21d, 21),
(&mut self.price_sma_1m, 30),
(&mut self.price_sma_34d, 34),
(&mut self.price_sma_55d, 55),
(&mut self.price_sma_89d, 89),
(&mut self.price_sma_111d, 111),
(&mut self.price_sma_144d, 144),
(&mut self.price_sma_200d, 200),
(&mut self.price_sma_350d, 350),
(&mut self.price_sma_1y, 365),
(&mut self.price_sma_2y, 2 * 365),
(&mut self.price_sma_200w, 200 * 7),
(&mut self.price_sma_4y, 4 * 365),
] {
let window_starts = blocks.count.start_vec(period);
sma.compute_all(blocks, prices, starting_indexes, exit, |v| {
@@ -40,22 +40,22 @@ impl Vecs {
}
for (ema, period) in [
(&mut self.price_1w_ema, 7),
(&mut self.price_8d_ema, 8),
(&mut self.price_12d_ema, 12),
(&mut self.price_13d_ema, 13),
(&mut self.price_21d_ema, 21),
(&mut self.price_26d_ema, 26),
(&mut self.price_1m_ema, 30),
(&mut self.price_34d_ema, 34),
(&mut self.price_55d_ema, 55),
(&mut self.price_89d_ema, 89),
(&mut self.price_144d_ema, 144),
(&mut self.price_200d_ema, 200),
(&mut self.price_1y_ema, 365),
(&mut self.price_2y_ema, 2 * 365),
(&mut self.price_200w_ema, 200 * 7),
(&mut self.price_4y_ema, 4 * 365),
(&mut self.price_ema_1w, 7),
(&mut self.price_ema_8d, 8),
(&mut self.price_ema_12d, 12),
(&mut self.price_ema_13d, 13),
(&mut self.price_ema_21d, 21),
(&mut self.price_ema_26d, 26),
(&mut self.price_ema_1m, 30),
(&mut self.price_ema_34d, 34),
(&mut self.price_ema_55d, 55),
(&mut self.price_ema_89d, 89),
(&mut self.price_ema_144d, 144),
(&mut self.price_ema_200d, 200),
(&mut self.price_ema_1y, 365),
(&mut self.price_ema_2y, 2 * 365),
(&mut self.price_ema_200w, 200 * 7),
(&mut self.price_ema_4y, 4 * 365),
] {
let window_starts = blocks.count.start_vec(period);
ema.compute_all(blocks, prices, starting_indexes, exit, |v| {

View File

@@ -25,66 +25,66 @@ impl Vecs {
};
}
let price_200d_sma = import!("price_200d_sma");
let price_350d_sma = import!("price_350d_sma");
let price_sma_200d = import!("price_sma_200d");
let price_sma_350d = import!("price_sma_350d");
let price_200d_sma_source = &price_200d_sma.price.cents;
let price_200d_sma_x2_4 = Price::from_cents_source::<CentsTimesTenths<24>>(
"price_200d_sma_x2_4",
let price_sma_200d_source = &price_sma_200d.price.cents;
let price_sma_200d_x2_4 = Price::from_cents_source::<CentsTimesTenths<24>>(
"price_sma_200d_x2_4",
version,
price_200d_sma_source,
price_sma_200d_source,
);
let price_200d_sma_x0_8 = Price::from_cents_source::<CentsTimesTenths<8>>(
"price_200d_sma_x0_8",
let price_sma_200d_x0_8 = Price::from_cents_source::<CentsTimesTenths<8>>(
"price_sma_200d_x0_8",
version,
price_200d_sma_source,
price_sma_200d_source,
);
let price_350d_sma_source = &price_350d_sma.price.cents;
let price_350d_sma_x2 = Price::from_cents_source::<CentsTimesTenths<20>>(
"price_350d_sma_x2",
let price_sma_350d_source = &price_sma_350d.price.cents;
let price_sma_350d_x2 = Price::from_cents_source::<CentsTimesTenths<20>>(
"price_sma_350d_x2",
version,
price_350d_sma_source,
price_sma_350d_source,
);
Ok(Self {
price_1w_sma: import!("price_1w_sma"),
price_8d_sma: import!("price_8d_sma"),
price_13d_sma: import!("price_13d_sma"),
price_21d_sma: import!("price_21d_sma"),
price_1m_sma: import!("price_1m_sma"),
price_34d_sma: import!("price_34d_sma"),
price_55d_sma: import!("price_55d_sma"),
price_89d_sma: import!("price_89d_sma"),
price_111d_sma: import!("price_111d_sma"),
price_144d_sma: import!("price_144d_sma"),
price_200d_sma,
price_350d_sma,
price_1y_sma: import!("price_1y_sma"),
price_2y_sma: import!("price_2y_sma"),
price_200w_sma: import!("price_200w_sma"),
price_4y_sma: import!("price_4y_sma"),
price_sma_1w: import!("price_sma_1w"),
price_sma_8d: import!("price_sma_8d"),
price_sma_13d: import!("price_sma_13d"),
price_sma_21d: import!("price_sma_21d"),
price_sma_1m: import!("price_sma_1m"),
price_sma_34d: import!("price_sma_34d"),
price_sma_55d: import!("price_sma_55d"),
price_sma_89d: import!("price_sma_89d"),
price_sma_111d: import!("price_sma_111d"),
price_sma_144d: import!("price_sma_144d"),
price_sma_200d,
price_sma_350d,
price_sma_1y: import!("price_sma_1y"),
price_sma_2y: import!("price_sma_2y"),
price_sma_200w: import!("price_sma_200w"),
price_sma_4y: import!("price_sma_4y"),
price_1w_ema: import!("price_1w_ema"),
price_8d_ema: import!("price_8d_ema"),
price_12d_ema: import!("price_12d_ema"),
price_13d_ema: import!("price_13d_ema"),
price_21d_ema: import!("price_21d_ema"),
price_26d_ema: import!("price_26d_ema"),
price_1m_ema: import!("price_1m_ema"),
price_34d_ema: import!("price_34d_ema"),
price_55d_ema: import!("price_55d_ema"),
price_89d_ema: import!("price_89d_ema"),
price_144d_ema: import!("price_144d_ema"),
price_200d_ema: import!("price_200d_ema"),
price_1y_ema: import!("price_1y_ema"),
price_2y_ema: import!("price_2y_ema"),
price_200w_ema: import!("price_200w_ema"),
price_4y_ema: import!("price_4y_ema"),
price_ema_1w: import!("price_ema_1w"),
price_ema_8d: import!("price_ema_8d"),
price_ema_12d: import!("price_ema_12d"),
price_ema_13d: import!("price_ema_13d"),
price_ema_21d: import!("price_ema_21d"),
price_ema_26d: import!("price_ema_26d"),
price_ema_1m: import!("price_ema_1m"),
price_ema_34d: import!("price_ema_34d"),
price_ema_55d: import!("price_ema_55d"),
price_ema_89d: import!("price_ema_89d"),
price_ema_144d: import!("price_ema_144d"),
price_ema_200d: import!("price_ema_200d"),
price_ema_1y: import!("price_ema_1y"),
price_ema_2y: import!("price_ema_2y"),
price_ema_200w: import!("price_ema_200w"),
price_ema_4y: import!("price_ema_4y"),
price_200d_sma_x2_4,
price_200d_sma_x0_8,
price_350d_sma_x2,
price_sma_200d_x2_4,
price_sma_200d_x0_8,
price_sma_350d_x2,
})
}
}

View File

@@ -7,41 +7,41 @@ use crate::internal::{ComputedFromHeightPriceWithRatioExtended, LazyFromHeight,
/// Simple and exponential moving average metrics
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub price_1w_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_8d_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_13d_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_21d_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_1m_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_34d_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_55d_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_89d_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_111d_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_144d_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_200d_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_350d_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_1y_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_2y_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_200w_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_4y_sma: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_1w: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_8d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_13d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_21d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_1m: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_34d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_55d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_89d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_111d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_144d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_200d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_350d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_1y: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_2y: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_200w: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_sma_4y: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_1w_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_8d_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_12d_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_13d_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_21d_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_26d_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_1m_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_34d_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_55d_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_89d_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_144d_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_200d_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_1y_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_2y_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_200w_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_4y_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_1w: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_8d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_12d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_13d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_21d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_26d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_1m: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_34d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_55d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_89d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_144d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_200d: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_1y: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_2y: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_200w: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_ema_4y: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_200d_sma_x2_4: Price<LazyFromHeight<Cents, Cents>>,
pub price_200d_sma_x0_8: Price<LazyFromHeight<Cents, Cents>>,
pub price_350d_sma_x2: Price<LazyFromHeight<Cents, Cents>>,
pub price_sma_200d_x2_4: Price<LazyFromHeight<Cents, Cents>>,
pub price_sma_200d_x0_8: Price<LazyFromHeight<Cents, Cents>>,
pub price_sma_350d_x2: Price<LazyFromHeight<Cents, Cents>>,
}

View File

@@ -16,10 +16,10 @@ impl Vecs {
let price = &prices.price.cents.height;
for (min_vec, max_vec, starts) in [
(&mut self.price_1w_min.cents.height, &mut self.price_1w_max.cents.height, &blocks.count.height_1w_ago),
(&mut self.price_2w_min.cents.height, &mut self.price_2w_max.cents.height, &blocks.count.height_2w_ago),
(&mut self.price_1m_min.cents.height, &mut self.price_1m_max.cents.height, &blocks.count.height_1m_ago),
(&mut self.price_1y_min.cents.height, &mut self.price_1y_max.cents.height, &blocks.count.height_1y_ago),
(&mut self.price_min_1w.cents.height, &mut self.price_max_1w.cents.height, &blocks.count.height_1w_ago),
(&mut self.price_min_2w.cents.height, &mut self.price_max_2w.cents.height, &blocks.count.height_2w_ago),
(&mut self.price_min_1m.cents.height, &mut self.price_max_1m.cents.height, &blocks.count.height_1m_ago),
(&mut self.price_min_1y.cents.height, &mut self.price_max_1y.cents.height, &blocks.count.height_1y_ago),
] {
min_vec.compute_rolling_min_from_starts(starting_indexes.height, starts, price, exit)?;
max_vec.compute_rolling_max_from_starts(starting_indexes.height, starts, price, exit)?;
@@ -47,18 +47,18 @@ impl Vecs {
)?;
// 2w rolling sum of true range
self.price_true_range_2w_sum.height.compute_rolling_sum(
self.price_true_range_sum_2w.height.compute_rolling_sum(
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.price_true_range.height,
exit,
)?;
self.price_2w_choppiness_index.height.compute_transform4(
self.price_choppiness_index_2w.height.compute_transform4(
starting_indexes.height,
&self.price_true_range_2w_sum.height,
&self.price_2w_max.cents.height,
&self.price_2w_min.cents.height,
&self.price_true_range_sum_2w.height,
&self.price_max_2w.cents.height,
&self.price_min_2w.cents.height,
&blocks.count.height_2w_ago,
|(h, tr_sum, max, min, window_start, ..)| {
let range = f64::from(max) - f64::from(min);

View File

@@ -10,22 +10,22 @@ impl Vecs {
let v1 = Version::ONE;
Ok(Self {
price_1w_min: Price::forced_import(db, "price_1w_min", version + v1, indexes)?,
price_1w_max: Price::forced_import(db, "price_1w_max", version + v1, indexes)?,
price_2w_min: Price::forced_import(db, "price_2w_min", version + v1, indexes)?,
price_2w_max: Price::forced_import(db, "price_2w_max", version + v1, indexes)?,
price_1m_min: Price::forced_import(db, "price_1m_min", version + v1, indexes)?,
price_1m_max: Price::forced_import(db, "price_1m_max", version + v1, indexes)?,
price_1y_min: Price::forced_import(db, "price_1y_min", version + v1, indexes)?,
price_1y_max: Price::forced_import(db, "price_1y_max", version + v1, indexes)?,
price_min_1w: Price::forced_import(db, "price_min_1w", version + v1, indexes)?,
price_max_1w: Price::forced_import(db, "price_max_1w", version + v1, indexes)?,
price_min_2w: Price::forced_import(db, "price_min_2w", version + v1, indexes)?,
price_max_2w: Price::forced_import(db, "price_max_2w", version + v1, indexes)?,
price_min_1m: Price::forced_import(db, "price_min_1m", version + v1, indexes)?,
price_max_1m: Price::forced_import(db, "price_max_1m", version + v1, indexes)?,
price_min_1y: Price::forced_import(db, "price_min_1y", version + v1, indexes)?,
price_max_1y: Price::forced_import(db, "price_max_1y", version + v1, indexes)?,
price_true_range: ComputedFromHeight::forced_import(
db, "price_true_range", version + v1, indexes,
)?,
price_true_range_2w_sum: ComputedFromHeight::forced_import(
db, "price_true_range_2w_sum", version + v1, indexes,
price_true_range_sum_2w: ComputedFromHeight::forced_import(
db, "price_true_range_sum_2w", version + v1, indexes,
)?,
price_2w_choppiness_index: ComputedFromHeight::forced_import(
db, "price_2w_choppiness_index", version + v1, indexes,
price_choppiness_index_2w: ComputedFromHeight::forced_import(
db, "price_choppiness_index_2w", version + v1, indexes,
)?,
})
}

View File

@@ -7,15 +7,15 @@ use crate::internal::{ComputedFromHeight, Price};
/// Price range and choppiness metrics
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub price_1w_min: Price<ComputedFromHeight<Cents, M>>,
pub price_1w_max: Price<ComputedFromHeight<Cents, M>>,
pub price_2w_min: Price<ComputedFromHeight<Cents, M>>,
pub price_2w_max: Price<ComputedFromHeight<Cents, M>>,
pub price_1m_min: Price<ComputedFromHeight<Cents, M>>,
pub price_1m_max: Price<ComputedFromHeight<Cents, M>>,
pub price_1y_min: Price<ComputedFromHeight<Cents, M>>,
pub price_1y_max: Price<ComputedFromHeight<Cents, M>>,
pub price_min_1w: Price<ComputedFromHeight<Cents, M>>,
pub price_max_1w: Price<ComputedFromHeight<Cents, M>>,
pub price_min_2w: Price<ComputedFromHeight<Cents, M>>,
pub price_max_2w: Price<ComputedFromHeight<Cents, M>>,
pub price_min_1m: Price<ComputedFromHeight<Cents, M>>,
pub price_max_1m: Price<ComputedFromHeight<Cents, M>>,
pub price_min_1y: Price<ComputedFromHeight<Cents, M>>,
pub price_max_1y: Price<ComputedFromHeight<Cents, M>>,
pub price_true_range: ComputedFromHeight<StoredF32, M>,
pub price_true_range_2w_sum: ComputedFromHeight<StoredF32, M>,
pub price_2w_choppiness_index: ComputedFromHeight<StoredF32, M>,
pub price_true_range_sum_2w: ComputedFromHeight<StoredF32, M>,
pub price_choppiness_index_2w: ComputedFromHeight<StoredF32, M>,
}

View File

@@ -1,9 +1,9 @@
use brk_error::Result;
use brk_types::{Dollars, StoredF32};
use brk_types::{BasisPointsSigned32, Dollars, StoredF32};
use vecdb::Exit;
use super::Vecs;
use crate::{ComputeIndexes, blocks, internal::PercentageDiffDollars, market::lookback, prices};
use crate::{ComputeIndexes, blocks, internal::RatioDiffDollarsBps32, market::lookback, prices};
impl Vecs {
pub(crate) fn compute(
@@ -16,11 +16,11 @@ impl Vecs {
) -> Result<()> {
// Compute price returns at height level
for ((returns, _), (lookback_price, _)) in self
.price_returns
.price_return
.iter_mut_with_days()
.zip(lookback.price_ago.iter_with_days())
.zip(lookback.price_lookback.iter_with_days())
{
returns.compute_binary::<Dollars, Dollars, PercentageDiffDollars>(
returns.compute_binary::<Dollars, Dollars, RatioDiffDollarsBps32>(
starting_indexes.height,
&prices.price.usd.height,
&lookback_price.usd.height,
@@ -29,44 +29,48 @@ impl Vecs {
}
// CAGR computed from returns at height level (2y+ periods only)
let price_returns_dca = self.price_returns.as_dca_period();
for (cagr, returns, days) in self.cagr.zip_mut_with_period(&price_returns_dca) {
let years = days as f32 / 365.0;
cagr.height.compute_transform(
let price_return_dca = self.price_return.as_dca_period();
for (cagr, returns, days) in self.price_cagr.zip_mut_with_period(&price_return_dca) {
let years = days as f64 / 365.0;
cagr.bps.height.compute_transform(
starting_indexes.height,
&returns.height,
&returns.bps.height,
|(h, r, ..)| {
let v = ((*r / 100.0 + 1.0).powf(1.0 / years) - 1.0) * 100.0;
(h, StoredF32::from(v))
let ratio = f64::from(r);
let v = (ratio + 1.0).powf(1.0 / years) - 1.0;
(h, BasisPointsSigned32::from(v))
},
exit,
)?;
}
let _24h_price_returns_height = &self.price_returns._24h.height;
let _24h_price_return_height = &self.price_return._24h.bps.height;
self._1d_returns_1w_sd
.compute_all(blocks, starting_indexes, exit, _24h_price_returns_height)?;
self._1d_returns_1m_sd
.compute_all(blocks, starting_indexes, exit, _24h_price_returns_height)?;
self._1d_returns_1y_sd
.compute_all(blocks, starting_indexes, exit, _24h_price_returns_height)?;
self.price_return_24h_sd_1w
.compute_all(blocks, starting_indexes, exit, _24h_price_return_height)?;
self.price_return_24h_sd_1m
.compute_all(blocks, starting_indexes, exit, _24h_price_return_height)?;
self.price_return_24h_sd_1y
.compute_all(blocks, starting_indexes, exit, _24h_price_return_height)?;
// Downside returns: min(return, 0)
self.downside_returns.compute_transform(
self.price_downside_24h.compute_transform(
starting_indexes.height,
_24h_price_returns_height,
|(i, ret, ..)| (i, StoredF32::from((*ret).min(0.0))),
_24h_price_return_height,
|(i, ret, ..)| {
let v = f64::from(ret).min(0.0);
(i, StoredF32::from(v as f32))
},
exit,
)?;
// Downside deviation (SD of downside returns)
self.downside_1w_sd
.compute_all(blocks, starting_indexes, exit, &self.downside_returns)?;
self.downside_1m_sd
.compute_all(blocks, starting_indexes, exit, &self.downside_returns)?;
self.downside_1y_sd
.compute_all(blocks, starting_indexes, exit, &self.downside_returns)?;
self.price_downside_24h_sd_1w
.compute_all(blocks, starting_indexes, exit, &self.price_downside_24h)?;
self.price_downside_24h_sd_1m
.compute_all(blocks, starting_indexes, exit, &self.price_downside_24h)?;
self.price_downside_24h_sd_1y
.compute_all(blocks, starting_indexes, exit, &self.price_downside_24h)?;
Ok(())
}

View File

@@ -6,8 +6,7 @@ use super::super::lookback::ByLookbackPeriod;
use super::Vecs;
use crate::{
indexes,
internal::ComputedFromHeight,
internal::ComputedFromHeightStdDev,
internal::{Bps32ToFloat, Bps32ToPercent, ComputedFromHeightStdDev, PercentFromHeight},
market::dca::ByDcaCagr,
};
@@ -19,75 +18,86 @@ impl Vecs {
) -> Result<Self> {
let v1 = Version::ONE;
let price_returns = ByLookbackPeriod::try_new(|name, _days| {
ComputedFromHeight::forced_import(
let price_return = ByLookbackPeriod::try_new(|name, _days| {
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
db,
&format!("{name}_price_returns"),
&format!("price_return_{name}"),
version,
indexes,
)
})?;
// CAGR (computed, 2y+ only)
let cagr = ByDcaCagr::try_new(|name, _days| {
ComputedFromHeight::forced_import(db, &format!("{name}_cagr"), version, indexes)
let price_cagr = ByDcaCagr::try_new(|name, _days| {
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
db,
&format!("price_cagr_{name}"),
version,
indexes,
)
})?;
let _1d_returns_1w_sd = ComputedFromHeightStdDev::forced_import(
let price_return_24h_sd_1w = ComputedFromHeightStdDev::forced_import(
db,
"1d_returns_1w_sd",
"price_return_24h",
"1w",
7,
version + v1,
indexes,
)?;
let _1d_returns_1m_sd = ComputedFromHeightStdDev::forced_import(
let price_return_24h_sd_1m = ComputedFromHeightStdDev::forced_import(
db,
"1d_returns_1m_sd",
"price_return_24h",
"1m",
30,
version + v1,
indexes,
)?;
let _1d_returns_1y_sd = ComputedFromHeightStdDev::forced_import(
let price_return_24h_sd_1y = ComputedFromHeightStdDev::forced_import(
db,
"1d_returns_1y_sd",
"price_return_24h",
"1y",
365,
version + v1,
indexes,
)?;
let downside_returns = EagerVec::forced_import(db, "downside_returns", version)?;
let downside_1w_sd = ComputedFromHeightStdDev::forced_import(
let price_downside_24h = EagerVec::forced_import(db, "price_downside_24h", version)?;
let price_downside_24h_sd_1w = ComputedFromHeightStdDev::forced_import(
db,
"downside_1w_sd",
"price_downside_24h",
"1w",
7,
version + v1,
indexes,
)?;
let downside_1m_sd = ComputedFromHeightStdDev::forced_import(
let price_downside_24h_sd_1m = ComputedFromHeightStdDev::forced_import(
db,
"downside_1m_sd",
"price_downside_24h",
"1m",
30,
version + v1,
indexes,
)?;
let downside_1y_sd = ComputedFromHeightStdDev::forced_import(
let price_downside_24h_sd_1y = ComputedFromHeightStdDev::forced_import(
db,
"downside_1y_sd",
"price_downside_24h",
"1y",
365,
version + v1,
indexes,
)?;
Ok(Self {
price_returns,
cagr,
_1d_returns_1w_sd,
_1d_returns_1m_sd,
_1d_returns_1y_sd,
downside_returns,
downside_1w_sd,
downside_1m_sd,
downside_1y_sd,
price_return,
price_cagr,
price_return_24h_sd_1w,
price_return_24h_sd_1m,
price_return_24h_sd_1y,
price_downside_24h,
price_downside_24h_sd_1w,
price_downside_24h_sd_1m,
price_downside_24h_sd_1y,
})
}
}

View File

@@ -1,28 +1,28 @@
use brk_traversable::Traversable;
use brk_types::{Height, StoredF32};
use brk_types::{BasisPointsSigned32, Height, StoredF32};
use vecdb::{EagerVec, PcoVec, Rw, StorageMode};
use crate::{
internal::{ComputedFromHeight, ComputedFromHeightStdDev},
internal::{ComputedFromHeight, ComputedFromHeightStdDev, PercentFromHeight},
market::{dca::ByDcaCagr, lookback::ByLookbackPeriod},
};
/// Price returns, CAGR, and returns standard deviation metrics
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub price_returns: ByLookbackPeriod<ComputedFromHeight<StoredF32, M>>,
pub price_return: ByLookbackPeriod<PercentFromHeight<BasisPointsSigned32, M>>,
// CAGR (computed from returns, 2y+ only)
pub cagr: ByDcaCagr<ComputedFromHeight<StoredF32, M>>,
pub price_cagr: ByDcaCagr<PercentFromHeight<BasisPointsSigned32, M>>,
// Returns standard deviation (computed from 1d returns)
pub _1d_returns_1w_sd: ComputedFromHeightStdDev<M>,
pub _1d_returns_1m_sd: ComputedFromHeightStdDev<M>,
pub _1d_returns_1y_sd: ComputedFromHeightStdDev<M>,
// Returns standard deviation (computed from 24h returns)
pub price_return_24h_sd_1w: ComputedFromHeightStdDev<M>,
pub price_return_24h_sd_1m: ComputedFromHeightStdDev<M>,
pub price_return_24h_sd_1y: ComputedFromHeightStdDev<M>,
// Downside returns and deviation (for Sortino ratio)
pub downside_returns: M::Stored<EagerVec<PcoVec<Height, StoredF32>>>,
pub downside_1w_sd: ComputedFromHeightStdDev<M>,
pub downside_1m_sd: ComputedFromHeightStdDev<M>,
pub downside_1y_sd: ComputedFromHeightStdDev<M>,
pub price_downside_24h: M::Stored<EagerVec<PcoVec<Height, StoredF32>>>,
pub price_downside_24h_sd_1w: ComputedFromHeightStdDev<M>,
pub price_downside_24h_sd_1m: ComputedFromHeightStdDev<M>,
pub price_downside_24h_sd_1y: ComputedFromHeightStdDev<M>,
}

View File

@@ -14,17 +14,17 @@ impl Vecs {
) -> Result<()> {
// Sharpe ratios: returns / volatility
for (out, ret, vol) in [
(&mut self.sharpe_1w, &returns.price_returns._1w.height, &self.price_1w_volatility.height),
(&mut self.sharpe_1m, &returns.price_returns._1m.height, &self.price_1m_volatility.height),
(&mut self.sharpe_1y, &returns.price_returns._1y.height, &self.price_1y_volatility.height),
(&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),
] {
compute_ratio(&mut out.height, starting_indexes_height, ret, vol, exit)?;
}
// Sortino ratios: returns / downside volatility
compute_ratio(&mut self.sortino_1w.height, starting_indexes_height, &returns.price_returns._1w.height, &returns.downside_1w_sd.sd.height, exit)?;
compute_ratio(&mut self.sortino_1m.height, starting_indexes_height, &returns.price_returns._1m.height, &returns.downside_1m_sd.sd.height, exit)?;
compute_ratio(&mut self.sortino_1y.height, starting_indexes_height, &returns.price_returns._1y.height, &returns.downside_1y_sd.sd.height, exit)?;
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)?;
Ok(())
}

View File

@@ -18,51 +18,51 @@ impl Vecs {
) -> Result<Self> {
let v2 = Version::TWO;
let price_1w_volatility = LazyFromHeight::from_computed::<TimesSqrt<Days7>>(
"price_1w_volatility",
let price_volatility_1w = LazyFromHeight::from_computed::<TimesSqrt<Days7>>(
"price_volatility_1w",
version + v2,
returns._1d_returns_1w_sd.sd.height.read_only_boxed_clone(),
&returns._1d_returns_1w_sd.sd,
returns.price_return_24h_sd_1w.sd.height.read_only_boxed_clone(),
&returns.price_return_24h_sd_1w.sd,
);
let price_1m_volatility = LazyFromHeight::from_computed::<TimesSqrt<Days30>>(
"price_1m_volatility",
let price_volatility_1m = LazyFromHeight::from_computed::<TimesSqrt<Days30>>(
"price_volatility_1m",
version + v2,
returns._1d_returns_1m_sd.sd.height.read_only_boxed_clone(),
&returns._1d_returns_1m_sd.sd,
returns.price_return_24h_sd_1m.sd.height.read_only_boxed_clone(),
&returns.price_return_24h_sd_1m.sd,
);
let price_1y_volatility = LazyFromHeight::from_computed::<TimesSqrt<Days365>>(
"price_1y_volatility",
let price_volatility_1y = LazyFromHeight::from_computed::<TimesSqrt<Days365>>(
"price_volatility_1y",
version + v2,
returns._1d_returns_1y_sd.sd.height.read_only_boxed_clone(),
&returns._1d_returns_1y_sd.sd,
returns.price_return_24h_sd_1y.sd.height.read_only_boxed_clone(),
&returns.price_return_24h_sd_1y.sd,
);
let sharpe_1w =
ComputedFromHeight::forced_import(db, "sharpe_1w", version + v2, indexes)?;
let sharpe_1m =
ComputedFromHeight::forced_import(db, "sharpe_1m", version + v2, indexes)?;
let sharpe_1y =
ComputedFromHeight::forced_import(db, "sharpe_1y", version + v2, indexes)?;
let price_sharpe_1w =
ComputedFromHeight::forced_import(db, "price_sharpe_1w", version + v2, indexes)?;
let price_sharpe_1m =
ComputedFromHeight::forced_import(db, "price_sharpe_1m", version + v2, indexes)?;
let price_sharpe_1y =
ComputedFromHeight::forced_import(db, "price_sharpe_1y", version + v2, indexes)?;
let sortino_1w =
ComputedFromHeight::forced_import(db, "sortino_1w", version + v2, indexes)?;
let sortino_1m =
ComputedFromHeight::forced_import(db, "sortino_1m", version + v2, indexes)?;
let sortino_1y =
ComputedFromHeight::forced_import(db, "sortino_1y", version + v2, indexes)?;
let price_sortino_1w =
ComputedFromHeight::forced_import(db, "price_sortino_1w", version + v2, indexes)?;
let price_sortino_1m =
ComputedFromHeight::forced_import(db, "price_sortino_1m", version + v2, indexes)?;
let price_sortino_1y =
ComputedFromHeight::forced_import(db, "price_sortino_1y", version + v2, indexes)?;
Ok(Self {
price_1w_volatility,
price_1m_volatility,
price_1y_volatility,
sharpe_1w,
sharpe_1m,
sharpe_1y,
sortino_1w,
sortino_1m,
sortino_1y,
price_volatility_1w,
price_volatility_1m,
price_volatility_1y,
price_sharpe_1w,
price_sharpe_1m,
price_sharpe_1y,
price_sortino_1w,
price_sortino_1m,
price_sortino_1y,
})
}
}

View File

@@ -8,15 +8,15 @@ use brk_types::StoredF32;
/// Price volatility metrics (derived from returns standard deviation)
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub price_1w_volatility: LazyFromHeight<StoredF32>,
pub price_1m_volatility: LazyFromHeight<StoredF32>,
pub price_1y_volatility: LazyFromHeight<StoredF32>,
pub price_volatility_1w: LazyFromHeight<StoredF32>,
pub price_volatility_1m: LazyFromHeight<StoredF32>,
pub price_volatility_1y: LazyFromHeight<StoredF32>,
pub sharpe_1w: ComputedFromHeight<StoredF32, M>,
pub sharpe_1m: ComputedFromHeight<StoredF32, M>,
pub sharpe_1y: ComputedFromHeight<StoredF32, M>,
pub price_sharpe_1w: ComputedFromHeight<StoredF32, M>,
pub price_sharpe_1m: ComputedFromHeight<StoredF32, M>,
pub price_sharpe_1y: ComputedFromHeight<StoredF32, M>,
pub sortino_1w: ComputedFromHeight<StoredF32, M>,
pub sortino_1m: ComputedFromHeight<StoredF32, M>,
pub sortino_1y: ComputedFromHeight<StoredF32, M>,
pub price_sortino_1w: ComputedFromHeight<StoredF32, M>,
pub price_sortino_1m: ComputedFromHeight<StoredF32, M>,
pub price_sortino_1y: ComputedFromHeight<StoredF32, M>,
}

View File

@@ -35,28 +35,28 @@ impl Vecs {
exit,
)?;
self.hash_rate_1w_sma.height.compute_rolling_average(
self.hash_rate_sma_1w.height.compute_rolling_average(
starting_indexes.height,
&count_vecs.height_1w_ago,
&self.hash_rate.height,
exit,
)?;
self.hash_rate_1m_sma.height.compute_rolling_average(
self.hash_rate_sma_1m.height.compute_rolling_average(
starting_indexes.height,
&count_vecs.height_1m_ago,
&self.hash_rate.height,
exit,
)?;
self.hash_rate_2m_sma.height.compute_rolling_average(
self.hash_rate_sma_2m.height.compute_rolling_average(
starting_indexes.height,
&count_vecs.height_2m_ago,
&self.hash_rate.height,
exit,
)?;
self.hash_rate_1y_sma.height.compute_rolling_average(
self.hash_rate_sma_1y.height.compute_rolling_average(
starting_indexes.height,
&count_vecs.height_1y_ago,
&self.hash_rate.height,
@@ -69,7 +69,7 @@ impl Vecs {
exit,
)?;
self.hash_rate_drawdown.height.compute_drawdown(
self.hash_rate_drawdown.compute_drawdown(
starting_indexes.height,
&self.hash_rate.height,
&self.hash_rate_ath.height,

View File

@@ -5,7 +5,7 @@ use vecdb::Database;
use super::Vecs;
use crate::{
indexes,
internal::ComputedFromHeight,
internal::{Bps16ToFloat, Bps16ToPercent, ComputedFromHeight, PercentFromHeight},
};
impl Vecs {
@@ -19,27 +19,27 @@ impl Vecs {
Ok(Self {
hash_rate: ComputedFromHeight::forced_import(db, "hash_rate", version + v5, indexes)?,
hash_rate_1w_sma: ComputedFromHeight::forced_import(
hash_rate_sma_1w: ComputedFromHeight::forced_import(
db,
"hash_rate_1w_sma",
"hash_rate_sma_1w",
version,
indexes,
)?,
hash_rate_1m_sma: ComputedFromHeight::forced_import(
hash_rate_sma_1m: ComputedFromHeight::forced_import(
db,
"hash_rate_1m_sma",
"hash_rate_sma_1m",
version,
indexes,
)?,
hash_rate_2m_sma: ComputedFromHeight::forced_import(
hash_rate_sma_2m: ComputedFromHeight::forced_import(
db,
"hash_rate_2m_sma",
"hash_rate_sma_2m",
version,
indexes,
)?,
hash_rate_1y_sma: ComputedFromHeight::forced_import(
hash_rate_sma_1y: ComputedFromHeight::forced_import(
db,
"hash_rate_1y_sma",
"hash_rate_sma_1y",
version,
indexes,
)?,
@@ -49,7 +49,7 @@ impl Vecs {
version,
indexes,
)?,
hash_rate_drawdown: ComputedFromHeight::forced_import(
hash_rate_drawdown: PercentFromHeight::forced_import::<Bps16ToFloat, Bps16ToPercent>(
db,
"hash_rate_drawdown",
version,

View File

@@ -1,19 +1,19 @@
use brk_traversable::Traversable;
use brk_types::{StoredF32, StoredF64};
use brk_types::{BasisPointsSigned16, StoredF32, StoredF64};
use vecdb::{Rw, StorageMode};
use crate::internal::ComputedFromHeight;
use crate::internal::{ComputedFromHeight, PercentFromHeight};
/// Mining-related metrics: hash rate, hash price, hash value
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub hash_rate: ComputedFromHeight<StoredF64, M>,
pub hash_rate_1w_sma: ComputedFromHeight<StoredF64, M>,
pub hash_rate_1m_sma: ComputedFromHeight<StoredF64, M>,
pub hash_rate_2m_sma: ComputedFromHeight<StoredF64, M>,
pub hash_rate_1y_sma: ComputedFromHeight<StoredF64, M>,
pub hash_rate_sma_1w: ComputedFromHeight<StoredF64, M>,
pub hash_rate_sma_1m: ComputedFromHeight<StoredF64, M>,
pub hash_rate_sma_2m: ComputedFromHeight<StoredF64, M>,
pub hash_rate_sma_1y: ComputedFromHeight<StoredF64, M>,
pub hash_rate_ath: ComputedFromHeight<StoredF64, M>,
pub hash_rate_drawdown: ComputedFromHeight<StoredF32, M>,
pub hash_rate_drawdown: PercentFromHeight<BasisPointsSigned16, M>,
pub hash_price_ths: ComputedFromHeight<StoredF32, M>,
pub hash_price_ths_min: ComputedFromHeight<StoredF32, M>,
pub hash_price_phs: ComputedFromHeight<StoredF32, M>,

View File

@@ -1,10 +1,10 @@
use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::{CheckedSub, HalvingEpoch, Sats, StoredF32};
use brk_types::{BasisPoints16, CheckedSub, HalvingEpoch, Sats};
use vecdb::{Exit, ReadableVec, VecIndex};
use super::Vecs;
use crate::{ComputeIndexes, blocks, indexes, prices, transactions};
use crate::{ComputeIndexes, blocks, indexes, internal::RatioSatsBp16, prices, transactions};
impl Vecs {
#[allow(clippy::too_many_arguments)]
@@ -122,14 +122,14 @@ impl Vecs {
)?;
// All-time cumulative fee dominance
self.fee_dominance.height.compute_percentage(
self.fee_dominance.compute_binary::<Sats, Sats, RatioSatsBp16>(
starting_indexes.height,
&self.fees.cumulative.sats.height,
&self.coinbase.cumulative.sats.height,
exit,
)?;
// Rolling fee dominance = sum(fees) / sum(coinbase) * 100
// Rolling fee dominance = sum(fees) / sum(coinbase)
for ((fee_dom, fees_w), coinbase_w) in self
.fee_dominance_rolling
.as_mut_array()
@@ -137,7 +137,7 @@ impl Vecs {
.zip(self.fees.rolling.as_array())
.zip(self.coinbase.rolling.as_array())
{
fee_dom.height.compute_percentage(
fee_dom.compute_binary::<Sats, Sats, RatioSatsBp16>(
starting_indexes.height,
&fees_w.sum.sats.height,
&coinbase_w.sum.sats.height,
@@ -146,30 +146,29 @@ impl Vecs {
}
// All-time cumulative subsidy dominance
self.subsidy_dominance.height.compute_percentage(
self.subsidy_dominance.compute_binary::<Sats, Sats, RatioSatsBp16>(
starting_indexes.height,
&self.subsidy.cumulative.sats.height,
&self.coinbase.cumulative.sats.height,
exit,
)?;
// Rolling subsidy dominance = 100 - fee_dominance
let hundred = StoredF32::from(100u8);
// Rolling subsidy dominance = 1 - fee_dominance
for (sub_dom, fee_dom) in self
.subsidy_dominance_rolling
.as_mut_array()
.into_iter()
.zip(self.fee_dominance_rolling.as_array())
{
sub_dom.height.compute_transform(
sub_dom.bps.height.compute_transform(
starting_indexes.height,
&fee_dom.height,
|(height, fee_dom, _)| (height, hundred - fee_dom),
&fee_dom.bps.height,
|(height, fee, _)| (height, BasisPoints16::ONE - fee),
exit,
)?;
}
self.subsidy_usd_1y_sma.cents.height.compute_rolling_average(
self.subsidy_sma_1y.cents.height.compute_rolling_average(
starting_indexes.height,
&count_vecs.height_1y_ago,
&self.subsidy.base.cents.height,

View File

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

View File

@@ -1,10 +1,10 @@
use brk_traversable::Traversable;
use brk_types::{Cents, StoredF32};
use brk_types::{BasisPoints16, Cents};
use vecdb::{Rw, StorageMode};
use crate::internal::{
ComputedFromHeight, FiatFromHeight, RollingWindows, ValueFromHeightFull,
ValueFromHeightCumulativeSum,
FiatFromHeight, PercentFromHeight, PercentRollingWindows, RollingWindows,
ValueFromHeightFull, ValueFromHeightCumulativeSum,
};
/// Coinbase/subsidy/rewards metrics
@@ -14,9 +14,9 @@ pub struct Vecs<M: StorageMode = Rw> {
pub subsidy: ValueFromHeightFull<M>,
pub fees: ValueFromHeightFull<M>,
pub unclaimed_rewards: ValueFromHeightCumulativeSum<M>,
pub fee_dominance: ComputedFromHeight<StoredF32, M>,
pub fee_dominance_rolling: RollingWindows<StoredF32, M>,
pub subsidy_dominance: ComputedFromHeight<StoredF32, M>,
pub subsidy_dominance_rolling: RollingWindows<StoredF32, M>,
pub subsidy_usd_1y_sma: FiatFromHeight<Cents, M>,
pub fee_dominance: PercentFromHeight<BasisPoints16, M>,
pub fee_dominance_rolling: PercentRollingWindows<BasisPoints16, M>,
pub subsidy_dominance: PercentFromHeight<BasisPoints16, M>,
pub subsidy_dominance_rolling: PercentRollingWindows<BasisPoints16, M>,
pub subsidy_sma_1y: FiatFromHeight<Cents, M>,
}

View File

@@ -1,14 +1,15 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, PoolSlug, StoredF32, StoredU16, StoredU32};
use brk_types::{BasisPoints16, Height, PoolSlug, StoredU32};
use vecdb::{AnyVec, BinaryTransform, Database, Exit, ReadableVec, Rw, StorageMode, VecIndex, Version};
use crate::{
blocks,
indexes::{self, ComputeIndexes},
internal::{
ComputedFromHeightCumulativeSum, ComputedFromHeight, MaskSats, PercentageU32F32,
RollingWindows, ValueFromHeightCumulativeSum,
Bp16ToFloat, Bp16ToPercent, ComputedFromHeightCumulativeSum, ComputedFromHeight, MaskSats,
PercentFromHeight, PercentRollingWindows, RatioU32Bp16, RollingWindows,
ValueFromHeightCumulativeSum,
},
mining, prices,
};
@@ -22,10 +23,9 @@ pub struct Vecs<M: StorageMode = Rw> {
pub subsidy: ValueFromHeightCumulativeSum<M>,
pub fee: ValueFromHeightCumulativeSum<M>,
pub coinbase: ValueFromHeightCumulativeSum<M>,
pub dominance: ComputedFromHeight<StoredF32, M>,
pub dominance_rolling: RollingWindows<StoredF32, M>,
pub blocks_since_block: ComputedFromHeight<StoredU32, M>,
pub days_since_block: ComputedFromHeight<StoredU16, M>,
pub dominance: PercentFromHeight<BasisPoints16, M>,
pub dominance_rolling: PercentRollingWindows<BasisPoints16, M>,
pub blocks_since_last_mined: ComputedFromHeight<StoredU32, M>,
}
impl Vecs {
@@ -58,9 +58,9 @@ impl Vecs {
ValueFromHeightCumulativeSum::forced_import(db, &suffix("coinbase"), version, indexes)?;
let dominance =
ComputedFromHeight::forced_import(db, &suffix("dominance"), version, indexes)?;
PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(db, &suffix("dominance"), version, indexes)?;
let dominance_rolling =
RollingWindows::forced_import(db, &suffix("dominance"), version, indexes)?;
PercentRollingWindows::forced_import::<Bp16ToFloat, Bp16ToPercent>(db, &suffix("dominance"), version, indexes)?;
Ok(Self {
dominance,
@@ -71,15 +71,9 @@ impl Vecs {
coinbase,
subsidy,
fee,
blocks_since_block: ComputedFromHeight::forced_import(
blocks_since_last_mined: ComputedFromHeight::forced_import(
db,
&suffix("blocks_since_block"),
version,
indexes,
)?,
days_since_block: ComputedFromHeight::forced_import(
db,
&suffix("days_since_block"),
&suffix("blocks_since_last_mined"),
version,
indexes,
)?,
@@ -126,7 +120,7 @@ impl Vecs {
)?;
self.dominance
.compute_binary::<StoredU32, StoredU32, PercentageU32F32>(
.compute_binary::<StoredU32, StoredU32, RatioU32Bp16>(
starting_indexes.height,
&self.blocks_mined.cumulative.height,
&blocks.count.block_count.cumulative.height,
@@ -140,7 +134,7 @@ impl Vecs {
.zip(self.blocks_mined_sum.as_array())
.zip(blocks.count.block_count_sum.as_array())
{
dom.compute_binary::<StoredU32, StoredU32, PercentageU32F32>(
dom.compute_binary::<StoredU32, StoredU32, RatioU32Bp16>(
starting_indexes.height,
&mined.height,
&total.height,
@@ -198,19 +192,19 @@ impl Vecs {
{
let resume_from = self
.blocks_since_block
.blocks_since_last_mined
.height
.len()
.min(starting_indexes.height.to_usize());
let mut prev = if resume_from > 0 {
self.blocks_since_block
self.blocks_since_last_mined
.height
.collect_one_at(resume_from - 1)
.unwrap()
} else {
StoredU32::ZERO
};
self.blocks_since_block.height.compute_transform(
self.blocks_since_last_mined.height.compute_transform(
starting_indexes.height,
&self.blocks_mined.height,
|(h, mined, ..)| {
@@ -226,18 +220,6 @@ impl Vecs {
)?;
}
self.days_since_block.height.compute_transform(
starting_indexes.height,
&self.blocks_since_block.height,
|(h, blocks, ..)| {
(
h,
StoredU16::from(u16::try_from(*blocks).unwrap_or(u16::MAX)),
)
},
exit,
)?;
Ok(())
}
}

View File

@@ -1,16 +1,20 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{StoredF32, Version};
use brk_types::{BasisPoints16, Version};
use vecdb::{Database, Exit, Rw, StorageMode};
use crate::{ComputeIndexes, indexes, internal::{ComputedFromHeight, RatioU64F32}, outputs};
use crate::{
ComputeIndexes, indexes,
internal::{Bp16ToFloat, Bp16ToPercent, PercentFromHeight, RatioU64Bp16},
outputs,
};
use super::count::Vecs as CountVecs;
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub taproot: ComputedFromHeight<StoredF32, M>,
pub segwit: ComputedFromHeight<StoredF32, M>,
pub taproot: PercentFromHeight<BasisPoints16, M>,
pub segwit: PercentFromHeight<BasisPoints16, M>,
}
impl Vecs {
@@ -20,13 +24,18 @@ impl Vecs {
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self {
taproot: ComputedFromHeight::forced_import(
taproot: PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
db,
"taproot_adoption",
version,
indexes,
)?,
segwit: ComputedFromHeight::forced_import(db, "segwit_adoption", version, indexes)?,
segwit: PercentFromHeight::forced_import::<Bp16ToFloat, Bp16ToPercent>(
db,
"segwit_adoption",
version,
indexes,
)?,
})
}
@@ -37,14 +46,14 @@ impl Vecs {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self.taproot.compute_binary::<_, _, RatioU64F32>(
self.taproot.compute_binary::<_, _, RatioU64Bp16>(
starting_indexes.height,
&count.p2tr.height,
&outputs_count.total_count.full.sum_cumulative.sum.0,
exit,
)?;
self.segwit.compute_binary::<_, _, RatioU64F32>(
self.segwit.compute_binary::<_, _, RatioU64Bp16>(
starting_indexes.height,
&count.segwit.height,
&outputs_count.total_count.full.sum_cumulative.sum.0,

View File

@@ -1,5 +1,4 @@
use brk_error::Result;
use brk_types::StoredF32;
use vecdb::Exit;
use super::Vecs;
@@ -22,9 +21,9 @@ impl Vecs {
self.burned
.compute(scripts, mining, &blocks.count, prices, starting_indexes, exit)?;
// 2. Compute inflation rate at height level: (supply[h] - supply[1y_ago]) / supply[1y_ago] * 100
// 2. Compute inflation rate: (supply[h] / supply[1y_ago]) - 1
let circulating_supply = &distribution.utxo_cohorts.all.metrics.supply.total.sats;
self.inflation.height.compute_rolling_percentage_change(
self.inflation_rate.bps.height.compute_rolling_ratio_change(
starting_indexes.height,
&blocks.count.height_1y_ago,
&circulating_supply.height,
@@ -35,10 +34,11 @@ impl Vecs {
self.velocity
.compute(blocks, transactions, distribution, starting_indexes, exit)?;
// 4. Compute cap growth rates at height level using 1y lookback
// 4. Compute cap growth rates using 1y lookback
self.market_cap_growth_rate
.bps
.height
.compute_rolling_percentage_change(
.compute_rolling_ratio_change(
starting_indexes.height,
&blocks.count.height_1y_ago,
&self.market_cap.height,
@@ -46,20 +46,20 @@ impl Vecs {
)?;
self.realized_cap_growth_rate
.bps
.height
.compute_rolling_percentage_change(
.compute_rolling_ratio_change(
starting_indexes.height,
&blocks.count.height_1y_ago,
&distribution.utxo_cohorts.all.metrics.realized.realized_cap.height,
exit,
)?;
// 5. Compute cap growth rate diff: market_cap_growth_rate - realized_cap_growth_rate
self.cap_growth_rate_diff.height.compute_transform2(
// 5. Compute cap growth rate diff: market - realized
self.market_minus_realized_cap_growth_rate.height.compute_subtract(
starting_indexes.height,
&self.market_cap_growth_rate.height,
&self.realized_cap_growth_rate.height,
|(h, a, b, ..)| (h, StoredF32::from(*a - *b)),
&self.market_cap_growth_rate.bps.height,
&self.realized_cap_growth_rate.bps.height,
exit,
)?;

View File

@@ -9,7 +9,8 @@ use super::Vecs;
use crate::{
distribution, indexes,
internal::{
ComputedFromHeight, Identity, LazyFromHeight, LazyValueFromHeight, SatsToBitcoin,
Bps32ToFloat, Bps32ToPercent, ComputedFromHeight, Identity, LazyFromHeight,
LazyValueFromHeight, PercentFromHeight, SatsToBitcoin,
},
};
@@ -40,8 +41,8 @@ impl Vecs {
let burned = super::burned::Vecs::forced_import(&db, version, indexes)?;
// Inflation rate
let inflation =
ComputedFromHeight::forced_import(&db, "inflation_rate", version, indexes)?;
let inflation_rate =
PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(&db, "inflation_rate", version, indexes)?;
// Velocity
let velocity = super::velocity::Vecs::forced_import(&db, version, indexes)?;
@@ -54,31 +55,31 @@ impl Vecs {
);
// Growth rates
let market_cap_growth_rate = ComputedFromHeight::forced_import(
let market_cap_growth_rate = PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
&db,
"market_cap_growth_rate",
version + Version::ONE,
indexes,
)?;
let realized_cap_growth_rate = ComputedFromHeight::forced_import(
let realized_cap_growth_rate = PercentFromHeight::forced_import::<Bps32ToFloat, Bps32ToPercent>(
&db,
"realized_cap_growth_rate",
version + Version::ONE,
indexes,
)?;
let cap_growth_rate_diff =
ComputedFromHeight::forced_import(&db, "cap_growth_rate_diff", version, indexes)?;
let market_minus_realized_cap_growth_rate =
ComputedFromHeight::forced_import(&db, "market_minus_realized_cap_growth_rate", version, indexes)?;
let this = Self {
db,
circulating,
burned,
inflation,
inflation_rate,
velocity,
market_cap,
market_cap_growth_rate,
realized_cap_growth_rate,
cap_growth_rate_diff,
market_minus_realized_cap_growth_rate,
};
this.db.retain_regions(

View File

@@ -1,10 +1,10 @@
use brk_traversable::Traversable;
use brk_types::{Dollars, StoredF32};
use brk_types::{BasisPointsSigned32, Dollars};
use vecdb::{Database, Rw, StorageMode};
use super::{burned, velocity};
use crate::internal::{
ComputedFromHeight, LazyFromHeight, LazyValueFromHeight,
ComputedFromHeight, LazyFromHeight, LazyValueFromHeight, PercentFromHeight,
};
#[derive(Traversable)]
@@ -14,10 +14,10 @@ pub struct Vecs<M: StorageMode = Rw> {
pub circulating: LazyValueFromHeight,
pub burned: burned::Vecs<M>,
pub inflation: ComputedFromHeight<StoredF32, M>,
pub inflation_rate: PercentFromHeight<BasisPointsSigned32, M>,
pub velocity: velocity::Vecs<M>,
pub market_cap: LazyFromHeight<Dollars>,
pub market_cap_growth_rate: ComputedFromHeight<StoredF32, M>,
pub realized_cap_growth_rate: ComputedFromHeight<StoredF32, M>,
pub cap_growth_rate_diff: ComputedFromHeight<StoredF32, M>,
pub market_cap_growth_rate: PercentFromHeight<BasisPointsSigned32, M>,
pub realized_cap_growth_rate: PercentFromHeight<BasisPointsSigned32, M>,
pub market_minus_realized_cap_growth_rate: ComputedFromHeight<BasisPointsSigned32, M>,
}

View File

@@ -12,8 +12,8 @@ impl Vecs {
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self {
btc: ComputedFromHeight::forced_import(db, "btc_velocity", version, indexes)?,
usd: ComputedFromHeight::forced_import(db, "usd_velocity", version, indexes)?,
btc: ComputedFromHeight::forced_import(db, "velocity_btc", version, indexes)?,
usd: ComputedFromHeight::forced_import(db, "velocity_usd", version, indexes)?,
})
}
}

View File

@@ -1,5 +1,5 @@
use brk_error::Result;
use brk_types::StoredF32;
use brk_types::{BasisPointsSigned16, StoredF32};
use vecdb::{
AnyStoredVec, AnyVec, EagerVec, Exit, PcoVec, PcoVecValue, ReadableVec, VecIndex, VecValue,
WritableVec,
@@ -243,3 +243,38 @@ where
Ok(())
}
}
impl<I> ComputeDrawdown<I> for EagerVec<PcoVec<I, BasisPointsSigned16>>
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 {
BasisPointsSigned16::default()
} else {
BasisPointsSigned16::from((f64::from(current) - ath_f64) / ath_f64)
};
(i, drawdown)
},
exit,
)?;
Ok(())
}
}