mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-03 15:23:41 -07:00
website: snapshot
This commit is contained in:
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
|
||||
@@ -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,
|
||||
)?;
|
||||
|
||||
Reference in New Issue
Block a user