global: snapshot

This commit is contained in:
nym21
2026-03-01 20:06:25 +01:00
parent 7bf0220f25
commit 4abb00b86d
71 changed files with 2432 additions and 2157 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
use brk_error::Result;
use brk_types::Cents;
use vecdb::Exit;
use super::super::{activity, cap, supply};
@@ -32,13 +33,16 @@ impl Vecs {
.metrics
.realized
.realized_price
.usd
.cents
.height;
self.vaulted_price.usd.height.compute_divide(
self.vaulted_price.cents.height.compute_transform2(
starting_indexes.height,
realized_price,
&activity.vaultedness.height,
|(i, price, vaultedness, ..)| {
(i, Cents::from(f64::from(price) / f64::from(vaultedness)))
},
exit,
)?;
@@ -47,13 +51,16 @@ impl Vecs {
prices,
starting_indexes,
exit,
&self.vaulted_price.usd.height,
&self.vaulted_price.cents.height,
)?;
self.active_price.usd.height.compute_multiply(
self.active_price.cents.height.compute_transform2(
starting_indexes.height,
realized_price,
&activity.liveliness.height,
|(i, price, liveliness, ..)| {
(i, Cents::from(f64::from(price) * f64::from(liveliness)))
},
exit,
)?;
@@ -62,13 +69,16 @@ impl Vecs {
prices,
starting_indexes,
exit,
&self.active_price.usd.height,
&self.active_price.cents.height,
)?;
self.true_market_mean.usd.height.compute_divide(
self.true_market_mean.cents.height.compute_transform2(
starting_indexes.height,
&cap.investor_cap.height,
&supply.active_supply.btc.height,
|(i, cap_dollars, supply_btc, ..)| {
(i, Cents::from(f64::from(Cents::from(cap_dollars)) / f64::from(supply_btc)))
},
exit,
)?;
@@ -77,14 +87,17 @@ impl Vecs {
prices,
starting_indexes,
exit,
&self.true_market_mean.usd.height,
&self.true_market_mean.cents.height,
)?;
// cointime_price = cointime_cap / circulating_supply
self.cointime_price.usd.height.compute_divide(
self.cointime_price.cents.height.compute_transform2(
starting_indexes.height,
&cap.cointime_cap.height,
circulating_supply,
|(i, cap_dollars, supply_btc, ..)| {
(i, Cents::from(f64::from(Cents::from(cap_dollars)) / f64::from(supply_btc)))
},
exit,
)?;
@@ -93,7 +106,7 @@ impl Vecs {
prices,
starting_indexes,
exit,
&self.cointime_price.usd.height,
&self.cointime_price.cents.height,
)?;
Ok(())

View File

@@ -1,17 +1,17 @@
use brk_traversable::Traversable;
use brk_types::Dollars;
use brk_types::Cents;
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeightLast, ComputedFromHeightRatioExtended, Price};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub vaulted_price: Price<ComputedFromHeightLast<Dollars, M>>,
pub vaulted_price: Price<ComputedFromHeightLast<Cents, M>>,
pub vaulted_price_ratio: ComputedFromHeightRatioExtended<M>,
pub active_price: Price<ComputedFromHeightLast<Dollars, M>>,
pub active_price: Price<ComputedFromHeightLast<Cents, M>>,
pub active_price_ratio: ComputedFromHeightRatioExtended<M>,
pub true_market_mean: Price<ComputedFromHeightLast<Dollars, M>>,
pub true_market_mean: Price<ComputedFromHeightLast<Cents, M>>,
pub true_market_mean_ratio: ComputedFromHeightRatioExtended<M>,
pub cointime_price: Price<ComputedFromHeightLast<Dollars, M>>,
pub cointime_price: Price<ComputedFromHeightLast<Cents, M>>,
pub cointime_price_ratio: ComputedFromHeightRatioExtended<M>,
}

View File

@@ -828,7 +828,7 @@ impl UTXOCohorts<Rw> {
pub(crate) fn truncate_push_aggregate_percentiles(
&mut self,
height: Height,
spot: Dollars,
spot: Cents,
day1_opt: Option<Day1>,
states_path: &Path,
) -> Result<()> {
@@ -895,7 +895,7 @@ impl UTXOCohorts<Rw> {
.collect();
if total_sats == 0 {
let nan_prices = [Dollars::NAN; PERCENTILES_LEN];
let nan_prices = [Cents::ZERO; PERCENTILES_LEN];
target
.extended
.percentiles
@@ -928,8 +928,8 @@ impl UTXOCohorts<Rw> {
let sat_targets = PERCENTILES.map(|p| total_sats * u64::from(p) / 100);
let usd_targets = PERCENTILES.map(|p| total_usd * u128::from(p) / 100);
let mut sat_result = [Dollars::NAN; PERCENTILES_LEN];
let mut usd_result = [Dollars::NAN; PERCENTILES_LEN];
let mut sat_result = [Cents::ZERO; PERCENTILES_LEN];
let mut usd_result = [Cents::ZERO; PERCENTILES_LEN];
let mut cumsum_sats: u64 = 0;
let mut cumsum_usd: u128 = 0;
@@ -953,13 +953,12 @@ impl UTXOCohorts<Rw> {
cumsum_usd += usd;
if sat_idx < PERCENTILES_LEN || usd_idx < PERCENTILES_LEN {
let dollars = price.to_dollars();
while sat_idx < PERCENTILES_LEN && cumsum_sats >= sat_targets[sat_idx] {
sat_result[sat_idx] = dollars;
sat_result[sat_idx] = price;
sat_idx += 1;
}
while usd_idx < PERCENTILES_LEN && cumsum_usd >= usd_targets[usd_idx] {
usd_result[usd_idx] = dollars;
usd_result[usd_idx] = price;
usd_idx += 1;
}
}

View File

@@ -451,10 +451,9 @@ pub(crate) fn process_blocks(
)?;
// Compute and push percentiles for aggregate cohorts (all, sth, lth)
let spot = block_price.to_dollars();
vecs.utxo_cohorts.truncate_push_aggregate_percentiles(
height,
spot,
block_price,
day1_opt,
&vecs.states_path,
)?;

View File

@@ -170,7 +170,7 @@ impl ActivityMetrics {
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent.base.sats.height,
&self.sent.base.usd.height,
&self.sent.base.cents.height,
exit,
)?;

View File

@@ -99,8 +99,8 @@ impl AdjustedCohortMetrics {
prices: &prices::Vecs,
starting_indexes: &ComputeIndexes,
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_created: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_created: &impl ReadableVec<Height, Cents>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
all_supply_sats: &impl ReadableVec<Height, Sats>,
exit: &Exit,
) -> Result<()> {

View File

@@ -87,10 +87,9 @@ impl CohortMetricsBase for AllCohortMetrics {
self.unrealized
.base
.truncate_push(height, &height_unrealized_state)?;
let spot = height_price.to_dollars();
self.cost_basis
.extended
.truncate_push_percentiles(height, state, spot)?;
.truncate_push_percentiles(height, state, height_price)?;
Ok(())
}
fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
@@ -140,8 +139,8 @@ impl AllCohortMetrics {
prices: &prices::Vecs,
starting_indexes: &ComputeIndexes,
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_created: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_created: &impl ReadableVec<Height, Cents>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
self.realized.compute_rest_part2(

View File

@@ -56,8 +56,7 @@ impl CohortMetricsBase for ExtendedCohortMetrics {
self.cost_basis.truncate_push_minmax(height, state)?;
let (height_unrealized_state, _) = state.compute_unrealized_states(height_price, None);
self.unrealized.base.truncate_push(height, &height_unrealized_state)?;
let spot = height_price.to_dollars();
self.cost_basis.extended.truncate_push_percentiles(height, state, spot)?;
self.cost_basis.extended.truncate_push_percentiles(height, state, height_price)?;
Ok(())
}
fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {

View File

@@ -55,8 +55,7 @@ impl CohortMetricsBase for ExtendedAdjustedCohortMetrics {
self.cost_basis.truncate_push_minmax(height, state)?;
let (height_unrealized_state, _) = state.compute_unrealized_states(height_price, None);
self.unrealized.base.truncate_push(height, &height_unrealized_state)?;
let spot = height_price.to_dollars();
self.cost_basis.extended.truncate_push_percentiles(height, state, spot)?;
self.cost_basis.extended.truncate_push_percentiles(height, state, height_price)?;
Ok(())
}
fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
@@ -102,8 +101,8 @@ impl ExtendedAdjustedCohortMetrics {
prices: &prices::Vecs,
starting_indexes: &ComputeIndexes,
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_created: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_created: &impl ReadableVec<Height, Cents>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
all_supply_sats: &impl ReadableVec<Height, Sats>,
exit: &Exit,
) -> Result<()> {

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height};
use brk_types::{Cents, Height};
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
use crate::{
@@ -15,10 +15,10 @@ use crate::distribution::metrics::ImportConfig;
#[derive(Traversable)]
pub struct CostBasisBase<M: StorageMode = Rw> {
/// Minimum cost basis for any UTXO at this height
pub min: Price<ComputedFromHeightLast<Dollars, M>>,
pub min: Price<ComputedFromHeightLast<Cents, M>>,
/// Maximum cost basis for any UTXO at this height
pub max: Price<ComputedFromHeightLast<Dollars, M>>,
pub max: Price<ComputedFromHeightLast<Cents, M>>,
}
impl CostBasisBase {
@@ -40,7 +40,7 @@ impl CostBasisBase {
}
pub(crate) fn min_stateful_height_len(&self) -> usize {
self.min.usd.height.len().min(self.max.usd.height.len())
self.min.cents.height.len().min(self.max.cents.height.len())
}
pub(crate) fn truncate_push_minmax(
@@ -48,27 +48,27 @@ impl CostBasisBase {
height: Height,
state: &CohortState,
) -> Result<()> {
self.min.usd.height.truncate_push(
self.min.cents.height.truncate_push(
height,
state
.cost_basis_data_first_key_value()
.map(|(cents, _)| cents.into())
.unwrap_or(Dollars::NAN),
.map(|(cents, _)| cents)
.unwrap_or(Cents::ZERO),
)?;
self.max.usd.height.truncate_push(
self.max.cents.height.truncate_push(
height,
state
.cost_basis_data_last_key_value()
.map(|(cents, _)| cents.into())
.unwrap_or(Dollars::NAN),
.map(|(cents, _)| cents)
.unwrap_or(Cents::ZERO),
)?;
Ok(())
}
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
vec![
&mut self.min.usd.height as &mut dyn AnyStoredVec,
&mut self.max.usd.height,
&mut self.min.cents.height as &mut dyn AnyStoredVec,
&mut self.max.cents.height,
]
}
@@ -78,14 +78,14 @@ impl CostBasisBase {
others: &[&Self],
exit: &Exit,
) -> Result<()> {
self.min.usd.height.compute_min_of_others(
self.min.cents.height.compute_min_of_others(
starting_indexes.height,
&others.iter().map(|v| &v.min.usd.height).collect::<Vec<_>>(),
&others.iter().map(|v| &v.min.cents.height).collect::<Vec<_>>(),
exit,
)?;
self.max.usd.height.compute_max_of_others(
self.max.cents.height.compute_max_of_others(
starting_indexes.height,
&others.iter().map(|v| &v.max.usd.height).collect::<Vec<_>>(),
&others.iter().map(|v| &v.max.cents.height).collect::<Vec<_>>(),
exit,
)?;
Ok(())

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, StoredF32, Version};
use brk_types::{Cents, Height, StoredF32, Version};
use vecdb::{AnyStoredVec, Rw, StorageMode, WritableVec};
use crate::{
@@ -62,14 +62,14 @@ impl CostBasisExtended {
&mut self,
height: Height,
state: &mut CohortState,
spot: Dollars,
spot: Cents,
) -> Result<()> {
let computed = state.compute_percentiles();
let sat_prices = computed
.as_ref()
.map(|p| p.sat_weighted.map(|c| c.to_dollars()))
.unwrap_or([Dollars::NAN; PERCENTILES_LEN]);
.map(|p| p.sat_weighted)
.unwrap_or([Cents::ZERO; PERCENTILES_LEN]);
self.percentiles.truncate_push(height, &sat_prices)?;
let rank = compute_spot_percentile_rank(&sat_prices, spot);
@@ -79,8 +79,8 @@ impl CostBasisExtended {
let usd_prices = computed
.as_ref()
.map(|p| p.usd_weighted.map(|c| c.to_dollars()))
.unwrap_or([Dollars::NAN; PERCENTILES_LEN]);
.map(|p| p.usd_weighted)
.unwrap_or([Cents::ZERO; PERCENTILES_LEN]);
self.invested_capital.truncate_push(height, &usd_prices)?;
let rank = compute_spot_percentile_rank(&usd_prices, spot);
@@ -97,13 +97,13 @@ impl CostBasisExtended {
self.percentiles
.vecs
.iter_mut()
.map(|v| &mut v.usd.height as &mut dyn AnyStoredVec),
.map(|v| &mut v.cents.height as &mut dyn AnyStoredVec),
);
vecs.extend(
self.invested_capital
.vecs
.iter_mut()
.map(|v| &mut v.usd.height as &mut dyn AnyStoredVec),
.map(|v| &mut v.cents.height as &mut dyn AnyStoredVec),
);
vecs.push(&mut self.spot_cost_basis_percentile.height);
vecs.push(&mut self.spot_invested_capital_percentile.height);

View File

@@ -1,11 +1,11 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, StoredF64, Version};
use brk_types::{Cents, Height, StoredF64, Version};
use vecdb::{Exit, Ident, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
use crate::{
ComputeIndexes, blocks,
internal::{ComputedFromHeightLast, LazyFromHeightLast, Ratio64},
internal::{ComputedFromHeightLast, LazyFromHeightLast, RatioCents64},
};
use crate::distribution::metrics::ImportConfig;
@@ -14,18 +14,18 @@ use crate::distribution::metrics::ImportConfig;
#[derive(Traversable)]
pub struct RealizedAdjusted<M: StorageMode = Rw> {
// === Adjusted Value (computed: cohort - up_to_1h) ===
pub adjusted_value_created: ComputedFromHeightLast<Dollars, M>,
pub adjusted_value_destroyed: ComputedFromHeightLast<Dollars, M>,
pub adjusted_value_created: ComputedFromHeightLast<Cents, M>,
pub adjusted_value_destroyed: ComputedFromHeightLast<Cents, M>,
// === Adjusted Value Created/Destroyed Rolling Sums ===
pub adjusted_value_created_24h: ComputedFromHeightLast<Dollars, M>,
pub adjusted_value_created_7d: ComputedFromHeightLast<Dollars, M>,
pub adjusted_value_created_30d: ComputedFromHeightLast<Dollars, M>,
pub adjusted_value_created_1y: ComputedFromHeightLast<Dollars, M>,
pub adjusted_value_destroyed_24h: ComputedFromHeightLast<Dollars, M>,
pub adjusted_value_destroyed_7d: ComputedFromHeightLast<Dollars, M>,
pub adjusted_value_destroyed_30d: ComputedFromHeightLast<Dollars, M>,
pub adjusted_value_destroyed_1y: ComputedFromHeightLast<Dollars, M>,
pub adjusted_value_created_24h: ComputedFromHeightLast<Cents, M>,
pub adjusted_value_created_7d: ComputedFromHeightLast<Cents, M>,
pub adjusted_value_created_30d: ComputedFromHeightLast<Cents, M>,
pub adjusted_value_created_1y: ComputedFromHeightLast<Cents, M>,
pub adjusted_value_destroyed_24h: ComputedFromHeightLast<Cents, M>,
pub adjusted_value_destroyed_7d: ComputedFromHeightLast<Cents, M>,
pub adjusted_value_destroyed_30d: ComputedFromHeightLast<Cents, M>,
pub adjusted_value_destroyed_1y: ComputedFromHeightLast<Cents, M>,
// === Adjusted SOPR (rolling window ratios) ===
pub adjusted_sopr: LazyFromHeightLast<StoredF64>,
@@ -150,10 +150,10 @@ impl RealizedAdjusted {
&mut self,
blocks: &blocks::Vecs,
starting_indexes: &ComputeIndexes,
base_value_created: &impl ReadableVec<Height, Dollars>,
base_value_destroyed: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_created: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Dollars>,
base_value_created: &impl ReadableVec<Height, Cents>,
base_value_destroyed: &impl ReadableVec<Height, Cents>,
up_to_1h_value_created: &impl ReadableVec<Height, Cents>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
// Compute adjusted_value_created = base.value_created - up_to_1h.value_created
@@ -231,28 +231,28 @@ impl RealizedAdjusted {
// SOPR ratios from rolling sums
self.adjusted_sopr_24h
.compute_binary::<Dollars, Dollars, Ratio64>(
.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.adjusted_value_created_24h.height,
&self.adjusted_value_destroyed_24h.height,
exit,
)?;
self.adjusted_sopr_7d
.compute_binary::<Dollars, Dollars, Ratio64>(
.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.adjusted_value_created_7d.height,
&self.adjusted_value_destroyed_7d.height,
exit,
)?;
self.adjusted_sopr_30d
.compute_binary::<Dollars, Dollars, Ratio64>(
.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.adjusted_value_created_30d.height,
&self.adjusted_value_destroyed_30d.height,
exit,
)?;
self.adjusted_sopr_1y
.compute_binary::<Dollars, Dollars, Ratio64>(
.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.adjusted_value_created_1y.height,
&self.adjusted_value_destroyed_1y.height,

View File

@@ -1,10 +1,10 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
Bitcoin, Cents, CentsSats, CentsSquaredSats, Dollars, Height, StoredF32, StoredF64, Version,
Bitcoin, Cents, CentsSats, CentsSigned, CentsSquaredSats, Dollars, Height, Sats, StoredF32, StoredF64, Version,
};
use vecdb::{
AnyStoredVec, AnyVec, BytesVec, Exit, Ident, ImportableVec, Negate, ReadableCloneableVec,
AnyStoredVec, AnyVec, BytesVec, Exit, Ident, ImportableVec, ReadableCloneableVec,
ReadableVec, Rw, StorageMode, WritableVec,
};
@@ -12,9 +12,9 @@ use crate::{
ComputeIndexes, blocks,
distribution::state::RealizedState,
internal::{
CentsUnsignedToDollars, ComputedFromHeightCumulative, ComputedFromHeightLast,
ComputedFromHeightRatio, DollarsPlus, ValueFromHeightCumulative, LazyFromHeightLast,
PercentageDollarsF32, Price, Ratio64,
CentsPlus, CentsUnsignedToDollars, ComputedFromHeightCumulative, ComputedFromHeightLast,
ComputedFromHeightRatio, NegCentsUnsignedToDollars, ValueFromHeightCumulative, LazyFromHeightLast,
PercentageCentsF32, PercentageCentsSignedCentsF32, PercentageCentsSignedDollarsF32, Price, RatioCents64,
StoredF32Identity, ValueFromHeightLast,
},
prices,
@@ -28,18 +28,17 @@ pub struct RealizedBase<M: StorageMode = Rw> {
// === Realized Cap ===
pub realized_cap_cents: ComputedFromHeightLast<Cents, M>,
pub realized_cap: LazyFromHeightLast<Dollars, Cents>,
pub realized_price: Price<ComputedFromHeightLast<Dollars, M>>,
pub realized_price: Price<ComputedFromHeightLast<Cents, M>>,
pub realized_price_extra: ComputedFromHeightRatio<M>,
pub realized_cap_30d_delta: ComputedFromHeightLast<Dollars, M>,
pub realized_cap_30d_delta: ComputedFromHeightLast<CentsSigned, M>,
// === Investor Price ===
pub investor_price_cents: ComputedFromHeightLast<Cents, M>,
pub investor_price: Price<LazyFromHeightLast<Dollars, Cents>>,
pub investor_price: Price<ComputedFromHeightLast<Cents, M>>,
pub investor_price_extra: ComputedFromHeightRatio<M>,
// === Floor/Ceiling Price Bands ===
pub lower_price_band: Price<ComputedFromHeightLast<Dollars, M>>,
pub upper_price_band: Price<ComputedFromHeightLast<Dollars, M>>,
pub lower_price_band: Price<ComputedFromHeightLast<Cents, M>>,
pub upper_price_band: Price<ComputedFromHeightLast<Cents, M>>,
// === Raw values for aggregation ===
pub cap_raw: M::Stored<BytesVec<Height, CentsSats>>,
@@ -49,14 +48,14 @@ pub struct RealizedBase<M: StorageMode = Rw> {
pub mvrv: LazyFromHeightLast<StoredF32>,
// === Realized Profit/Loss ===
pub realized_profit: ComputedFromHeightCumulative<Dollars, M>,
pub realized_profit_7d_ema: ComputedFromHeightLast<Dollars, M>,
pub realized_loss: ComputedFromHeightCumulative<Dollars, M>,
pub realized_loss_7d_ema: ComputedFromHeightLast<Dollars, M>,
pub neg_realized_loss: LazyFromHeightLast<Dollars>,
pub net_realized_pnl: ComputedFromHeightCumulative<Dollars, M>,
pub net_realized_pnl_7d_ema: ComputedFromHeightLast<Dollars, M>,
pub realized_value: ComputedFromHeightLast<Dollars, M>,
pub realized_profit: ComputedFromHeightCumulative<Cents, M>,
pub realized_profit_7d_ema: ComputedFromHeightLast<Cents, M>,
pub realized_loss: ComputedFromHeightCumulative<Cents, M>,
pub realized_loss_7d_ema: ComputedFromHeightLast<Cents, M>,
pub neg_realized_loss: LazyFromHeightLast<Dollars, Cents>,
pub net_realized_pnl: ComputedFromHeightCumulative<CentsSigned, M>,
pub net_realized_pnl_7d_ema: ComputedFromHeightLast<CentsSigned, M>,
pub realized_value: ComputedFromHeightLast<Cents, M>,
// === Realized vs Realized Cap Ratios ===
pub realized_profit_rel_to_realized_cap: ComputedFromHeightLast<StoredF32, M>,
@@ -64,31 +63,31 @@ pub struct RealizedBase<M: StorageMode = Rw> {
pub net_realized_pnl_rel_to_realized_cap: ComputedFromHeightLast<StoredF32, M>,
// === Total Realized PnL ===
pub total_realized_pnl: LazyFromHeightLast<Dollars>,
pub total_realized_pnl: LazyFromHeightLast<Dollars, Cents>,
// === Value Created/Destroyed Splits (stored) ===
pub profit_value_created: ComputedFromHeightLast<Dollars, M>,
pub profit_value_destroyed: ComputedFromHeightLast<Dollars, M>,
pub loss_value_created: ComputedFromHeightLast<Dollars, M>,
pub loss_value_destroyed: ComputedFromHeightLast<Dollars, M>,
pub profit_value_created: ComputedFromHeightLast<Cents, M>,
pub profit_value_destroyed: ComputedFromHeightLast<Cents, M>,
pub loss_value_created: ComputedFromHeightLast<Cents, M>,
pub loss_value_destroyed: ComputedFromHeightLast<Cents, M>,
// === Value Created/Destroyed Totals ===
pub value_created: ComputedFromHeightLast<Dollars, M>,
pub value_destroyed: ComputedFromHeightLast<Dollars, M>,
pub value_created: ComputedFromHeightLast<Cents, M>,
pub value_destroyed: ComputedFromHeightLast<Cents, M>,
// === Capitulation/Profit Flow (lazy aliases) ===
pub capitulation_flow: LazyFromHeightLast<Dollars>,
pub profit_flow: LazyFromHeightLast<Dollars>,
pub capitulation_flow: LazyFromHeightLast<Dollars, Cents>,
pub profit_flow: LazyFromHeightLast<Dollars, Cents>,
// === Value Created/Destroyed Rolling Sums ===
pub value_created_24h: ComputedFromHeightLast<Dollars, M>,
pub value_created_7d: ComputedFromHeightLast<Dollars, M>,
pub value_created_30d: ComputedFromHeightLast<Dollars, M>,
pub value_created_1y: ComputedFromHeightLast<Dollars, M>,
pub value_destroyed_24h: ComputedFromHeightLast<Dollars, M>,
pub value_destroyed_7d: ComputedFromHeightLast<Dollars, M>,
pub value_destroyed_30d: ComputedFromHeightLast<Dollars, M>,
pub value_destroyed_1y: ComputedFromHeightLast<Dollars, M>,
pub value_created_24h: ComputedFromHeightLast<Cents, M>,
pub value_created_7d: ComputedFromHeightLast<Cents, M>,
pub value_created_30d: ComputedFromHeightLast<Cents, M>,
pub value_created_1y: ComputedFromHeightLast<Cents, M>,
pub value_destroyed_24h: ComputedFromHeightLast<Cents, M>,
pub value_destroyed_7d: ComputedFromHeightLast<Cents, M>,
pub value_destroyed_30d: ComputedFromHeightLast<Cents, M>,
pub value_destroyed_1y: ComputedFromHeightLast<Cents, M>,
// === SOPR (rolling window ratios) ===
pub sopr: LazyFromHeightLast<StoredF64>,
@@ -102,10 +101,10 @@ pub struct RealizedBase<M: StorageMode = Rw> {
pub sopr_30d_ema: LazyFromHeightLast<StoredF64>,
// === Sell Side Risk Rolling Sum Intermediates ===
pub realized_value_24h: ComputedFromHeightLast<Dollars, M>,
pub realized_value_7d: ComputedFromHeightLast<Dollars, M>,
pub realized_value_30d: ComputedFromHeightLast<Dollars, M>,
pub realized_value_1y: ComputedFromHeightLast<Dollars, M>,
pub realized_value_24h: ComputedFromHeightLast<Cents, M>,
pub realized_value_7d: ComputedFromHeightLast<Cents, M>,
pub realized_value_30d: ComputedFromHeightLast<Cents, M>,
pub realized_value_1y: ComputedFromHeightLast<Cents, M>,
// === Sell Side Risk (rolling window ratios) ===
pub sell_side_risk_ratio: LazyFromHeightLast<StoredF32>,
@@ -119,14 +118,14 @@ pub struct RealizedBase<M: StorageMode = Rw> {
pub sell_side_risk_ratio_30d_ema: LazyFromHeightLast<StoredF32>,
// === Net Realized PnL Deltas ===
pub net_realized_pnl_cumulative_30d_delta: ComputedFromHeightLast<Dollars, M>,
pub net_realized_pnl_cumulative_30d_delta: ComputedFromHeightLast<CentsSigned, M>,
pub net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap:
ComputedFromHeightLast<StoredF32, M>,
pub net_realized_pnl_cumulative_30d_delta_rel_to_market_cap:
ComputedFromHeightLast<StoredF32, M>,
// === Peak Regret ===
pub peak_regret: ComputedFromHeightCumulative<Dollars, M>,
pub peak_regret: ComputedFromHeightCumulative<Cents, M>,
pub peak_regret_rel_to_realized_cap: ComputedFromHeightLast<StoredF32, M>,
// === Sent in Profit/Loss ===
@@ -185,7 +184,7 @@ impl RealizedBase {
cfg.indexes,
)?;
let neg_realized_loss = LazyFromHeightLast::from_height_source::<Negate>(
let neg_realized_loss = LazyFromHeightLast::from_height_source::<NegCentsUnsignedToDollars>(
&cfg.name("neg_realized_loss"),
cfg.version + v1,
realized_loss.height.read_only_boxed_clone(),
@@ -220,7 +219,7 @@ impl RealizedBase {
cfg.indexes,
)?;
let total_realized_pnl = LazyFromHeightLast::from_computed::<Ident>(
let total_realized_pnl = LazyFromHeightLast::from_computed::<CentsUnsignedToDollars>(
&cfg.name("total_realized_pnl"),
cfg.version + v1,
realized_value.height.read_only_boxed_clone(),
@@ -255,19 +254,13 @@ impl RealizedBase {
cfg.indexes,
)?;
let investor_price_cents = ComputedFromHeightLast::forced_import(
let investor_price = Price::forced_import(
cfg.db,
&cfg.name("investor_price_cents"),
&cfg.name("investor_price"),
cfg.version,
cfg.indexes,
)?;
let investor_price = Price::from_computed::<CentsUnsignedToDollars>(
&cfg.name("investor_price"),
cfg.version,
&investor_price_cents,
);
let investor_price_extra = ComputedFromHeightRatio::forced_import(
cfg.db,
&cfg.name("investor_price"),
@@ -331,13 +324,13 @@ impl RealizedBase {
cfg.indexes,
)?;
let capitulation_flow = LazyFromHeightLast::from_computed::<Ident>(
let capitulation_flow = LazyFromHeightLast::from_computed::<CentsUnsignedToDollars>(
&cfg.name("capitulation_flow"),
cfg.version,
loss_value_destroyed.height.read_only_boxed_clone(),
&loss_value_destroyed,
);
let profit_flow = LazyFromHeightLast::from_computed::<Ident>(
let profit_flow = LazyFromHeightLast::from_computed::<CentsUnsignedToDollars>(
&cfg.name("profit_flow"),
cfg.version,
profit_value_destroyed.height.read_only_boxed_clone(),
@@ -460,7 +453,6 @@ impl RealizedBase {
cfg.version,
cfg.indexes,
)?,
investor_price_cents,
investor_price,
investor_price_extra,
lower_price_band,
@@ -574,7 +566,7 @@ impl RealizedBase {
.len()
.min(self.realized_profit.height.len())
.min(self.realized_loss.height.len())
.min(self.investor_price_cents.height.len())
.min(self.investor_price.cents.height.len())
.min(self.cap_raw.len())
.min(self.investor_cap_raw.len())
.min(self.profit_value_created.height.len())
@@ -593,11 +585,11 @@ impl RealizedBase {
.truncate_push(height, state.cap())?;
self.realized_profit
.height
.truncate_push(height, state.profit().to_dollars())?;
.truncate_push(height, state.profit())?;
self.realized_loss
.height
.truncate_push(height, state.loss().to_dollars())?;
self.investor_price_cents
.truncate_push(height, state.loss())?;
self.investor_price.cents
.height
.truncate_push(height, state.investor_price())?;
self.cap_raw.truncate_push(height, state.cap_raw())?;
@@ -605,19 +597,19 @@ impl RealizedBase {
.truncate_push(height, state.investor_cap_raw())?;
self.profit_value_created
.height
.truncate_push(height, state.profit_value_created().to_dollars())?;
.truncate_push(height, state.profit_value_created())?;
self.profit_value_destroyed
.height
.truncate_push(height, state.profit_value_destroyed().to_dollars())?;
.truncate_push(height, state.profit_value_destroyed())?;
self.loss_value_created
.height
.truncate_push(height, state.loss_value_created().to_dollars())?;
.truncate_push(height, state.loss_value_created())?;
self.loss_value_destroyed
.height
.truncate_push(height, state.loss_value_destroyed().to_dollars())?;
.truncate_push(height, state.loss_value_destroyed())?;
self.peak_regret
.height
.truncate_push(height, state.peak_regret().to_dollars())?;
.truncate_push(height, state.peak_regret())?;
self.sent_in_profit
.base
.sats
@@ -638,7 +630,7 @@ impl RealizedBase {
&mut self.realized_cap_cents.height as &mut dyn AnyStoredVec,
&mut self.realized_profit.height,
&mut self.realized_loss.height,
&mut self.investor_price_cents.height,
&mut self.investor_price.cents.height,
&mut self.cap_raw as &mut dyn AnyStoredVec,
&mut self.investor_cap_raw as &mut dyn AnyStoredVec,
&mut self.profit_value_created.height,
@@ -686,9 +678,9 @@ impl RealizedBase {
// Aggregate raw values for investor_price computation
let investor_price_dep_version = others
.iter()
.map(|o| o.investor_price_cents.height.version())
.map(|o| o.investor_price.cents.height.version())
.fold(vecdb::Version::ZERO, |acc, v| acc + v);
self.investor_price_cents
self.investor_price.cents
.height
.validate_computed_version_or_reset(investor_price_dep_version)?;
@@ -696,7 +688,7 @@ impl RealizedBase {
.cap_raw
.len()
.min(self.investor_cap_raw.len())
.min(self.investor_price_cents.height.len());
.min(self.investor_price.cents.height.len());
let end = others.iter().map(|o| o.cap_raw.len()).min().unwrap_or(0);
// Pre-collect all cohort data to avoid per-element BytesVec reads in nested loop
@@ -730,14 +722,14 @@ impl RealizedBase {
} else {
Cents::new((sum_investor_cap / sum_cap.inner()) as u64)
};
self.investor_price_cents
self.investor_price.cents
.height
.truncate_push(height, investor_price)?;
}
{
let _lock = exit.lock();
self.investor_price_cents.height.write()?;
self.investor_price.cents.height.write()?;
}
self.profit_value_created.height.compute_sum_of_others(
@@ -813,10 +805,13 @@ impl RealizedBase {
self.net_realized_pnl
.compute(starting_indexes.height, exit, |vec| {
vec.compute_subtract(
vec.compute_transform2(
starting_indexes.height,
&self.realized_profit.height,
&self.realized_loss.height,
|(i, profit, loss, ..)| {
(i, CentsSigned::new(profit.inner() as i64 - loss.inner() as i64))
},
exit,
)?;
Ok(())
@@ -846,47 +841,64 @@ impl RealizedBase {
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
exit: &Exit,
) -> Result<()> {
self.realized_price.usd.height.compute_divide(
self.realized_price.cents.height.compute_transform2(
starting_indexes.height,
&self.realized_cap.height,
&self.realized_cap_cents.height,
height_to_supply,
|(i, cap_cents, supply, ..)| {
let cap = cap_cents.as_u128();
let supply_sats = Sats::from(supply).as_u128();
if supply_sats == 0 {
(i, Cents::ZERO)
} else {
(i, Cents::from(cap * Sats::ONE_BTC_U128 / supply_sats))
}
},
exit,
)?;
self.realized_price_extra.compute_ratio(
starting_indexes,
&prices.price.usd.height,
&self.realized_price.usd.height,
&prices.price.cents.height,
&self.realized_price.cents.height,
exit,
)?;
self.investor_price_extra.compute_ratio(
starting_indexes,
&prices.price.usd.height,
&self.investor_price.usd.height,
&prices.price.cents.height,
&self.investor_price.cents.height,
exit,
)?;
self.lower_price_band.usd.height.compute_transform2(
self.lower_price_band.cents.height.compute_transform2(
starting_indexes.height,
&self.realized_price.usd.height,
&self.investor_price.usd.height,
&self.realized_price.cents.height,
&self.investor_price.cents.height,
|(i, rp, ip, ..)| {
let rp = f64::from(rp);
let ip = f64::from(ip);
(i, Dollars::from(rp * rp / ip))
let rp = rp.as_u128();
let ip = ip.as_u128();
if ip == 0 {
(i, Cents::ZERO)
} else {
(i, Cents::from(rp * rp / ip))
}
},
exit,
)?;
self.upper_price_band.usd.height.compute_transform2(
self.upper_price_band.cents.height.compute_transform2(
starting_indexes.height,
&self.investor_price.usd.height,
&self.realized_price.usd.height,
&self.investor_price.cents.height,
&self.realized_price.cents.height,
|(i, ip, rp, ..)| {
let ip = f64::from(ip);
let rp = f64::from(rp);
(i, Dollars::from(ip * ip / rp))
let ip = ip.as_u128();
let rp = rp.as_u128();
if rp == 0 {
(i, Cents::ZERO)
} else {
(i, Cents::from(ip * ip / rp))
}
},
exit,
)?;
@@ -894,20 +906,20 @@ impl RealizedBase {
self.realized_cap_30d_delta.height.compute_rolling_change(
starting_indexes.height,
&blocks.count.height_1m_ago,
&self.realized_cap.height,
&self.realized_cap_cents.height,
exit,
)?;
// Compute value_created/destroyed from stored components
self.value_created
.compute_binary::<Dollars, Dollars, DollarsPlus>(
.compute_binary::<Cents, Cents, CentsPlus>(
starting_indexes.height,
&self.profit_value_created.height,
&self.loss_value_created.height,
exit,
)?;
self.value_destroyed
.compute_binary::<Dollars, Dollars, DollarsPlus>(
.compute_binary::<Cents, Cents, CentsPlus>(
starting_indexes.height,
&self.profit_value_destroyed.height,
&self.loss_value_destroyed.height,
@@ -990,25 +1002,25 @@ impl RealizedBase {
);
// Compute SOPR from rolling sums
self.sopr_24h.compute_binary::<Dollars, Dollars, Ratio64>(
self.sopr_24h.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.value_created_24h.height,
&self.value_destroyed_24h.height,
exit,
)?;
self.sopr_7d.compute_binary::<Dollars, Dollars, Ratio64>(
self.sopr_7d.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.value_created_7d.height,
&self.value_destroyed_7d.height,
exit,
)?;
self.sopr_30d.compute_binary::<Dollars, Dollars, Ratio64>(
self.sopr_30d.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.value_created_30d.height,
&self.value_destroyed_30d.height,
exit,
)?;
self.sopr_1y.compute_binary::<Dollars, Dollars, Ratio64>(
self.sopr_1y.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.value_created_1y.height,
&self.value_destroyed_1y.height,
@@ -1017,31 +1029,31 @@ impl RealizedBase {
// Compute sell-side risk ratios
self.sell_side_risk_ratio_24h
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Cents, Cents, PercentageCentsF32>(
starting_indexes.height,
&self.realized_value_24h.height,
&self.realized_cap.height,
&self.realized_cap_cents.height,
exit,
)?;
self.sell_side_risk_ratio_7d
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Cents, Cents, PercentageCentsF32>(
starting_indexes.height,
&self.realized_value_7d.height,
&self.realized_cap.height,
&self.realized_cap_cents.height,
exit,
)?;
self.sell_side_risk_ratio_30d
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Cents, Cents, PercentageCentsF32>(
starting_indexes.height,
&self.realized_value_30d.height,
&self.realized_cap.height,
&self.realized_cap_cents.height,
exit,
)?;
self.sell_side_risk_ratio_1y
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Cents, Cents, PercentageCentsF32>(
starting_indexes.height,
&self.realized_value_1y.height,
&self.realized_cap.height,
&self.realized_cap_cents.height,
exit,
)?;
@@ -1072,14 +1084,14 @@ impl RealizedBase {
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent_in_profit.base.sats.height,
&self.sent_in_profit.base.usd.height,
&self.sent_in_profit.base.cents.height,
exit,
)?;
self.sent_in_loss_14d_ema.compute_ema(
starting_indexes.height,
&blocks.count.height_2w_ago,
&self.sent_in_loss.base.sats.height,
&self.sent_in_loss.base.usd.height,
&self.sent_in_loss.base.cents.height,
exit,
)?;
@@ -1117,31 +1129,31 @@ impl RealizedBase {
// Realized profit/loss/net relative to realized cap
self.realized_profit_rel_to_realized_cap
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Cents, Cents, PercentageCentsF32>(
starting_indexes.height,
&self.realized_profit.height,
&self.realized_cap.height,
&self.realized_cap_cents.height,
exit,
)?;
self.realized_loss_rel_to_realized_cap
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Cents, Cents, PercentageCentsF32>(
starting_indexes.height,
&self.realized_loss.height,
&self.realized_cap.height,
&self.realized_cap_cents.height,
exit,
)?;
self.net_realized_pnl_rel_to_realized_cap
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<CentsSigned, Cents, PercentageCentsSignedCentsF32>(
starting_indexes.height,
&self.net_realized_pnl.height,
&self.realized_cap.height,
&self.realized_cap_cents.height,
exit,
)?;
self.peak_regret_rel_to_realized_cap
.compute_binary::<Dollars, Dollars, PercentageDollarsF32>(
.compute_binary::<Cents, Cents, PercentageCentsF32>(
starting_indexes.height,
&self.peak_regret.height,
&self.realized_cap.height,
&self.realized_cap_cents.height,
exit,
)?;
@@ -1156,17 +1168,15 @@ impl RealizedBase {
)?;
self.net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap
.height
.compute_percentage(
.compute_binary::<CentsSigned, Cents, PercentageCentsSignedCentsF32>(
starting_indexes.height,
&self.net_realized_pnl_cumulative_30d_delta.height,
&self.realized_cap.height,
&self.realized_cap_cents.height,
exit,
)?;
self.net_realized_pnl_cumulative_30d_delta_rel_to_market_cap
.height
.compute_percentage(
.compute_binary::<CentsSigned, Dollars, PercentageCentsSignedDollarsF32>(
starting_indexes.height,
&self.net_realized_pnl_cumulative_30d_delta.height,
height_to_market_cap,

View File

@@ -1,11 +1,11 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, StoredF32, StoredF64, Version};
use brk_types::{Cents, Dollars, Height, StoredF32, StoredF64, Version};
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
use crate::{
ComputeIndexes, blocks,
internal::{ComputedFromHeightLast, ComputedFromHeightRatioExtension, Ratio64},
internal::{ComputedFromHeightLast, ComputedFromHeightRatioExtension, RatioCents64},
};
use crate::distribution::metrics::ImportConfig;
@@ -18,14 +18,14 @@ pub struct RealizedExtended<M: StorageMode = Rw> {
pub realized_cap_rel_to_own_market_cap: ComputedFromHeightLast<StoredF32, M>,
// === Realized Profit/Loss Rolling Sums ===
pub realized_profit_24h: ComputedFromHeightLast<Dollars, M>,
pub realized_profit_7d: ComputedFromHeightLast<Dollars, M>,
pub realized_profit_30d: ComputedFromHeightLast<Dollars, M>,
pub realized_profit_1y: ComputedFromHeightLast<Dollars, M>,
pub realized_loss_24h: ComputedFromHeightLast<Dollars, M>,
pub realized_loss_7d: ComputedFromHeightLast<Dollars, M>,
pub realized_loss_30d: ComputedFromHeightLast<Dollars, M>,
pub realized_loss_1y: ComputedFromHeightLast<Dollars, M>,
pub realized_profit_24h: ComputedFromHeightLast<Cents, M>,
pub realized_profit_7d: ComputedFromHeightLast<Cents, M>,
pub realized_profit_30d: ComputedFromHeightLast<Cents, M>,
pub realized_profit_1y: ComputedFromHeightLast<Cents, M>,
pub realized_loss_24h: ComputedFromHeightLast<Cents, M>,
pub realized_loss_7d: ComputedFromHeightLast<Cents, M>,
pub realized_loss_30d: ComputedFromHeightLast<Cents, M>,
pub realized_loss_1y: ComputedFromHeightLast<Cents, M>,
// === Realized Profit to Loss Ratio (from rolling sums) ===
pub realized_profit_to_loss_ratio_24h: ComputedFromHeightLast<StoredF64, M>,
@@ -158,28 +158,28 @@ impl RealizedExtended {
// Realized profit to loss ratios
self.realized_profit_to_loss_ratio_24h
.compute_binary::<Dollars, Dollars, Ratio64>(
.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.realized_profit_24h.height,
&self.realized_loss_24h.height,
exit,
)?;
self.realized_profit_to_loss_ratio_7d
.compute_binary::<Dollars, Dollars, Ratio64>(
.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.realized_profit_7d.height,
&self.realized_loss_7d.height,
exit,
)?;
self.realized_profit_to_loss_ratio_30d
.compute_binary::<Dollars, Dollars, Ratio64>(
.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.realized_profit_30d.height,
&self.realized_loss_30d.height,
exit,
)?;
self.realized_profit_to_loss_ratio_1y
.compute_binary::<Dollars, Dollars, Ratio64>(
.compute_binary::<Cents, Cents, RatioCents64>(
starting_indexes.height,
&self.realized_profit_1y.height,
&self.realized_loss_1y.height,
@@ -193,9 +193,9 @@ impl RealizedExtended {
exit,
&base.realized_price_extra.ratio.height,
)?;
self.realized_price_ratio_ext.compute_usd_bands(
self.realized_price_ratio_ext.compute_cents_bands(
starting_indexes,
&base.realized_price.usd.height,
&base.realized_price.cents.height,
exit,
)?;
@@ -205,9 +205,9 @@ impl RealizedExtended {
exit,
&base.investor_price_extra.ratio.height,
)?;
self.investor_price_ratio_ext.compute_usd_bands(
self.investor_price_ratio_ext.compute_cents_bands(
starting_indexes,
&base.investor_price.usd.height,
&base.investor_price.cents.height,
exit,
)?;

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Height};
use brk_types::{Bitcoin, Cents, Dollars, Height};
use derive_more::{Deref, DerefMut};
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
@@ -36,8 +36,8 @@ impl RealizedWithAdjusted {
starting_indexes: &ComputeIndexes,
height_to_supply: &impl ReadableVec<Height, Bitcoin>,
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_created: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_created: &impl ReadableVec<Height, Cents>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
self.base.compute_rest_part2_base(

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Height};
use brk_types::{Bitcoin, Cents, Dollars, Height};
use derive_more::{Deref, DerefMut};
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
@@ -43,8 +43,8 @@ impl RealizedWithExtendedAdjusted {
starting_indexes: &ComputeIndexes,
height_to_supply: &impl ReadableVec<Height, Bitcoin>,
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_created: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Dollars>,
up_to_1h_value_created: &impl ReadableVec<Height, Cents>,
up_to_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
self.base.compute_rest_part2_base(

View File

@@ -67,7 +67,7 @@ impl SupplyMetrics {
pub(crate) fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
vec![
&mut self.total.base.sats.height as &mut dyn AnyStoredVec,
&mut self.total.base.usd.height as &mut dyn AnyStoredVec,
&mut self.total.base.cents.height as &mut dyn AnyStoredVec,
]
.into_par_iter()
}
@@ -117,7 +117,7 @@ impl SupplyMetrics {
starting_indexes.height,
&blocks.count.height_1m_ago,
&self.total.sats.height,
&self.total.usd.height,
&self.total.cents.height,
exit,
)
}

View File

@@ -237,9 +237,9 @@ impl UnrealizedBase {
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
vec![
&mut self.supply_in_profit.base.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_profit.base.usd.height as &mut dyn AnyStoredVec,
&mut self.supply_in_profit.base.cents.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.base.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.base.usd.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.base.cents.height as &mut dyn AnyStoredVec,
&mut self.unrealized_profit.height,
&mut self.unrealized_loss.height,
&mut self.invested_capital_in_profit.height,

View File

@@ -4,12 +4,14 @@ mod windows;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Sats, Version};
use brk_types::{Bitcoin, Cents, Dollars, Sats, Version};
use vecdb::{Database, ReadableCloneableVec, Rw, StorageMode};
use crate::{
indexes,
internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin},
internal::{
CentsUnsignedToDollars, ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin,
},
};
pub use rolling_full::*;
@@ -19,7 +21,8 @@ pub use rolling_sum::*;
pub struct ByUnit<M: StorageMode = Rw> {
pub sats: ComputedFromHeightLast<Sats, M>,
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
pub usd: ComputedFromHeightLast<Dollars, M>,
pub cents: ComputedFromHeightLast<Cents, M>,
pub usd: LazyFromHeightLast<Dollars, Cents>,
}
impl ByUnit {
@@ -38,9 +41,25 @@ impl ByUnit {
&sats,
);
let usd =
ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), version, indexes)?;
let cents = ComputedFromHeightLast::forced_import(
db,
&format!("{name}_cents"),
version,
indexes,
)?;
Ok(Self { sats, btc, usd })
let usd = LazyFromHeightLast::from_computed::<CentsUnsignedToDollars>(
&format!("{name}_usd"),
version,
cents.height.read_only_boxed_clone(),
&cents,
);
Ok(Self {
sats,
btc,
cents,
usd,
})
}
}

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Sats, Version};
use brk_types::{Cents, Height, Sats, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
@@ -47,11 +47,11 @@ impl RollingFullSlot {
max_from: Height,
starts: &impl ReadableVec<Height, Height>,
sats_source: &impl ReadableVec<Height, Sats>,
usd_source: &impl ReadableVec<Height, Dollars>,
cents_source: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
self.sum.sats.height.compute_rolling_sum(max_from, starts, sats_source, exit)?;
self.sum.usd.height.compute_rolling_sum(max_from, starts, usd_source, exit)?;
self.sum.cents.height.compute_rolling_sum(max_from, starts, cents_source, exit)?;
let d = &mut self.distribution;
@@ -64,11 +64,11 @@ impl RollingFullSlot {
)?;
compute_rolling_distribution_from_starts(
max_from, starts, usd_source,
&mut d.average.usd.height, &mut d.min.usd.height,
&mut d.max.usd.height, &mut d.pct10.usd.height,
&mut d.pct25.usd.height, &mut d.median.usd.height,
&mut d.pct75.usd.height, &mut d.pct90.usd.height, exit,
max_from, starts, cents_source,
&mut d.average.cents.height, &mut d.min.cents.height,
&mut d.max.cents.height, &mut d.pct10.cents.height,
&mut d.pct25.cents.height, &mut d.median.cents.height,
&mut d.pct75.cents.height, &mut d.pct90.cents.height, exit,
)?;
Ok(())
@@ -105,11 +105,11 @@ impl RollingFullByUnit {
max_from: Height,
windows: &WindowStarts<'_>,
sats_source: &impl ReadableVec<Height, Sats>,
usd_source: &impl ReadableVec<Height, Dollars>,
cents_source: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
for (slot, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) {
slot.compute(max_from, starts, sats_source, usd_source, exit)?;
slot.compute(max_from, starts, sats_source, cents_source, exit)?;
}
Ok(())
}

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Sats, Version};
use brk_types::{Cents, Height, Sats, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
@@ -34,12 +34,12 @@ impl RollingSumByUnit {
max_from: Height,
windows: &WindowStarts<'_>,
sats_source: &impl ReadableVec<Height, Sats>,
usd_source: &impl ReadableVec<Height, Dollars>,
cents_source: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
for (w, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) {
w.sats.height.compute_rolling_sum(max_from, starts, sats_source, exit)?;
w.usd.height.compute_rolling_sum(max_from, starts, usd_source, exit)?;
w.cents.height.compute_rolling_sum(max_from, starts, cents_source, exit)?;
}
Ok(())
}

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::{Traversable, TreeNode};
use brk_types::{Dollars, Height, StoredF32, Version};
use brk_types::{Cents, Height, StoredF32, Version};
use vecdb::{AnyExportableVec, Database, ReadOnlyClone, Ro, Rw, StorageMode, WritableVec};
use crate::indexes;
@@ -14,10 +14,10 @@ pub const PERCENTILES_LEN: usize = PERCENTILES.len();
/// Compute spot percentile rank by interpolating within percentile bands.
/// Returns a value between 0 and 100 indicating where spot sits in the distribution.
pub(crate) fn compute_spot_percentile_rank(
percentile_prices: &[Dollars; PERCENTILES_LEN],
spot: Dollars,
percentile_prices: &[Cents; PERCENTILES_LEN],
spot: Cents,
) -> StoredF32 {
if spot.is_nan() || percentile_prices[0].is_nan() {
if spot == Cents::ZERO && percentile_prices[0] == Cents::ZERO {
return StoredF32::NAN;
}
@@ -68,7 +68,7 @@ pub(crate) fn compute_spot_percentile_rank(
}
pub struct PercentilesVecs<M: StorageMode = Rw> {
pub vecs: [Price<ComputedFromHeightLast<Dollars, M>>; PERCENTILES_LEN],
pub vecs: [Price<ComputedFromHeightLast<Cents, M>>; PERCENTILES_LEN],
}
const VERSION: Version = Version::ONE;
@@ -94,14 +94,14 @@ impl PercentilesVecs {
Ok(Self { vecs })
}
/// Push percentile prices at this height.
/// Push percentile prices at this height (in cents).
pub(crate) fn truncate_push(
&mut self,
height: Height,
percentile_prices: &[Dollars; PERCENTILES_LEN],
percentile_prices: &[Cents; PERCENTILES_LEN],
) -> Result<()> {
for (i, v) in self.vecs.iter_mut().enumerate() {
v.usd.height.truncate_push(height, percentile_prices[i])?;
v.cents.height.truncate_push(height, percentile_prices[i])?;
}
Ok(())
}
@@ -109,7 +109,7 @@ impl PercentilesVecs {
/// Validate computed versions or reset if mismatched.
pub(crate) fn validate_computed_version_or_reset(&mut self, version: Version) -> Result<()> {
for vec in self.vecs.iter_mut() {
vec.usd.height.validate_computed_version_or_reset(version)?;
vec.cents.height.validate_computed_version_or_reset(version)?;
}
Ok(())
}
@@ -130,7 +130,7 @@ impl ReadOnlyClone for PercentilesVecs {
impl<M: StorageMode> Traversable for PercentilesVecs<M>
where
Price<ComputedFromHeightLast<Dollars, M>>: Traversable,
Price<ComputedFromHeightLast<Cents, M>>: Traversable,
{
fn to_tree_node(&self) -> TreeNode {
TreeNode::Branch(

View File

@@ -1,65 +1,104 @@
//! Generic price wrapper with both USD and sats representations.
//! Generic price wrapper with cents, USD, and sats representations.
//!
//! All prices use this single struct with different USD types.
//! All prices use this single struct with different cents types.
//! USD is always lazily derived from cents via CentsUnsignedToDollars.
//! Sats is always lazily derived from USD via DollarsToSatsFract.
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, SatsFract, Version};
use brk_types::{Cents, Dollars, SatsFract, Version};
use schemars::JsonSchema;
use vecdb::{Database, ReadableCloneableVec, UnaryTransform};
use super::{ComputedFromHeightLast, LazyFromHeightLast};
use crate::{
indexes,
internal::{ComputedVecValue, DollarsToSatsFract, NumericValue},
internal::{CentsUnsignedToDollars, ComputedVecValue, DollarsToSatsFract, NumericValue},
};
/// Generic price metric with both USD and sats representations.
/// Generic price metric with cents, USD, and sats representations.
#[derive(Clone, Traversable)]
pub struct Price<U> {
pub usd: U,
pub struct Price<C> {
pub cents: C,
pub usd: LazyFromHeightLast<Dollars, Cents>,
pub sats: LazyFromHeightLast<SatsFract, Dollars>,
}
impl Price<ComputedFromHeightLast<Dollars>> {
impl Price<ComputedFromHeightLast<Cents>> {
/// Import from database: stored cents, lazy USD + sats.
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let usd = ComputedFromHeightLast::forced_import(db, name, version, indexes)?;
let sats = LazyFromHeightLast::from_computed::<DollarsToSatsFract>(
let cents = ComputedFromHeightLast::forced_import(
db,
&format!("{name}_cents"),
version,
indexes,
)?;
let usd = LazyFromHeightLast::from_computed::<CentsUnsignedToDollars>(
&format!("{name}_usd"),
version,
cents.height.read_only_boxed_clone(),
&cents,
);
let sats = LazyFromHeightLast::from_lazy::<DollarsToSatsFract, Cents>(
&format!("{name}_sats"),
version,
usd.height.read_only_boxed_clone(),
&usd,
);
Ok(Self { usd, sats })
Ok(Self { cents, usd, sats })
}
/// Wrap an already-imported ComputedFromHeightLast<Cents> with lazy USD + sats.
pub(crate) fn from_cents(
name: &str,
version: Version,
cents: ComputedFromHeightLast<Cents>,
) -> Self {
let usd = LazyFromHeightLast::from_computed::<CentsUnsignedToDollars>(
&format!("{name}_usd"),
version,
cents.height.read_only_boxed_clone(),
&cents,
);
let sats = LazyFromHeightLast::from_lazy::<DollarsToSatsFract, Cents>(
&format!("{name}_sats"),
version,
&usd,
);
Self { cents, usd, sats }
}
}
impl<ST> Price<LazyFromHeightLast<Dollars, ST>>
impl<ST> Price<LazyFromHeightLast<Cents, ST>>
where
ST: ComputedVecValue + NumericValue + JsonSchema + 'static,
{
pub(crate) fn from_computed<F: UnaryTransform<ST, Dollars>>(
/// Create from a computed source, applying a transform to produce Cents.
pub(crate) fn from_cents_source<F: UnaryTransform<ST, Cents>>(
name: &str,
version: Version,
source: &ComputedFromHeightLast<ST>,
) -> Self {
let usd = LazyFromHeightLast::from_computed::<F>(
name,
let cents = LazyFromHeightLast::from_computed::<F>(
&format!("{name}_cents"),
version,
source.height.read_only_boxed_clone(),
source,
);
let sats = LazyFromHeightLast::from_lazy::<DollarsToSatsFract, ST>(
let usd = LazyFromHeightLast::from_lazy::<CentsUnsignedToDollars, ST>(
&format!("{name}_usd"),
version,
&cents,
);
let sats = LazyFromHeightLast::from_lazy::<DollarsToSatsFract, Cents>(
&format!("{name}_sats"),
version,
&usd,
);
Self { usd, sats }
Self { cents, usd, sats }
}
}

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Version};
use brk_types::{Cents, Height, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
@@ -31,22 +31,22 @@ impl ComputedFromHeightRatioExtended {
})
}
/// Compute ratio and all extended metrics from an externally-provided metric price.
/// Compute ratio and all extended metrics from an externally-provided metric price (in cents).
pub(crate) fn compute_rest(
&mut self,
blocks: &blocks::Vecs,
prices: &prices::Vecs,
starting_indexes: &ComputeIndexes,
exit: &Exit,
metric_price: &impl ReadableVec<Height, Dollars>,
metric_price: &impl ReadableVec<Height, Cents>,
) -> Result<()> {
let close_price = &prices.price.usd.height;
let close_price = &prices.price.cents.height;
self.base
.compute_ratio(starting_indexes, close_price, metric_price, exit)?;
self.extended
.compute_rest(blocks, starting_indexes, exit, &self.base.ratio.height)?;
self.extended
.compute_usd_bands(starting_indexes, metric_price, exit)?;
.compute_cents_bands(starting_indexes, metric_price, exit)?;
Ok(())
}
}

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, StoredF32, Version};
use brk_types::{Cents, Height, StoredF32, Version};
use vecdb::{AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex, WritableVec};
use crate::{
@@ -21,12 +21,12 @@ pub struct ComputedFromHeightRatioExtension<M: StorageMode = Rw> {
pub ratio_pct5: ComputedFromHeightLast<StoredF32, M>,
pub ratio_pct2: ComputedFromHeightLast<StoredF32, M>,
pub ratio_pct1: ComputedFromHeightLast<StoredF32, M>,
pub ratio_pct99_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub ratio_pct98_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub ratio_pct95_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub ratio_pct5_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub ratio_pct2_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub ratio_pct1_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub ratio_pct99_price: Price<ComputedFromHeightLast<Cents, M>>,
pub ratio_pct98_price: Price<ComputedFromHeightLast<Cents, M>>,
pub ratio_pct95_price: Price<ComputedFromHeightLast<Cents, M>>,
pub ratio_pct5_price: Price<ComputedFromHeightLast<Cents, M>>,
pub ratio_pct2_price: Price<ComputedFromHeightLast<Cents, M>>,
pub ratio_pct1_price: Price<ComputedFromHeightLast<Cents, M>>,
pub ratio_sd: ComputedFromHeightStdDevExtended<M>,
pub ratio_4y_sd: ComputedFromHeightStdDevExtended<M>,
@@ -68,7 +68,7 @@ impl ComputedFromHeightRatioExtension {
};
}
macro_rules! import_usd {
macro_rules! import_price {
($suffix:expr) => {
Price::forced_import(db, &format!("{name}_{}", $suffix), v, indexes)?
};
@@ -87,12 +87,12 @@ impl ComputedFromHeightRatioExtension {
ratio_pct5: import!("ratio_pct5"),
ratio_pct2: import!("ratio_pct2"),
ratio_pct1: import!("ratio_pct1"),
ratio_pct99_usd: import_usd!("ratio_pct99_usd"),
ratio_pct98_usd: import_usd!("ratio_pct98_usd"),
ratio_pct95_usd: import_usd!("ratio_pct95_usd"),
ratio_pct5_usd: import_usd!("ratio_pct5_usd"),
ratio_pct2_usd: import_usd!("ratio_pct2_usd"),
ratio_pct1_usd: import_usd!("ratio_pct1_usd"),
ratio_pct99_price: import_price!("ratio_pct99"),
ratio_pct98_price: import_price!("ratio_pct98"),
ratio_pct95_price: import_price!("ratio_pct95"),
ratio_pct5_price: import_price!("ratio_pct5"),
ratio_pct2_price: import_price!("ratio_pct2"),
ratio_pct1_price: import_price!("ratio_pct1"),
})
}
@@ -219,20 +219,20 @@ impl ComputedFromHeightRatioExtension {
Ok(())
}
/// Compute USD ratio bands: usd_band = metric_price * ratio_percentile
pub(crate) fn compute_usd_bands(
/// Compute cents ratio bands: cents_band = metric_price_cents * ratio_percentile
pub(crate) fn compute_cents_bands(
&mut self,
starting_indexes: &ComputeIndexes,
metric_price: &impl ReadableVec<Height, Dollars>,
metric_price: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
use crate::internal::PriceTimesRatio;
use crate::internal::PriceTimesRatioCents;
macro_rules! compute_band {
($usd_field:ident, $band_source:expr) => {
self.$usd_field
.usd
.compute_binary::<Dollars, StoredF32, PriceTimesRatio>(
.cents
.compute_binary::<Cents, StoredF32, PriceTimesRatioCents>(
starting_indexes.height,
metric_price,
$band_source,
@@ -241,22 +241,22 @@ impl ComputedFromHeightRatioExtension {
};
}
compute_band!(ratio_pct99_usd, &self.ratio_pct99.height);
compute_band!(ratio_pct98_usd, &self.ratio_pct98.height);
compute_band!(ratio_pct95_usd, &self.ratio_pct95.height);
compute_band!(ratio_pct5_usd, &self.ratio_pct5.height);
compute_band!(ratio_pct2_usd, &self.ratio_pct2.height);
compute_band!(ratio_pct1_usd, &self.ratio_pct1.height);
compute_band!(ratio_pct99_price, &self.ratio_pct99.height);
compute_band!(ratio_pct98_price, &self.ratio_pct98.height);
compute_band!(ratio_pct95_price, &self.ratio_pct95.height);
compute_band!(ratio_pct5_price, &self.ratio_pct5.height);
compute_band!(ratio_pct2_price, &self.ratio_pct2.height);
compute_band!(ratio_pct1_price, &self.ratio_pct1.height);
// Stddev USD bands
// Stddev cents bands
self.ratio_sd
.compute_usd_bands(starting_indexes, metric_price, exit)?;
.compute_cents_bands(starting_indexes, metric_price, exit)?;
self.ratio_4y_sd
.compute_usd_bands(starting_indexes, metric_price, exit)?;
.compute_cents_bands(starting_indexes, metric_price, exit)?;
self.ratio_2y_sd
.compute_usd_bands(starting_indexes, metric_price, exit)?;
.compute_cents_bands(starting_indexes, metric_price, exit)?;
self.ratio_1y_sd
.compute_usd_bands(starting_indexes, metric_price, exit)?;
.compute_cents_bands(starting_indexes, metric_price, exit)?;
Ok(())
}

View File

@@ -8,7 +8,7 @@ pub use price_extended::*;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, StoredF32, Version};
use brk_types::{Cents, Height, StoredF32, Version};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
use crate::{ComputeIndexes, indexes};
@@ -36,12 +36,12 @@ impl ComputedFromHeightRatio {
})
}
/// Compute ratio = close_price / metric_price at height level
/// Compute ratio = close_price / metric_price at height level (both in cents)
pub(crate) fn compute_ratio(
&mut self,
starting_indexes: &ComputeIndexes,
close_price: &impl ReadableVec<Height, Dollars>,
metric_price: &impl ReadableVec<Height, Dollars>,
close_price: &impl ReadableVec<Height, Cents>,
metric_price: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
self.ratio.height.compute_transform2(
@@ -49,10 +49,10 @@ impl ComputedFromHeightRatio {
close_price,
metric_price,
|(i, close, price, ..)| {
if price == Dollars::ZERO {
if price == Cents::ZERO {
(i, StoredF32::from(1.0))
} else {
(i, StoredF32::from(close / price))
(i, StoredF32::from(f64::from(close) / f64::from(price)))
}
},
exit,

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Version};
use brk_types::{Cents, Height, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
@@ -15,7 +15,7 @@ pub struct ComputedFromHeightPriceWithRatioExtended<M: StorageMode = Rw> {
#[deref_mut]
#[traversable(flatten)]
pub inner: ComputedFromHeightRatioExtended<M>,
pub price: Price<ComputedFromHeightLast<Dollars, M>>,
pub price: Price<ComputedFromHeightLast<Cents, M>>,
}
impl ComputedFromHeightPriceWithRatioExtended {
@@ -32,7 +32,7 @@ impl ComputedFromHeightPriceWithRatioExtended {
})
}
/// Compute price via closure, then compute ratio + extended metrics.
/// Compute price via closure (in cents), then compute ratio + extended metrics.
pub(crate) fn compute_all<F>(
&mut self,
blocks: &blocks::Vecs,
@@ -42,15 +42,15 @@ impl ComputedFromHeightPriceWithRatioExtended {
mut compute_price: F,
) -> Result<()>
where
F: FnMut(&mut EagerVec<PcoVec<Height, Dollars>>) -> Result<()>,
F: FnMut(&mut EagerVec<PcoVec<Height, Cents>>) -> Result<()>,
{
compute_price(&mut self.price.usd.height)?;
compute_price(&mut self.price.cents.height)?;
self.inner.compute_rest(
blocks,
prices,
starting_indexes,
exit,
&self.price.usd.height,
&self.price.cents.height,
)?;
Ok(())
}

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, StoredF32, Version};
use brk_types::{Cents, Height, StoredF32, Version};
use vecdb::{
AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex,
WritableVec,
@@ -32,19 +32,19 @@ pub struct ComputedFromHeightStdDevExtended<M: StorageMode = Rw> {
pub m2_5sd: ComputedFromHeightLast<StoredF32, M>,
pub m3sd: ComputedFromHeightLast<StoredF32, M>,
pub _0sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub p0_5sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub p1sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub p1_5sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub p2sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub p2_5sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub p3sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub m0_5sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub m1sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub m1_5sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub m2sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub m2_5sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub m3sd_usd: Price<ComputedFromHeightLast<Dollars, M>>,
pub _0sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub p0_5sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub p1sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub p1_5sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub p2sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub p2_5sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub p3sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub m0_5sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub m1sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub m1_5sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub m2sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub m2_5sd_price: Price<ComputedFromHeightLast<Cents, M>>,
pub m3sd_price: Price<ComputedFromHeightLast<Cents, M>>,
}
impl ComputedFromHeightStdDevExtended {
@@ -68,7 +68,7 @@ impl ComputedFromHeightStdDevExtended {
};
}
macro_rules! import_usd {
macro_rules! import_price {
($suffix:expr) => {
Price::forced_import(db, &format!("{name}_{}", $suffix), version, indexes)?
};
@@ -89,19 +89,19 @@ impl ComputedFromHeightStdDevExtended {
m2sd: import!("m2sd"),
m2_5sd: import!("m2_5sd"),
m3sd: import!("m3sd"),
_0sd_usd: import_usd!("0sd_usd"),
p0_5sd_usd: import_usd!("p0_5sd_usd"),
p1sd_usd: import_usd!("p1sd_usd"),
p1_5sd_usd: import_usd!("p1_5sd_usd"),
p2sd_usd: import_usd!("p2sd_usd"),
p2_5sd_usd: import_usd!("p2_5sd_usd"),
p3sd_usd: import_usd!("p3sd_usd"),
m0_5sd_usd: import_usd!("m0_5sd_usd"),
m1sd_usd: import_usd!("m1sd_usd"),
m1_5sd_usd: import_usd!("m1_5sd_usd"),
m2sd_usd: import_usd!("m2sd_usd"),
m2_5sd_usd: import_usd!("m2_5sd_usd"),
m3sd_usd: import_usd!("m3sd_usd"),
_0sd_price: import_price!("0sd"),
p0_5sd_price: import_price!("p0_5sd"),
p1sd_price: import_price!("p1sd"),
p1_5sd_price: import_price!("p1_5sd"),
p2sd_price: import_price!("p2sd"),
p2_5sd_price: import_price!("p2_5sd"),
p3sd_price: import_price!("p3sd"),
m0_5sd_price: import_price!("m0_5sd"),
m1sd_price: import_price!("m1sd"),
m1_5sd_price: import_price!("m1_5sd"),
m2sd_price: import_price!("m2sd"),
m2_5sd_price: import_price!("m2_5sd"),
m3sd_price: import_price!("m3sd"),
})
}
@@ -217,20 +217,20 @@ impl ComputedFromHeightStdDevExtended {
Ok(())
}
/// Compute USD price bands: usd_band = metric_price * band_ratio
pub(crate) fn compute_usd_bands(
/// Compute cents price bands: cents_band = metric_price_cents * band_ratio
pub(crate) fn compute_cents_bands(
&mut self,
starting_indexes: &ComputeIndexes,
metric_price: &impl ReadableVec<Height, Dollars>,
metric_price: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
use crate::internal::PriceTimesRatio;
use crate::internal::PriceTimesRatioCents;
macro_rules! compute_band {
($usd_field:ident, $band_source:expr) => {
self.$usd_field
.usd
.compute_binary::<Dollars, StoredF32, PriceTimesRatio>(
.cents
.compute_binary::<Cents, StoredF32, PriceTimesRatioCents>(
starting_indexes.height,
metric_price,
$band_source,
@@ -239,19 +239,19 @@ impl ComputedFromHeightStdDevExtended {
};
}
compute_band!(_0sd_usd, &self.base.sma.height);
compute_band!(p0_5sd_usd, &self.p0_5sd.height);
compute_band!(p1sd_usd, &self.p1sd.height);
compute_band!(p1_5sd_usd, &self.p1_5sd.height);
compute_band!(p2sd_usd, &self.p2sd.height);
compute_band!(p2_5sd_usd, &self.p2_5sd.height);
compute_band!(p3sd_usd, &self.p3sd.height);
compute_band!(m0_5sd_usd, &self.m0_5sd.height);
compute_band!(m1sd_usd, &self.m1sd.height);
compute_band!(m1_5sd_usd, &self.m1_5sd.height);
compute_band!(m2sd_usd, &self.m2sd.height);
compute_band!(m2_5sd_usd, &self.m2_5sd.height);
compute_band!(m3sd_usd, &self.m3sd.height);
compute_band!(_0sd_price, &self.base.sma.height);
compute_band!(p0_5sd_price, &self.p0_5sd.height);
compute_band!(p1sd_price, &self.p1sd.height);
compute_band!(p1_5sd_price, &self.p1_5sd.height);
compute_band!(p2sd_price, &self.p2sd.height);
compute_band!(p2_5sd_price, &self.p2_5sd.height);
compute_band!(p3sd_price, &self.p3sd.height);
compute_band!(m0_5sd_price, &self.m0_5sd.height);
compute_band!(m1sd_price, &self.m1sd.height);
compute_band!(m1_5sd_price, &self.m1_5sd.height);
compute_band!(m2sd_price, &self.m2sd.height);
compute_band!(m2_5sd_price, &self.m2_5sd.height);
compute_band!(m3sd_price, &self.m3sd.height);
Ok(())
}

View File

@@ -2,22 +2,25 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Height, Sats, SatsSigned, Version};
use brk_types::{Bitcoin, Cents, CentsSigned, Dollars, Height, Sats, SatsSigned, Version};
use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
use crate::{
indexes,
internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsSignedToBitcoin},
internal::{
CentsSignedToDollars, ComputedFromHeightLast, LazyFromHeightLast, SatsSignedToBitcoin,
},
};
const VERSION: Version = Version::ZERO;
/// Change values indexed by height - sats (stored), btc (lazy), usd (stored).
/// Change values indexed by height - sats (stored), btc (lazy), cents (stored), usd (lazy).
#[derive(Traversable)]
pub struct ValueFromHeightChange<M: StorageMode = Rw> {
pub sats: ComputedFromHeightLast<SatsSigned, M>,
pub btc: LazyFromHeightLast<Bitcoin, SatsSigned>,
pub usd: ComputedFromHeightLast<Dollars, M>,
pub cents: ComputedFromHeightLast<CentsSigned, M>,
pub usd: LazyFromHeightLast<Dollars, CentsSigned>,
}
impl ValueFromHeightChange {
@@ -38,31 +41,38 @@ impl ValueFromHeightChange {
&sats,
);
let usd = ComputedFromHeightLast::forced_import(
let cents = ComputedFromHeightLast::forced_import(
db,
&format!("{name}_usd"),
&format!("{name}_cents"),
v,
indexes,
)?;
Ok(Self { sats, btc, usd })
let usd = LazyFromHeightLast::from_computed::<CentsSignedToDollars>(
&format!("{name}_usd"),
v,
cents.height.read_only_boxed_clone(),
&cents,
);
Ok(Self { sats, btc, cents, usd })
}
/// Compute rolling change for both sats and dollars in one call.
/// Compute rolling change for both sats and cents in one call.
pub(crate) fn compute_rolling(
&mut self,
starting_height: Height,
window_starts: &impl ReadableVec<Height, Height>,
sats_source: &impl ReadableVec<Height, Sats>,
dollars_source: &(impl ReadableVec<Height, Dollars> + Sync),
cents_source: &(impl ReadableVec<Height, Cents> + Sync),
exit: &Exit,
) -> Result<()> {
self.sats
.height
.compute_rolling_change(starting_height, window_starts, sats_source, exit)?;
self.usd
self.cents
.height
.compute_rolling_change(starting_height, window_starts, dollars_source, exit)?;
.compute_rolling_change(starting_height, window_starts, cents_source, exit)?;
Ok(())
}
}

View File

@@ -1,11 +1,11 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Sats, Version};
use brk_types::{Cents, Height, Sats, Version};
use vecdb::{Database, Exit, Rw, StorageMode};
use crate::{
indexes,
internal::{ByUnit, SatsToDollars},
internal::{ByUnit, SatsToCents},
prices,
};
@@ -44,18 +44,18 @@ impl ValueFromHeightCumulative {
.compute_cumulative(max_from, &self.base.sats.height, exit)?;
self.base
.usd
.compute_binary::<Sats, Dollars, SatsToDollars>(
.cents
.compute_binary::<Sats, Cents, SatsToCents>(
max_from,
&self.base.sats.height,
&prices.price.usd.height,
&prices.price.cents.height,
exit,
)?;
self.cumulative
.usd
.cents
.height
.compute_cumulative(max_from, &self.base.usd.height, exit)?;
.compute_cumulative(max_from, &self.base.cents.height, exit)?;
Ok(())
}

View File

@@ -1,11 +1,11 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Sats, Version};
use brk_types::{Cents, Height, Sats, Version};
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
use crate::{
indexes,
internal::{ByUnit, RollingFullByUnit, SatsToDollars, WindowStarts},
internal::{ByUnit, RollingFullByUnit, SatsToCents, WindowStarts},
prices,
};
@@ -51,25 +51,25 @@ impl ValueFromHeightFull {
.compute_cumulative(max_from, &self.base.sats.height, exit)?;
self.base
.usd
.cents
.height
.compute_binary::<Sats, Dollars, SatsToDollars>(
.compute_binary::<Sats, Cents, SatsToCents>(
max_from,
&self.base.sats.height,
&prices.price.usd.height,
&prices.price.cents.height,
exit,
)?;
self.cumulative
.usd
.cents
.height
.compute_cumulative(max_from, &self.base.usd.height, exit)?;
.compute_cumulative(max_from, &self.base.cents.height, exit)?;
self.rolling.compute(
max_from,
windows,
&self.base.sats.height,
&self.base.usd.height,
&self.base.cents.height,
exit,
)?;

View File

@@ -1,12 +1,12 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Sats, Version};
use brk_types::{Cents, Height, Sats, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
use crate::{
indexes, prices,
internal::{ByUnit, SatsToDollars},
internal::{ByUnit, SatsToCents},
};
#[derive(Deref, DerefMut, Traversable)]
@@ -38,10 +38,10 @@ impl ValueFromHeightLast {
max_from: Height,
exit: &Exit,
) -> Result<()> {
self.base.usd.compute_binary::<Sats, Dollars, SatsToDollars>(
self.base.cents.compute_binary::<Sats, Cents, SatsToCents>(
max_from,
&self.base.sats.height,
&prices.price.usd.height,
&prices.price.cents.height,
exit,
)?;
Ok(())
@@ -52,7 +52,7 @@ impl ValueFromHeightLast {
max_from: Height,
window_starts: &impl ReadableVec<Height, Height>,
sats_source: &impl ReadableVec<Height, Sats>,
usd_source: &impl ReadableVec<Height, Dollars>,
cents_source: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
self.base
@@ -60,9 +60,9 @@ impl ValueFromHeightLast {
.height
.compute_rolling_sum(max_from, window_starts, sats_source, exit)?;
self.base
.usd
.cents
.height
.compute_rolling_sum(max_from, window_starts, usd_source, exit)?;
.compute_rolling_sum(max_from, window_starts, cents_source, exit)?;
Ok(())
}
@@ -71,7 +71,7 @@ impl ValueFromHeightLast {
starting_height: Height,
window_starts: &impl ReadableVec<Height, Height>,
sats_source: &impl ReadableVec<Height, Sats>,
dollars_source: &(impl ReadableVec<Height, Dollars> + Sync),
cents_source: &(impl ReadableVec<Height, Cents> + Sync),
exit: &Exit,
) -> Result<()> {
self.base
@@ -79,9 +79,9 @@ impl ValueFromHeightLast {
.height
.compute_rolling_ema(starting_height, window_starts, sats_source, exit)?;
self.base
.usd
.cents
.height
.compute_rolling_ema(starting_height, window_starts, dollars_source, exit)?;
.compute_rolling_ema(starting_height, window_starts, cents_source, exit)?;
Ok(())
}
}

View File

@@ -41,7 +41,7 @@ impl ValueFromHeightLastRolling {
})
}
/// Compute sats height via closure, then USD from price, then rolling windows.
/// Compute sats height via closure, then cents from price, then rolling windows.
pub(crate) fn compute(
&mut self,
max_from: Height,
@@ -51,12 +51,12 @@ impl ValueFromHeightLastRolling {
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
) -> Result<()> {
compute_sats(&mut self.value.sats)?;
self.value.compute_usd(prices, max_from, exit)?;
self.value.compute_cents(prices, max_from, exit)?;
self.rolling.compute_rolling_sum(
max_from,
windows,
&self.value.sats,
&self.value.usd,
&self.value.cents,
exit,
)?;
Ok(())

View File

@@ -1,11 +1,11 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Sats, Version};
use brk_types::{Cents, Height, Sats, Version};
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
use crate::{
indexes,
internal::{ByUnit, RollingSumByUnit, SatsToDollars, WindowStarts},
internal::{ByUnit, RollingSumByUnit, SatsToCents, WindowStarts},
prices,
};
@@ -50,25 +50,25 @@ impl ValueFromHeightSumCumulative {
.compute_cumulative(max_from, &self.base.sats.height, exit)?;
self.base
.usd
.cents
.height
.compute_binary::<Sats, Dollars, SatsToDollars>(
.compute_binary::<Sats, Cents, SatsToCents>(
max_from,
&self.base.sats.height,
&prices.price.usd.height,
&prices.price.cents.height,
exit,
)?;
self.cumulative
.usd
.cents
.height
.compute_cumulative(max_from, &self.base.usd.height, exit)?;
.compute_cumulative(max_from, &self.base.cents.height, exit)?;
self.sum.compute_rolling_sum(
max_from,
windows,
&self.base.sats.height,
&self.base.usd.height,
&self.base.cents.height,
exit,
)?;

View File

@@ -1,17 +1,17 @@
//! Value type with height-level data only (no period-derived views).
//!
//! Stores sats and USD per height, plus a lazy btc transform.
//! Stores sats and cents per height, plus lazy btc and usd transforms.
//! Use when period views are unnecessary (e.g., rolling windows provide windowed data).
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, Version};
use vecdb::{
Database, EagerVec, Exit, ImportableVec, LazyVecFrom1, PcoVec, ReadableCloneableVec, Rw,
StorageMode,
};
use crate::{internal::{SatsToBitcoin, SatsToDollars}, prices};
use crate::{internal::{CentsUnsignedToDollars, SatsToBitcoin, SatsToCents}, prices};
const VERSION: Version = Version::TWO; // Match ValueFromHeightLast versioning
@@ -19,7 +19,8 @@ const VERSION: Version = Version::TWO; // Match ValueFromHeightLast versioning
pub struct ValueFromHeight<M: StorageMode = Rw> {
pub sats: M::Stored<EagerVec<PcoVec<Height, Sats>>>,
pub btc: LazyVecFrom1<Height, Bitcoin, Height, Sats>,
pub usd: M::Stored<EagerVec<PcoVec<Height, Dollars>>>,
pub cents: M::Stored<EagerVec<PcoVec<Height, Cents>>>,
pub usd: LazyVecFrom1<Height, Dollars, Height, Cents>,
}
impl ValueFromHeight {
@@ -36,22 +37,28 @@ impl ValueFromHeight {
v,
sats.read_only_boxed_clone(),
);
let usd = EagerVec::forced_import(db, &format!("{name}_usd"), v)?;
let cents: EagerVec<PcoVec<Height, Cents>> =
EagerVec::forced_import(db, &format!("{name}_cents"), v)?;
let usd = LazyVecFrom1::transformed::<CentsUnsignedToDollars>(
&format!("{name}_usd"),
v,
cents.read_only_boxed_clone(),
);
Ok(Self { sats, btc, usd })
Ok(Self { sats, btc, cents, usd })
}
/// Eagerly compute USD height values: sats[h] * price[h].
pub(crate) fn compute_usd(
/// Eagerly compute cents height values: sats[h] * price_cents[h] / 1e8.
pub(crate) fn compute_cents(
&mut self,
prices: &prices::Vecs,
max_from: Height,
exit: &Exit,
) -> Result<()> {
self.usd.compute_binary::<Sats, Dollars, SatsToDollars>(
self.cents.compute_binary::<Sats, Cents, SatsToCents>(
max_from,
&self.sats,
&prices.price.usd.height,
&prices.price.cents.height,
exit,
)?;
Ok(())

View File

@@ -1,7 +1,7 @@
//! Lazy value type for Last pattern across all height-derived indexes.
use brk_traversable::Traversable;
use brk_types::{Bitcoin, Dollars, Sats, Version};
use brk_types::{Bitcoin, Cents, Dollars, Sats, Version};
use vecdb::UnaryTransform;
use crate::internal::{LazyHeightDerivedLast, ValueFromHeightLast};
@@ -40,7 +40,7 @@ impl LazyValueHeightDerivedLast {
&source.sats.rest,
);
let usd = LazyHeightDerivedLast::from_derived_computed::<DollarsTransform>(
let usd = LazyHeightDerivedLast::from_lazy::<DollarsTransform, Cents>(
&format!("{name}_usd"),
v,
&source.usd.rest,

View File

@@ -10,7 +10,7 @@ use brk_types::{Height, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
use brk_types::{Dollars, Sats};
use brk_types::{Cents, Sats};
use crate::{
indexes,
@@ -69,11 +69,11 @@ impl ValueFromHeightLastWindows {
max_from: Height,
windows: &WindowStarts<'_>,
sats_source: &impl ReadableVec<Height, Sats>,
usd_source: &impl ReadableVec<Height, Dollars>,
cents_source: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
for (w, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) {
w.compute_rolling_sum(max_from, starts, sats_source, usd_source, exit)?;
w.compute_rolling_sum(max_from, starts, sats_source, cents_source, exit)?;
}
Ok(())
}

View File

@@ -0,0 +1,13 @@
use brk_types::Cents;
use vecdb::BinaryTransform;
/// (Cents, Cents) -> Cents addition
/// Used for computing total = profit + loss
pub struct CentsPlus;
impl BinaryTransform<Cents, Cents, Cents> for CentsPlus {
#[inline(always)]
fn apply(lhs: Cents, rhs: Cents) -> Cents {
lhs + rhs
}
}

View File

@@ -0,0 +1,12 @@
use brk_types::{CentsSigned, Dollars};
use vecdb::UnaryTransform;
/// CentsSigned -> Dollars (convert signed cents to dollars for display)
pub struct CentsSignedToDollars;
impl UnaryTransform<CentsSigned, Dollars> for CentsSignedToDollars {
#[inline(always)]
fn apply(cents: CentsSigned) -> Dollars {
cents.into()
}
}

View File

@@ -0,0 +1,13 @@
use brk_types::Cents;
use vecdb::UnaryTransform;
/// Cents * (V/10) -> Cents (e.g., V=8 -> * 0.8, V=24 -> * 2.4)
pub struct CentsTimesTenths<const V: u16>;
impl<const V: u16> UnaryTransform<Cents, Cents> for CentsTimesTenths<V> {
#[inline(always)]
fn apply(c: Cents) -> Cents {
// Use u128 to avoid overflow: c * V / 10
Cents::from(c.as_u128() * V as u128 / 10)
}
}

View File

@@ -1,13 +0,0 @@
use brk_types::Dollars;
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> Dollars addition
/// Used for computing total = profit + loss
pub struct DollarsPlus;
impl BinaryTransform<Dollars, Dollars, Dollars> for DollarsPlus {
#[inline(always)]
fn apply(lhs: Dollars, rhs: Dollars) -> Dollars {
lhs + rhs
}
}

View File

@@ -1,12 +0,0 @@
use brk_types::Dollars;
use vecdb::UnaryTransform;
/// Dollars * (V/10) -> Dollars (e.g., V=8 -> * 0.8, V=24 -> * 2.4)
pub struct DollarsTimesTenths<const V: u16>;
impl<const V: u16> UnaryTransform<Dollars, Dollars> for DollarsTimesTenths<V> {
#[inline(always)]
fn apply(d: Dollars) -> Dollars {
d * (V as f64 / 10.0)
}
}

View File

@@ -1,68 +1,80 @@
mod block_count_target;
mod cents_plus;
mod cents_signed_to_dollars;
mod cents_times_tenths;
mod cents_to_dollars;
mod cents_to_sats;
mod dollar_halve;
mod dollar_identity;
mod dollar_plus;
mod dollar_times_tenths;
mod dollars_to_sats_fract;
mod f32_identity;
mod neg_cents_to_dollars;
mod ohlc_cents_to_dollars;
mod ohlc_cents_to_sats;
mod percentage_cents_f32;
mod percentage_cents_signed_dollars_f32;
mod percentage_cents_signed_f32;
mod percentage_diff_close_cents;
mod percentage_diff_close_dollars;
mod percentage_dollars_f32;
mod percentage_dollars_f32_neg;
mod percentage_sats_f64;
mod percentage_u32_f32;
mod price_times_ratio;
mod price_times_ratio_cents;
mod ratio32;
mod ratio64;
mod ratio_cents64;
mod ratio_u64_f32;
mod return_f32_tenths;
mod return_i8;
mod return_u16;
mod sats_to_cents;
mod sat_halve;
mod sat_halve_to_bitcoin;
mod sat_identity;
mod sat_mask;
mod sat_to_bitcoin;
mod sats_to_dollars;
mod u16_to_years;
mod volatility_sqrt30;
mod volatility_sqrt365;
mod volatility_sqrt7;
pub use block_count_target::*;
pub use cents_plus::*;
pub use cents_signed_to_dollars::*;
pub use cents_times_tenths::*;
pub use cents_to_dollars::*;
pub use cents_to_sats::*;
pub use neg_cents_to_dollars::*;
pub use ohlc_cents_to_dollars::*;
pub use ohlc_cents_to_sats::*;
pub use percentage_cents_f32::*;
pub use percentage_cents_signed_dollars_f32::*;
pub use percentage_cents_signed_f32::*;
pub use dollar_halve::*;
pub use dollar_identity::*;
pub use dollar_plus::*;
pub use dollar_times_tenths::*;
pub use dollars_to_sats_fract::*;
pub use f32_identity::*;
pub use percentage_diff_close_cents::*;
pub use percentage_diff_close_dollars::*;
pub use percentage_dollars_f32::*;
pub use percentage_dollars_f32_neg::*;
pub use percentage_sats_f64::*;
pub use percentage_u32_f32::*;
pub use price_times_ratio::*;
pub use price_times_ratio_cents::*;
pub use ratio32::*;
pub use ratio64::*;
pub use ratio_cents64::*;
pub use ratio_u64_f32::*;
pub use return_f32_tenths::*;
pub use return_i8::*;
pub use return_u16::*;
pub use sats_to_cents::*;
pub use sat_halve::*;
pub use sat_halve_to_bitcoin::*;
pub use sat_identity::*;
pub use sat_mask::*;
pub use sat_to_bitcoin::*;
pub use sats_to_dollars::*;
pub use u16_to_years::*;
pub use volatility_sqrt7::*;
pub use volatility_sqrt30::*;

View File

@@ -0,0 +1,13 @@
use brk_types::{Cents, Dollars};
use vecdb::UnaryTransform;
/// Cents -> -Dollars (negate after converting to dollars)
/// Avoids lazy-from-lazy by combining both transforms.
pub struct NegCentsUnsignedToDollars;
impl UnaryTransform<Cents, Dollars> for NegCentsUnsignedToDollars {
#[inline(always)]
fn apply(cents: Cents) -> Dollars {
-Dollars::from(cents)
}
}

View File

@@ -0,0 +1,16 @@
use brk_types::{Cents, StoredF32};
use vecdb::BinaryTransform;
/// (Cents, Cents) -> StoredF32 percentage (a/b × 100)
pub struct PercentageCentsF32;
impl BinaryTransform<Cents, Cents, StoredF32> for PercentageCentsF32 {
#[inline(always)]
fn apply(numerator: Cents, denominator: Cents) -> StoredF32 {
if denominator == Cents::ZERO {
StoredF32::default()
} else {
StoredF32::from(numerator.inner() as f64 / denominator.inner() as f64 * 100.0)
}
}
}

View File

@@ -0,0 +1,17 @@
use brk_types::{CentsSigned, Dollars, StoredF32};
use vecdb::BinaryTransform;
/// (CentsSigned, Dollars) -> StoredF32 percentage (a/b × 100)
/// For cross-type percentage when numerator is CentsSigned and denominator is Dollars.
pub struct PercentageCentsSignedDollarsF32;
impl BinaryTransform<CentsSigned, Dollars, StoredF32> for PercentageCentsSignedDollarsF32 {
#[inline(always)]
fn apply(numerator: CentsSigned, denominator: Dollars) -> StoredF32 {
if denominator == Dollars::ZERO {
StoredF32::default()
} else {
StoredF32::from(numerator.inner() as f64 / *denominator * 100.0)
}
}
}

View File

@@ -0,0 +1,17 @@
use brk_types::{Cents, CentsSigned, StoredF32};
use vecdb::BinaryTransform;
/// (CentsSigned, Cents) -> StoredF32 percentage (a/b × 100)
/// For cross-type percentage when numerator is signed.
pub struct PercentageCentsSignedCentsF32;
impl BinaryTransform<CentsSigned, Cents, StoredF32> for PercentageCentsSignedCentsF32 {
#[inline(always)]
fn apply(numerator: CentsSigned, denominator: Cents) -> StoredF32 {
if denominator == Cents::ZERO {
StoredF32::default()
} else {
StoredF32::from(numerator.inner() as f64 / denominator.inner() as f64 * 100.0)
}
}
}

View File

@@ -0,0 +1,17 @@
use brk_types::{Cents, StoredF32};
use vecdb::BinaryTransform;
/// (Cents, Cents) -> StoredF32 percentage difference ((a/b - 1) * 100)
pub struct PercentageDiffCents;
impl BinaryTransform<Cents, Cents, StoredF32> for PercentageDiffCents {
#[inline(always)]
fn apply(close: Cents, base: Cents) -> StoredF32 {
let base_f64 = f64::from(base);
if base_f64 == 0.0 {
StoredF32::default()
} else {
StoredF32::from((f64::from(close) / base_f64 - 1.0) * 100.0)
}
}
}

View File

@@ -1,12 +0,0 @@
use brk_types::{Dollars, StoredF32};
use vecdb::BinaryTransform;
/// Dollars * StoredF32 -> Dollars (price × ratio)
pub struct PriceTimesRatio;
impl BinaryTransform<Dollars, StoredF32, Dollars> for PriceTimesRatio {
#[inline(always)]
fn apply(price: Dollars, ratio: StoredF32) -> Dollars {
price * ratio
}
}

View File

@@ -0,0 +1,11 @@
use brk_types::{Cents, StoredF32};
use vecdb::BinaryTransform;
pub struct PriceTimesRatioCents;
impl BinaryTransform<Cents, StoredF32, Cents> for PriceTimesRatioCents {
#[inline(always)]
fn apply(price: Cents, ratio: StoredF32) -> Cents {
Cents::from(f64::from(price) * f64::from(ratio))
}
}

View File

@@ -1,13 +0,0 @@
use brk_types::{Dollars, StoredF64};
use vecdb::BinaryTransform;
/// (Dollars, Dollars) -> StoredF64 ratio
/// Used for computing ratios like SOPR where f64 precision is needed.
pub struct Ratio64;
impl BinaryTransform<Dollars, Dollars, StoredF64> for Ratio64 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> StoredF64 {
numerator / denominator
}
}

View File

@@ -0,0 +1,17 @@
use brk_types::{Cents, StoredF64};
use vecdb::BinaryTransform;
/// (Cents, Cents) -> StoredF64 ratio
/// Used for computing ratios like SOPR where f64 precision is needed.
pub struct RatioCents64;
impl BinaryTransform<Cents, Cents, StoredF64> for RatioCents64 {
#[inline(always)]
fn apply(numerator: Cents, denominator: Cents) -> StoredF64 {
if denominator == Cents::ZERO {
StoredF64::from(1.0)
} else {
StoredF64::from(numerator.inner() as f64 / denominator.inner() as f64)
}
}
}

View File

@@ -0,0 +1,13 @@
use brk_types::{Cents, Sats};
use vecdb::BinaryTransform;
/// Sats × Cents → Cents (sats × price_cents / 1e8)
/// Uses u128 intermediate to avoid overflow.
pub struct SatsToCents;
impl BinaryTransform<Sats, Cents, Cents> for SatsToCents {
#[inline(always)]
fn apply(sats: Sats, price_cents: Cents) -> Cents {
Cents::from(sats.as_u128() * price_cents.as_u128() / Sats::ONE_BTC_U128)
}
}

View File

@@ -1,12 +0,0 @@
use brk_types::{Dollars, Sats};
use vecdb::BinaryTransform;
/// Sats × Dollars → Dollars (price * sats)
pub struct SatsToDollars;
impl BinaryTransform<Sats, Dollars, Dollars> for SatsToDollars {
#[inline(always)]
fn apply(sats: Sats, price: Dollars) -> Dollars {
price * sats
}
}

View File

@@ -12,17 +12,17 @@ impl Vecs {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self.price_ath.usd.height.compute_all_time_high(
self.price_ath.cents.height.compute_all_time_high(
starting_indexes.height,
&prices.price.usd.height,
&prices.price.cents.height,
exit,
)?;
let mut prev = None;
self.days_since_price_ath.height.compute_transform2(
starting_indexes.height,
&self.price_ath.usd.height,
&prices.price.usd.height,
&self.price_ath.cents.height,
&prices.price.cents.height,
|(i, ath, price, slf)| {
if prev.is_none() {
let i = i.to_usize();
@@ -32,7 +32,7 @@ impl Vecs {
StoredU16::default()
});
}
let days = if *price == *ath {
let days = if price == ath {
StoredU16::default()
} else {
prev.unwrap() + StoredU16::new(1)
@@ -65,8 +65,8 @@ impl Vecs {
self.price_drawdown.height.compute_drawdown(
starting_indexes.height,
&prices.price.usd.height,
&self.price_ath.usd.height,
&prices.price.cents.height,
&self.price_ath.cents.height,
exit,
)?;

View File

@@ -1,5 +1,5 @@
use brk_traversable::Traversable;
use brk_types::{Dollars, StoredF32, StoredU16};
use brk_types::{Cents, StoredF32, StoredU16};
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeightLast, LazyHeightDerivedLast, Price};
@@ -7,7 +7,7 @@ use crate::internal::{ComputedFromHeightLast, LazyHeightDerivedLast, Price};
/// All-time high related metrics
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub price_ath: Price<ComputedFromHeightLast<Dollars, M>>,
pub price_ath: Price<ComputedFromHeightLast<Cents, M>>,
pub price_drawdown: ComputedFromHeightLast<StoredF32, M>,
pub days_since_price_ath: ComputedFromHeightLast<StoredU16, M>,
pub years_since_price_ath: LazyHeightDerivedLast<StoredF32, StoredU16>,

View File

@@ -1,11 +1,11 @@
use brk_error::Result;
use brk_types::{Bitcoin, Day1, Date, Dollars, Height, Sats, StoredF32, StoredU32};
use brk_types::{Bitcoin, Cents, Day1, Date, Dollars, Height, Sats, StoredF32, StoredU32};
use vecdb::{AnyVec, EagerVec, Exit, ReadableOptionVec, ReadableVec, PcoVec, PcoVecValue, VecIndex};
use super::{ByDcaClass, ByDcaPeriod, Vecs};
use crate::{
ComputeIndexes, blocks, indexes,
internal::{ComputedFromHeightLast, PercentageDiffDollars},
internal::{ComputedFromHeightLast, PercentageDiffCents},
market::lookback,
prices,
};
@@ -69,7 +69,7 @@ impl Vecs {
{
let days = days as usize;
let stack_data = stack.sats.height.collect_range_at(sh, stack.sats.height.len());
average_price.usd.height.compute_transform(
average_price.cents.height.compute_transform(
starting_indexes.height,
h2d,
|(h, di, _)| {
@@ -79,9 +79,9 @@ impl Vecs {
let num_days = days
.min(di_usize + 1)
.min(di_usize + 1 - first_price_di);
DCA_AMOUNT * num_days / Bitcoin::from(stack_sats)
Cents::from(DCA_AMOUNT * num_days / Bitcoin::from(stack_sats))
} else {
Dollars::NAN
Cents::ZERO
};
(h, avg)
},
@@ -95,10 +95,10 @@ impl Vecs {
.iter_mut()
.zip(self.period_average_price.iter_with_days())
{
returns.compute_binary::<Dollars, Dollars, PercentageDiffDollars>(
returns.compute_binary::<Cents, Cents, PercentageDiffCents>(
starting_indexes.height,
&prices.price.usd.height,
&average_price.usd.height,
&prices.price.cents.height,
&average_price.cents.height,
exit,
)?;
}
@@ -139,16 +139,16 @@ impl Vecs {
self.period_lump_sum_stack.zip_mut_with_days(&lookback_dca)
{
let total_invested = DCA_AMOUNT * days as usize;
let lookback_data = lookback_price.usd.height.collect_range_at(sh, lookback_price.usd.height.len());
let lookback_data = lookback_price.cents.height.collect_range_at(sh, lookback_price.cents.height.len());
stack.sats.height.compute_transform(
starting_indexes.height,
h2d,
|(h, _di, _)| {
let lp = lookback_data[h.to_usize() - sh];
let sats = if lp == Dollars::ZERO {
let sats = if lp == Cents::ZERO {
Sats::ZERO
} else {
Sats::from(Bitcoin::from(total_invested / lp))
Sats::from(Bitcoin::from(total_invested / Dollars::from(lp)))
};
(h, sats)
},
@@ -163,10 +163,10 @@ impl Vecs {
.iter_mut()
.zip(lookback_dca2.iter_with_days())
{
returns.compute_binary::<Dollars, Dollars, PercentageDiffDollars>(
returns.compute_binary::<Cents, Cents, PercentageDiffCents>(
starting_indexes.height,
&prices.price.usd.height,
&lookback_price.usd.height,
&prices.price.cents.height,
&lookback_price.cents.height,
exit,
)?;
}
@@ -242,17 +242,17 @@ impl Vecs {
{
let from_usize = from.to_usize();
let stack_data = stack.sats.height.collect_range_at(sh, stack.sats.height.len());
average_price.usd.height.compute_transform(
average_price.cents.height.compute_transform(
starting_indexes.height,
h2d,
|(h, di, _)| {
let di_usize = di.to_usize();
if di_usize < from_usize {
return (h, Dollars::NAN);
return (h, Cents::ZERO);
}
let stack_sats = stack_data[h.to_usize() - sh];
let num_days = di_usize + 1 - from_usize;
let avg = DCA_AMOUNT * num_days / Bitcoin::from(stack_sats);
let avg = Cents::from(DCA_AMOUNT * num_days / Bitcoin::from(stack_sats));
(h, avg)
},
exit,
@@ -266,10 +266,10 @@ impl Vecs {
.zip(self.class_average_price.iter())
{
returns.compute_binary::<Dollars, Dollars, PercentageDiffDollars>(
returns.compute_binary::<Cents, Cents, PercentageDiffCents>(
starting_indexes.height,
&prices.price.usd.height,
&average_price.usd.height,
&prices.price.cents.height,
&average_price.cents.height,
exit,
)?;
}

View File

@@ -1,5 +1,5 @@
use brk_traversable::Traversable;
use brk_types::{Dollars, Height, Sats, StoredF32, StoredU32};
use brk_types::{Cents, Height, Sats, StoredF32, StoredU32};
use vecdb::{EagerVec, PcoVec, Rw, StorageMode};
use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod};
@@ -16,7 +16,7 @@ pub struct Vecs<M: StorageMode = Rw> {
// DCA by period - KISS types
pub period_stack: ByDcaPeriod<ValueFromHeightLast<M>>,
pub period_average_price: ByDcaPeriod<Price<ComputedFromHeightLast<Dollars, M>>>,
pub period_average_price: ByDcaPeriod<Price<ComputedFromHeightLast<Cents, M>>>,
pub period_returns: ByDcaPeriod<ComputedFromHeightLast<StoredF32, M>>,
pub period_cagr: ByDcaCagr<ComputedFromHeightLast<StoredF32, M>>,
@@ -38,7 +38,7 @@ pub struct Vecs<M: StorageMode = Rw> {
// DCA by year class - KISS types
pub class_stack: ByDcaClass<ValueFromHeightLast<M>>,
pub class_average_price: ByDcaClass<Price<ComputedFromHeightLast<Dollars, M>>>,
pub class_average_price: ByDcaClass<Price<ComputedFromHeightLast<Cents, M>>>,
pub class_returns: ByDcaClass<ComputedFromHeightLast<StoredF32, M>>,
// DCA by year class - profitability

View File

@@ -1,5 +1,5 @@
use brk_error::Result;
use brk_types::Dollars;
use brk_types::Cents;
use vecdb::{Exit, ReadableVec, VecIndex};
use super::Vecs;
@@ -13,11 +13,11 @@ impl Vecs {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
let close_data: Vec<Dollars> = prices.price.usd.height.collect();
let close_data: Vec<Cents> = prices.price.cents.height.collect();
for (price_ago, days) in self.price_ago.iter_mut_with_days() {
let window_starts = blocks.count.start_vec(days as usize);
price_ago.usd.height.compute_transform(
price_ago.cents.height.compute_transform(
starting_indexes.height,
window_starts,
|(h, start_h, _)| {

View File

@@ -1,5 +1,5 @@
use brk_traversable::Traversable;
use brk_types::Dollars;
use brk_types::Cents;
use vecdb::{Rw, StorageMode};
use super::ByLookbackPeriod;
@@ -9,5 +9,5 @@ use crate::internal::{ComputedFromHeightLast, Price};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
#[traversable(flatten)]
pub price_ago: ByLookbackPeriod<Price<ComputedFromHeightLast<Dollars, M>>>,
pub price_ago: ByLookbackPeriod<Price<ComputedFromHeightLast<Cents, M>>>,
}

View File

@@ -1,5 +1,5 @@
use brk_error::Result;
use brk_types::Dollars;
use brk_types::Cents;
use vecdb::{Exit, ReadableOptionVec, VecIndex};
use super::Vecs;
@@ -14,7 +14,7 @@ impl Vecs {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
let close = &prices.price.usd.height;
let close = &prices.price.cents.height;
for (sma, period) in [
(&mut self.price_1w_sma, 7),
@@ -42,7 +42,7 @@ impl Vecs {
}
let h2d = &indexes.height.day1;
let closes: Vec<Dollars> = prices.split.close.usd.day1.collect_or_default();
let closes: Vec<Cents> = prices.split.close.cents.day1.collect_or_default();
for (ema, period) in [
(&mut self.price_1w_ema, 7),
@@ -71,7 +71,7 @@ impl Vecs {
v.compute_transform(
starting_indexes.height,
h2d,
|(h, date, ..)| (h, Dollars::from(date_ema[date.to_usize()])),
|(h, date, ..)| (h, Cents::from(date_ema[date.to_usize()])),
exit,
)?;
Ok(())
@@ -82,7 +82,7 @@ impl Vecs {
}
}
fn compute_date_ema(closes: &[Dollars], k: f64) -> Vec<f64> {
fn compute_date_ema(closes: &[Cents], k: f64) -> Vec<f64> {
let mut date_ema: Vec<f64> = Vec::with_capacity(closes.len());
let mut ema_val = 0.0f64;
for (d, close) in closes.iter().enumerate() {

View File

@@ -5,7 +5,7 @@ use vecdb::Database;
use super::Vecs;
use crate::{
indexes,
internal::{ComputedFromHeightPriceWithRatioExtended, DollarsTimesTenths, Price},
internal::{CentsTimesTenths, ComputedFromHeightPriceWithRatioExtended, Price},
};
impl Vecs {
@@ -28,20 +28,20 @@ impl Vecs {
let price_200d_sma = import!("price_200d_sma");
let price_350d_sma = import!("price_350d_sma");
let price_200d_sma_source = &price_200d_sma.price.usd;
let price_200d_sma_x2_4 = Price::from_computed::<DollarsTimesTenths<24>>(
let price_200d_sma_source = &price_200d_sma.price.cents;
let price_200d_sma_x2_4 = Price::from_cents_source::<CentsTimesTenths<24>>(
"price_200d_sma_x2_4",
version,
price_200d_sma_source,
);
let price_200d_sma_x0_8 = Price::from_computed::<DollarsTimesTenths<8>>(
let price_200d_sma_x0_8 = Price::from_cents_source::<CentsTimesTenths<8>>(
"price_200d_sma_x0_8",
version,
price_200d_sma_source,
);
let price_350d_sma_source = &price_350d_sma.price.usd;
let price_350d_sma_x2 = Price::from_computed::<DollarsTimesTenths<20>>(
let price_350d_sma_source = &price_350d_sma.price.cents;
let price_350d_sma_x2 = Price::from_cents_source::<CentsTimesTenths<20>>(
"price_350d_sma_x2",
version,
price_350d_sma_source,

View File

@@ -1,5 +1,5 @@
use brk_traversable::Traversable;
use brk_types::Dollars;
use brk_types::Cents;
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeightPriceWithRatioExtended, LazyFromHeightLast, Price};
@@ -41,7 +41,7 @@ pub struct Vecs<M: StorageMode = Rw> {
pub price_200w_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_4y_ema: ComputedFromHeightPriceWithRatioExtended<M>,
pub price_200d_sma_x2_4: Price<LazyFromHeightLast<Dollars, Dollars>>,
pub price_200d_sma_x0_8: Price<LazyFromHeightLast<Dollars, Dollars>>,
pub price_350d_sma_x2: Price<LazyFromHeightLast<Dollars, Dollars>>,
pub price_200d_sma_x2_4: Price<LazyFromHeightLast<Cents, Cents>>,
pub price_200d_sma_x0_8: Price<LazyFromHeightLast<Cents, Cents>>,
pub price_350d_sma_x2: Price<LazyFromHeightLast<Cents, Cents>>,
}

View File

@@ -16,58 +16,58 @@ impl Vecs {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
let price = &prices.price.usd.height;
let price = &prices.price.cents.height;
self.price_1w_min.usd.height.compute_rolling_min_from_starts(
self.price_1w_min.cents.height.compute_rolling_min_from_starts(
starting_indexes.height,
&blocks.count.height_1w_ago,
price,
exit,
)?;
self.price_1w_max.usd.height.compute_rolling_max_from_starts(
self.price_1w_max.cents.height.compute_rolling_max_from_starts(
starting_indexes.height,
&blocks.count.height_1w_ago,
price,
exit,
)?;
self.price_2w_min.usd.height.compute_rolling_min_from_starts(
self.price_2w_min.cents.height.compute_rolling_min_from_starts(
starting_indexes.height,
&blocks.count.height_2w_ago,
price,
exit,
)?;
self.price_2w_max.usd.height.compute_rolling_max_from_starts(
self.price_2w_max.cents.height.compute_rolling_max_from_starts(
starting_indexes.height,
&blocks.count.height_2w_ago,
price,
exit,
)?;
self.price_1m_min.usd.height.compute_rolling_min_from_starts(
self.price_1m_min.cents.height.compute_rolling_min_from_starts(
starting_indexes.height,
&blocks.count.height_1m_ago,
price,
exit,
)?;
self.price_1m_max.usd.height.compute_rolling_max_from_starts(
self.price_1m_max.cents.height.compute_rolling_max_from_starts(
starting_indexes.height,
&blocks.count.height_1m_ago,
price,
exit,
)?;
self.price_1y_min.usd.height.compute_rolling_min_from_starts(
self.price_1y_min.cents.height.compute_rolling_min_from_starts(
starting_indexes.height,
&blocks.count.height_1y_ago,
price,
exit,
)?;
self.price_1y_max.usd.height.compute_rolling_max_from_starts(
self.price_1y_max.cents.height.compute_rolling_max_from_starts(
starting_indexes.height,
&blocks.count.height_1y_ago,
price,
@@ -88,7 +88,8 @@ impl Vecs {
}
});
prev_price = Some(current);
let tr = (*current - *prev).abs();
let (c, p) = (f64::from(current), f64::from(prev));
let tr = (c - p).abs();
(h, StoredF32::from(tr))
},
exit,
@@ -105,11 +106,11 @@ impl Vecs {
self.price_2w_choppiness_index.height.compute_transform4(
starting_indexes.height,
&self.price_true_range_2w_sum.height,
&self.price_2w_max.usd.height,
&self.price_2w_min.usd.height,
&self.price_2w_max.cents.height,
&self.price_2w_min.cents.height,
&blocks.count.height_2w_ago,
|(h, tr_sum, max, min, window_start, ..)| {
let range = *max - *min;
let range = f64::from(max) - f64::from(min);
let n = (h.to_usize() - window_start.to_usize() + 1) as f32;
let ci = if range > 0.0 && n > 1.0 {
StoredF32::from(

View File

@@ -1,5 +1,5 @@
use brk_traversable::Traversable;
use brk_types::{Dollars, StoredF32};
use brk_types::{Cents, StoredF32};
use vecdb::{Rw, StorageMode};
use crate::internal::{ComputedFromHeightLast, Price};
@@ -7,14 +7,14 @@ use crate::internal::{ComputedFromHeightLast, Price};
/// Price range and choppiness metrics
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub price_1w_min: Price<ComputedFromHeightLast<Dollars, M>>,
pub price_1w_max: Price<ComputedFromHeightLast<Dollars, M>>,
pub price_2w_min: Price<ComputedFromHeightLast<Dollars, M>>,
pub price_2w_max: Price<ComputedFromHeightLast<Dollars, M>>,
pub price_1m_min: Price<ComputedFromHeightLast<Dollars, M>>,
pub price_1m_max: Price<ComputedFromHeightLast<Dollars, M>>,
pub price_1y_min: Price<ComputedFromHeightLast<Dollars, M>>,
pub price_1y_max: Price<ComputedFromHeightLast<Dollars, M>>,
pub price_1w_min: Price<ComputedFromHeightLast<Cents, M>>,
pub price_1w_max: Price<ComputedFromHeightLast<Cents, M>>,
pub price_2w_min: Price<ComputedFromHeightLast<Cents, M>>,
pub price_2w_max: Price<ComputedFromHeightLast<Cents, M>>,
pub price_1m_min: Price<ComputedFromHeightLast<Cents, M>>,
pub price_1m_max: Price<ComputedFromHeightLast<Cents, M>>,
pub price_1y_min: Price<ComputedFromHeightLast<Cents, M>>,
pub price_1y_max: Price<ComputedFromHeightLast<Cents, M>>,
pub price_true_range: ComputedFromHeightLast<StoredF32, M>,
pub price_true_range_2w_sum: ComputedFromHeightLast<StoredF32, M>,
pub price_2w_choppiness_index: ComputedFromHeightLast<StoredF32, M>,

View File

@@ -2,7 +2,7 @@ use std::path::Path;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::Version;
use brk_types::{Cents, Version};
use vecdb::{Database, ReadableCloneableVec, PAGE_SIZE};
use super::Vecs;
@@ -49,10 +49,9 @@ impl Vecs {
super::velocity::Vecs::forced_import(&db, version, indexes)?;
// Market cap - lazy identity from distribution supply in USD
let market_cap = LazyFromHeightLast::from_computed::<DollarsIdentity>(
let market_cap = LazyFromHeightLast::from_lazy::<DollarsIdentity, Cents>(
"market_cap",
version,
supply_metrics.total.usd.height.read_only_boxed_clone(),
&supply_metrics.total.usd,
);

View File

@@ -2,7 +2,7 @@ use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use vecdb::{Formattable, Pco};
use vecdb::{CheckedSub, Formattable, Pco};
use super::{CentsSats, Dollars, Sats};
@@ -258,6 +258,12 @@ impl Div<usize> for Cents {
}
}
impl CheckedSub for Cents {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
}
impl std::fmt::Display for Cents {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buf = itoa::Buffer::new();

View File

@@ -2,7 +2,7 @@ use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use vecdb::{Formattable, Pco};
use vecdb::{CheckedSub, Formattable, Pco};
use super::Dollars;
@@ -90,6 +90,13 @@ impl From<CentsSigned> for f64 {
}
}
impl From<f64> for CentsSigned {
#[inline]
fn from(value: f64) -> Self {
Self(value as i64)
}
}
impl From<i64> for CentsSigned {
#[inline]
fn from(value: i64) -> Self {
@@ -238,6 +245,12 @@ impl Mul<usize> for CentsSigned {
}
}
impl CheckedSub for CentsSigned {
fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
}
impl std::fmt::Display for CentsSigned {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buf = itoa::Buffer::new();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff