diff --git a/crates/brk_computer/src/blocks/count/compute.rs b/crates/brk_computer/src/blocks/count/compute.rs index af8b5614f..3e352681e 100644 --- a/crates/brk_computer/src/blocks/count/compute.rs +++ b/crates/brk_computer/src/blocks/count/compute.rs @@ -193,31 +193,13 @@ impl Vecs { where F: FnOnce(&mut Self) -> &mut EagerVec>, { - let field = get_field(self); - let resume_from = field.len().min(starting_indexes.height.to_usize()); - let mut prev = if resume_from > 0 { - field.collect_one_at(resume_from - 1).unwrap() - } else { - Height::ZERO - }; - let mut cursor = Cursor::new(&time.timestamp_monotonic); - cursor.advance(prev.to_usize()); - let mut prev_ts = cursor.next().unwrap(); - Ok(field.compute_transform( - starting_indexes.height, - &time.timestamp_monotonic, - |(h, t, ..)| { - while t.difference_in_days_between(prev_ts) >= days { - prev.increment(); - prev_ts = cursor.next().unwrap(); - if prev > h { - unreachable!() - } - } - (h, prev) - }, + self.compute_rolling_start_inner( + time, + starting_indexes, exit, - )?) + get_field, + |t, prev_ts| t.difference_in_days_between(prev_ts) >= days, + ) } fn compute_rolling_start_hours( @@ -230,6 +212,27 @@ impl Vecs { ) -> Result<()> where F: FnOnce(&mut Self) -> &mut EagerVec>, + { + self.compute_rolling_start_inner( + time, + starting_indexes, + exit, + get_field, + |t, prev_ts| t.difference_in_hours_between(prev_ts) >= hours, + ) + } + + fn compute_rolling_start_inner( + &mut self, + time: &time::Vecs, + starting_indexes: &ComputeIndexes, + exit: &Exit, + get_field: F, + expired: D, + ) -> Result<()> + where + F: FnOnce(&mut Self) -> &mut EagerVec>, + D: Fn(Timestamp, Timestamp) -> bool, { let field = get_field(self); let resume_from = field.len().min(starting_indexes.height.to_usize()); @@ -245,7 +248,7 @@ impl Vecs { starting_indexes.height, &time.timestamp_monotonic, |(h, t, ..)| { - while t.difference_in_hours_between(prev_ts) >= hours { + while expired(t, prev_ts) { prev.increment(); prev_ts = cursor.next().unwrap(); if prev > h { diff --git a/crates/brk_computer/src/distribution/metrics/realized/base.rs b/crates/brk_computer/src/distribution/metrics/realized/base.rs index 709c45f9b..373aa96c2 100644 --- a/crates/brk_computer/src/distribution/metrics/realized/base.rs +++ b/crates/brk_computer/src/distribution/metrics/realized/base.rs @@ -15,7 +15,7 @@ use crate::{ CentsPlus, CentsUnsignedToDollars, ComputedFromHeightCumulative, ComputedFromHeight, ComputedFromHeightRatio, NegCentsUnsignedToDollars, ValueFromHeightCumulative, LazyFromHeight, PercentageCentsF32, PercentageCentsSignedCentsF32, PercentageCentsSignedDollarsF32, Price, RatioCents64, - StoredF32Identity, ValueFromHeight, + Identity, ValueFromHeight, }, prices, }; @@ -344,7 +344,7 @@ impl RealizedBase { cfg.indexes, )?; - let mvrv = LazyFromHeight::from_computed::( + let mvrv = LazyFromHeight::from_computed::>( &cfg.name("mvrv"), cfg.version, realized_price_extra.ratio.height.read_only_boxed_clone(), diff --git a/crates/brk_computer/src/distribution/metrics/relative/base.rs b/crates/brk_computer/src/distribution/metrics/relative/base.rs index 51bb858ba..938dfe28a 100644 --- a/crates/brk_computer/src/distribution/metrics/relative/base.rs +++ b/crates/brk_computer/src/distribution/metrics/relative/base.rs @@ -4,9 +4,8 @@ use brk_types::{Dollars, Height, Sats, StoredF32, StoredF64, Version}; use vecdb::{Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode}; use crate::internal::{ - ComputedFromHeight, LazyFromHeight, + ComputedFromHeight, Identity, LazyFromHeight, NegPercentageDollarsF32, PercentageDollarsF32, PercentageSatsF64, - StoredF32Identity, }; use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase}; @@ -41,7 +40,7 @@ impl RelativeBase { cfg.db, &cfg.name("net_unrealized_pnl_rel_to_market_cap"), cfg.version + v2, cfg.indexes, )?; - let nupl = LazyFromHeight::from_computed::( + let nupl = LazyFromHeight::from_computed::>( &cfg.name("nupl"), cfg.version + v2, net_unrealized_pnl_rel_to_market_cap.height.read_only_boxed_clone(), diff --git a/crates/brk_computer/src/internal/from_height/stddev/extended.rs b/crates/brk_computer/src/internal/from_height/stddev/extended.rs index 1109354cc..789ff85ad 100644 --- a/crates/brk_computer/src/internal/from_height/stddev/extended.rs +++ b/crates/brk_computer/src/internal/from_height/stddev/extended.rs @@ -160,35 +160,15 @@ impl ComputedFromHeightStdDevExtended { .height .collect_range_at(start, self.base.sd.height.len()); - for (offset, _ratio) in source_data.into_iter().enumerate() { - let index = start + offset; - let average = sma_data[offset]; - let sd = sd_data[offset]; - - self.p0_5sd - .height - .truncate_push_at(index, average + StoredF32::from(0.5 * *sd))?; - self.p1sd.height.truncate_push_at(index, average + sd)?; - self.p1_5sd - .height - .truncate_push_at(index, average + StoredF32::from(1.5 * *sd))?; - self.p2sd.height.truncate_push_at(index, average + 2 * sd)?; - self.p2_5sd - .height - .truncate_push_at(index, average + StoredF32::from(2.5 * *sd))?; - self.p3sd.height.truncate_push_at(index, average + 3 * sd)?; - self.m0_5sd - .height - .truncate_push_at(index, average - StoredF32::from(0.5 * *sd))?; - self.m1sd.height.truncate_push_at(index, average - sd)?; - self.m1_5sd - .height - .truncate_push_at(index, average - StoredF32::from(1.5 * *sd))?; - self.m2sd.height.truncate_push_at(index, average - 2 * sd)?; - self.m2_5sd - .height - .truncate_push_at(index, average - StoredF32::from(2.5 * *sd))?; - self.m3sd.height.truncate_push_at(index, average - 3 * sd)?; + const MULTIPLIERS: [f32; 12] = [0.5, 1.0, 1.5, 2.0, 2.5, 3.0, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0]; + let band_vecs: Vec<_> = self.mut_band_height_vecs().collect(); + for (vec, mult) in band_vecs.into_iter().zip(MULTIPLIERS) { + for (offset, _) in source_data.iter().enumerate() { + let index = start + offset; + let average = sma_data[offset]; + let sd = sd_data[offset]; + vec.truncate_push_at(index, average + StoredF32::from(mult * *sd))?; + } } { diff --git a/crates/brk_computer/src/internal/sliding_window.rs b/crates/brk_computer/src/internal/sliding_window.rs index e1096ddc0..d672f9acd 100644 --- a/crates/brk_computer/src/internal/sliding_window.rs +++ b/crates/brk_computer/src/internal/sliding_window.rs @@ -56,26 +56,28 @@ impl SortedBlocks { /// Remove one occurrence of value. O(sqrt(n)). fn remove(&mut self, value: f64) -> bool { - for (bi, block) in self.blocks.iter_mut().enumerate() { - if block.is_empty() { - continue; - } - // If value > block max, it's not in this block - if *block.last().unwrap() < value { - continue; - } - let pos = block.partition_point(|a| *a < value); - if pos < block.len() && block[pos] == value { - block.remove(pos); - self.total_len -= 1; - if block.is_empty() { - self.blocks.remove(bi); - } - return true; - } - // Value not found (would be in this block range but isn't) + if self.blocks.is_empty() { return false; } + + // Binary search for first block whose max >= value + let bi = self + .blocks + .partition_point(|b| b.last().is_some_and(|&last| last < value)); + if bi >= self.blocks.len() { + return false; + } + + let block = &mut self.blocks[bi]; + let pos = block.partition_point(|a| *a < value); + if pos < block.len() && block[pos] == value { + block.remove(pos); + self.total_len -= 1; + if block.is_empty() { + self.blocks.remove(bi); + } + return true; + } false } diff --git a/crates/brk_computer/src/internal/tdigest.rs b/crates/brk_computer/src/internal/tdigest.rs index 7ed70d3d4..6d7f4e83f 100644 --- a/crates/brk_computer/src/internal/tdigest.rs +++ b/crates/brk_computer/src/internal/tdigest.rs @@ -66,20 +66,25 @@ impl TDigest { return; } - // Find nearest centroid by mean - let pos = self + // Single binary search: unclamped position doubles as insert point + let search = self .centroids - .binary_search_by(|c| c.mean.partial_cmp(&value).unwrap_or(std::cmp::Ordering::Equal)) - .unwrap_or_else(|i| i.min(self.centroids.len() - 1)); + .binary_search_by(|c| c.mean.partial_cmp(&value).unwrap_or(std::cmp::Ordering::Equal)); + let insert_pos = match search { + Ok(i) | Err(i) => i, + }; - // Check neighbors for the actual nearest - let nearest = if pos > 0 - && (value - self.centroids[pos - 1].mean).abs() - < (value - self.centroids[pos].mean).abs() + // Find nearest centroid from insert_pos + let nearest = if insert_pos >= self.centroids.len() { + self.centroids.len() - 1 + } else if insert_pos == 0 { + 0 + } else if (value - self.centroids[insert_pos - 1].mean).abs() + < (value - self.centroids[insert_pos].mean).abs() { - pos - 1 + insert_pos - 1 } else { - pos + insert_pos }; // Compute quantile of nearest centroid @@ -97,15 +102,7 @@ impl TDigest { c.mean = (c.mean * c.weight + value) / (c.weight + 1.0); c.weight += 1.0; } else { - // Insert new centroid at sorted position - let insert_pos = self - .centroids - .binary_search_by(|c| { - c.mean - .partial_cmp(&value) - .unwrap_or(std::cmp::Ordering::Equal) - }) - .unwrap_or_else(|i| i); + // Insert new centroid at sorted position (reuse insert_pos) self.centroids.insert( insert_pos, Centroid { @@ -148,65 +145,84 @@ impl TDigest { self.centroids = merged; } - pub fn quantile(&self, q: f64) -> f64 { + /// Batch quantile query in a single pass. `qs` must be sorted ascending. + pub fn quantiles(&self, qs: &[f64], out: &mut [f64]) { if self.centroids.is_empty() { - return 0.0; - } - if q <= 0.0 { - return self.min; - } - if q >= 1.0 { - return self.max; + out.iter_mut().for_each(|o| *o = 0.0); + return; } if self.centroids.len() == 1 { - return self.centroids[0].mean; + let mean = self.centroids[0].mean; + for (i, &q) in qs.iter().enumerate() { + out[i] = if q <= 0.0 { + self.min + } else if q >= 1.0 { + self.max + } else { + mean + }; + } + return; } let total: f64 = self.centroids.iter().map(|c| c.weight).sum(); - let target = q * total; let mut cum = 0.0; + let mut ci = 0; - for i in 0..self.centroids.len() { - let c = &self.centroids[i]; - let mid = cum + c.weight / 2.0; + for (qi, &q) in qs.iter().enumerate() { + if q <= 0.0 { + out[qi] = self.min; + continue; + } + if q >= 1.0 { + out[qi] = self.max; + continue; + } - if target < mid { - // Interpolate between previous centroid (or min) and this one - if i == 0 { - // Between min and first centroid center - let first_mid = c.weight / 2.0; - if first_mid == 0.0 { - return self.min; - } - return self.min + (c.mean - self.min) * (target / first_mid); + let target = q * total; + + // Advance centroids until the current centroid's midpoint exceeds target + while ci < self.centroids.len() { + let mid = cum + self.centroids[ci].weight / 2.0; + if target < mid { + break; } - let prev = &self.centroids[i - 1]; + cum += self.centroids[ci].weight; + ci += 1; + } + + if ci >= self.centroids.len() { + // Past all centroids — interpolate between last centroid and max + let last = self.centroids.last().unwrap(); + let last_mid = total - last.weight / 2.0; + let remaining = total - last_mid; + out[qi] = if remaining == 0.0 { + self.max + } else { + last.mean + (self.max - last.mean) * ((target - last_mid) / remaining) + }; + } else if ci == 0 { + // Before first centroid — interpolate between min and first centroid + let c = &self.centroids[0]; + let first_mid = c.weight / 2.0; + out[qi] = if first_mid == 0.0 { + self.min + } else { + self.min + (c.mean - self.min) * (target / first_mid) + }; + } else { + // Between centroid ci-1 and ci + let c = &self.centroids[ci]; + let prev = &self.centroids[ci - 1]; + let mid = cum + c.weight / 2.0; let prev_center = cum - prev.weight / 2.0; let frac = if mid == prev_center { 0.5 } else { (target - prev_center) / (mid - prev_center) }; - return prev.mean + (c.mean - prev.mean) * frac; + out[qi] = prev.mean + (c.mean - prev.mean) * frac; } - - cum += c.weight; - } - - // Between last centroid center and max - let last = self.centroids.last().unwrap(); - let last_mid = total - last.weight / 2.0; - let remaining = total - last_mid; - if remaining == 0.0 { - return self.max; - } - last.mean + (self.max - last.mean) * ((target - last_mid) / remaining) - } - - /// Batch quantile query. `qs` must be sorted ascending. - pub fn quantiles(&self, qs: &[f64], out: &mut [f64]) { - for (i, &q) in qs.iter().enumerate() { - out[i] = self.quantile(q); } } } @@ -215,6 +231,12 @@ impl TDigest { mod tests { use super::*; + fn quantile(td: &TDigest, q: f64) -> f64 { + let mut out = [0.0]; + td.quantiles(&[q], &mut out); + out[0] + } + #[test] fn basic_quantiles() { let mut td = TDigest::default(); @@ -223,13 +245,13 @@ mod tests { } assert_eq!(td.count(), 1000); - let median = td.quantile(0.5); + let median = quantile(&td, 0.5); assert!((median - 500.0).abs() < 10.0, "median was {median}"); - let p99 = td.quantile(0.99); + let p99 = quantile(&td, 0.99); assert!((p99 - 990.0).abs() < 15.0, "p99 was {p99}"); - let p01 = td.quantile(0.01); + let p01 = quantile(&td, 0.01); assert!((p01 - 10.0).abs() < 15.0, "p01 was {p01}"); } @@ -237,16 +259,16 @@ mod tests { fn empty_digest() { let td = TDigest::default(); assert_eq!(td.count(), 0); - assert_eq!(td.quantile(0.5), 0.0); + assert_eq!(quantile(&td, 0.5), 0.0); } #[test] fn single_value() { let mut td = TDigest::default(); td.add(42.0); - assert_eq!(td.quantile(0.0), 42.0); - assert_eq!(td.quantile(0.5), 42.0); - assert_eq!(td.quantile(1.0), 42.0); + assert_eq!(quantile(&td, 0.0), 42.0); + assert_eq!(quantile(&td, 0.5), 42.0); + assert_eq!(quantile(&td, 1.0), 42.0); } #[test] @@ -258,6 +280,6 @@ mod tests { assert_eq!(td.count(), 100); td.reset(); assert_eq!(td.count(), 0); - assert_eq!(td.quantile(0.5), 0.0); + assert_eq!(quantile(&td, 0.5), 0.0); } } diff --git a/crates/brk_computer/src/internal/transform/cents_identity.rs b/crates/brk_computer/src/internal/transform/cents_identity.rs deleted file mode 100644 index f4c799fd1..000000000 --- a/crates/brk_computer/src/internal/transform/cents_identity.rs +++ /dev/null @@ -1,12 +0,0 @@ -use brk_types::Cents; -use vecdb::UnaryTransform; - -/// Cents -> Cents (identity transform for lazy references) -pub struct CentsIdentity; - -impl UnaryTransform for CentsIdentity { - #[inline(always)] - fn apply(cents: Cents) -> Cents { - cents - } -} diff --git a/crates/brk_computer/src/internal/transform/dollar_identity.rs b/crates/brk_computer/src/internal/transform/dollar_identity.rs deleted file mode 100644 index 12754ac68..000000000 --- a/crates/brk_computer/src/internal/transform/dollar_identity.rs +++ /dev/null @@ -1,12 +0,0 @@ -use brk_types::Dollars; -use vecdb::UnaryTransform; - -/// Dollars -> Dollars (identity transform for lazy references) -pub struct DollarsIdentity; - -impl UnaryTransform for DollarsIdentity { - #[inline(always)] - fn apply(dollars: Dollars) -> Dollars { - dollars - } -} diff --git a/crates/brk_computer/src/internal/transform/f32_identity.rs b/crates/brk_computer/src/internal/transform/f32_identity.rs deleted file mode 100644 index 345f69c7b..000000000 --- a/crates/brk_computer/src/internal/transform/f32_identity.rs +++ /dev/null @@ -1,12 +0,0 @@ -use brk_types::StoredF32; -use vecdb::UnaryTransform; - -/// StoredF32 -> StoredF32 (identity transform for lazy references/proxies) -pub struct StoredF32Identity; - -impl UnaryTransform for StoredF32Identity { - #[inline(always)] - fn apply(v: StoredF32) -> StoredF32 { - v - } -} diff --git a/crates/brk_computer/src/internal/transform/identity.rs b/crates/brk_computer/src/internal/transform/identity.rs new file mode 100644 index 000000000..6d387f6f8 --- /dev/null +++ b/crates/brk_computer/src/internal/transform/identity.rs @@ -0,0 +1,13 @@ +use std::marker::PhantomData; + +use vecdb::{UnaryTransform, VecValue}; + +/// T -> T (identity transform for lazy references) +pub struct Identity(PhantomData); + +impl UnaryTransform for Identity { + #[inline(always)] + fn apply(v: T) -> T { + v + } +} diff --git a/crates/brk_computer/src/internal/transform/mod.rs b/crates/brk_computer/src/internal/transform/mod.rs index d4b96edc0..b52c6e49f 100644 --- a/crates/brk_computer/src/internal/transform/mod.rs +++ b/crates/brk_computer/src/internal/transform/mod.rs @@ -4,7 +4,7 @@ mod bps16_to_float; mod bps32_to_float; mod block_count_target; mod cents_halve; -mod cents_identity; +mod identity; mod cents_plus; mod cents_signed_to_dollars; mod cents_subtract_to_cents_signed; @@ -12,9 +12,7 @@ mod cents_times_tenths; mod cents_to_dollars; mod cents_to_sats; mod dollar_halve; -mod dollar_identity; mod dollars_to_sats_fract; -mod f32_identity; mod neg_cents_to_dollars; mod ohlc_cents_to_dollars; mod ohlc_cents_to_sats; @@ -30,6 +28,7 @@ mod percentage_u32_f32; mod price_times_ratio_cents; mod ratio32; mod ratio_cents64; +mod per_sec; mod ratio_u64_f32; mod return_f32_tenths; mod return_i8; @@ -38,13 +37,10 @@ mod sats_to_cents; mod sat_halve; mod sat_halve_to_bitcoin; -mod sat_identity; mod sat_mask; mod sat_to_bitcoin; mod days_to_years; -mod volatility_sqrt30; -mod volatility_sqrt365; -mod volatility_sqrt7; +mod volatility; pub use bp16_to_float::*; pub use bp32_to_float::*; @@ -52,7 +48,7 @@ pub use bps16_to_float::*; pub use bps32_to_float::*; pub use block_count_target::*; pub use cents_halve::*; -pub use cents_identity::*; +pub use identity::*; pub use cents_plus::*; pub use cents_signed_to_dollars::*; pub use cents_subtract_to_cents_signed::*; @@ -67,9 +63,7 @@ pub use percentage_cents_signed_dollars_f32::*; pub use percentage_cents_signed_f32::*; pub use dollar_halve::*; -pub use dollar_identity::*; 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::*; @@ -79,6 +73,7 @@ pub use percentage_u32_f32::*; pub use price_times_ratio_cents::*; pub use ratio32::*; pub use ratio_cents64::*; +pub use per_sec::*; pub use ratio_u64_f32::*; pub use return_f32_tenths::*; pub use return_i8::*; @@ -86,10 +81,7 @@ 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 days_to_years::*; -pub use volatility_sqrt7::*; -pub use volatility_sqrt30::*; -pub use volatility_sqrt365::*; +pub use volatility::*; diff --git a/crates/brk_computer/src/internal/transform/per_sec.rs b/crates/brk_computer/src/internal/transform/per_sec.rs new file mode 100644 index 000000000..9383f34b1 --- /dev/null +++ b/crates/brk_computer/src/internal/transform/per_sec.rs @@ -0,0 +1,17 @@ +use brk_types::{StoredF32, StoredU64, Timestamp}; +use vecdb::BinaryTransform; + +/// (StoredU64, Timestamp) -> StoredF32 rate (count / interval_seconds) +pub struct PerSec; + +impl BinaryTransform for PerSec { + #[inline(always)] + fn apply(count: StoredU64, interval: Timestamp) -> StoredF32 { + let interval_f64 = f64::from(*interval); + if interval_f64 > 0.0 { + StoredF32::from(*count as f64 / interval_f64) + } else { + StoredF32::NAN + } + } +} diff --git a/crates/brk_computer/src/internal/transform/sat_identity.rs b/crates/brk_computer/src/internal/transform/sat_identity.rs deleted file mode 100644 index 92e642337..000000000 --- a/crates/brk_computer/src/internal/transform/sat_identity.rs +++ /dev/null @@ -1,12 +0,0 @@ -use brk_types::Sats; -use vecdb::UnaryTransform; - -/// Sats -> Sats (identity transform for lazy references) -pub struct SatsIdentity; - -impl UnaryTransform for SatsIdentity { - #[inline(always)] - fn apply(sats: Sats) -> Sats { - sats - } -} diff --git a/crates/brk_computer/src/internal/transform/volatility.rs b/crates/brk_computer/src/internal/transform/volatility.rs new file mode 100644 index 000000000..8b280e403 --- /dev/null +++ b/crates/brk_computer/src/internal/transform/volatility.rs @@ -0,0 +1,33 @@ +use std::marker::PhantomData; + +use brk_types::StoredF32; +use vecdb::UnaryTransform; + +pub trait SqrtDays { + const FACTOR: f32; +} + +pub struct Days7; +impl SqrtDays for Days7 { + const FACTOR: f32 = 2.6457513; // 7.0_f32.sqrt() +} + +pub struct Days30; +impl SqrtDays for Days30 { + const FACTOR: f32 = 5.477226; // 30.0_f32.sqrt() +} + +pub struct Days365; +impl SqrtDays for Days365 { + const FACTOR: f32 = 19.104973; // 365.0_f32.sqrt() +} + +/// StoredF32 × sqrt(D) -> StoredF32 (annualize daily volatility to D-day period) +pub struct TimesSqrt(PhantomData); + +impl UnaryTransform for TimesSqrt { + #[inline(always)] + fn apply(v: StoredF32) -> StoredF32 { + (*v * D::FACTOR).into() + } +} diff --git a/crates/brk_computer/src/internal/transform/volatility_sqrt30.rs b/crates/brk_computer/src/internal/transform/volatility_sqrt30.rs deleted file mode 100644 index 74a499566..000000000 --- a/crates/brk_computer/src/internal/transform/volatility_sqrt30.rs +++ /dev/null @@ -1,13 +0,0 @@ -use brk_types::StoredF32; -use vecdb::UnaryTransform; - -/// StoredF32 × sqrt(30) -> StoredF32 (1-month volatility from daily SD) -pub struct StoredF32TimesSqrt30; - -impl UnaryTransform for StoredF32TimesSqrt30 { - #[inline(always)] - fn apply(v: StoredF32) -> StoredF32 { - // 30.0_f32.sqrt() = 5.477226 - (*v * 5.477226_f32).into() - } -} diff --git a/crates/brk_computer/src/internal/transform/volatility_sqrt365.rs b/crates/brk_computer/src/internal/transform/volatility_sqrt365.rs deleted file mode 100644 index 57e466511..000000000 --- a/crates/brk_computer/src/internal/transform/volatility_sqrt365.rs +++ /dev/null @@ -1,13 +0,0 @@ -use brk_types::StoredF32; -use vecdb::UnaryTransform; - -/// StoredF32 × sqrt(365) -> StoredF32 (1-year volatility from daily SD) -pub struct StoredF32TimesSqrt365; - -impl UnaryTransform for StoredF32TimesSqrt365 { - #[inline(always)] - fn apply(v: StoredF32) -> StoredF32 { - // 365.0_f32.sqrt() = 19.104973 - (*v * 19.104973_f32).into() - } -} diff --git a/crates/brk_computer/src/internal/transform/volatility_sqrt7.rs b/crates/brk_computer/src/internal/transform/volatility_sqrt7.rs deleted file mode 100644 index d359978a0..000000000 --- a/crates/brk_computer/src/internal/transform/volatility_sqrt7.rs +++ /dev/null @@ -1,13 +0,0 @@ -use brk_types::StoredF32; -use vecdb::UnaryTransform; - -/// StoredF32 × sqrt(7) -> StoredF32 (1-week volatility from daily SD) -pub struct StoredF32TimesSqrt7; - -impl UnaryTransform for StoredF32TimesSqrt7 { - #[inline(always)] - fn apply(v: StoredF32) -> StoredF32 { - // 7.0_f32.sqrt() = 2.6457513 - (*v * 2.6457513_f32).into() - } -} diff --git a/crates/brk_computer/src/market/range/compute.rs b/crates/brk_computer/src/market/range/compute.rs index dc9513987..cbbb6063c 100644 --- a/crates/brk_computer/src/market/range/compute.rs +++ b/crates/brk_computer/src/market/range/compute.rs @@ -15,61 +15,15 @@ impl Vecs { ) -> Result<()> { let price = &prices.price.cents.height; - 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.cents.height.compute_rolling_max_from_starts( - starting_indexes.height, - &blocks.count.height_1w_ago, - price, - exit, - )?; - - 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.cents.height.compute_rolling_max_from_starts( - starting_indexes.height, - &blocks.count.height_2w_ago, - price, - exit, - )?; - - 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.cents.height.compute_rolling_max_from_starts( - starting_indexes.height, - &blocks.count.height_1m_ago, - price, - exit, - )?; - - 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.cents.height.compute_rolling_max_from_starts( - starting_indexes.height, - &blocks.count.height_1y_ago, - price, - exit, - )?; + for (min_vec, max_vec, starts) in [ + (&mut self.price_1w_min.cents.height, &mut self.price_1w_max.cents.height, &blocks.count.height_1w_ago), + (&mut self.price_2w_min.cents.height, &mut self.price_2w_max.cents.height, &blocks.count.height_2w_ago), + (&mut self.price_1m_min.cents.height, &mut self.price_1m_max.cents.height, &blocks.count.height_1m_ago), + (&mut self.price_1y_min.cents.height, &mut self.price_1y_max.cents.height, &blocks.count.height_1y_ago), + ] { + min_vec.compute_rolling_min_from_starts(starting_indexes.height, starts, price, exit)?; + max_vec.compute_rolling_max_from_starts(starting_indexes.height, starts, price, exit)?; + } // True range at block level: |price[h] - price[h-1]| let mut prev_price = None; diff --git a/crates/brk_computer/src/market/volatility/import.rs b/crates/brk_computer/src/market/volatility/import.rs index c10bcbda1..824c3ddf6 100644 --- a/crates/brk_computer/src/market/volatility/import.rs +++ b/crates/brk_computer/src/market/volatility/import.rs @@ -6,8 +6,7 @@ use super::super::returns; use super::Vecs; use crate::indexes; use crate::internal::{ - ComputedFromHeight, LazyFromHeight, StoredF32TimesSqrt7, StoredF32TimesSqrt30, - StoredF32TimesSqrt365, + ComputedFromHeight, Days30, Days365, Days7, LazyFromHeight, TimesSqrt, }; impl Vecs { @@ -19,21 +18,21 @@ impl Vecs { ) -> Result { let v2 = Version::TWO; - let price_1w_volatility = LazyFromHeight::from_computed::( + let price_1w_volatility = LazyFromHeight::from_computed::>( "price_1w_volatility", version + v2, returns._1d_returns_1w_sd.sd.height.read_only_boxed_clone(), &returns._1d_returns_1w_sd.sd, ); - let price_1m_volatility = LazyFromHeight::from_computed::( + let price_1m_volatility = LazyFromHeight::from_computed::>( "price_1m_volatility", version + v2, returns._1d_returns_1m_sd.sd.height.read_only_boxed_clone(), &returns._1d_returns_1m_sd.sd, ); - let price_1y_volatility = LazyFromHeight::from_computed::( + let price_1y_volatility = LazyFromHeight::from_computed::>( "price_1y_volatility", version + v2, returns._1d_returns_1y_sd.sd.height.read_only_boxed_clone(), diff --git a/crates/brk_computer/src/supply/import.rs b/crates/brk_computer/src/supply/import.rs index 8118829cf..50a0cd8cb 100644 --- a/crates/brk_computer/src/supply/import.rs +++ b/crates/brk_computer/src/supply/import.rs @@ -2,15 +2,14 @@ use std::path::Path; use brk_error::Result; use brk_traversable::Traversable; -use brk_types::{Cents, Version}; +use brk_types::{Cents, Dollars, Sats, Version}; use vecdb::{Database, PAGE_SIZE}; use super::Vecs; use crate::{ distribution, indexes, internal::{ - CentsIdentity, ComputedFromHeight, DollarsIdentity, LazyFromHeight, LazyValueFromHeight, - SatsIdentity, SatsToBitcoin, + ComputedFromHeight, Identity, LazyFromHeight, LazyValueFromHeight, SatsToBitcoin, }, }; @@ -31,10 +30,10 @@ impl Vecs { // Circulating supply - lazy refs to distribution let circulating = LazyValueFromHeight::from_block_source::< - SatsIdentity, + Identity, SatsToBitcoin, - CentsIdentity, - DollarsIdentity, + Identity, + Identity, >("circulating_supply", &supply_metrics.total, version); // Burned/unspendable supply - computed from scripts @@ -48,7 +47,7 @@ impl Vecs { let velocity = super::velocity::Vecs::forced_import(&db, version, indexes)?; // Market cap - lazy identity from distribution supply in USD - let market_cap = LazyFromHeight::from_lazy::( + let market_cap = LazyFromHeight::from_lazy::, Cents>( "market_cap", version, &supply_metrics.total.usd, diff --git a/crates/brk_computer/src/transactions/volume/compute.rs b/crates/brk_computer/src/transactions/volume/compute.rs index 77d9372fd..be8599b98 100644 --- a/crates/brk_computer/src/transactions/volume/compute.rs +++ b/crates/brk_computer/src/transactions/volume/compute.rs @@ -1,11 +1,11 @@ use brk_error::Result; use brk_indexer::Indexer; -use brk_types::StoredF32; +use brk_types::Timestamp; use vecdb::Exit; use super::Vecs; use crate::transactions::{count, fees}; -use crate::{ComputeIndexes, blocks, indexes, inputs, outputs, prices}; +use crate::{ComputeIndexes, blocks, indexes, inputs, internal::PerSec, outputs, prices}; impl Vecs { #[allow(clippy::too_many_arguments)] @@ -67,54 +67,22 @@ impl Vecs { self.annualized_volume .compute(prices, starting_indexes.height, exit)?; - // tx_per_sec: per-block tx count / block interval - self.tx_per_sec.height.compute_transform2( + self.tx_per_sec.height.compute_binary::<_, Timestamp, PerSec>( starting_indexes.height, &count_vecs.tx_count.height, &blocks.interval.height, - |(h, tx_count, interval, ..)| { - let interval_f64 = f64::from(*interval); - let per_sec = if interval_f64 > 0.0 { - StoredF32::from(*tx_count as f64 / interval_f64) - } else { - StoredF32::NAN - }; - (h, per_sec) - }, exit, )?; - - // inputs_per_sec: per-block input count / block interval - self.inputs_per_sec.height.compute_transform2( + self.inputs_per_sec.height.compute_binary::<_, Timestamp, PerSec>( starting_indexes.height, &inputs_count.full.sum_cumulative.sum.0, &blocks.interval.height, - |(h, input_count, interval, ..)| { - let interval_f64 = f64::from(*interval); - let per_sec = if interval_f64 > 0.0 { - StoredF32::from(*input_count as f64 / interval_f64) - } else { - StoredF32::NAN - }; - (h, per_sec) - }, exit, )?; - - // outputs_per_sec: per-block output count / block interval - self.outputs_per_sec.height.compute_transform2( + self.outputs_per_sec.height.compute_binary::<_, Timestamp, PerSec>( starting_indexes.height, &outputs_count.total_count.full.sum_cumulative.sum.0, &blocks.interval.height, - |(h, output_count, interval, ..)| { - let interval_f64 = f64::from(*interval); - let per_sec = if interval_f64 > 0.0 { - StoredF32::from(*output_count as f64 / interval_f64) - } else { - StoredF32::NAN - }; - (h, per_sec) - }, exit, )?;