mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot
This commit is contained in:
@@ -15,8 +15,8 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// Block count height + cumulative
|
||||
self.block_count.height.compute_range(
|
||||
// Block count raw + cumulative
|
||||
self.block_count.raw.height.compute_range(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.weight,
|
||||
|h| (h, StoredU32::from(1_u32)),
|
||||
@@ -24,7 +24,7 @@ impl Vecs {
|
||||
)?;
|
||||
self.block_count.cumulative.height.compute_cumulative(
|
||||
starting_indexes.height,
|
||||
&self.block_count.height,
|
||||
&self.block_count.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -33,7 +33,7 @@ impl Vecs {
|
||||
self.block_count.sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&ws,
|
||||
&self.block_count.height,
|
||||
&self.block_count.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ impl Vecs {
|
||||
.compute(starting_indexes.height, &window_starts, exit, |vec| {
|
||||
vec.compute_subtract(
|
||||
starting_indexes.height,
|
||||
&self.coinblocks_created.height,
|
||||
&all_metrics.activity.coinblocks_destroyed.raw.height,
|
||||
&self.coinblocks_created.raw.height,
|
||||
&distribution.coinblocks_destroyed.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -42,7 +42,7 @@ impl Vecs {
|
||||
|
||||
self.liveliness.height.compute_divide(
|
||||
starting_indexes.height,
|
||||
&all_metrics.activity.coinblocks_destroyed.cumulative.height,
|
||||
&distribution.coinblocks_destroyed.cumulative.height,
|
||||
&self.coinblocks_created.cumulative.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -17,7 +17,7 @@ impl Vecs {
|
||||
self.vocdd_median_1y.compute_rolling_median_from_starts(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_1y_ago,
|
||||
&value.vocdd.height,
|
||||
&value.vocdd.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ impl Vecs {
|
||||
let window_starts = blocks.lookback.window_starts();
|
||||
|
||||
let all_metrics = &distribution.utxo_cohorts.all.metrics;
|
||||
let coinblocks_destroyed = &all_metrics.activity.coinblocks_destroyed;
|
||||
let coinblocks_destroyed = &distribution.coinblocks_destroyed;
|
||||
let coindays_destroyed = &all_metrics.activity.coindays_destroyed;
|
||||
let circulating_supply = &all_metrics.supply.total.btc.height;
|
||||
|
||||
@@ -39,7 +39,7 @@ impl Vecs {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&prices.price.usd.height,
|
||||
&activity.coinblocks_created.height,
|
||||
&activity.coinblocks_created.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -50,7 +50,7 @@ impl Vecs {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&prices.price.usd.height,
|
||||
&activity.coinblocks_stored.height,
|
||||
&activity.coinblocks_stored.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
|
||||
@@ -47,8 +47,7 @@ pub(crate) fn process_sent(
|
||||
for (receive_height, by_type) in sent_data.into_iter() {
|
||||
let prev_price = height_to_price[receive_height.to_usize()];
|
||||
let prev_timestamp = height_to_timestamp[receive_height.to_usize()];
|
||||
let blocks_old = current_height.to_usize() - receive_height.to_usize();
|
||||
let age = Age::new(current_timestamp, prev_timestamp, blocks_old);
|
||||
let age = Age::new(current_timestamp, prev_timestamp);
|
||||
|
||||
// Compute peak price during holding period for peak regret
|
||||
// This is the max HIGH price between receive and send heights
|
||||
|
||||
@@ -43,8 +43,7 @@ impl UTXOCohorts<Rw> {
|
||||
|
||||
let block_state = &chain_state[receive_height.to_usize()];
|
||||
let prev_price = block_state.price;
|
||||
let blocks_old = chain_len - 1 - receive_height.to_usize();
|
||||
let age = Age::new(last_timestamp, block_state.timestamp, blocks_old);
|
||||
let age = Age::new(last_timestamp, block_state.timestamp);
|
||||
|
||||
// Compute peak price during holding period for peak regret
|
||||
// This is the max price between receive and send heights
|
||||
|
||||
@@ -2,12 +2,12 @@ use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{
|
||||
Cents, Date, Height, ONE_DAY_IN_SEC, OutputType, Sats, Timestamp, TxIndex, TypeIndex,
|
||||
Cents, Date, Height, ONE_DAY_IN_SEC, OutputType, Sats, StoredF64, Timestamp, TxIndex, TypeIndex,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use rustc_hash::FxHashSet;
|
||||
use tracing::{debug, info};
|
||||
use vecdb::{AnyVec, Exit, ReadableVec, VecIndex};
|
||||
use vecdb::{AnyVec, Exit, ReadableVec, VecIndex, WritableVec};
|
||||
|
||||
use crate::{
|
||||
distribution::{
|
||||
@@ -66,7 +66,7 @@ pub(crate) fn process_blocks(
|
||||
let height_to_first_txindex = &indexer.vecs.transactions.first_txindex;
|
||||
let height_to_first_txoutindex = &indexer.vecs.outputs.first_txoutindex;
|
||||
let height_to_first_txinindex = &indexer.vecs.inputs.first_txinindex;
|
||||
let height_to_tx_count = &transactions.count.tx_count.height;
|
||||
let height_to_tx_count = &transactions.count.tx_count.raw.height;
|
||||
let height_to_output_count = &outputs.count.total_count.full.sum;
|
||||
let height_to_input_count = &inputs.count.full.sum;
|
||||
let txindex_to_output_count = &indexes.txindex.output_count;
|
||||
@@ -353,6 +353,23 @@ pub(crate) fn process_blocks(
|
||||
timestamp,
|
||||
});
|
||||
|
||||
// Compute total coinblocks destroyed (once globally, before send() consumes height_to_sent)
|
||||
{
|
||||
let h = height.to_usize();
|
||||
let total_satblocks: u128 = height_to_sent
|
||||
.iter()
|
||||
.filter(|(rh, _)| rh.to_usize() < h)
|
||||
.map(|(rh, sent)| {
|
||||
let blocks_old = h - rh.to_usize();
|
||||
blocks_old as u128 * u64::from(sent.spendable_supply.value) as u128
|
||||
})
|
||||
.sum();
|
||||
vecs.coinblocks_destroyed.raw.height.truncate_push(
|
||||
height,
|
||||
StoredF64::from(total_satblocks as f64 / Sats::ONE_BTC_U128 as f64),
|
||||
)?;
|
||||
}
|
||||
|
||||
// Record maturation (sats crossing age boundaries)
|
||||
vecs.utxo_cohorts.push_maturation(height, &matured)?;
|
||||
|
||||
|
||||
@@ -79,9 +79,13 @@ pub(crate) fn write(
|
||||
.chain(vecs.addr_count.par_iter_height_mut())
|
||||
.chain(vecs.empty_addr_count.par_iter_height_mut())
|
||||
.chain(vecs.address_activity.par_iter_height_mut())
|
||||
.chain(rayon::iter::once(
|
||||
&mut vecs.supply_state as &mut dyn AnyStoredVec,
|
||||
))
|
||||
.chain(
|
||||
[
|
||||
&mut vecs.supply_state as &mut dyn AnyStoredVec,
|
||||
&mut vecs.coinblocks_destroyed.raw.height,
|
||||
]
|
||||
.into_par_iter(),
|
||||
)
|
||||
.chain(vecs.utxo_cohorts.par_iter_vecs_mut())
|
||||
.chain(vecs.address_cohorts.par_iter_vecs_mut())
|
||||
.try_for_each(|v| v.any_stamped_write_maybe_with_changes(stamp, with_changes))?;
|
||||
|
||||
@@ -2,7 +2,7 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Height, Indexes, Sats, StoredF32, StoredF64, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
||||
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedPerBlock, RollingWindowsFrom1w};
|
||||
|
||||
@@ -10,12 +10,6 @@ use crate::{blocks, distribution::{metrics::ImportConfig, state::{CohortState, R
|
||||
|
||||
use super::ActivityCore;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ActivityCoinblocks<M: StorageMode = Rw> {
|
||||
pub raw: ComputedPerBlock<StoredF64, M>,
|
||||
pub cumulative: ComputedPerBlock<StoredF64, M>,
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct ActivityFull<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
@@ -23,8 +17,6 @@ pub struct ActivityFull<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub inner: ActivityCore<M>,
|
||||
|
||||
pub coinblocks_destroyed: ActivityCoinblocks<M>,
|
||||
|
||||
#[traversable(wrap = "coindays_destroyed", rename = "cumulative")]
|
||||
pub coindays_destroyed_cumulative: ComputedPerBlock<StoredF64, M>,
|
||||
#[traversable(wrap = "coindays_destroyed", rename = "sum")]
|
||||
@@ -42,10 +34,6 @@ impl ActivityFull {
|
||||
let v1 = Version::ONE;
|
||||
Ok(Self {
|
||||
inner: ActivityCore::forced_import(cfg)?,
|
||||
coinblocks_destroyed: ActivityCoinblocks {
|
||||
raw: cfg.import("coinblocks_destroyed", v1)?,
|
||||
cumulative: cfg.import("coinblocks_destroyed_cumulative", v1)?,
|
||||
},
|
||||
coindays_destroyed_cumulative: cfg.import("coindays_destroyed_cumulative", v1)?,
|
||||
coindays_destroyed_sum: cfg.import("coindays_destroyed", v1)?,
|
||||
sent_sum_extended: cfg.import("sent", v1)?,
|
||||
@@ -55,9 +43,7 @@ impl ActivityFull {
|
||||
}
|
||||
|
||||
pub(crate) fn full_min_len(&self) -> usize {
|
||||
self.inner
|
||||
.min_len()
|
||||
.min(self.coinblocks_destroyed.raw.height.len())
|
||||
self.inner.min_len()
|
||||
}
|
||||
|
||||
pub(crate) fn full_truncate_push(
|
||||
@@ -65,17 +51,11 @@ impl ActivityFull {
|
||||
height: Height,
|
||||
state: &CohortState<impl RealizedOps>,
|
||||
) -> Result<()> {
|
||||
self.inner.truncate_push(height, state)?;
|
||||
self.coinblocks_destroyed.raw.height.truncate_push(
|
||||
height,
|
||||
StoredF64::from(Bitcoin::from(state.satblocks_destroyed)),
|
||||
)?;
|
||||
Ok(())
|
||||
self.inner.truncate_push(height, state)
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.inner.collect_vecs_mut();
|
||||
vecs.push(&mut self.coinblocks_destroyed.raw.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.dormancy.height);
|
||||
vecs.push(&mut self.velocity.height);
|
||||
vecs
|
||||
@@ -100,15 +80,6 @@ impl ActivityFull {
|
||||
self.inner
|
||||
.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
|
||||
self.coinblocks_destroyed
|
||||
.cumulative
|
||||
.height
|
||||
.compute_cumulative(
|
||||
starting_indexes.height,
|
||||
&self.coinblocks_destroyed.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.coindays_destroyed_cumulative
|
||||
.height
|
||||
.compute_cumulative(
|
||||
|
||||
@@ -289,7 +289,7 @@ impl RealizedFull {
|
||||
.min(self.investor.price.cents.height.len())
|
||||
.min(self.cap_raw.len())
|
||||
.min(self.investor.cap_raw.len())
|
||||
.min(self.peak_regret.value.height.len())
|
||||
.min(self.peak_regret.value.raw.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(
|
||||
@@ -326,6 +326,7 @@ impl RealizedFull {
|
||||
.truncate_push(height, state.realized.investor_cap_raw())?;
|
||||
self.peak_regret
|
||||
.value
|
||||
.raw
|
||||
.height
|
||||
.truncate_push(height, state.realized.peak_regret())?;
|
||||
|
||||
@@ -341,7 +342,7 @@ impl RealizedFull {
|
||||
vecs.push(&mut self.investor.price.cents.height);
|
||||
vecs.push(&mut self.cap_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.investor.cap_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.peak_regret.value.height);
|
||||
vecs.push(&mut self.peak_regret.value.raw.height);
|
||||
vecs
|
||||
}
|
||||
|
||||
@@ -400,6 +401,7 @@ impl RealizedFull {
|
||||
|
||||
self.peak_regret
|
||||
.value
|
||||
.raw
|
||||
.height
|
||||
.truncate_push(height, accum.peak_regret)?;
|
||||
|
||||
@@ -609,7 +611,7 @@ impl RealizedFull {
|
||||
.rel_to_rcap
|
||||
.compute_binary::<Cents, Cents, RatioCentsBp32>(
|
||||
starting_indexes.height,
|
||||
&self.peak_regret.value.height,
|
||||
&self.peak_regret.value.raw.height,
|
||||
&self.core.minimal.cap.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
96
crates/brk_computer/src/distribution/metrics/supply/core.rs
Normal file
96
crates/brk_computer/src/distribution/metrics/supply/core.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Indexes, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{distribution::state::UnrealizedState, prices};
|
||||
|
||||
use crate::internal::AmountPerBlock;
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
use super::SupplyBase;
|
||||
|
||||
/// Core supply metrics: total + halved + in_profit/in_loss (4 stored vecs).
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct SupplyCore<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub base: SupplyBase<M>,
|
||||
|
||||
pub in_profit: AmountPerBlock<M>,
|
||||
pub in_loss: AmountPerBlock<M>,
|
||||
}
|
||||
|
||||
impl SupplyCore {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
let base = SupplyBase::forced_import(cfg)?;
|
||||
|
||||
Ok(Self {
|
||||
base,
|
||||
in_profit: cfg.import("supply_in_profit", v0)?,
|
||||
in_loss: cfg.import("supply_in_loss", v0)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_len(&self) -> usize {
|
||||
self.base
|
||||
.min_len()
|
||||
.min(self.in_profit.sats.height.len())
|
||||
.min(self.in_loss.sats.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push_profitability(
|
||||
&mut self,
|
||||
height: Height,
|
||||
state: &UnrealizedState,
|
||||
) -> Result<()> {
|
||||
self.in_profit
|
||||
.sats
|
||||
.height
|
||||
.truncate_push(height, state.supply_in_profit)?;
|
||||
self.in_loss
|
||||
.sats
|
||||
.height
|
||||
.truncate_push(height, state.supply_in_loss)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.base.collect_vecs_mut();
|
||||
vecs.push(&mut self.in_profit.sats.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.in_profit.cents.height);
|
||||
vecs.push(&mut self.in_loss.sats.height);
|
||||
vecs.push(&mut self.in_loss.cents.height);
|
||||
vecs
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.base.compute(prices, max_from, exit)?;
|
||||
self.in_profit.compute(prices, max_from, exit)?;
|
||||
self.in_loss.compute(prices, max_from, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let base_refs: Vec<&SupplyBase> = others.iter().map(|o| &o.base).collect();
|
||||
self.base
|
||||
.compute_from_stateful(starting_indexes, &base_refs, exit)?;
|
||||
sum_others!(self, starting_indexes, others, exit; in_profit.sats.height);
|
||||
sum_others!(self, starting_indexes, others, exit; in_loss.sats.height);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
mod base;
|
||||
mod core;
|
||||
mod full;
|
||||
|
||||
pub use base::SupplyBase;
|
||||
pub use self::core::SupplyCore;
|
||||
pub use full::SupplyFull;
|
||||
|
||||
@@ -28,7 +28,6 @@ impl<R: RealizedOps> AddressCohortState<R> {
|
||||
self.addr_count = 0;
|
||||
self.inner.supply = SupplyState::default();
|
||||
self.inner.sent = Sats::ZERO;
|
||||
self.inner.satblocks_destroyed = Sats::ZERO;
|
||||
self.inner.satdays_destroyed = Sats::ZERO;
|
||||
self.inner.realized = R::default();
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ pub struct CohortState<R: RealizedOps> {
|
||||
pub supply: SupplyState,
|
||||
pub realized: R,
|
||||
pub sent: Sats,
|
||||
pub satblocks_destroyed: Sats,
|
||||
pub satdays_destroyed: Sats,
|
||||
cost_basis_data: CostBasisData,
|
||||
}
|
||||
@@ -64,7 +63,6 @@ impl<R: RealizedOps> CohortState<R> {
|
||||
supply: SupplyState::default(),
|
||||
realized: R::default(),
|
||||
sent: Sats::ZERO,
|
||||
satblocks_destroyed: Sats::ZERO,
|
||||
satdays_destroyed: Sats::ZERO,
|
||||
cost_basis_data: CostBasisData::create(path, name),
|
||||
}
|
||||
@@ -104,7 +102,6 @@ impl<R: RealizedOps> CohortState<R> {
|
||||
pub(crate) fn reset_single_iteration_values(&mut self) {
|
||||
self.sent = Sats::ZERO;
|
||||
self.satdays_destroyed = Sats::ZERO;
|
||||
self.satblocks_destroyed = Sats::ZERO;
|
||||
self.realized.reset_single_iteration_values();
|
||||
}
|
||||
|
||||
@@ -202,7 +199,6 @@ impl<R: RealizedOps> CohortState<R> {
|
||||
) {
|
||||
self.supply -= supply;
|
||||
self.sent += pre.sats;
|
||||
self.satblocks_destroyed += pre.age.satblocks_destroyed(pre.sats);
|
||||
self.satdays_destroyed += pre.age.satdays_destroyed(pre.sats);
|
||||
|
||||
self.realized
|
||||
@@ -246,7 +242,6 @@ impl<R: RealizedOps> CohortState<R> {
|
||||
|
||||
if supply.value > Sats::ZERO {
|
||||
self.sent += supply.value;
|
||||
self.satblocks_destroyed += age.satblocks_destroyed(supply.value);
|
||||
self.satdays_destroyed += age.satdays_destroyed(supply.value);
|
||||
|
||||
let sats = supply.value;
|
||||
|
||||
@@ -23,7 +23,6 @@ impl<R: RealizedOps> UTXOCohortState<R> {
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.0.supply = SupplyState::default();
|
||||
self.0.sent = Sats::ZERO;
|
||||
self.0.satblocks_destroyed = Sats::ZERO;
|
||||
self.0.satdays_destroyed = Sats::ZERO;
|
||||
self.0.realized = R::default();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use brk_indexer::Indexer;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Cents, EmptyAddressData, EmptyAddressIndex, FundedAddressData, FundedAddressIndex, Height,
|
||||
Indexes, SupplyState, Timestamp, TxIndex, Version,
|
||||
Indexes, StoredF64, SupplyState, Timestamp, TxIndex, Version,
|
||||
};
|
||||
use tracing::{debug, info};
|
||||
use vecdb::{
|
||||
@@ -23,7 +23,7 @@ use crate::{
|
||||
state::BlockState,
|
||||
},
|
||||
indexes, inputs,
|
||||
internal::{finalize_db, open_db},
|
||||
internal::{finalize_db, open_db, ComputedPerBlockCumulative},
|
||||
outputs, prices, transactions,
|
||||
};
|
||||
|
||||
@@ -49,6 +49,8 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub utxo_cohorts: UTXOCohorts<M>,
|
||||
pub address_cohorts: AddressCohorts<M>,
|
||||
|
||||
pub coinblocks_destroyed: ComputedPerBlockCumulative<StoredF64, M>,
|
||||
|
||||
pub addr_count: AddrCountsVecs<M>,
|
||||
pub empty_addr_count: AddrCountsVecs<M>,
|
||||
pub address_activity: AddressActivityVecs<M>,
|
||||
@@ -159,6 +161,13 @@ impl Vecs {
|
||||
utxo_cohorts,
|
||||
address_cohorts,
|
||||
|
||||
coinblocks_destroyed: ComputedPerBlockCumulative::forced_import(
|
||||
&db,
|
||||
"coinblocks_destroyed",
|
||||
version + Version::TWO,
|
||||
indexes,
|
||||
)?,
|
||||
|
||||
any_address_indexes: AnyAddressIndexesVecs::forced_import(&db, version)?,
|
||||
addresses_data: AddressesDataVecs {
|
||||
funded: fundedaddressindex_to_fundedaddressdata,
|
||||
@@ -390,6 +399,10 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// 5b. Compute coinblocks_destroyed cumulative from raw
|
||||
self.coinblocks_destroyed
|
||||
.compute_rest(starting_indexes.height, exit)?;
|
||||
|
||||
// 6. Compute rest part1 (day1 mappings)
|
||||
aggregates::compute_rest_part1(
|
||||
&mut self.utxo_cohorts,
|
||||
@@ -474,5 +487,6 @@ impl Vecs {
|
||||
.min(Height::from(self.addr_count.min_stateful_height()))
|
||||
.min(Height::from(self.empty_addr_count.min_stateful_height()))
|
||||
.min(Height::from(self.address_activity.min_stateful_height()))
|
||||
.min(Height::from(self.coinblocks_destroyed.raw.height.len()))
|
||||
}
|
||||
}
|
||||
|
||||
78
crates/brk_computer/src/indicators/compute.rs
Normal file
78
crates/brk_computer/src/indicators/compute.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{Dollars, Indexes};
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::{gini, Vecs};
|
||||
use crate::{distribution, internal::RatioDollarsBp32, mining, transactions};
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
mining: &mining::Vecs,
|
||||
distribution: &distribution::Vecs,
|
||||
transactions: &transactions::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// Puell Multiple: daily_subsidy_usd / sma_365d_subsidy_usd
|
||||
self.puell_multiple
|
||||
.bps
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||||
starting_indexes.height,
|
||||
&mining.rewards.subsidy.base.usd.height,
|
||||
&mining.rewards.subsidy_sma_1y.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Gini coefficient (UTXO distribution inequality)
|
||||
gini::compute(&mut self.gini, distribution, starting_indexes, exit)?;
|
||||
|
||||
// RHODL Ratio: 1d-1w realized cap / 1y-2y realized cap
|
||||
self.rhodl_ratio
|
||||
.bps
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||||
starting_indexes.height,
|
||||
&distribution
|
||||
.utxo_cohorts
|
||||
.age_range
|
||||
._1d_to_1w
|
||||
.metrics
|
||||
.realized
|
||||
.cap
|
||||
.usd
|
||||
.height,
|
||||
&distribution
|
||||
.utxo_cohorts
|
||||
.age_range
|
||||
._1y_to_2y
|
||||
.metrics
|
||||
.realized
|
||||
.cap
|
||||
.usd
|
||||
.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// NVT: market_cap / tx_volume_24h
|
||||
let market_cap = &distribution
|
||||
.utxo_cohorts
|
||||
.all
|
||||
.metrics
|
||||
.supply
|
||||
.total
|
||||
.usd
|
||||
.height;
|
||||
self.nvt
|
||||
.bps
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||||
starting_indexes.height,
|
||||
market_cap,
|
||||
&transactions.volume.sent_sum.rolling._24h.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let _lock = exit.lock();
|
||||
self.db.compact()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
38
crates/brk_computer/src/indicators/import.rs
Normal file
38
crates/brk_computer/src/indicators/import.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_types::Version;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{finalize_db, open_db, PercentPerBlock, RatioPerBlock},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::new(1);
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(
|
||||
parent_path: &Path,
|
||||
parent_version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let db = open_db(parent_path, super::DB_NAME, 100_000)?;
|
||||
let v = parent_version + VERSION;
|
||||
|
||||
let puell_multiple = RatioPerBlock::forced_import_raw(&db, "puell_multiple", v, indexes)?;
|
||||
let nvt = RatioPerBlock::forced_import_raw(&db, "nvt", v, indexes)?;
|
||||
let gini = PercentPerBlock::forced_import(&db, "gini", v, indexes)?;
|
||||
let rhodl_ratio = RatioPerBlock::forced_import_raw(&db, "rhodl_ratio", v, indexes)?;
|
||||
|
||||
let this = Self {
|
||||
db,
|
||||
puell_multiple,
|
||||
nvt,
|
||||
gini,
|
||||
rhodl_ratio,
|
||||
};
|
||||
finalize_db(&this.db, &this)?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
8
crates/brk_computer/src/indicators/mod.rs
Normal file
8
crates/brk_computer/src/indicators/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
mod compute;
|
||||
mod gini;
|
||||
mod import;
|
||||
mod vecs;
|
||||
|
||||
pub use vecs::Vecs;
|
||||
|
||||
pub const DB_NAME: &str = "indicators";
|
||||
15
crates/brk_computer/src/indicators/vecs.rs
Normal file
15
crates/brk_computer/src/indicators/vecs.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints16, BasisPoints32};
|
||||
use vecdb::{Database, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{PercentPerBlock, RatioPerBlock};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
#[traversable(skip)]
|
||||
pub(crate) db: Database,
|
||||
pub puell_multiple: RatioPerBlock<BasisPoints32, M>,
|
||||
pub nvt: RatioPerBlock<BasisPoints32, M>,
|
||||
pub gini: PercentPerBlock<BasisPoints16, M>,
|
||||
pub rhodl_ratio: RatioPerBlock<BasisPoints32, M>,
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
//! ComputedPerBlockCumulative - stored height + LazyAggVec + cumulative (from height).
|
||||
//! ComputedPerBlockCumulative - raw ComputedPerBlock + cumulative ComputedPerBlock.
|
||||
//!
|
||||
//! Like ComputedPerBlockCumulativeSum but without RollingWindows.
|
||||
//! Used for distribution metrics where rolling is optional per cohort.
|
||||
//! Cumulative gets its own ComputedPerBlock so it has LazyAggVec index views.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -20,7 +19,7 @@ pub struct ComputedPerBlockCumulative<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
pub raw: ComputedPerBlock<T, M>,
|
||||
pub cumulative: ComputedPerBlock<T, M>,
|
||||
}
|
||||
|
||||
@@ -34,35 +33,35 @@ where
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, version)?;
|
||||
let raw = ComputedPerBlock::forced_import(db, name, version, indexes)?;
|
||||
let cumulative =
|
||||
ComputedPerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?;
|
||||
|
||||
Ok(Self { height, cumulative })
|
||||
Ok(Self { raw, cumulative })
|
||||
}
|
||||
|
||||
/// Compute height data via closure, then cumulative only (no rolling).
|
||||
/// Compute raw data via closure, then cumulative only (no rolling).
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
compute_height: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
compute_raw: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
compute_height(&mut self.height)?;
|
||||
compute_raw(&mut self.raw.height)?;
|
||||
self.compute_rest(max_from, exit)
|
||||
}
|
||||
|
||||
/// Compute cumulative from already-filled height vec.
|
||||
/// Compute cumulative from already-filled raw vec.
|
||||
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()>
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
self.cumulative
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.height, exit)?;
|
||||
.compute_cumulative(max_from, &self.raw.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
//! ComputedPerBlockCumulativeSum - stored height + LazyAggVec + cumulative (from height) + RollingWindows (sum).
|
||||
//! ComputedPerBlockCumulativeSum - raw ComputedPerBlock + cumulative ComputedPerBlock + RollingWindows (sum).
|
||||
//!
|
||||
//! Like ComputedPerBlockFull but with rolling sum only (no distribution).
|
||||
//! Used for count metrics where distribution stats aren't meaningful.
|
||||
//! Cumulative gets its own ComputedPerBlock so it has LazyAggVec index views too.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
@@ -10,7 +9,7 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -22,7 +21,7 @@ pub struct ComputedPerBlockCumulativeSum<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
pub raw: ComputedPerBlock<T, M>,
|
||||
pub cumulative: ComputedPerBlock<T, M>,
|
||||
pub sum: RollingWindows<T, M>,
|
||||
}
|
||||
@@ -37,34 +36,34 @@ where
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, version)?;
|
||||
let raw = ComputedPerBlock::forced_import(db, name, version, indexes)?;
|
||||
let cumulative =
|
||||
ComputedPerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?;
|
||||
let rolling = RollingWindows::forced_import(db, name, version, indexes)?;
|
||||
let sum = RollingWindows::forced_import(db, name, version, indexes)?;
|
||||
|
||||
Ok(Self {
|
||||
height,
|
||||
raw,
|
||||
cumulative,
|
||||
sum: rolling,
|
||||
sum,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute height data via closure, then cumulative + rolling sum.
|
||||
/// Compute raw data via closure, then cumulative + rolling sum.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_height: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
compute_raw: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Default + SubAssign,
|
||||
{
|
||||
compute_height(&mut self.height)?;
|
||||
compute_raw(&mut self.raw.height)?;
|
||||
self.compute_rest(max_from, windows, exit)
|
||||
}
|
||||
|
||||
/// Compute cumulative + rolling sum from already-populated height data.
|
||||
/// Compute cumulative + rolling sum from already-populated raw data.
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
@@ -76,9 +75,9 @@ where
|
||||
{
|
||||
self.cumulative
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.height, exit)?;
|
||||
.compute_cumulative(max_from, &self.raw.height, exit)?;
|
||||
self.sum
|
||||
.compute_rolling_sum(max_from, windows, &self.height, exit)?;
|
||||
.compute_rolling_sum(max_from, windows, &self.raw.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! ComputedPerBlockFull - stored height + LazyAggVec + cumulative (from height) + RollingFull.
|
||||
//! ComputedPerBlockFull - raw ComputedPerBlock + cumulative ComputedPerBlock + RollingFull.
|
||||
//!
|
||||
//! For metrics with stored per-block data, cumulative sums, and rolling windows.
|
||||
//! Cumulative gets its own ComputedPerBlock so it has LazyAggVec index views too.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
@@ -9,7 +8,7 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -21,7 +20,7 @@ pub struct ComputedPerBlockFull<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
pub raw: ComputedPerBlock<T, M>,
|
||||
pub cumulative: ComputedPerBlock<T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: RollingFull<T, M>,
|
||||
@@ -37,36 +36,36 @@ where
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, version)?;
|
||||
let raw = ComputedPerBlock::forced_import(db, name, version, indexes)?;
|
||||
let cumulative =
|
||||
ComputedPerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?;
|
||||
let rolling = RollingFull::forced_import(db, name, version, indexes)?;
|
||||
|
||||
Ok(Self {
|
||||
height,
|
||||
raw,
|
||||
cumulative,
|
||||
rolling,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute height data via closure, then cumulative + rolling.
|
||||
/// Compute raw data via closure, then cumulative + rolling.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_height: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
compute_raw: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: From<f64> + Default + SubAssign + Copy + Ord,
|
||||
f64: From<T>,
|
||||
{
|
||||
compute_height(&mut self.height)?;
|
||||
compute_raw(&mut self.raw.height)?;
|
||||
self.cumulative
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.height, exit)?;
|
||||
.compute_cumulative(max_from, &self.raw.height, exit)?;
|
||||
self.rolling
|
||||
.compute(max_from, windows, &self.height, exit)?;
|
||||
.compute(max_from, windows, &self.raw.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! ComputedPerBlockSum - stored height + RollingWindows (sum only).
|
||||
//! ComputedPerBlockSum - raw ComputedPerBlock + RollingWindows (sum only).
|
||||
//!
|
||||
//! Like ComputedPerBlockCumulativeSum but without the cumulative vec.
|
||||
|
||||
@@ -8,11 +8,11 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{NumericValue, RollingWindows, WindowStarts},
|
||||
internal::{ComputedPerBlock, NumericValue, RollingWindows, WindowStarts},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
@@ -20,7 +20,7 @@ pub struct ComputedPerBlockSum<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
pub raw: ComputedPerBlock<T, M>,
|
||||
pub sum: RollingWindows<T, M>,
|
||||
}
|
||||
|
||||
@@ -34,26 +34,26 @@ where
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, version)?;
|
||||
let raw = ComputedPerBlock::forced_import(db, name, version, indexes)?;
|
||||
let sum = RollingWindows::forced_import(db, &format!("{name}_sum"), version, indexes)?;
|
||||
|
||||
Ok(Self { height, sum })
|
||||
Ok(Self { raw, sum })
|
||||
}
|
||||
|
||||
/// Compute height data via closure, then rolling sum.
|
||||
/// Compute raw data via closure, then rolling sum.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_height: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
compute_raw: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Default + SubAssign,
|
||||
{
|
||||
compute_height(&mut self.height)?;
|
||||
compute_raw(&mut self.raw.height)?;
|
||||
self.sum
|
||||
.compute_rolling_sum(max_from, windows, &self.height, exit)?;
|
||||
.compute_rolling_sum(max_from, windows, &self.raw.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ mod blocks;
|
||||
mod cointime;
|
||||
mod constants;
|
||||
mod distribution;
|
||||
mod indicators;
|
||||
pub mod indexes;
|
||||
mod inputs;
|
||||
mod internal;
|
||||
@@ -37,6 +38,7 @@ pub struct Computer<M: StorageMode = Rw> {
|
||||
pub cointime: Box<cointime::Vecs<M>>,
|
||||
pub constants: Box<constants::Vecs>,
|
||||
pub indexes: Box<indexes::Vecs<M>>,
|
||||
pub indicators: Box<indicators::Vecs<M>>,
|
||||
pub market: Box<market::Vecs<M>>,
|
||||
pub pools: Box<pools::Vecs<M>>,
|
||||
pub prices: Box<prices::Vecs<M>>,
|
||||
@@ -174,28 +176,38 @@ impl Computer {
|
||||
})
|
||||
})?;
|
||||
|
||||
// Market and distribution are independent; import in parallel.
|
||||
// Market, indicators, and distribution are independent; import in parallel.
|
||||
// Supply depends on distribution so it runs after.
|
||||
let (distribution, market) = timed("Imported distribution/market", || {
|
||||
thread::scope(|s| -> Result<_> {
|
||||
let market_handle = big_thread().spawn_scoped(s, || -> Result<_> {
|
||||
Ok(Box::new(market::Vecs::forced_import(
|
||||
let (distribution, market, indicators) =
|
||||
timed("Imported distribution/market/indicators", || {
|
||||
thread::scope(|s| -> Result<_> {
|
||||
let market_handle = big_thread().spawn_scoped(s, || -> Result<_> {
|
||||
Ok(Box::new(market::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION,
|
||||
&indexes,
|
||||
)?))
|
||||
})?;
|
||||
|
||||
let indicators_handle = big_thread().spawn_scoped(s, || -> Result<_> {
|
||||
Ok(Box::new(indicators::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION,
|
||||
&indexes,
|
||||
)?))
|
||||
})?;
|
||||
|
||||
let distribution = Box::new(distribution::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION,
|
||||
&indexes,
|
||||
)?))
|
||||
})?;
|
||||
)?);
|
||||
|
||||
let distribution = Box::new(distribution::Vecs::forced_import(
|
||||
&computed_path,
|
||||
VERSION,
|
||||
&indexes,
|
||||
)?);
|
||||
|
||||
let market = market_handle.join().unwrap()?;
|
||||
Ok((distribution, market))
|
||||
})
|
||||
})?;
|
||||
let market = market_handle.join().unwrap()?;
|
||||
let indicators = indicators_handle.join().unwrap()?;
|
||||
Ok((distribution, market, indicators))
|
||||
})
|
||||
})?;
|
||||
|
||||
let supply = timed("Imported supply", || -> Result<_> {
|
||||
Ok(Box::new(supply::Vecs::forced_import(
|
||||
@@ -214,6 +226,7 @@ impl Computer {
|
||||
transactions,
|
||||
scripts,
|
||||
constants,
|
||||
indicators,
|
||||
market,
|
||||
distribution,
|
||||
supply,
|
||||
@@ -240,6 +253,7 @@ impl Computer {
|
||||
scripts::DB_NAME,
|
||||
positions::DB_NAME,
|
||||
cointime::DB_NAME,
|
||||
indicators::DB_NAME,
|
||||
indexes::DB_NAME,
|
||||
market::DB_NAME,
|
||||
pools::DB_NAME,
|
||||
@@ -408,6 +422,15 @@ impl Computer {
|
||||
&self.indexes,
|
||||
&self.prices,
|
||||
&self.blocks,
|
||||
&starting_indexes,
|
||||
exit,
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
let indicators = scope.spawn(|| {
|
||||
timed("Computed indicators", || {
|
||||
self.indicators.compute(
|
||||
&self.mining,
|
||||
&self.distribution,
|
||||
&self.transactions,
|
||||
@@ -431,6 +454,7 @@ impl Computer {
|
||||
})?;
|
||||
|
||||
market.join().unwrap()?;
|
||||
indicators.join().unwrap()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
@@ -473,6 +497,7 @@ impl Computer<Ro> {
|
||||
positions,
|
||||
cointime,
|
||||
constants,
|
||||
indicators,
|
||||
indexes,
|
||||
market,
|
||||
pools,
|
||||
|
||||
@@ -2,20 +2,16 @@ use brk_error::Result;
|
||||
use brk_types::Indexes;
|
||||
use vecdb::Exit;
|
||||
|
||||
use crate::{blocks, distribution, indexes, mining, prices, transactions};
|
||||
use crate::{blocks, indexes, prices};
|
||||
|
||||
use super::Vecs;
|
||||
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
blocks: &blocks::Vecs,
|
||||
mining: &mining::Vecs,
|
||||
distribution: &distribution::Vecs,
|
||||
transactions: &transactions::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
@@ -63,14 +59,11 @@ impl Vecs {
|
||||
r4?;
|
||||
|
||||
// Phase 3: Depends on returns, range, moving_average
|
||||
self.indicators.compute(
|
||||
&mining.rewards,
|
||||
self.technical.compute(
|
||||
&self.returns,
|
||||
&self.range,
|
||||
prices,
|
||||
blocks,
|
||||
distribution,
|
||||
transactions,
|
||||
&self.moving_average,
|
||||
starting_indexes,
|
||||
exit,
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
AthVecs, DcaVecs, IndicatorsVecs, LookbackVecs, MovingAverageVecs, RangeVecs, ReturnsVecs,
|
||||
AthVecs, DcaVecs, TechnicalVecs, LookbackVecs, MovingAverageVecs, RangeVecs, ReturnsVecs,
|
||||
Vecs, VolatilityVecs,
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ impl Vecs {
|
||||
let range = RangeVecs::forced_import(&db, version, indexes)?;
|
||||
let moving_average = MovingAverageVecs::forced_import(&db, version, indexes)?;
|
||||
let dca = DcaVecs::forced_import(&db, version, indexes)?;
|
||||
let indicators = IndicatorsVecs::forced_import(&db, version, indexes)?;
|
||||
let technical = TechnicalVecs::forced_import(&db, version, indexes)?;
|
||||
|
||||
let this = Self {
|
||||
db,
|
||||
@@ -40,7 +40,7 @@ impl Vecs {
|
||||
range,
|
||||
moving_average,
|
||||
dca,
|
||||
indicators,
|
||||
technical,
|
||||
};
|
||||
finalize_db(&this.db, &this)?;
|
||||
Ok(this)
|
||||
|
||||
@@ -2,7 +2,7 @@ pub mod ath;
|
||||
mod compute;
|
||||
pub mod dca;
|
||||
mod import;
|
||||
pub mod indicators;
|
||||
pub mod technical;
|
||||
pub mod lookback;
|
||||
pub mod moving_average;
|
||||
pub mod range;
|
||||
@@ -14,7 +14,7 @@ use vecdb::{Database, Rw, StorageMode};
|
||||
|
||||
pub use ath::Vecs as AthVecs;
|
||||
pub use dca::Vecs as DcaVecs;
|
||||
pub use indicators::Vecs as IndicatorsVecs;
|
||||
pub use technical::Vecs as TechnicalVecs;
|
||||
pub use lookback::Vecs as LookbackVecs;
|
||||
pub use moving_average::Vecs as MovingAverageVecs;
|
||||
pub use range::Vecs as RangeVecs;
|
||||
@@ -33,5 +33,5 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub range: RangeVecs<M>,
|
||||
pub moving_average: MovingAverageVecs<M>,
|
||||
pub dca: DcaVecs<M>,
|
||||
pub indicators: IndicatorsVecs<M>,
|
||||
pub technical: TechnicalVecs<M>,
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ use vecdb::Exit;
|
||||
|
||||
use super::{
|
||||
super::{moving_average, range, returns},
|
||||
Vecs, gini, macd, rsi,
|
||||
Vecs, macd, rsi,
|
||||
};
|
||||
use crate::{blocks, distribution, internal::RatioDollarsBp32, mining, prices, transactions};
|
||||
use crate::{blocks, internal::RatioDollarsBp32, prices};
|
||||
|
||||
const TF_MULTIPLIERS: [usize; 4] = [1, 7, 30, 365];
|
||||
|
||||
@@ -14,27 +14,15 @@ impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
rewards: &mining::RewardsVecs,
|
||||
returns: &returns::Vecs,
|
||||
range: &range::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
blocks: &blocks::Vecs,
|
||||
distribution: &distribution::Vecs,
|
||||
transactions: &transactions::Vecs,
|
||||
moving_average: &moving_average::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.puell_multiple
|
||||
.bps
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||||
starting_indexes.height,
|
||||
&rewards.subsidy.base.usd.height,
|
||||
&rewards.subsidy_sma_1y.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Stochastic Oscillator: K = (close - low_2w) / (high_2w - low_2w), stored as ratio (0–1)
|
||||
// Stochastic Oscillator: K = (close - low_2w) / (high_2w - low_2w), stored as ratio (0-1)
|
||||
{
|
||||
let price = &prices.price.usd.height;
|
||||
self.stoch_k.bps.height.compute_transform3(
|
||||
@@ -101,53 +89,6 @@ impl Vecs {
|
||||
)?;
|
||||
}
|
||||
|
||||
// Gini (per height)
|
||||
gini::compute(&mut self.gini, distribution, starting_indexes, exit)?;
|
||||
|
||||
// RHODL Ratio: 1d-1w realized cap / 1y-2y realized cap
|
||||
self.rhodl_ratio
|
||||
.bps
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||||
starting_indexes.height,
|
||||
&distribution
|
||||
.utxo_cohorts
|
||||
.age_range
|
||||
._1d_to_1w
|
||||
.metrics
|
||||
.realized
|
||||
.cap
|
||||
.usd
|
||||
.height,
|
||||
&distribution
|
||||
.utxo_cohorts
|
||||
.age_range
|
||||
._1y_to_2y
|
||||
.metrics
|
||||
.realized
|
||||
.cap
|
||||
.usd
|
||||
.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// NVT: market_cap / tx_volume_24h
|
||||
let market_cap = &distribution
|
||||
.utxo_cohorts
|
||||
.all
|
||||
.metrics
|
||||
.supply
|
||||
.total
|
||||
.usd
|
||||
.height;
|
||||
self.nvt
|
||||
.bps
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||||
starting_indexes.height,
|
||||
market_cap,
|
||||
&transactions.volume.sent_sum.rolling._24h.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Pi Cycle: sma_111d / sma_350d_x2
|
||||
self.pi_cycle
|
||||
.bps
|
||||
@@ -5,7 +5,7 @@ use vecdb::Database;
|
||||
use super::{MacdChain, RsiChain, Vecs};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedPerBlock, RatioPerBlock, PercentPerBlock, Windows},
|
||||
internal::{ComputedPerBlock, PercentPerBlock, RatioPerBlock, Windows},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::new(2);
|
||||
@@ -106,34 +106,20 @@ impl Vecs {
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let nvt = RatioPerBlock::forced_import_raw(db, "nvt", v, indexes)?;
|
||||
|
||||
let rsi = Windows::try_from_fn(|tf| RsiChain::forced_import(db, tf, v, indexes))?;
|
||||
let macd = Windows::try_from_fn(|tf| MacdChain::forced_import(db, tf, v, indexes))?;
|
||||
|
||||
let stoch_k = PercentPerBlock::forced_import(db, "stoch_k", v, indexes)?;
|
||||
let stoch_d = PercentPerBlock::forced_import(db, "stoch_d", v, indexes)?;
|
||||
let gini = PercentPerBlock::forced_import(db, "gini", v, indexes)?;
|
||||
|
||||
let pi_cycle = RatioPerBlock::forced_import_raw(db, "pi_cycle", v, indexes)?;
|
||||
|
||||
let rhodl_ratio = RatioPerBlock::forced_import_raw(db, "rhodl_ratio", v, indexes)?;
|
||||
|
||||
Ok(Self {
|
||||
puell_multiple: RatioPerBlock::forced_import_raw(
|
||||
db,
|
||||
"puell_multiple",
|
||||
v,
|
||||
indexes,
|
||||
)?,
|
||||
nvt,
|
||||
rsi,
|
||||
stoch_k,
|
||||
stoch_d,
|
||||
pi_cycle,
|
||||
macd,
|
||||
gini,
|
||||
rhodl_ratio,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
mod compute;
|
||||
mod gini;
|
||||
mod import;
|
||||
mod macd;
|
||||
mod rsi;
|
||||
@@ -2,7 +2,7 @@ use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints16, BasisPoints32, StoredF32};
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedPerBlock, RatioPerBlock, PercentPerBlock, Windows};
|
||||
use crate::internal::{ComputedPerBlock, PercentPerBlock, RatioPerBlock, Windows};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RsiChain<M: StorageMode = Rw> {
|
||||
@@ -29,9 +29,6 @@ pub struct MacdChain<M: StorageMode = Rw> {
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub puell_multiple: RatioPerBlock<BasisPoints32, M>,
|
||||
pub nvt: RatioPerBlock<BasisPoints32, M>,
|
||||
|
||||
pub rsi: Windows<RsiChain<M>>,
|
||||
|
||||
pub stoch_k: PercentPerBlock<BasisPoints16, M>,
|
||||
@@ -40,8 +37,4 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub pi_cycle: RatioPerBlock<BasisPoints32, M>,
|
||||
|
||||
pub macd: Windows<MacdChain<M>>,
|
||||
|
||||
pub gini: PercentPerBlock<BasisPoints16, M>,
|
||||
|
||||
pub rhodl_ratio: RatioPerBlock<BasisPoints32, M>,
|
||||
}
|
||||
@@ -75,7 +75,7 @@ impl Vecs {
|
||||
self.blocks_mined_sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.base.blocks_mined.height,
|
||||
&self.base.blocks_mined.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -101,7 +101,7 @@ impl Vecs {
|
||||
|vec| {
|
||||
Ok(vec.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.base.blocks_mined.height,
|
||||
&self.base.blocks_mined.raw.height,
|
||||
&mining.rewards.coinbase.base.sats.height,
|
||||
|(h, mask, val, ..)| (h, MaskSats::apply(mask, val)),
|
||||
exit,
|
||||
|
||||
@@ -38,14 +38,14 @@ impl Vecs {
|
||||
) -> Result<()> {
|
||||
self.taproot.compute_binary::<_, _, RatioU64Bp16>(
|
||||
starting_indexes.height,
|
||||
&count.p2tr.height,
|
||||
&count.p2tr.raw.height,
|
||||
&outputs_count.total_count.full.sum,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.segwit.compute_binary::<_, _, RatioU64Bp16>(
|
||||
starting_indexes.height,
|
||||
&count.segwit.height,
|
||||
&count.segwit.raw.height,
|
||||
&outputs_count.total_count.full.sum,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -141,9 +141,9 @@ impl Vecs {
|
||||
.compute(starting_indexes.height, &window_starts, exit, |v| {
|
||||
Ok(v.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&self.p2wpkh.height,
|
||||
&self.p2wsh.height,
|
||||
&self.p2tr.height,
|
||||
&self.p2wpkh.raw.height,
|
||||
&self.p2wsh.raw.height,
|
||||
&self.p2tr.raw.height,
|
||||
|(h, p2wpkh, p2wsh, p2tr, ..)| (h, StoredU64::from(*p2wpkh + *p2wsh + *p2tr)),
|
||||
exit,
|
||||
)?)
|
||||
|
||||
@@ -71,7 +71,7 @@ impl Vecs {
|
||||
.height
|
||||
.compute_binary::<_, Timestamp, PerSec>(
|
||||
starting_indexes.height,
|
||||
&count_vecs.tx_count.height,
|
||||
&count_vecs.tx_count.raw.height,
|
||||
&blocks.interval.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -6,8 +6,6 @@ use crate::{Sats, Term, Timestamp};
|
||||
pub struct Age {
|
||||
/// Age in hours (primary internal unit for cohort boundaries)
|
||||
hours: usize,
|
||||
/// Age in blocks (for satblocks_destroyed calculation)
|
||||
blocks: usize,
|
||||
/// Age in days as float (for satdays_destroyed - established terminology)
|
||||
days: f64,
|
||||
}
|
||||
@@ -15,10 +13,9 @@ pub struct Age {
|
||||
impl Age {
|
||||
/// Create from timestamps and block count
|
||||
#[inline]
|
||||
pub fn new(current_timestamp: Timestamp, prev_timestamp: Timestamp, blocks: usize) -> Self {
|
||||
pub fn new(current_timestamp: Timestamp, prev_timestamp: Timestamp) -> Self {
|
||||
Self {
|
||||
hours: current_timestamp.difference_in_hours_between(prev_timestamp),
|
||||
blocks,
|
||||
days: current_timestamp.difference_in_days_between_float(prev_timestamp),
|
||||
}
|
||||
}
|
||||
@@ -29,12 +26,6 @@ impl Age {
|
||||
self.hours
|
||||
}
|
||||
|
||||
/// Blocks old (for satblocks_destroyed calculation)
|
||||
#[inline]
|
||||
pub fn blocks(&self) -> usize {
|
||||
self.blocks
|
||||
}
|
||||
|
||||
/// Days old as float (for satdays_destroyed - established terminology)
|
||||
#[inline]
|
||||
pub fn days(&self) -> f64 {
|
||||
@@ -51,21 +42,9 @@ impl Age {
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate satblocks destroyed for given supply
|
||||
#[inline]
|
||||
pub fn satblocks_destroyed(&self, supply: Sats) -> Sats {
|
||||
if self.blocks == 0 {
|
||||
return Sats::ZERO;
|
||||
}
|
||||
Sats::from(u64::from(supply) * self.blocks as u64)
|
||||
}
|
||||
|
||||
/// Calculate satdays destroyed for given supply
|
||||
#[inline]
|
||||
pub fn satdays_destroyed(&self, supply: Sats) -> Sats {
|
||||
if self.blocks == 0 {
|
||||
return Sats::ZERO;
|
||||
}
|
||||
Sats::from((u64::from(supply) as f64 * self.days).floor() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user