mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
210 lines
7.1 KiB
Rust
210 lines
7.1 KiB
Rust
use brk_error::Result;
|
||
use brk_types::{Bitcoin, Dollars, Indexes, StoredF32};
|
||
use vecdb::Exit;
|
||
|
||
use super::{Vecs, gini};
|
||
use crate::{distribution, internal::RatioDollarsBp32, market, mining, transactions};
|
||
|
||
impl Vecs {
|
||
#[allow(clippy::too_many_arguments)]
|
||
pub(crate) fn compute(
|
||
&mut self,
|
||
mining: &mining::Vecs,
|
||
distribution: &distribution::Vecs,
|
||
transactions: &transactions::Vecs,
|
||
market: &market::Vecs,
|
||
starting_indexes: &Indexes,
|
||
exit: &Exit,
|
||
) -> Result<()> {
|
||
self.db.sync_bg_tasks()?;
|
||
|
||
// Puell Multiple: daily_subsidy_usd / sma_365d_subsidy_usd
|
||
self.puell_multiple
|
||
.bps
|
||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||
starting_indexes.height,
|
||
&mining.rewards.subsidy.block.usd,
|
||
&mining.rewards.subsidy.average._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.transfer_volume.sum._24h.usd.height,
|
||
exit,
|
||
)?;
|
||
|
||
// Thermocap Multiple: market_cap / thermo_cap
|
||
self.thermo_cap_multiple
|
||
.bps
|
||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||
starting_indexes.height,
|
||
market_cap,
|
||
&mining.rewards.subsidy.cumulative.usd.height,
|
||
exit,
|
||
)?;
|
||
|
||
let all_metrics = &distribution.utxo_cohorts.all.metrics;
|
||
let all_activity = &all_metrics.activity;
|
||
let supply_total_sats = &all_metrics.supply.total.sats.height;
|
||
|
||
// Supply-Adjusted CDD = sum_24h(CDD) / circulating_supply_btc
|
||
self.coindays_destroyed_supply_adj
|
||
.height
|
||
.compute_transform2(
|
||
starting_indexes.height,
|
||
&all_activity.coindays_destroyed.sum._24h.height,
|
||
supply_total_sats,
|
||
|(i, cdd_24h, supply_sats, ..)| {
|
||
let supply = f64::from(Bitcoin::from(supply_sats));
|
||
if supply == 0.0 {
|
||
(i, StoredF32::from(0.0f32))
|
||
} else {
|
||
(i, StoredF32::from((f64::from(cdd_24h) / supply) as f32))
|
||
}
|
||
},
|
||
exit,
|
||
)?;
|
||
|
||
// Supply-Adjusted CYD = CYD / circulating_supply_btc
|
||
self.coinyears_destroyed_supply_adj
|
||
.height
|
||
.compute_transform2(
|
||
starting_indexes.height,
|
||
&all_activity.coinyears_destroyed.height,
|
||
supply_total_sats,
|
||
|(i, cyd, supply_sats, ..)| {
|
||
let supply = f64::from(Bitcoin::from(supply_sats));
|
||
if supply == 0.0 {
|
||
(i, StoredF32::from(0.0f32))
|
||
} else {
|
||
(i, StoredF32::from((f64::from(cyd) / supply) as f32))
|
||
}
|
||
},
|
||
exit,
|
||
)?;
|
||
|
||
// Supply-Adjusted Dormancy = dormancy / circulating_supply_btc
|
||
self.dormancy.supply_adj.height.compute_transform2(
|
||
starting_indexes.height,
|
||
&all_activity.dormancy._24h.height,
|
||
supply_total_sats,
|
||
|(i, dormancy, supply_sats, ..)| {
|
||
let supply = f64::from(Bitcoin::from(supply_sats));
|
||
if supply == 0.0 {
|
||
(i, StoredF32::from(0.0f32))
|
||
} else {
|
||
(i, StoredF32::from((f64::from(dormancy) / supply) as f32))
|
||
}
|
||
},
|
||
exit,
|
||
)?;
|
||
|
||
// Stock-to-Flow: supply / annual_issuance
|
||
// annual_issuance ≈ subsidy_per_block × 52560 (blocks/year)
|
||
self.stock_to_flow.height.compute_transform2(
|
||
starting_indexes.height,
|
||
supply_total_sats,
|
||
&mining.rewards.subsidy.block.sats,
|
||
|(i, supply_sats, subsidy_sats, ..)| {
|
||
let annual_flow = subsidy_sats.as_u128() as f64 * 52560.0;
|
||
if annual_flow == 0.0 {
|
||
(i, StoredF32::from(0.0f32))
|
||
} else {
|
||
(
|
||
i,
|
||
StoredF32::from((supply_sats.as_u128() as f64 / annual_flow) as f32),
|
||
)
|
||
}
|
||
},
|
||
exit,
|
||
)?;
|
||
|
||
// Dormancy Flow: supply_btc / dormancy
|
||
self.dormancy.flow.height.compute_transform2(
|
||
starting_indexes.height,
|
||
supply_total_sats,
|
||
&all_activity.dormancy._24h.height,
|
||
|(i, supply_sats, dormancy, ..)| {
|
||
let d = f64::from(dormancy);
|
||
if d == 0.0 {
|
||
(i, StoredF32::from(0.0f32))
|
||
} else {
|
||
let supply = f64::from(Bitcoin::from(supply_sats));
|
||
(i, StoredF32::from((supply / d) as f32))
|
||
}
|
||
},
|
||
exit,
|
||
)?;
|
||
|
||
// Seller Exhaustion Constant: % supply_in_profit × 30d_volatility
|
||
self.seller_exhaustion.height.compute_transform3(
|
||
starting_indexes.height,
|
||
&all_metrics.supply.in_profit.sats.height,
|
||
&market.volatility._1m.height,
|
||
supply_total_sats,
|
||
|(i, profit_sats, volatility, total_sats, ..)| {
|
||
let total = total_sats.as_u128() as f64;
|
||
if total == 0.0 {
|
||
(i, StoredF32::from(0.0f32))
|
||
} else {
|
||
let pct_in_profit = profit_sats.as_u128() as f64 / total;
|
||
(
|
||
i,
|
||
StoredF32::from((pct_in_profit * f64::from(volatility)) as f32),
|
||
)
|
||
}
|
||
},
|
||
exit,
|
||
)?;
|
||
|
||
let exit = exit.clone();
|
||
self.db.run_bg(move |db| {
|
||
let _lock = exit.lock();
|
||
db.compact_deferred_default()
|
||
});
|
||
Ok(())
|
||
}
|
||
}
|