website: snapshot

This commit is contained in:
nym21
2026-02-03 23:43:52 +01:00
parent 277a0eb6a7
commit 0d5d7da70f
44 changed files with 2999 additions and 1591 deletions
@@ -4,7 +4,7 @@ use vecdb::Exit;
use super::super::{ONE_TERA_HASH, TARGET_BLOCKS_PER_DAY_F64, count, difficulty, rewards};
use super::Vecs;
use crate::{ComputeIndexes, indexes};
use crate::{ComputeIndexes, indexes, traits::ComputeDrawdown};
impl Vecs {
pub fn compute(
@@ -80,6 +80,27 @@ impl Vecs {
Ok(())
})?;
self.hash_rate_ath
.compute_all(indexes, starting_indexes, exit, |v| {
v.compute_all_time_high(
starting_indexes.height,
&self.hash_rate.height,
exit,
)?;
Ok(())
})?;
self.hash_rate_drawdown
.compute_all(indexes, starting_indexes, exit, |v| {
v.compute_drawdown(
starting_indexes.height,
&self.hash_rate.height,
&self.hash_rate_ath.height,
exit,
)?;
Ok(())
})?;
self.hash_price_ths
.compute_all(indexes, starting_indexes, exit, |v| {
v.compute_transform2(
@@ -43,6 +43,18 @@ impl Vecs {
version,
indexes,
)?,
hash_rate_ath: ComputedFromHeightLast::forced_import(
db,
"hash_rate_ath",
version,
indexes,
)?,
hash_rate_drawdown: ComputedFromHeightLast::forced_import(
db,
"hash_rate_drawdown",
version,
indexes,
)?,
hash_price_ths: ComputedFromHeightLast::forced_import(
db,
"hash_price_ths",
@@ -11,6 +11,8 @@ pub struct Vecs {
pub hash_rate_1m_sma: ComputedFromDateLast<StoredF32>,
pub hash_rate_2m_sma: ComputedFromDateLast<StoredF32>,
pub hash_rate_1y_sma: ComputedFromDateLast<StoredF32>,
pub hash_rate_ath: ComputedFromHeightLast<StoredF64>,
pub hash_rate_drawdown: ComputedFromHeightLast<StoredF32>,
pub hash_price_ths: ComputedFromHeightLast<StoredF32>,
pub hash_price_ths_min: ComputedFromHeightLast<StoredF32>,
pub hash_price_phs: ComputedFromHeightLast<StoredF32>,
@@ -15,7 +15,7 @@ impl Vecs {
) -> Result<()> {
let vocdd_dateindex_sum = &value.vocdd.dateindex.sum.0;
self.vocdd_365d_sma.compute_sma(
self.vocdd_365d_median.compute_rolling_median(
starting_indexes.dateindex,
vocdd_dateindex_sum,
365,
@@ -27,8 +27,8 @@ impl Vecs {
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)),
&self.vocdd_365d_median,
|price: Close<Dollars>, median: StoredF64| StoredF64::from(f64::from(price) - f64::from(median)),
exit,
)?;
@@ -47,54 +47,3 @@ impl Vecs {
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);
}
}
@@ -12,11 +12,12 @@ impl Vecs {
indexes: &indexes::Vecs,
compute_dollars: bool,
) -> Result<Self> {
let v1 = version + Version::ONE;
Ok(Self {
vocdd_365d_sma: EagerVec::forced_import(db, "vocdd_365d_sma", version)?,
hodl_bank: EagerVec::forced_import(db, "hodl_bank", version)?,
vocdd_365d_median: EagerVec::forced_import(db, "vocdd_365d_median", v1)?,
hodl_bank: EagerVec::forced_import(db, "hodl_bank", v1)?,
reserve_risk: compute_dollars
.then(|| ComputedFromDateLast::forced_import(db, "reserve_risk", version, indexes))
.then(|| ComputedFromDateLast::forced_import(db, "reserve_risk", v1, indexes))
.transpose()?,
})
}
@@ -6,7 +6,7 @@ use crate::internal::ComputedFromDateLast;
#[derive(Clone, Traversable)]
pub struct Vecs {
pub vocdd_365d_sma: EagerVec<PcoVec<DateIndex, StoredF64>>,
pub vocdd_365d_median: EagerVec<PcoVec<DateIndex, StoredF64>>,
pub hodl_bank: EagerVec<PcoVec<DateIndex, StoredF64>>,
pub reserve_risk: Option<ComputedFromDateLast<StoredF64>>,
}
@@ -1,5 +1,6 @@
use brk_error::Result;
use vecdb::Exit;
use brk_types::{Bitcoin, Close, Dollars, StoredF64};
use vecdb::{Exit, TypedVecIterator};
use super::super::activity;
use super::Vecs;
@@ -29,6 +30,15 @@ impl Vecs {
.activity
.coindays_destroyed;
let circulating_supply = &distribution
.utxo_cohorts
.all
.metrics
.supply
.total
.bitcoin
.height;
self.cointime_value_destroyed
.compute_all(indexes, starting_indexes, exit, |vec| {
vec.compute_multiply(
@@ -62,14 +72,27 @@ impl Vecs {
Ok(())
})?;
// VOCDD: Value-weighted Coin Days Destroyed = CDD × price
// This is a key input for Reserve Risk calculation
// VOCDD: Value of Coin Days Destroyed = price × (CDD / circulating_supply)
// Supply-adjusted to account for growing supply over time
// This is a key input for Reserve Risk / HODL Bank calculation
self.vocdd
.compute_all(indexes, starting_indexes, exit, |vec| {
vec.compute_multiply(
let mut supply_iter = circulating_supply.into_iter();
vec.compute_transform2(
starting_indexes.height,
&price.usd.split.close.height,
&coindays_destroyed.height,
|(i, price, cdd, _): (_, Close<Dollars>, StoredF64, _)| {
let supply: Bitcoin = supply_iter.get_unwrap(i);
let supply_f64 = f64::from(supply);
if supply_f64 == 0.0 {
(i, StoredF64::from(0.0))
} else {
// VOCDD = price × (CDD / supply)
let vocdd = f64::from(price) * f64::from(cdd) / supply_f64;
(i, StoredF64::from(vocdd))
}
},
exit,
)?;
Ok(())
@@ -29,7 +29,7 @@ impl Vecs {
vocdd: ComputedFromHeightSumCum::forced_import(
db,
"vocdd",
version,
version + Version::ONE,
indexes,
)?,
})
@@ -1,14 +1,63 @@
use brk_cohort::ByAddressType;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredU64, Version};
use brk_types::{Height, StoredF64, StoredU64, Version};
use derive_more::{Deref, DerefMut};
use rayon::prelude::*;
use vecdb::{
AnyStoredVec, AnyVec, Database, EagerVec, Exit, GenericStoredVec, PcoVec, TypedVecIterator,
};
use crate::{ComputeIndexes, indexes, internal::ComputedFromHeightLast};
use crate::{ComputeIndexes, indexes, internal::{ComputedFromDateLast, ComputedFromHeightLast}};
/// Address count with 30d change metric for a single type.
#[derive(Clone, Traversable)]
pub struct AddrCountVecs {
#[traversable(flatten)]
pub count: ComputedFromHeightLast<StoredU64>,
pub _30d_change: ComputedFromDateLast<StoredF64>,
}
impl AddrCountVecs {
pub fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self {
count: ComputedFromHeightLast::forced_import(db, name, version, indexes)?,
_30d_change: ComputedFromDateLast::forced_import(
db,
&format!("{name}_30d_change"),
version,
indexes,
)?,
})
}
pub fn compute_rest(
&mut self,
indexes: &indexes::Vecs,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self.count.compute_rest(indexes, starting_indexes, exit)?;
self._30d_change
.compute_all(starting_indexes, exit, |v| {
v.compute_change(
starting_indexes.dateindex,
&*self.count.dateindex,
30,
exit,
)?;
Ok(())
})?;
Ok(())
}
}
/// Address count per address type (runtime state).
#[derive(Debug, Default, Deref, DerefMut)]
@@ -28,47 +77,54 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount {
Self(ByAddressType {
p2pk65: groups
.p2pk65
.count
.height
.into_iter()
.get_unwrap(prev_height)
.into(),
p2pk33: groups
.p2pk33
.count
.height
.into_iter()
.get_unwrap(prev_height)
.into(),
p2pkh: groups
.p2pkh
.count
.height
.into_iter()
.get_unwrap(prev_height)
.into(),
p2sh: groups
.p2sh
.count
.height
.into_iter()
.get_unwrap(prev_height)
.into(),
p2wpkh: groups
.p2wpkh
.count
.height
.into_iter()
.get_unwrap(prev_height)
.into(),
p2wsh: groups
.p2wsh
.count
.height
.into_iter()
.get_unwrap(prev_height)
.into(),
p2tr: groups
.p2tr
.count
.height
.into_iter()
.get_unwrap(prev_height)
.into(),
p2a: groups.p2a.height.into_iter().get_unwrap(prev_height).into(),
p2a: groups.p2a.count.height.into_iter().get_unwrap(prev_height).into(),
})
} else {
Default::default()
@@ -76,13 +132,13 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount {
}
}
/// Address count per address type, with height + derived indexes.
/// Address count per address type, with height + derived indexes + 30d change.
#[derive(Clone, Deref, DerefMut, Traversable)]
pub struct AddressTypeToAddrCountVecs(ByAddressType<ComputedFromHeightLast<StoredU64>>);
pub struct AddressTypeToAddrCountVecs(ByAddressType<AddrCountVecs>);
impl From<ByAddressType<ComputedFromHeightLast<StoredU64>>> for AddressTypeToAddrCountVecs {
impl From<ByAddressType<AddrCountVecs>> for AddressTypeToAddrCountVecs {
#[inline]
fn from(value: ByAddressType<ComputedFromHeightLast<StoredU64>>) -> Self {
fn from(value: ByAddressType<AddrCountVecs>) -> Self {
Self(value)
}
}
@@ -95,8 +151,8 @@ impl AddressTypeToAddrCountVecs {
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self::from(
ByAddressType::<ComputedFromHeightLast<StoredU64>>::new_with_name(|type_name| {
ComputedFromHeightLast::forced_import(
ByAddressType::<AddrCountVecs>::new_with_name(|type_name| {
AddrCountVecs::forced_import(
db,
&format!("{type_name}_{name}"),
version,
@@ -108,28 +164,29 @@ impl AddressTypeToAddrCountVecs {
pub fn min_stateful_height(&self) -> usize {
self.p2pk65
.count
.height
.len()
.min(self.p2pk33.height.len())
.min(self.p2pkh.height.len())
.min(self.p2sh.height.len())
.min(self.p2wpkh.height.len())
.min(self.p2wsh.height.len())
.min(self.p2tr.height.len())
.min(self.p2a.height.len())
.min(self.p2pk33.count.height.len())
.min(self.p2pkh.count.height.len())
.min(self.p2sh.count.height.len())
.min(self.p2wpkh.count.height.len())
.min(self.p2wsh.count.height.len())
.min(self.p2tr.count.height.len())
.min(self.p2a.count.height.len())
}
pub fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
let inner = &mut self.0;
[
&mut inner.p2pk65.height as &mut dyn AnyStoredVec,
&mut inner.p2pk33.height as &mut dyn AnyStoredVec,
&mut inner.p2pkh.height as &mut dyn AnyStoredVec,
&mut inner.p2sh.height as &mut dyn AnyStoredVec,
&mut inner.p2wpkh.height as &mut dyn AnyStoredVec,
&mut inner.p2wsh.height as &mut dyn AnyStoredVec,
&mut inner.p2tr.height as &mut dyn AnyStoredVec,
&mut inner.p2a.height as &mut dyn AnyStoredVec,
&mut inner.p2pk65.count.height as &mut dyn AnyStoredVec,
&mut inner.p2pk33.count.height as &mut dyn AnyStoredVec,
&mut inner.p2pkh.count.height as &mut dyn AnyStoredVec,
&mut inner.p2sh.count.height as &mut dyn AnyStoredVec,
&mut inner.p2wpkh.count.height as &mut dyn AnyStoredVec,
&mut inner.p2wsh.count.height as &mut dyn AnyStoredVec,
&mut inner.p2tr.count.height as &mut dyn AnyStoredVec,
&mut inner.p2a.count.height as &mut dyn AnyStoredVec,
]
.into_par_iter()
}
@@ -140,27 +197,35 @@ impl AddressTypeToAddrCountVecs {
addr_counts: &AddressTypeToAddressCount,
) -> Result<()> {
self.p2pk65
.count
.height
.truncate_push(height, addr_counts.p2pk65.into())?;
self.p2pk33
.count
.height
.truncate_push(height, addr_counts.p2pk33.into())?;
self.p2pkh
.count
.height
.truncate_push(height, addr_counts.p2pkh.into())?;
self.p2sh
.count
.height
.truncate_push(height, addr_counts.p2sh.into())?;
self.p2wpkh
.count
.height
.truncate_push(height, addr_counts.p2wpkh.into())?;
self.p2wsh
.count
.height
.truncate_push(height, addr_counts.p2wsh.into())?;
self.p2tr
.count
.height
.truncate_push(height, addr_counts.p2tr.into())?;
self.p2a
.count
.height
.truncate_push(height, addr_counts.p2a.into())?;
Ok(())
@@ -168,14 +233,14 @@ impl AddressTypeToAddrCountVecs {
pub fn reset_height(&mut self) -> Result<()> {
use vecdb::GenericStoredVec;
self.p2pk65.height.reset()?;
self.p2pk33.height.reset()?;
self.p2pkh.height.reset()?;
self.p2sh.height.reset()?;
self.p2wpkh.height.reset()?;
self.p2wsh.height.reset()?;
self.p2tr.height.reset()?;
self.p2a.height.reset()?;
self.p2pk65.count.height.reset()?;
self.p2pk33.count.height.reset()?;
self.p2pkh.count.height.reset()?;
self.p2sh.count.height.reset()?;
self.p2wpkh.count.height.reset()?;
self.p2wsh.count.height.reset()?;
self.p2tr.count.height.reset()?;
self.p2a.count.height.reset()?;
Ok(())
}
@@ -198,26 +263,26 @@ impl AddressTypeToAddrCountVecs {
pub fn by_height(&self) -> Vec<&EagerVec<PcoVec<Height, StoredU64>>> {
vec![
&self.p2pk65.height,
&self.p2pk33.height,
&self.p2pkh.height,
&self.p2sh.height,
&self.p2wpkh.height,
&self.p2wsh.height,
&self.p2tr.height,
&self.p2a.height,
&self.p2pk65.count.height,
&self.p2pk33.count.height,
&self.p2pkh.count.height,
&self.p2sh.count.height,
&self.p2wpkh.count.height,
&self.p2wsh.count.height,
&self.p2tr.count.height,
&self.p2a.count.height,
]
}
}
#[derive(Clone, Traversable)]
pub struct AddrCountVecs {
pub all: ComputedFromHeightLast<StoredU64>,
pub struct AddrCountsVecs {
pub all: AddrCountVecs,
#[traversable(flatten)]
pub by_addresstype: AddressTypeToAddrCountVecs,
}
impl AddrCountVecs {
impl AddrCountsVecs {
pub fn forced_import(
db: &Database,
name: &str,
@@ -225,22 +290,22 @@ impl AddrCountVecs {
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self {
all: ComputedFromHeightLast::forced_import(db, name, version, indexes)?,
all: AddrCountVecs::forced_import(db, name, version, indexes)?,
by_addresstype: AddressTypeToAddrCountVecs::forced_import(db, name, version, indexes)?,
})
}
pub fn min_stateful_height(&self) -> usize {
self.all.height.len().min(self.by_addresstype.min_stateful_height())
self.all.count.height.len().min(self.by_addresstype.min_stateful_height())
}
pub fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
rayon::iter::once(&mut self.all.height as &mut dyn AnyStoredVec)
rayon::iter::once(&mut self.all.count.height as &mut dyn AnyStoredVec)
.chain(self.by_addresstype.par_iter_height_mut())
}
pub fn reset_height(&mut self) -> Result<()> {
self.all.height.reset()?;
self.all.count.height.reset()?;
self.by_addresstype.reset_height()?;
Ok(())
}
@@ -251,7 +316,7 @@ impl AddrCountVecs {
total: u64,
addr_counts: &AddressTypeToAddressCount,
) -> Result<()> {
self.all.height.truncate_push(height, total.into())?;
self.all.count.height.truncate_push(height, total.into())?;
self.by_addresstype
.truncate_push_height(height, addr_counts)?;
Ok(())
@@ -268,10 +333,22 @@ impl AddrCountVecs {
let sources = self.by_addresstype.by_height();
self.all
.count
.compute_all(indexes, starting_indexes, exit, |height_vec| {
Ok(height_vec.compute_sum_of_others(starting_indexes.height, &sources, exit)?)
})?;
self.all._30d_change
.compute_all(starting_indexes, exit, |v| {
v.compute_change(
starting_indexes.dateindex,
&*self.all.count.dateindex,
30,
exit,
)?;
Ok(())
})?;
Ok(())
}
}
@@ -11,7 +11,7 @@ use crate::{
internal::{LazyBinaryComputedFromHeightDistribution, RatioU64F32},
};
use super::{AddrCountVecs, NewAddrCountVecs};
use super::{AddrCountsVecs, NewAddrCountVecs};
/// Growth rate by type - lazy ratio with distribution stats
pub type GrowthRateByType =
@@ -31,7 +31,7 @@ impl GrowthRateVecs {
version: Version,
indexes: &indexes::Vecs,
new_addr_count: &NewAddrCountVecs,
addr_count: &AddrCountVecs,
addr_count: &AddrCountsVecs,
) -> Result<Self> {
let all = make_growth_rate(
db,
@@ -39,7 +39,7 @@ impl GrowthRateVecs {
version,
indexes,
&new_addr_count.all.height,
&addr_count.all.height,
&addr_count.all.count.height,
)?;
let by_addresstype: GrowthRateByType = zip2_by_addresstype(
@@ -52,7 +52,7 @@ impl GrowthRateVecs {
version,
indexes,
&new.height,
&addr.height,
&addr.count.height,
)
},
)?;
@@ -8,7 +8,7 @@ mod total_addr_count;
mod type_map;
pub use activity::{AddressActivityVecs, AddressTypeToActivityCounts};
pub use address_count::{AddrCountVecs, AddressTypeToAddressCount};
pub use address_count::{AddrCountVecs, AddrCountsVecs, AddressTypeToAddressCount};
pub use data::AddressesDataVecs;
pub use growth_rate::GrowthRateVecs;
pub use indexes::AnyAddressIndexesVecs;
@@ -8,7 +8,7 @@ use vecdb::{Database, Exit, IterableCloneableVec};
use crate::{ComputeIndexes, indexes, internal::{LazyBinaryComputedFromHeightLast, U64Plus}};
use super::AddrCountVecs;
use super::AddrCountsVecs;
/// Total addresses by type - lazy sum with all derived indexes
pub type TotalAddrCountByType =
@@ -27,15 +27,15 @@ impl TotalAddrCountVecs {
db: &Database,
version: Version,
indexes: &indexes::Vecs,
addr_count: &AddrCountVecs,
empty_addr_count: &AddrCountVecs,
addr_count: &AddrCountsVecs,
empty_addr_count: &AddrCountsVecs,
) -> Result<Self> {
let all = LazyBinaryComputedFromHeightLast::forced_import::<U64Plus>(
db,
"total_addr_count",
version,
addr_count.all.height.boxed_clone(),
empty_addr_count.all.height.boxed_clone(),
addr_count.all.count.height.boxed_clone(),
empty_addr_count.all.count.height.boxed_clone(),
indexes,
)?;
@@ -47,8 +47,8 @@ impl TotalAddrCountVecs {
db,
&format!("{name}_total_addr_count"),
version,
addr.height.boxed_clone(),
empty.height.boxed_clone(),
addr.count.height.boxed_clone(),
empty.count.height.boxed_clone(),
indexes,
)
},
@@ -3,13 +3,15 @@ use std::path::Path;
use brk_cohort::{CohortContext, Filter, Filtered};
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{CentsUnsigned, DateIndex, Dollars, Height, StoredU64, Version};
use brk_types::{CentsUnsigned, DateIndex, Dollars, Height, StoredF64, StoredU64, Version};
use rayon::prelude::*;
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, GenericStoredVec, IterableVec};
use crate::{
ComputeIndexes, distribution::state::AddressCohortState, indexes, internal::ComputedFromHeightLast,
price,
distribution::state::AddressCohortState,
indexes,
internal::{ComputedFromDateLast, ComputedFromHeightLast},
price, ComputeIndexes,
};
use crate::distribution::metrics::{CohortMetrics, ImportConfig, SupplyMetrics};
@@ -33,6 +35,7 @@ pub struct AddressCohortVecs {
pub metrics: CohortMetrics,
pub addr_count: ComputedFromHeightLast<StoredU64>,
pub addr_count_30d_change: ComputedFromDateLast<StoredF64>,
}
impl AddressCohortVecs {
@@ -79,6 +82,12 @@ impl AddressCohortVecs {
version + VERSION,
indexes,
)?,
addr_count_30d_change: ComputedFromDateLast::forced_import(
db,
&cfg.name("addr_count_30d_change"),
version + VERSION,
indexes,
)?,
})
}
@@ -234,6 +243,18 @@ impl DynCohortVecs for AddressCohortVecs {
) -> Result<()> {
self.addr_count
.compute_rest(indexes, starting_indexes, exit)?;
self.addr_count_30d_change
.compute_all(starting_indexes, exit, |v| {
v.compute_change(
starting_indexes.dateindex,
&*self.addr_count.dateindex,
30,
exit,
)?;
Ok(())
})?;
self.metrics
.compute_rest_part1(indexes, price, starting_indexes, exit)?;
Ok(())
@@ -1,10 +1,10 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, StoredU64};
use brk_types::{Height, StoredF64, StoredU64};
use rayon::prelude::*;
use vecdb::{AnyStoredVec, AnyVec, Exit, GenericStoredVec};
use crate::{ComputeIndexes, indexes, internal::ComputedFromHeightLast};
use crate::{ComputeIndexes, indexes, internal::{ComputedFromDateLast, ComputedFromHeightLast}};
use super::ImportConfig;
@@ -12,6 +12,7 @@ use super::ImportConfig;
#[derive(Clone, Traversable)]
pub struct OutputsMetrics {
pub utxo_count: ComputedFromHeightLast<StoredU64>,
pub utxo_count_30d_change: ComputedFromDateLast<StoredF64>,
}
impl OutputsMetrics {
@@ -24,6 +25,12 @@ impl OutputsMetrics {
cfg.version,
cfg.indexes,
)?,
utxo_count_30d_change: ComputedFromDateLast::forced_import(
cfg.db,
&cfg.name("utxo_count_30d_change"),
cfg.version,
cfg.indexes,
)?,
})
}
@@ -42,7 +49,11 @@ impl OutputsMetrics {
/// Returns a parallel iterator over all vecs for parallel writing.
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
vec![&mut self.utxo_count.height as &mut dyn AnyStoredVec].into_par_iter()
vec![
&mut self.utxo_count.height as &mut dyn AnyStoredVec,
&mut self.utxo_count_30d_change.dateindex as &mut dyn AnyStoredVec,
]
.into_par_iter()
}
/// Compute aggregate values from separate cohorts.
@@ -70,6 +81,20 @@ impl OutputsMetrics {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self.utxo_count.compute_rest(indexes, starting_indexes, exit)
self.utxo_count
.compute_rest(indexes, starting_indexes, exit)?;
self.utxo_count_30d_change
.compute_all(starting_indexes, exit, |v| {
v.compute_change(
starting_indexes.dateindex,
&*self.utxo_count.dateindex,
30,
exit,
)?;
Ok(())
})?;
Ok(())
}
}
+5 -5
View File
@@ -25,7 +25,7 @@ use crate::{
use super::{
AddressCohorts, AddressesDataVecs, AnyAddressIndexesVecs, UTXOCohorts,
address::{
AddrCountVecs, AddressActivityVecs, GrowthRateVecs, NewAddrCountVecs, TotalAddrCountVecs,
AddrCountsVecs, AddressActivityVecs, GrowthRateVecs, NewAddrCountVecs, TotalAddrCountVecs,
},
compute::aggregates,
};
@@ -44,8 +44,8 @@ pub struct Vecs {
pub utxo_cohorts: UTXOCohorts,
pub address_cohorts: AddressCohorts,
pub addr_count: AddrCountVecs,
pub empty_addr_count: AddrCountVecs,
pub addr_count: AddrCountsVecs,
pub empty_addr_count: AddrCountsVecs,
pub address_activity: AddressActivityVecs,
/// Total addresses ever seen (addr_count + empty_addr_count) - lazy, global + per-type
@@ -115,9 +115,9 @@ impl Vecs {
|index, _| Some(index),
);
let addr_count = AddrCountVecs::forced_import(&db, "addr_count", version, indexes)?;
let addr_count = AddrCountsVecs::forced_import(&db, "addr_count", version, indexes)?;
let empty_addr_count =
AddrCountVecs::forced_import(&db, "empty_addr_count", version, indexes)?;
AddrCountsVecs::forced_import(&db, "empty_addr_count", version, indexes)?;
let address_activity =
AddressActivityVecs::forced_import(&db, "address_activity", version, indexes)?;
+27 -16
View File
@@ -1,7 +1,8 @@
use brk_error::Result;
use brk_types::{Bitcoin, CheckedSub, Close, Date, DateIndex, Dollars, Sats, StoredF32};
use vecdb::{
AnyStoredVec, AnyVec, EagerVec, Exit, GenericStoredVec, IterableVec, PcoVec, VecIndex, Version,
AnyStoredVec, AnyVec, EagerVec, Exit, GenericStoredVec, IterableVec, PcoVec, VecIndex, VecValue,
Version,
};
mod pricing;
@@ -295,37 +296,47 @@ where
}
pub trait ComputeDrawdown<I> {
fn compute_drawdown(
fn compute_drawdown<C, A>(
&mut self,
max_from: I,
close: &impl IterableVec<I, Close<Dollars>>,
ath: &impl IterableVec<I, Dollars>,
current: &impl IterableVec<I, C>,
ath: &impl IterableVec<I, A>,
exit: &Exit,
) -> Result<()>;
) -> Result<()>
where
C: VecValue,
A: VecValue,
f64: From<C> + From<A>;
}
impl<I> ComputeDrawdown<I> for EagerVec<PcoVec<I, StoredF32>>
where
I: VecIndex,
{
fn compute_drawdown(
fn compute_drawdown<C, A>(
&mut self,
max_from: I,
close: &impl IterableVec<I, Close<Dollars>>,
ath: &impl IterableVec<I, Dollars>,
current: &impl IterableVec<I, C>,
ath: &impl IterableVec<I, A>,
exit: &Exit,
) -> Result<()> {
) -> Result<()>
where
C: VecValue,
A: VecValue,
f64: From<C> + From<A>,
{
self.compute_transform2(
max_from,
current,
ath,
close,
|(i, ath, close, _)| {
if ath == Dollars::ZERO {
(i, StoredF32::default())
|(i, current, ath, _)| {
let ath_f64 = f64::from(ath);
let drawdown = if ath_f64 == 0.0 {
StoredF32::default()
} else {
let drawdown = StoredF32::from((*ath - **close) / *ath * -100.0);
(i, drawdown)
}
StoredF32::from((f64::from(current) - ath_f64) / ath_f64 * 100.0)
};
(i, drawdown)
},
exit,
)?;