Files
brk/crates/brk_computer/src/indicators/compute.rs
2026-04-14 22:53:10 +02:00

210 lines
7.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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(())
}
}