merge: #28 brandoncollins7/feat/reserve-risk

feat(cointime): add Reserve Risk metric
This commit is contained in:
nym21
2026-01-21 20:34:06 +01:00
committed by GitHub
10 changed files with 184 additions and 2 deletions
+9 -1
View File
@@ -40,7 +40,7 @@ impl Vecs {
// Price-dependent metrics
if let Some(price) = price {
// Value computes (cointime value destroyed/created/stored)
// Value computes (cointime value destroyed/created/stored, VOCDD)
self.value.compute(
indexes,
starting_indexes,
@@ -72,6 +72,14 @@ impl Vecs {
&self.cap,
exit,
)?;
// Reserve Risk computes (depends on value.vocdd and price)
self.reserve_risk.compute(
starting_indexes,
price,
&self.value,
exit,
)?;
}
let _lock = exit.lock();
+5 -1
View File
@@ -6,7 +6,8 @@ use brk_types::Version;
use vecdb::{Database, PAGE_SIZE};
use super::{
ActivityVecs, AdjustedVecs, CapVecs, PricingVecs, SupplyVecs, ValueVecs, Vecs, DB_NAME, VERSION,
ActivityVecs, AdjustedVecs, CapVecs, PricingVecs, ReserveRiskVecs, SupplyVecs, ValueVecs, Vecs,
DB_NAME, VERSION,
};
use crate::{indexes, price};
@@ -22,6 +23,7 @@ impl Vecs {
let version = parent_version + VERSION;
let v1 = version + Version::ONE;
let compute_dollars = price.is_some();
let activity = ActivityVecs::forced_import(&db, version, indexes)?;
let supply = SupplyVecs::forced_import(&db, v1, indexes, price)?;
@@ -29,6 +31,7 @@ impl Vecs {
let cap = CapVecs::forced_import(&db, v1, indexes)?;
let pricing = PricingVecs::forced_import(&db, version, indexes, price)?;
let adjusted = AdjustedVecs::forced_import(&db, version, indexes)?;
let reserve_risk = ReserveRiskVecs::forced_import(&db, v1, indexes, compute_dollars)?;
let this = Self {
db,
@@ -38,6 +41,7 @@ impl Vecs {
cap,
pricing,
adjusted,
reserve_risk,
};
this.db.retain_regions(
+3
View File
@@ -2,6 +2,7 @@ pub mod activity;
pub mod adjusted;
pub mod cap;
pub mod pricing;
pub mod reserve_risk;
pub mod supply;
pub mod value;
@@ -16,6 +17,7 @@ pub use activity::Vecs as ActivityVecs;
pub use adjusted::Vecs as AdjustedVecs;
pub use cap::Vecs as CapVecs;
pub use pricing::Vecs as PricingVecs;
pub use reserve_risk::Vecs as ReserveRiskVecs;
pub use supply::Vecs as SupplyVecs;
pub use value::Vecs as ValueVecs;
@@ -33,4 +35,5 @@ pub struct Vecs {
pub cap: CapVecs,
pub pricing: PricingVecs,
pub adjusted: AdjustedVecs,
pub reserve_risk: ReserveRiskVecs,
}
@@ -0,0 +1,100 @@
use brk_error::Result;
use brk_types::{Close, Dollars, StoredF64};
use vecdb::Exit;
use super::{super::value, Vecs};
use crate::{price, ComputeIndexes};
impl Vecs {
pub fn compute(
&mut self,
starting_indexes: &ComputeIndexes,
price: &price::Vecs,
value: &value::Vecs,
exit: &Exit,
) -> Result<()> {
let vocdd_dateindex_sum = &value.vocdd.dateindex.sum.0;
self.vocdd_365d_sma.compute_sma(
starting_indexes.dateindex,
vocdd_dateindex_sum,
365,
exit,
)?;
let price_close = &price.usd.split.close.dateindex;
self.hodl_bank.compute_cumulative_transformed_binary(
starting_indexes.dateindex,
price_close,
&self.vocdd_365d_sma,
|price: Close<Dollars>, sma: StoredF64| StoredF64::from(f64::from(price) - f64::from(sma)),
exit,
)?;
if let Some(reserve_risk) = self.reserve_risk.as_mut() {
reserve_risk.compute_all(starting_indexes, exit, |v| {
v.compute_divide(
starting_indexes.dateindex,
price_close,
&self.hodl_bank,
exit,
)?;
Ok(())
})?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_hodl_bank_formula() {
let prices = [100.0, 110.0, 105.0, 120.0, 115.0];
let vocdd_sma = [50.0, 55.0, 52.0, 60.0, 58.0];
let mut hodl_bank = 0.0_f64;
let mut expected = Vec::new();
for i in 0..prices.len() {
hodl_bank += prices[i] - vocdd_sma[i];
expected.push(hodl_bank);
}
assert!((expected[0] - 50.0).abs() < 0.001);
assert!((expected[1] - 105.0).abs() < 0.001);
assert!((expected[2] - 158.0).abs() < 0.001);
assert!((expected[3] - 218.0).abs() < 0.001);
assert!((expected[4] - 275.0).abs() < 0.001);
}
#[test]
fn test_reserve_risk_formula() {
let price = 100.0_f64;
let hodl_bank = 1000.0_f64;
let reserve_risk = price / hodl_bank;
assert!((reserve_risk - 0.1).abs() < 0.0001);
}
#[test]
fn test_reserve_risk_interpretation() {
let high_confidence = 100.0 / 10000.0;
let low_confidence = 100.0 / 100.0;
assert!(high_confidence < low_confidence);
}
#[test]
fn test_hodl_bank_negative_contribution() {
let prices = [100.0, 80.0, 90.0];
let vocdd_sma = [50.0, 100.0, 85.0];
let mut hodl_bank = 0.0_f64;
for i in 0..prices.len() {
hodl_bank += prices[i] - vocdd_sma[i];
}
assert!((hodl_bank - 35.0).abs() < 0.001);
}
}
@@ -0,0 +1,23 @@
use brk_error::Result;
use brk_types::Version;
use vecdb::{Database, EagerVec, ImportableVec};
use super::Vecs;
use crate::{indexes, internal::ComputedFromDateLast};
impl Vecs {
pub fn forced_import(
db: &Database,
version: Version,
indexes: &indexes::Vecs,
compute_dollars: bool,
) -> Result<Self> {
Ok(Self {
vocdd_365d_sma: EagerVec::forced_import(db, "vocdd_365d_sma", version)?,
hodl_bank: EagerVec::forced_import(db, "hodl_bank", version)?,
reserve_risk: compute_dollars
.then(|| ComputedFromDateLast::forced_import(db, "reserve_risk", version, indexes))
.transpose()?,
})
}
}
@@ -0,0 +1,5 @@
mod compute;
mod import;
mod vecs;
pub use vecs::Vecs;
@@ -0,0 +1,12 @@
use brk_traversable::Traversable;
use brk_types::{DateIndex, StoredF64};
use vecdb::{EagerVec, PcoVec};
use crate::internal::ComputedFromDateLast;
#[derive(Clone, Traversable)]
pub struct Vecs {
pub vocdd_365d_sma: EagerVec<PcoVec<DateIndex, StoredF64>>,
pub hodl_bank: EagerVec<PcoVec<DateIndex, StoredF64>>,
pub reserve_risk: Option<ComputedFromDateLast<StoredF64>>,
}
@@ -22,6 +22,13 @@ impl Vecs {
.activity
.coinblocks_destroyed;
let coindays_destroyed = &distribution
.utxo_cohorts
.all
.metrics
.activity
.coindays_destroyed;
self.cointime_value_destroyed
.compute_all(indexes, starting_indexes, exit, |vec| {
vec.compute_multiply(
@@ -55,6 +62,19 @@ impl Vecs {
Ok(())
})?;
// VOCDD: Value-weighted Coin Days Destroyed = CDD × price
// This is a key input for Reserve Risk calculation
self.vocdd
.compute_all(indexes, starting_indexes, exit, |vec| {
vec.compute_multiply(
starting_indexes.height,
&price.usd.split.close.height,
&coindays_destroyed.height,
exit,
)?;
Ok(())
})?;
Ok(())
}
}
@@ -26,6 +26,12 @@ impl Vecs {
version,
indexes,
)?,
vocdd: ComputedFromHeightSumCum::forced_import(
db,
"vocdd",
version,
indexes,
)?,
})
}
}
@@ -8,4 +8,5 @@ pub struct Vecs {
pub cointime_value_destroyed: ComputedFromHeightSumCum<StoredF64>,
pub cointime_value_created: ComputedFromHeightSumCum<StoredF64>,
pub cointime_value_stored: ComputedFromHeightSumCum<StoredF64>,
pub vocdd: ComputedFromHeightSumCum<StoredF64>,
}