diff --git a/crates/brk_alloc/src/lib.rs b/crates/brk_alloc/src/lib.rs index 344d303f2..a4c7d2c86 100644 --- a/crates/brk_alloc/src/lib.rs +++ b/crates/brk_alloc/src/lib.rs @@ -2,11 +2,7 @@ //! //! This crate sets mimalloc as the global allocator and provides //! utilities for monitoring and managing memory. -//! ``` -use std::{fmt, mem::MaybeUninit}; - -use log::info; use mimalloc::MiMalloc as Allocator; #[global_allocator] @@ -16,131 +12,10 @@ static GLOBAL: Allocator = Allocator; pub struct Mimalloc; impl Mimalloc { - /// Get current mimalloc memory statistics. - /// Very fast (~100-500ns) - uses getrusage/mach syscalls, no file I/O. - #[inline] - pub fn stats() -> Stats { - let mut elapsed_msecs = MaybeUninit::uninit(); - let mut user_msecs = MaybeUninit::uninit(); - let mut system_msecs = MaybeUninit::uninit(); - let mut current_rss = MaybeUninit::uninit(); - let mut peak_rss = MaybeUninit::uninit(); - let mut current_commit = MaybeUninit::uninit(); - let mut peak_commit = MaybeUninit::uninit(); - let mut page_faults = MaybeUninit::uninit(); - - unsafe { - libmimalloc_sys::mi_process_info( - elapsed_msecs.as_mut_ptr(), - user_msecs.as_mut_ptr(), - system_msecs.as_mut_ptr(), - current_rss.as_mut_ptr(), - peak_rss.as_mut_ptr(), - current_commit.as_mut_ptr(), - peak_commit.as_mut_ptr(), - page_faults.as_mut_ptr(), - ); - - Stats { - current_rss: current_rss.assume_init(), - peak_rss: peak_rss.assume_init(), - current_commit: current_commit.assume_init(), - peak_commit: peak_commit.assume_init(), - } - } - } - /// Eagerly free memory back to OS. - /// This is expensive - only call at natural pause points. + /// Only call at natural pause points. #[inline] pub fn collect() { unsafe { libmimalloc_sys::mi_collect(true) } } - - /// Collect if wasted memory exceeds threshold (in MB). - /// Returns true if collection was triggered. - pub fn collect_if_wasted_above(threshold_mb: usize) -> bool { - let stats = Self::stats(); - - info!("Mimalloc stats: {:?}", stats); - - if stats.wasted_mb() > threshold_mb { - info!( - "Mimalloc wasted {} MB (commit: {} MB, rss: {} MB), collecting...", - stats.wasted_mb(), - stats.commit_mb(), - stats.rss_mb(), - ); - Self::collect(); - true - } else { - false - } - } - - /// Force collection and return stats before/after. - pub fn force_collect() -> (Stats, Stats) { - let before = Self::stats(); - Self::collect(); - let after = Self::stats(); - - info!( - "Mimalloc collected: {} MB -> {} MB (freed {} MB)", - before.commit_mb(), - after.commit_mb(), - before.commit_mb().saturating_sub(after.commit_mb()), - ); - - (before, after) - } -} - -/// Memory stats from mimalloc -#[derive(Debug, Clone, Copy)] -pub struct Stats { - /// Resident set size (physical memory used) - pub current_rss: usize, - pub peak_rss: usize, - /// Committed memory (virtual memory reserved) - pub current_commit: usize, - pub peak_commit: usize, -} - -impl Stats { - /// Returns wasted memory in bytes (commit - rss). - /// High values suggest fragmentation. - #[inline] - pub fn wasted(&self) -> usize { - self.current_commit.saturating_sub(self.current_rss) - } - - /// Returns wasted memory in MB. - #[inline] - pub fn wasted_mb(&self) -> usize { - self.wasted() / 1024 / 1024 - } - - /// Returns current RSS in MB. - #[inline] - pub fn rss_mb(&self) -> usize { - self.current_rss / 1024 / 1024 - } - - /// Returns current commit in MB. - #[inline] - pub fn commit_mb(&self) -> usize { - self.current_commit / 1024 / 1024 - } -} - -impl fmt::Display for Stats { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "rss: {} MB, commit: {} MB, wasted: {} MB", - self.rss_mb(), - self.commit_mb(), - self.wasted_mb() - ) - } } diff --git a/crates/brk_binder/src/javascript.rs b/crates/brk_binder/src/javascript.rs index 6b6e258b1..78fad5205 100644 --- a/crates/brk_binder/src/javascript.rs +++ b/crates/brk_binder/src/javascript.rs @@ -85,10 +85,11 @@ fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) { .map(|arr| arr.iter().any(|v| v.as_str() == Some(prop_name))) .unwrap_or(false); let optional = if required { "" } else { "=" }; + let safe_name = to_camel_case(prop_name); writeln!( output, " * @property {{{}{}}} {}", - prop_type, optional, prop_name + prop_type, optional, safe_name ) .unwrap(); } @@ -281,13 +282,17 @@ class MetricNode {{ /** * Fetch data points within a range. - * @param {{string | number}} from - * @param {{string | number}} to + * @param {{string | number}} [from] + * @param {{string | number}} [to] * @param {{(value: T[]) => void}} [onUpdate] - Called when data is available (may be called twice: cache then fresh) * @returns {{Promise}} */ getRange(from, to, onUpdate) {{ - return this._client.get(`${{this._path}}?from=${{from}}&to=${{to}}`, onUpdate); + const params = new URLSearchParams(); + if (from !== undefined) params.set('from', String(from)); + if (to !== undefined) params.set('to', String(to)); + const query = params.toString(); + return this._client.get(query ? `${{this._path}}?${{query}}` : this._path, onUpdate); }} }} @@ -352,14 +357,25 @@ fn generate_index_accessors(output: &mut String, patterns: &[IndexSetPattern]) { writeln!(output, "// Index accessor factory functions\n").unwrap(); + // Generate the ByIndexes type for each pattern for pattern in patterns { + let by_type_name = format!("{}By", pattern.name); + + // Inner 'by' object type + writeln!(output, "/**").unwrap(); + writeln!(output, " * @template T").unwrap(); + writeln!(output, " * @typedef {{Object}} {}", by_type_name).unwrap(); + for index in &pattern.indexes { + let index_name = index.serialize_long(); + writeln!(output, " * @property {{MetricNode}} {}", index_name).unwrap(); + } + writeln!(output, " */\n").unwrap(); + + // Outer type with 'by' property writeln!(output, "/**").unwrap(); writeln!(output, " * @template T").unwrap(); writeln!(output, " * @typedef {{Object}} {}", pattern.name).unwrap(); - for index in &pattern.indexes { - let field_name = index_to_camel_case(index); - writeln!(output, " * @property {{MetricNode}} {}", field_name).unwrap(); - } + writeln!(output, " * @property {{{}}} by", by_type_name).unwrap(); writeln!(output, " */\n").unwrap(); // Generate factory function @@ -377,10 +393,10 @@ fn generate_index_accessors(output: &mut String, patterns: &[IndexSetPattern]) { ) .unwrap(); writeln!(output, " return {{").unwrap(); + writeln!(output, " by: {{").unwrap(); for (i, index) in pattern.indexes.iter().enumerate() { - let field_name = index_to_camel_case(index); - let path_segment = index.serialize_long(); + let index_name = index.serialize_long(); let comma = if i < pattern.indexes.len() - 1 { "," } else { @@ -388,21 +404,18 @@ fn generate_index_accessors(output: &mut String, patterns: &[IndexSetPattern]) { }; writeln!( output, - " {}: new MetricNode(client, `${{basePath}}/{}`){}", - field_name, path_segment, comma + " {}: new MetricNode(client, `${{basePath}}/{}`){}", + index_name, index_name, comma ) .unwrap(); } + writeln!(output, " }}").unwrap(); writeln!(output, " }};").unwrap(); writeln!(output, "}}\n").unwrap(); } } -fn index_to_camel_case(index: &Index) -> String { - format!("by{}", to_pascal_case(index.serialize_long())) -} - fn generate_structural_patterns( output: &mut String, patterns: &[StructuralPattern], diff --git a/crates/brk_binder/src/python.rs b/crates/brk_binder/src/python.rs index 0e311506f..e19e69fb0 100644 --- a/crates/brk_binder/src/python.rs +++ b/crates/brk_binder/src/python.rs @@ -403,9 +403,15 @@ fn generate_metric_node(output: &mut String) { """Fetch all data points for this metric.""" return self._client.get(self._path) - def get_range(self, from_date: str, to_date: str) -> List[T]: - """Fetch data points within a date range.""" - return self._client.get(f"{{self._path}}?from={{from_date}}&to={{to_date}}") + def get_range(self, from_val: Optional[str] = None, to_val: Optional[str] = None) -> List[T]: + """Fetch data points within a range.""" + params = [] + if from_val is not None: + params.append(f"from={{from_val}}") + if to_val is not None: + params.append(f"to={{to_val}}") + query = "&".join(params) + return self._client.get(f"{{self._path}}?{{query}}" if query else self._path) "# ) diff --git a/crates/brk_binder/src/rust.rs b/crates/brk_binder/src/rust.rs index 26642f526..42174dbdb 100644 --- a/crates/brk_binder/src/rust.rs +++ b/crates/brk_binder/src/rust.rs @@ -158,8 +158,15 @@ impl MetricNode {{ }} /// Fetch data points within a range. - pub fn get_range(&self, from: &str, to: &str) -> Result> {{ - let path = format!("{{}}?from={{}}&to={{}}", self.path, from, to); + pub fn get_range(&self, from: Option<&str>, to: Option<&str>) -> Result> {{ + let mut params = Vec::new(); + if let Some(f) = from {{ params.push(format!("from={{}}", f)); }} + if let Some(t) = to {{ params.push(format!("to={{}}", t)); }} + let path = if params.is_empty() {{ + self.path.clone() + }} else {{ + format!("{{}}?{{}}", self.path, params.join("&")) + }}; self.client.get(&path) }} }} diff --git a/crates/brk_cli/src/main.rs b/crates/brk_cli/src/main.rs index bde94c376..89e257def 100644 --- a/crates/brk_cli/src/main.rs +++ b/crates/brk_cli/src/main.rs @@ -162,7 +162,7 @@ pub fn run() -> color_eyre::Result<()> { indexer.index(&blocks, &client, &exit)? }; - Mimalloc::collect_if_wasted_above(500); + Mimalloc::collect(); computer.compute(&indexer, starting_indexes, &reader, &exit)?; diff --git a/crates/brk_computer/examples/computer.rs b/crates/brk_computer/examples/computer.rs index 02eab1299..274bfea44 100644 --- a/crates/brk_computer/examples/computer.rs +++ b/crates/brk_computer/examples/computer.rs @@ -69,7 +69,7 @@ fn run() -> Result<()> { let i = Instant::now(); let starting_indexes = indexer.checked_index(&blocks, &client, &exit)?; - Mimalloc::collect_if_wasted_above(500); + Mimalloc::collect(); computer.compute(&indexer, starting_indexes, &reader, &exit)?; dbg!(i.elapsed()); diff --git a/crates/brk_computer/examples/computer_bench.rs b/crates/brk_computer/examples/computer_bench.rs index cd09fa007..03e9e20ba 100644 --- a/crates/brk_computer/examples/computer_bench.rs +++ b/crates/brk_computer/examples/computer_bench.rs @@ -62,7 +62,7 @@ fn run() -> Result<()> { let starting_indexes = indexer.index(&blocks, &client, &exit)?; info!("Done in {:?}", i.elapsed()); - Mimalloc::collect_if_wasted_above(500); + Mimalloc::collect(); let i = Instant::now(); computer.compute(&indexer, starting_indexes, &reader, &exit)?; diff --git a/crates/brk_computer/examples/full_bench.rs b/crates/brk_computer/examples/full_bench.rs index 2b90055af..9c04e14c9 100644 --- a/crates/brk_computer/examples/full_bench.rs +++ b/crates/brk_computer/examples/full_bench.rs @@ -83,7 +83,7 @@ fn run() -> Result<()> { let starting_indexes = indexer.index(&blocks, &client, &exit)?; info!("Done in {:?}", i.elapsed()); - Mimalloc::collect_if_wasted_above(500); + Mimalloc::collect(); let i = Instant::now(); computer.compute(&indexer, starting_indexes, &reader, &exit)?; diff --git a/crates/brk_computer/src/grouped/lazy_value2_from_height.rs b/crates/brk_computer/src/grouped/lazy_value2_from_height.rs new file mode 100644 index 000000000..5659ab492 --- /dev/null +++ b/crates/brk_computer/src/grouped/lazy_value2_from_height.rs @@ -0,0 +1,69 @@ +use brk_traversable::Traversable; +use brk_types::{Bitcoin, Dollars, Height, Sats, Version}; +use vecdb::{BinaryTransform, IterableBoxedVec, IterableCloneableVec}; + +use super::{ComputedValueVecsFromHeight, LazyVecsFrom2FromHeight}; + +/// Lazy value vecs computed from two `ComputedValueVecsFromHeight` sources via binary transforms. +/// Used for computing coinbase = subsidy + fee. +#[derive(Clone, Traversable)] +pub struct LazyValueVecsFrom2FromHeight { + pub sats: LazyVecsFrom2FromHeight, + pub bitcoin: LazyVecsFrom2FromHeight, + pub dollars: Option>, +} + +impl LazyValueVecsFrom2FromHeight { + pub fn from_computed( + name: &str, + version: Version, + height_source1: IterableBoxedVec, + height_source2: IterableBoxedVec, + source1: &ComputedValueVecsFromHeight, + source2: &ComputedValueVecsFromHeight, + ) -> Self + where + SatsF: BinaryTransform, + BitcoinF: BinaryTransform, + DollarsF: BinaryTransform, + { + let sats = LazyVecsFrom2FromHeight::from_computed::( + name, + version, + height_source1.boxed_clone(), + height_source2.boxed_clone(), + &source1.sats, + &source2.sats, + ); + + let bitcoin = LazyVecsFrom2FromHeight::from_computed::( + &format!("{name}_btc"), + version, + height_source1, + height_source2, + &source1.sats, + &source2.sats, + ); + + let dollars = source1 + .dollars + .as_ref() + .zip(source2.dollars.as_ref()) + .map(|(d1, d2)| { + LazyVecsFrom2FromHeight::from_computed::( + &format!("{name}_usd"), + version, + d1.height.as_ref().unwrap().boxed_clone(), + d2.height.as_ref().unwrap().boxed_clone(), + d1, + d2, + ) + }); + + Self { + sats, + bitcoin, + dollars, + } + } +} diff --git a/crates/brk_computer/src/grouped/mod.rs b/crates/brk_computer/src/grouped/mod.rs index a4031f5de..4708890da 100644 --- a/crates/brk_computer/src/grouped/mod.rs +++ b/crates/brk_computer/src/grouped/mod.rs @@ -14,6 +14,7 @@ mod lazy_from_dateindex; mod lazy_from_height; mod lazy_value_from_dateindex; mod lazy_value_height; +mod lazy_value2_from_height; // mod lazy_from_height_strict; // mod lazy_from_txindex; mod price_percentiles; @@ -40,6 +41,7 @@ pub use lazy_from_dateindex::*; pub use lazy_from_height::*; pub use lazy_value_from_dateindex::*; pub use lazy_value_height::*; +pub use lazy_value2_from_height::*; pub use lazy2_from_dateindex::*; pub use lazy2_from_height::*; // pub use lazy_from_height_strict::*; diff --git a/crates/brk_computer/src/grouped/transforms.rs b/crates/brk_computer/src/grouped/transforms.rs index 529fc3377..c2a2ddf27 100644 --- a/crates/brk_computer/src/grouped/transforms.rs +++ b/crates/brk_computer/src/grouped/transforms.rs @@ -1,4 +1,4 @@ -use brk_types::{Bitcoin, Close, Dollars, Sats, StoredF32, StoredF64, StoredU32}; +use brk_types::{Bitcoin, Close, Dollars, High, Sats, StoredF32, StoredF64, StoredU32}; use vecdb::{BinaryTransform, UnaryTransform}; /// (Dollars, Dollars) -> Dollars addition @@ -23,6 +23,43 @@ impl BinaryTransform for DollarsMinus { } } +/// (Sats, Sats) -> Sats addition +/// Used for computing coinbase = subsidy + fee +pub struct SatsPlus; + +impl BinaryTransform for SatsPlus { + #[inline(always)] + fn apply(lhs: Sats, rhs: Sats) -> Sats { + lhs + rhs + } +} + +/// (Sats, Sats) -> Bitcoin addition with conversion +/// Used for computing coinbase_btc = (subsidy + fee) / 1e8 +pub struct SatsPlusToBitcoin; + +impl BinaryTransform for SatsPlusToBitcoin { + #[inline(always)] + fn apply(lhs: Sats, rhs: Sats) -> Bitcoin { + Bitcoin::from(lhs + rhs) + } +} + +/// (StoredU32, Sats) -> Sats mask +/// Returns value if mask == 1, else 0. Used for pool fee/subsidy from chain data. +pub struct MaskSats; + +impl BinaryTransform for MaskSats { + #[inline(always)] + fn apply(mask: StoredU32, value: Sats) -> Sats { + if mask == StoredU32::ONE { + value + } else { + Sats::ZERO + } + } +} + /// (Dollars, Dollars) -> StoredF32 ratio /// Used for computing percentage ratios like profit/total, loss/total, etc. pub struct Ratio32; @@ -223,3 +260,78 @@ impl BinaryTransform for PercentageU32F32 { StoredF32::from((*numerator as f64 / *denominator as f64) * 100.0) } } + +// === Volatility Transforms (SD × sqrt(N)) === + +/// StoredF32 × sqrt(7) -> StoredF32 (1-week volatility from daily SD) +pub struct StoredF32TimesSqrt7; + +impl UnaryTransform for StoredF32TimesSqrt7 { + #[inline(always)] + fn apply(v: StoredF32) -> StoredF32 { + (*v * 7.0_f32.sqrt()).into() + } +} + +/// StoredF32 × sqrt(30) -> StoredF32 (1-month volatility from daily SD) +pub struct StoredF32TimesSqrt30; + +impl UnaryTransform for StoredF32TimesSqrt30 { + #[inline(always)] + fn apply(v: StoredF32) -> StoredF32 { + (*v * 30.0_f32.sqrt()).into() + } +} + +/// StoredF32 × sqrt(365) -> StoredF32 (1-year volatility from daily SD) +pub struct StoredF32TimesSqrt365; + +impl UnaryTransform for StoredF32TimesSqrt365 { + #[inline(always)] + fn apply(v: StoredF32) -> StoredF32 { + (*v * 365.0_f32.sqrt()).into() + } +} + +/// StoredU16 / 365.0 -> StoredF32 (days to years conversion) +pub struct StoredU16ToYears; + +impl UnaryTransform for StoredU16ToYears { + #[inline(always)] + fn apply(v: StoredU16) -> StoredF32 { + StoredF32::from(*v as f64 / 365.0) + } +} + +// === Percentage Difference Transforms === + +/// (Close, Dollars) -> StoredF32 percentage difference ((a/b - 1) × 100) +/// Used for DCA returns: (price / dca_avg_price - 1) × 100 +/// Also used for drawdown: (close / ath - 1) × 100 (note: drawdown is typically negative) +pub struct PercentageDiffCloseDollars; + +impl BinaryTransform, Dollars, StoredF32> for PercentageDiffCloseDollars { + #[inline(always)] + fn apply(close: Close, base: Dollars) -> StoredF32 { + if base == Dollars::ZERO { + StoredF32::default() + } else { + StoredF32::from((**close / *base - 1.0) * 100.0) + } + } +} + +/// (High, Dollars) -> StoredF32 percentage difference ((a/b - 1) × 100) +/// Used for drawdown calculation from high prices +pub struct PercentageDiffHighDollars; + +impl BinaryTransform, Dollars, StoredF32> for PercentageDiffHighDollars { + #[inline(always)] + fn apply(high: High, base: Dollars) -> StoredF32 { + if base == Dollars::ZERO { + StoredF32::default() + } else { + StoredF32::from((**high / *base - 1.0) * 100.0) + } + } +} diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index b0662b370..863bf0b4a 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -97,19 +97,12 @@ impl Computer { let i = Instant::now(); let constants = constants::Vecs::new(VERSION, &indexes); - let (price, market) = thread::scope(|s| -> Result<_> { - let market_handle = big_thread().spawn_scoped(s, || { - market::Vecs::forced_import(&computed_path, VERSION, &indexes) - })?; - - let price = fetched - .is_some() - .then(|| price::Vecs::forced_import(&computed_path, VERSION, &indexes).unwrap()); - - let market = market_handle.join().unwrap()?; - - Ok((price, market)) - })?; + // Price must be created before market since market's lazy vecs reference price + let price = fetched + .is_some() + .then(|| price::Vecs::forced_import(&computed_path, VERSION, &indexes).unwrap()); + let market = + market::Vecs::forced_import(&computed_path, VERSION, &indexes, price.as_ref())?; info!("Imported price/constants/market in {:?}", i.elapsed()); let i = Instant::now(); @@ -130,8 +123,13 @@ impl Computer { let chain = chain_handle.join().unwrap()?; // pools depends on chain for lazy dominance vecs - let pools = - pools::Vecs::forced_import(&computed_path, VERSION, &indexes, price.as_ref(), &chain)?; + let pools = pools::Vecs::forced_import( + &computed_path, + VERSION, + &indexes, + price.as_ref(), + &chain, + )?; Ok((chain, pools, cointime)) })?; @@ -287,7 +285,6 @@ impl Computer { indexer, &self.indexes, &starting_indexes_clone, - &self.chain, self.price.as_ref(), exit, )?; diff --git a/crates/brk_computer/src/market/compute.rs b/crates/brk_computer/src/market/compute.rs index cc86a5ac0..3646bc278 100644 --- a/crates/brk_computer/src/market/compute.rs +++ b/crates/brk_computer/src/market/compute.rs @@ -53,17 +53,6 @@ impl Vecs { Ok(()) })?; - self.indexes_to_price_drawdown - .compute_all(starting_indexes, exit, |v| { - v.compute_drawdown( - starting_indexes.dateindex, - price.timeindexes_to_price_close.dateindex.u(), - self.indexes_to_price_ath.dateindex.u(), - exit, - )?; - Ok(()) - })?; - self.indexes_to_days_since_price_ath .compute_all(starting_indexes, exit, |v| { let mut high_iter = price.timeindexes_to_price_high.dateindex.u().into_iter(); @@ -117,84 +106,63 @@ impl Vecs { Ok(()) })?; - self.indexes_to_max_years_between_price_aths - .compute_all(starting_indexes, exit, |v| { - v.compute_transform( - starting_indexes.dateindex, - self.indexes_to_max_days_between_price_aths.dateindex.u(), - |(i, max, ..)| (i, StoredF32::from(*max as f64 / 365.0)), - exit, - )?; - Ok(()) - })?; - [ - (1, &mut self.price_1d_ago, &mut self._1d_price_returns, None), - (7, &mut self.price_1w_ago, &mut self._1w_price_returns, None), - ( - 30, - &mut self.price_1m_ago, - &mut self._1m_price_returns, - None, - ), + (1, &mut self.price_1d_ago, &self._1d_price_returns, None), + (7, &mut self.price_1w_ago, &self._1w_price_returns, None), + (30, &mut self.price_1m_ago, &self._1m_price_returns, None), ( 3 * 30, &mut self.price_3m_ago, - &mut self._3m_price_returns, + &self._3m_price_returns, None, ), ( 6 * 30, &mut self.price_6m_ago, - &mut self._6m_price_returns, - None, - ), - ( - 365, - &mut self.price_1y_ago, - &mut self._1y_price_returns, + &self._6m_price_returns, None, ), + (365, &mut self.price_1y_ago, &self._1y_price_returns, None), ( 2 * 365, &mut self.price_2y_ago, - &mut self._2y_price_returns, + &self._2y_price_returns, Some(&mut self._2y_cagr), ), ( 3 * 365, &mut self.price_3y_ago, - &mut self._3y_price_returns, + &self._3y_price_returns, Some(&mut self._3y_cagr), ), ( 4 * 365, &mut self.price_4y_ago, - &mut self._4y_price_returns, + &self._4y_price_returns, Some(&mut self._4y_cagr), ), ( 5 * 365, &mut self.price_5y_ago, - &mut self._5y_price_returns, + &self._5y_price_returns, Some(&mut self._5y_cagr), ), ( 6 * 365, &mut self.price_6y_ago, - &mut self._6y_price_returns, + &self._6y_price_returns, Some(&mut self._6y_cagr), ), ( 8 * 365, &mut self.price_8y_ago, - &mut self._8y_price_returns, + &self._8y_price_returns, Some(&mut self._8y_cagr), ), ( 10 * 365, &mut self.price_10y_ago, - &mut self._10y_price_returns, + &self._10y_price_returns, Some(&mut self._10y_cagr), ), ] @@ -210,16 +178,6 @@ impl Vecs { Ok(()) })?; - returns.compute_all(starting_indexes, exit, |v| { - v.compute_percentage_change( - starting_indexes.dateindex, - price.timeindexes_to_price_close.dateindex.u(), - days, - exit, - )?; - Ok(()) - })?; - if let Some(cagr) = cagr { cagr.compute_all(starting_indexes, exit, |v| { v.compute_cagr( @@ -240,84 +198,84 @@ impl Vecs { 7, &mut self._1w_dca_stack, &mut self._1w_dca_avg_price, - &mut self._1w_dca_returns, + &self._1w_dca_returns, None, ), ( 30, &mut self._1m_dca_stack, &mut self._1m_dca_avg_price, - &mut self._1m_dca_returns, + &self._1m_dca_returns, None, ), ( 3 * 30, &mut self._3m_dca_stack, &mut self._3m_dca_avg_price, - &mut self._3m_dca_returns, + &self._3m_dca_returns, None, ), ( 6 * 30, &mut self._6m_dca_stack, &mut self._6m_dca_avg_price, - &mut self._6m_dca_returns, + &self._6m_dca_returns, None, ), ( 365, &mut self._1y_dca_stack, &mut self._1y_dca_avg_price, - &mut self._1y_dca_returns, + &self._1y_dca_returns, None, ), ( 2 * 365, &mut self._2y_dca_stack, &mut self._2y_dca_avg_price, - &mut self._2y_dca_returns, + &self._2y_dca_returns, Some(&mut self._2y_dca_cagr), ), ( 3 * 365, &mut self._3y_dca_stack, &mut self._3y_dca_avg_price, - &mut self._3y_dca_returns, + &self._3y_dca_returns, Some(&mut self._3y_dca_cagr), ), ( 4 * 365, &mut self._4y_dca_stack, &mut self._4y_dca_avg_price, - &mut self._4y_dca_returns, + &self._4y_dca_returns, Some(&mut self._4y_dca_cagr), ), ( 5 * 365, &mut self._5y_dca_stack, &mut self._5y_dca_avg_price, - &mut self._5y_dca_returns, + &self._5y_dca_returns, Some(&mut self._5y_dca_cagr), ), ( 6 * 365, &mut self._6y_dca_stack, &mut self._6y_dca_avg_price, - &mut self._6y_dca_returns, + &self._6y_dca_returns, Some(&mut self._6y_dca_cagr), ), ( 8 * 365, &mut self._8y_dca_stack, &mut self._8y_dca_avg_price, - &mut self._8y_dca_returns, + &self._8y_dca_returns, Some(&mut self._8y_dca_cagr), ), ( 10 * 365, &mut self._10y_dca_stack, &mut self._10y_dca_avg_price, - &mut self._10y_dca_returns, + &self._10y_dca_returns, Some(&mut self._10y_dca_cagr), ), ] @@ -344,16 +302,6 @@ impl Vecs { Ok(()) })?; - dca_returns.compute_all(starting_indexes, exit, |v| { - v.compute_percentage_difference( - starting_indexes.dateindex, - price.timeindexes_to_price_close.dateindex.u(), - dca_avg_price.dateindex.u(), - exit, - )?; - Ok(()) - })?; - if let Some(dca_cagr) = dca_cagr { dca_cagr.compute_all(starting_indexes, exit, |v| { v.compute_cagr( @@ -374,72 +322,61 @@ impl Vecs { ( 2015, &mut self.dca_class_2015_avg_price, - &mut self.dca_class_2015_returns, &mut self.dca_class_2015_stack, ), ( 2016, &mut self.dca_class_2016_avg_price, - &mut self.dca_class_2016_returns, &mut self.dca_class_2016_stack, ), ( 2017, &mut self.dca_class_2017_avg_price, - &mut self.dca_class_2017_returns, &mut self.dca_class_2017_stack, ), ( 2018, &mut self.dca_class_2018_avg_price, - &mut self.dca_class_2018_returns, &mut self.dca_class_2018_stack, ), ( 2019, &mut self.dca_class_2019_avg_price, - &mut self.dca_class_2019_returns, &mut self.dca_class_2019_stack, ), ( 2020, &mut self.dca_class_2020_avg_price, - &mut self.dca_class_2020_returns, &mut self.dca_class_2020_stack, ), ( 2021, &mut self.dca_class_2021_avg_price, - &mut self.dca_class_2021_returns, &mut self.dca_class_2021_stack, ), ( 2022, &mut self.dca_class_2022_avg_price, - &mut self.dca_class_2022_returns, &mut self.dca_class_2022_stack, ), ( 2023, &mut self.dca_class_2023_avg_price, - &mut self.dca_class_2023_returns, &mut self.dca_class_2023_stack, ), ( 2024, &mut self.dca_class_2024_avg_price, - &mut self.dca_class_2024_returns, &mut self.dca_class_2024_stack, ), ( 2025, &mut self.dca_class_2025_avg_price, - &mut self.dca_class_2025_returns, &mut self.dca_class_2025_stack, ), ] .into_iter() - .try_for_each(|(year, avg_price, returns, stack)| -> Result<()> { + .try_for_each(|(year, avg_price, stack)| -> Result<()> { let dateindex = DateIndex::try_from(Date::new(year, 1, 1)).unwrap(); stack.compute_all(starting_indexes, exit, |v| { @@ -462,16 +399,6 @@ impl Vecs { Ok(()) })?; - returns.compute_all(starting_indexes, exit, |v| { - v.compute_percentage_difference( - starting_indexes.dateindex, - price.timeindexes_to_price_close.dateindex.u(), - avg_price.dateindex.u(), - exit, - )?; - Ok(()) - })?; - Ok(()) })?; @@ -591,49 +518,6 @@ impl Vecs { self._1d_price_returns.dateindex.u(), )?; - self.indexes_to_price_1w_volatility - .compute_all(starting_indexes, exit, |v| { - v.compute_transform( - starting_indexes.dateindex, - self.indexes_to_1d_returns_1w_sd - .sd - .dateindex - .as_ref() - .unwrap(), - |(i, v, ..)| (i, (*v * 7.0_f32.sqrt()).into()), - exit, - )?; - Ok(()) - })?; - self.indexes_to_price_1m_volatility - .compute_all(starting_indexes, exit, |v| { - v.compute_transform( - starting_indexes.dateindex, - self.indexes_to_1d_returns_1m_sd - .sd - .dateindex - .as_ref() - .unwrap(), - |(i, v, ..)| (i, (*v * 30.0_f32.sqrt()).into()), - exit, - )?; - Ok(()) - })?; - self.indexes_to_price_1y_volatility - .compute_all(starting_indexes, exit, |v| { - v.compute_transform( - starting_indexes.dateindex, - self.indexes_to_1d_returns_1y_sd - .sd - .dateindex - .as_ref() - .unwrap(), - |(i, v, ..)| (i, (*v * 365.0_f32.sqrt()).into()), - exit, - )?; - Ok(()) - })?; - self.dateindex_to_price_true_range.compute_transform3( starting_indexes.dateindex, price.timeindexes_to_price_open.dateindex.u(), diff --git a/crates/brk_computer/src/market/import.rs b/crates/brk_computer/src/market/import.rs index 9af3db537..ad4325d0d 100644 --- a/crates/brk_computer/src/market/import.rs +++ b/crates/brk_computer/src/market/import.rs @@ -8,10 +8,12 @@ use vecdb::{Database, EagerVec, ImportableVec, IterableCloneableVec, PAGE_SIZE}; use crate::{ grouped::{ ComputedRatioVecsFromDateIndex, ComputedStandardDeviationVecsFromDateIndex, - ComputedVecsFromDateIndex, DollarsTimesTenths, LazyVecsFromDateIndex, Source, - StandardDeviationVecsOptions, VecBuilderOptions, + ComputedVecsFromDateIndex, DollarsTimesTenths, LazyVecsFrom2FromDateIndex, + LazyVecsFromDateIndex, PercentageDiffCloseDollars, Source, StandardDeviationVecsOptions, + StoredF32TimesSqrt30, StoredF32TimesSqrt365, StoredF32TimesSqrt7, StoredU16ToYears, + VecBuilderOptions, }, - indexes, + indexes, price, }; use super::Vecs; @@ -21,6 +23,7 @@ impl Vecs { parent_path: &Path, parent_version: Version, indexes: &indexes::Vecs, + price: Option<&price::Vecs>, ) -> Result { let db = Database::open(&parent_path.join(super::DB_NAME))?; db.set_min_len(PAGE_SIZE * 1_000_000)?; @@ -109,20 +112,199 @@ impl Vecs { price_200d_sma_source, ); + // SD vecs need to be created before lazy volatility vecs that reference them + let indexes_to_1d_returns_1w_sd = sd_di!("1d_returns_1w_sd", 7, v1); + let indexes_to_1d_returns_1m_sd = sd_di!("1d_returns_1m_sd", 30, v1); + let indexes_to_1d_returns_1y_sd = sd_di!("1d_returns_1y_sd", 365, v1); + let indexes_to_price_1w_volatility = + LazyVecsFromDateIndex::from_computed::( + "price_1w_volatility", + version + v2, + indexes_to_1d_returns_1w_sd + .sd + .dateindex + .as_ref() + .map(|v| v.boxed_clone()), + &indexes_to_1d_returns_1w_sd.sd, + ); + let indexes_to_price_1m_volatility = + LazyVecsFromDateIndex::from_computed::( + "price_1m_volatility", + version + v2, + indexes_to_1d_returns_1m_sd + .sd + .dateindex + .as_ref() + .map(|v| v.boxed_clone()), + &indexes_to_1d_returns_1m_sd.sd, + ); + let indexes_to_price_1y_volatility = + LazyVecsFromDateIndex::from_computed::( + "price_1y_volatility", + version + v2, + indexes_to_1d_returns_1y_sd + .sd + .dateindex + .as_ref() + .map(|v| v.boxed_clone()), + &indexes_to_1d_returns_1y_sd.sd, + ); + + // max_days needs to be created before lazy max_years that references it + let indexes_to_max_days_between_price_aths = computed_di!("max_days_between_price_aths"); + let indexes_to_max_years_between_price_aths = + LazyVecsFromDateIndex::from_computed::( + "max_years_between_price_aths", + version + v0, + indexes_to_max_days_between_price_aths + .dateindex + .as_ref() + .map(|v| v.boxed_clone()), + &indexes_to_max_days_between_price_aths, + ); + + // price_ath needed for lazy drawdown + let indexes_to_price_ath = computed_di!("price_ath"); + + // Lazy drawdown from (price_close, price_ath) + let price = price.expect("price required for market"); + let indexes_to_price_drawdown = + LazyVecsFrom2FromDateIndex::from_computed::( + "price_drawdown", + version + v0, + &price.timeindexes_to_price_close, + &indexes_to_price_ath, + ); + + // price_ago needed for lazy price_returns + let price_1d_ago = computed_di!("price_1d_ago"); + let price_1w_ago = computed_di!("price_1w_ago"); + let price_1m_ago = computed_di!("price_1m_ago"); + let price_3m_ago = computed_di!("price_3m_ago"); + let price_6m_ago = computed_di!("price_6m_ago"); + let price_1y_ago = computed_di!("price_1y_ago"); + let price_2y_ago = computed_di!("price_2y_ago"); + let price_3y_ago = computed_di!("price_3y_ago"); + let price_4y_ago = computed_di!("price_4y_ago"); + let price_5y_ago = computed_di!("price_5y_ago"); + let price_6y_ago = computed_di!("price_6y_ago"); + let price_8y_ago = computed_di!("price_8y_ago"); + let price_10y_ago = computed_di!("price_10y_ago"); + + // Lazy price_returns from (price_close, price_ago) + macro_rules! lazy_price_returns { + ($name:expr, $price_ago:expr) => { + LazyVecsFrom2FromDateIndex::from_computed::( + $name, + version + v0, + &price.timeindexes_to_price_close, + $price_ago, + ) + }; + } + + let _1d_price_returns = lazy_price_returns!("1d_price_returns", &price_1d_ago); + let _1w_price_returns = lazy_price_returns!("1w_price_returns", &price_1w_ago); + let _1m_price_returns = lazy_price_returns!("1m_price_returns", &price_1m_ago); + let _3m_price_returns = lazy_price_returns!("3m_price_returns", &price_3m_ago); + let _6m_price_returns = lazy_price_returns!("6m_price_returns", &price_6m_ago); + let _1y_price_returns = lazy_price_returns!("1y_price_returns", &price_1y_ago); + let _2y_price_returns = lazy_price_returns!("2y_price_returns", &price_2y_ago); + let _3y_price_returns = lazy_price_returns!("3y_price_returns", &price_3y_ago); + let _4y_price_returns = lazy_price_returns!("4y_price_returns", &price_4y_ago); + let _5y_price_returns = lazy_price_returns!("5y_price_returns", &price_5y_ago); + let _6y_price_returns = lazy_price_returns!("6y_price_returns", &price_6y_ago); + let _8y_price_returns = lazy_price_returns!("8y_price_returns", &price_8y_ago); + let _10y_price_returns = lazy_price_returns!("10y_price_returns", &price_10y_ago); + + // DCA avg prices needed for lazy DCA returns + let _1w_dca_avg_price = computed_di!("1w_dca_avg_price"); + let _1m_dca_avg_price = computed_di!("1m_dca_avg_price"); + let _3m_dca_avg_price = computed_di!("3m_dca_avg_price"); + let _6m_dca_avg_price = computed_di!("6m_dca_avg_price"); + let _1y_dca_avg_price = computed_di!("1y_dca_avg_price"); + let _2y_dca_avg_price = computed_di!("2y_dca_avg_price"); + let _3y_dca_avg_price = computed_di!("3y_dca_avg_price"); + let _4y_dca_avg_price = computed_di!("4y_dca_avg_price"); + let _5y_dca_avg_price = computed_di!("5y_dca_avg_price"); + let _6y_dca_avg_price = computed_di!("6y_dca_avg_price"); + let _8y_dca_avg_price = computed_di!("8y_dca_avg_price"); + let _10y_dca_avg_price = computed_di!("10y_dca_avg_price"); + + let dca_class_2025_avg_price = computed_di!("dca_class_2025_avg_price"); + let dca_class_2024_avg_price = computed_di!("dca_class_2024_avg_price"); + let dca_class_2023_avg_price = computed_di!("dca_class_2023_avg_price"); + let dca_class_2022_avg_price = computed_di!("dca_class_2022_avg_price"); + let dca_class_2021_avg_price = computed_di!("dca_class_2021_avg_price"); + let dca_class_2020_avg_price = computed_di!("dca_class_2020_avg_price"); + let dca_class_2019_avg_price = computed_di!("dca_class_2019_avg_price"); + let dca_class_2018_avg_price = computed_di!("dca_class_2018_avg_price"); + let dca_class_2017_avg_price = computed_di!("dca_class_2017_avg_price"); + let dca_class_2016_avg_price = computed_di!("dca_class_2016_avg_price"); + let dca_class_2015_avg_price = computed_di!("dca_class_2015_avg_price"); + + // Macro for creating lazy DCA returns from (price_close, dca_avg_price) + macro_rules! lazy_dca_returns { + ($name:expr, $avg_price:expr) => { + LazyVecsFrom2FromDateIndex::from_computed::( + $name, + version + v0, + &price.timeindexes_to_price_close, + $avg_price, + ) + }; + } + + let _1w_dca_returns = lazy_dca_returns!("1w_dca_returns", &_1w_dca_avg_price); + let _1m_dca_returns = lazy_dca_returns!("1m_dca_returns", &_1m_dca_avg_price); + let _3m_dca_returns = lazy_dca_returns!("3m_dca_returns", &_3m_dca_avg_price); + let _6m_dca_returns = lazy_dca_returns!("6m_dca_returns", &_6m_dca_avg_price); + let _1y_dca_returns = lazy_dca_returns!("1y_dca_returns", &_1y_dca_avg_price); + let _2y_dca_returns = lazy_dca_returns!("2y_dca_returns", &_2y_dca_avg_price); + let _3y_dca_returns = lazy_dca_returns!("3y_dca_returns", &_3y_dca_avg_price); + let _4y_dca_returns = lazy_dca_returns!("4y_dca_returns", &_4y_dca_avg_price); + let _5y_dca_returns = lazy_dca_returns!("5y_dca_returns", &_5y_dca_avg_price); + let _6y_dca_returns = lazy_dca_returns!("6y_dca_returns", &_6y_dca_avg_price); + let _8y_dca_returns = lazy_dca_returns!("8y_dca_returns", &_8y_dca_avg_price); + let _10y_dca_returns = lazy_dca_returns!("10y_dca_returns", &_10y_dca_avg_price); + + let dca_class_2025_returns = + lazy_dca_returns!("dca_class_2025_returns", &dca_class_2025_avg_price); + let dca_class_2024_returns = + lazy_dca_returns!("dca_class_2024_returns", &dca_class_2024_avg_price); + let dca_class_2023_returns = + lazy_dca_returns!("dca_class_2023_returns", &dca_class_2023_avg_price); + let dca_class_2022_returns = + lazy_dca_returns!("dca_class_2022_returns", &dca_class_2022_avg_price); + let dca_class_2021_returns = + lazy_dca_returns!("dca_class_2021_returns", &dca_class_2021_avg_price); + let dca_class_2020_returns = + lazy_dca_returns!("dca_class_2020_returns", &dca_class_2020_avg_price); + let dca_class_2019_returns = + lazy_dca_returns!("dca_class_2019_returns", &dca_class_2019_avg_price); + let dca_class_2018_returns = + lazy_dca_returns!("dca_class_2018_returns", &dca_class_2018_avg_price); + let dca_class_2017_returns = + lazy_dca_returns!("dca_class_2017_returns", &dca_class_2017_avg_price); + let dca_class_2016_returns = + lazy_dca_returns!("dca_class_2016_returns", &dca_class_2016_avg_price); + let dca_class_2015_returns = + lazy_dca_returns!("dca_class_2015_returns", &dca_class_2015_avg_price); + let this = Self { height_to_price_ath: eager_h!("price_ath", v0), height_to_price_drawdown: eager_h!("price_drawdown", v0), - indexes_to_price_ath: computed_di!("price_ath"), - indexes_to_price_drawdown: computed_di!("price_drawdown"), - indexes_to_1d_returns_1w_sd: sd_di!("1d_returns_1w_sd", 7, v1), - indexes_to_1d_returns_1m_sd: sd_di!("1d_returns_1m_sd", 30, v1), - indexes_to_1d_returns_1y_sd: sd_di!("1d_returns_1y_sd", 365, v1), - indexes_to_price_1w_volatility: computed_di!("price_1w_volatility", v2), - indexes_to_price_1m_volatility: computed_di!("price_1m_volatility", v2), - indexes_to_price_1y_volatility: computed_di!("price_1y_volatility", v2), + indexes_to_price_ath, + indexes_to_price_drawdown, + indexes_to_1d_returns_1w_sd, + indexes_to_1d_returns_1m_sd, + indexes_to_1d_returns_1y_sd, + indexes_to_price_1w_volatility, + indexes_to_price_1m_volatility, + indexes_to_price_1y_volatility, indexes_to_days_since_price_ath: computed_di!("days_since_price_ath"), - indexes_to_max_days_between_price_aths: computed_di!("max_days_between_price_aths"), - indexes_to_max_years_between_price_aths: computed_di!("max_years_between_price_aths"), + indexes_to_max_days_between_price_aths, + indexes_to_max_years_between_price_aths, indexes_to_price_1w_sma: ratio_di!("price_1w_sma"), indexes_to_price_8d_sma: ratio_di!("price_8d_sma"), @@ -154,19 +336,19 @@ impl Vecs { indexes_to_price_200w_ema: ratio_di!("price_200w_ema"), indexes_to_price_4y_ema: ratio_di!("price_4y_ema"), - _1d_price_returns: computed_di!("1d_price_returns"), - _1w_price_returns: computed_di!("1w_price_returns"), - _1m_price_returns: computed_di!("1m_price_returns"), - _3m_price_returns: computed_di!("3m_price_returns"), - _6m_price_returns: computed_di!("6m_price_returns"), - _1y_price_returns: computed_di!("1y_price_returns"), - _2y_price_returns: computed_di!("2y_price_returns"), - _3y_price_returns: computed_di!("3y_price_returns"), - _4y_price_returns: computed_di!("4y_price_returns"), - _5y_price_returns: computed_di!("5y_price_returns"), - _6y_price_returns: computed_di!("6y_price_returns"), - _8y_price_returns: computed_di!("8y_price_returns"), - _10y_price_returns: computed_di!("10y_price_returns"), + _1d_price_returns, + _1w_price_returns, + _1m_price_returns, + _3m_price_returns, + _6m_price_returns, + _1y_price_returns, + _2y_price_returns, + _3y_price_returns, + _4y_price_returns, + _5y_price_returns, + _6y_price_returns, + _8y_price_returns, + _10y_price_returns, _2y_cagr: computed_di!("2y_cagr"), _3y_cagr: computed_di!("3y_cagr"), _4y_cagr: computed_di!("4y_cagr"), @@ -175,18 +357,18 @@ impl Vecs { _8y_cagr: computed_di!("8y_cagr"), _10y_cagr: computed_di!("10y_cagr"), - _1w_dca_returns: computed_di!("1w_dca_returns"), - _1m_dca_returns: computed_di!("1m_dca_returns"), - _3m_dca_returns: computed_di!("3m_dca_returns"), - _6m_dca_returns: computed_di!("6m_dca_returns"), - _1y_dca_returns: computed_di!("1y_dca_returns"), - _2y_dca_returns: computed_di!("2y_dca_returns"), - _3y_dca_returns: computed_di!("3y_dca_returns"), - _4y_dca_returns: computed_di!("4y_dca_returns"), - _5y_dca_returns: computed_di!("5y_dca_returns"), - _6y_dca_returns: computed_di!("6y_dca_returns"), - _8y_dca_returns: computed_di!("8y_dca_returns"), - _10y_dca_returns: computed_di!("10y_dca_returns"), + _1w_dca_returns, + _1m_dca_returns, + _3m_dca_returns, + _6m_dca_returns, + _1y_dca_returns, + _2y_dca_returns, + _3y_dca_returns, + _4y_dca_returns, + _5y_dca_returns, + _6y_dca_returns, + _8y_dca_returns, + _10y_dca_returns, _2y_dca_cagr: computed_di!("2y_dca_cagr"), _3y_dca_cagr: computed_di!("3y_dca_cagr"), _4y_dca_cagr: computed_di!("4y_dca_cagr"), @@ -194,31 +376,31 @@ impl Vecs { _6y_dca_cagr: computed_di!("6y_dca_cagr"), _8y_dca_cagr: computed_di!("8y_dca_cagr"), _10y_dca_cagr: computed_di!("10y_dca_cagr"), - _1w_dca_avg_price: computed_di!("1w_dca_avg_price"), - _1m_dca_avg_price: computed_di!("1m_dca_avg_price"), - _3m_dca_avg_price: computed_di!("3m_dca_avg_price"), - _6m_dca_avg_price: computed_di!("6m_dca_avg_price"), - _1y_dca_avg_price: computed_di!("1y_dca_avg_price"), - _2y_dca_avg_price: computed_di!("2y_dca_avg_price"), - _3y_dca_avg_price: computed_di!("3y_dca_avg_price"), - _4y_dca_avg_price: computed_di!("4y_dca_avg_price"), - _5y_dca_avg_price: computed_di!("5y_dca_avg_price"), - _6y_dca_avg_price: computed_di!("6y_dca_avg_price"), - _8y_dca_avg_price: computed_di!("8y_dca_avg_price"), - _10y_dca_avg_price: computed_di!("10y_dca_avg_price"), - price_1d_ago: computed_di!("price_1d_ago"), - price_1w_ago: computed_di!("price_1w_ago"), - price_1m_ago: computed_di!("price_1m_ago"), - price_3m_ago: computed_di!("price_3m_ago"), - price_6m_ago: computed_di!("price_6m_ago"), - price_1y_ago: computed_di!("price_1y_ago"), - price_2y_ago: computed_di!("price_2y_ago"), - price_3y_ago: computed_di!("price_3y_ago"), - price_4y_ago: computed_di!("price_4y_ago"), - price_5y_ago: computed_di!("price_5y_ago"), - price_6y_ago: computed_di!("price_6y_ago"), - price_8y_ago: computed_di!("price_8y_ago"), - price_10y_ago: computed_di!("price_10y_ago"), + _1w_dca_avg_price, + _1m_dca_avg_price, + _3m_dca_avg_price, + _6m_dca_avg_price, + _1y_dca_avg_price, + _2y_dca_avg_price, + _3y_dca_avg_price, + _4y_dca_avg_price, + _5y_dca_avg_price, + _6y_dca_avg_price, + _8y_dca_avg_price, + _10y_dca_avg_price, + price_1d_ago, + price_1w_ago, + price_1m_ago, + price_3m_ago, + price_6m_ago, + price_1y_ago, + price_2y_ago, + price_3y_ago, + price_4y_ago, + price_5y_ago, + price_6y_ago, + price_8y_ago, + price_10y_ago, _1w_dca_stack: computed_di!("1w_dca_stack"), _1m_dca_stack: computed_di!("1m_dca_stack"), _3m_dca_stack: computed_di!("3m_dca_stack"), @@ -244,29 +426,29 @@ impl Vecs { dca_class_2016_stack: computed_di!("dca_class_2016_stack"), dca_class_2015_stack: computed_di!("dca_class_2015_stack"), - dca_class_2025_avg_price: computed_di!("dca_class_2025_avg_price"), - dca_class_2024_avg_price: computed_di!("dca_class_2024_avg_price"), - dca_class_2023_avg_price: computed_di!("dca_class_2023_avg_price"), - dca_class_2022_avg_price: computed_di!("dca_class_2022_avg_price"), - dca_class_2021_avg_price: computed_di!("dca_class_2021_avg_price"), - dca_class_2020_avg_price: computed_di!("dca_class_2020_avg_price"), - dca_class_2019_avg_price: computed_di!("dca_class_2019_avg_price"), - dca_class_2018_avg_price: computed_di!("dca_class_2018_avg_price"), - dca_class_2017_avg_price: computed_di!("dca_class_2017_avg_price"), - dca_class_2016_avg_price: computed_di!("dca_class_2016_avg_price"), - dca_class_2015_avg_price: computed_di!("dca_class_2015_avg_price"), + dca_class_2025_avg_price, + dca_class_2024_avg_price, + dca_class_2023_avg_price, + dca_class_2022_avg_price, + dca_class_2021_avg_price, + dca_class_2020_avg_price, + dca_class_2019_avg_price, + dca_class_2018_avg_price, + dca_class_2017_avg_price, + dca_class_2016_avg_price, + dca_class_2015_avg_price, - dca_class_2025_returns: computed_di!("dca_class_2025_returns"), - dca_class_2024_returns: computed_di!("dca_class_2024_returns"), - dca_class_2023_returns: computed_di!("dca_class_2023_returns"), - dca_class_2022_returns: computed_di!("dca_class_2022_returns"), - dca_class_2021_returns: computed_di!("dca_class_2021_returns"), - dca_class_2020_returns: computed_di!("dca_class_2020_returns"), - dca_class_2019_returns: computed_di!("dca_class_2019_returns"), - dca_class_2018_returns: computed_di!("dca_class_2018_returns"), - dca_class_2017_returns: computed_di!("dca_class_2017_returns"), - dca_class_2016_returns: computed_di!("dca_class_2016_returns"), - dca_class_2015_returns: computed_di!("dca_class_2015_returns"), + dca_class_2025_returns, + dca_class_2024_returns, + dca_class_2023_returns, + dca_class_2022_returns, + dca_class_2021_returns, + dca_class_2020_returns, + dca_class_2019_returns, + dca_class_2018_returns, + dca_class_2017_returns, + dca_class_2016_returns, + dca_class_2015_returns, indexes_to_price_200d_sma_x2_4, indexes_to_price_200d_sma_x0_8, diff --git a/crates/brk_computer/src/market/mod.rs b/crates/brk_computer/src/market/mod.rs index e7580eaca..483eec15d 100644 --- a/crates/brk_computer/src/market/mod.rs +++ b/crates/brk_computer/src/market/mod.rs @@ -2,12 +2,12 @@ mod compute; mod import; use brk_traversable::Traversable; -use brk_types::{DateIndex, Dollars, Height, Sats, StoredF32, StoredU16}; +use brk_types::{Close, DateIndex, Dollars, Height, Sats, StoredF32, StoredU16}; use vecdb::{Database, EagerVec, PcoVec}; use crate::grouped::{ ComputedRatioVecsFromDateIndex, ComputedStandardDeviationVecsFromDateIndex, - ComputedVecsFromDateIndex, LazyVecsFromDateIndex, + ComputedVecsFromDateIndex, LazyVecsFrom2FromDateIndex, LazyVecsFromDateIndex, }; pub const DB_NAME: &str = "market"; @@ -19,17 +19,17 @@ pub struct Vecs { pub height_to_price_ath: EagerVec>, pub height_to_price_drawdown: EagerVec>, pub indexes_to_price_ath: ComputedVecsFromDateIndex, - pub indexes_to_price_drawdown: ComputedVecsFromDateIndex, + pub indexes_to_price_drawdown: LazyVecsFrom2FromDateIndex, Dollars>, pub indexes_to_days_since_price_ath: ComputedVecsFromDateIndex, pub indexes_to_max_days_between_price_aths: ComputedVecsFromDateIndex, - pub indexes_to_max_years_between_price_aths: ComputedVecsFromDateIndex, + pub indexes_to_max_years_between_price_aths: LazyVecsFromDateIndex, pub indexes_to_1d_returns_1w_sd: ComputedStandardDeviationVecsFromDateIndex, pub indexes_to_1d_returns_1m_sd: ComputedStandardDeviationVecsFromDateIndex, pub indexes_to_1d_returns_1y_sd: ComputedStandardDeviationVecsFromDateIndex, - pub indexes_to_price_1w_volatility: ComputedVecsFromDateIndex, - pub indexes_to_price_1m_volatility: ComputedVecsFromDateIndex, - pub indexes_to_price_1y_volatility: ComputedVecsFromDateIndex, + pub indexes_to_price_1w_volatility: LazyVecsFromDateIndex, + pub indexes_to_price_1m_volatility: LazyVecsFromDateIndex, + pub indexes_to_price_1y_volatility: LazyVecsFromDateIndex, pub indexes_to_price_1w_min: ComputedVecsFromDateIndex, pub indexes_to_price_1w_max: ComputedVecsFromDateIndex, @@ -91,19 +91,19 @@ pub struct Vecs { pub price_8y_ago: ComputedVecsFromDateIndex, pub price_10y_ago: ComputedVecsFromDateIndex, - pub _1d_price_returns: ComputedVecsFromDateIndex, - pub _1w_price_returns: ComputedVecsFromDateIndex, - pub _1m_price_returns: ComputedVecsFromDateIndex, - pub _3m_price_returns: ComputedVecsFromDateIndex, - pub _6m_price_returns: ComputedVecsFromDateIndex, - pub _1y_price_returns: ComputedVecsFromDateIndex, - pub _2y_price_returns: ComputedVecsFromDateIndex, - pub _3y_price_returns: ComputedVecsFromDateIndex, - pub _4y_price_returns: ComputedVecsFromDateIndex, - pub _5y_price_returns: ComputedVecsFromDateIndex, - pub _6y_price_returns: ComputedVecsFromDateIndex, - pub _8y_price_returns: ComputedVecsFromDateIndex, - pub _10y_price_returns: ComputedVecsFromDateIndex, + pub _1d_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _1w_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _1m_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _3m_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _6m_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _1y_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _2y_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _3y_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _4y_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _5y_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _6y_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _8y_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _10y_price_returns: LazyVecsFrom2FromDateIndex, Dollars>, pub _2y_cagr: ComputedVecsFromDateIndex, pub _3y_cagr: ComputedVecsFromDateIndex, pub _4y_cagr: ComputedVecsFromDateIndex, @@ -136,18 +136,18 @@ pub struct Vecs { pub _6y_dca_avg_price: ComputedVecsFromDateIndex, pub _8y_dca_avg_price: ComputedVecsFromDateIndex, pub _10y_dca_avg_price: ComputedVecsFromDateIndex, - pub _1w_dca_returns: ComputedVecsFromDateIndex, - pub _1m_dca_returns: ComputedVecsFromDateIndex, - pub _3m_dca_returns: ComputedVecsFromDateIndex, - pub _6m_dca_returns: ComputedVecsFromDateIndex, - pub _1y_dca_returns: ComputedVecsFromDateIndex, - pub _2y_dca_returns: ComputedVecsFromDateIndex, - pub _3y_dca_returns: ComputedVecsFromDateIndex, - pub _4y_dca_returns: ComputedVecsFromDateIndex, - pub _5y_dca_returns: ComputedVecsFromDateIndex, - pub _6y_dca_returns: ComputedVecsFromDateIndex, - pub _8y_dca_returns: ComputedVecsFromDateIndex, - pub _10y_dca_returns: ComputedVecsFromDateIndex, + pub _1w_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _1m_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _3m_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _6m_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _1y_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _2y_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _3y_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _4y_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _5y_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _6y_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _8y_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub _10y_dca_returns: LazyVecsFrom2FromDateIndex, Dollars>, pub _2y_dca_cagr: ComputedVecsFromDateIndex, pub _3y_dca_cagr: ComputedVecsFromDateIndex, pub _4y_dca_cagr: ComputedVecsFromDateIndex, @@ -180,15 +180,15 @@ pub struct Vecs { pub dca_class_2016_avg_price: ComputedVecsFromDateIndex, pub dca_class_2015_avg_price: ComputedVecsFromDateIndex, - pub dca_class_2025_returns: ComputedVecsFromDateIndex, - pub dca_class_2024_returns: ComputedVecsFromDateIndex, - pub dca_class_2023_returns: ComputedVecsFromDateIndex, - pub dca_class_2022_returns: ComputedVecsFromDateIndex, - pub dca_class_2021_returns: ComputedVecsFromDateIndex, - pub dca_class_2020_returns: ComputedVecsFromDateIndex, - pub dca_class_2019_returns: ComputedVecsFromDateIndex, - pub dca_class_2018_returns: ComputedVecsFromDateIndex, - pub dca_class_2017_returns: ComputedVecsFromDateIndex, - pub dca_class_2016_returns: ComputedVecsFromDateIndex, - pub dca_class_2015_returns: ComputedVecsFromDateIndex, + pub dca_class_2025_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub dca_class_2024_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub dca_class_2023_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub dca_class_2022_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub dca_class_2021_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub dca_class_2020_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub dca_class_2019_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub dca_class_2018_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub dca_class_2017_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub dca_class_2016_returns: LazyVecsFrom2FromDateIndex, Dollars>, + pub dca_class_2015_returns: LazyVecsFrom2FromDateIndex, Dollars>, } diff --git a/crates/brk_computer/src/pools/mod.rs b/crates/brk_computer/src/pools/mod.rs index cf10e54f9..230d95271 100644 --- a/crates/brk_computer/src/pools/mod.rs +++ b/crates/brk_computer/src/pools/mod.rs @@ -79,11 +79,10 @@ impl Vecs { indexer: &Indexer, indexes: &indexes::Vecs, starting_indexes: &Indexes, - chain: &chain::Vecs, price: Option<&price::Vecs>, exit: &Exit, ) -> Result<()> { - self.compute_(indexer, indexes, starting_indexes, chain, price, exit)?; + self.compute_(indexer, indexes, starting_indexes, price, exit)?; let _lock = exit.lock(); self.db.compact()?; Ok(()) @@ -94,21 +93,13 @@ impl Vecs { indexer: &Indexer, indexes: &indexes::Vecs, starting_indexes: &Indexes, - chain: &chain::Vecs, price: Option<&price::Vecs>, exit: &Exit, ) -> Result<()> { self.compute_height_to_pool(indexer, indexes, starting_indexes, exit)?; self.vecs.par_iter_mut().try_for_each(|(_, vecs)| { - vecs.compute( - indexes, - starting_indexes, - &self.height_to_pool, - chain, - price, - exit, - ) + vecs.compute(indexes, starting_indexes, &self.height_to_pool, price, exit) })?; Ok(()) diff --git a/crates/brk_computer/src/pools/vecs.rs b/crates/brk_computer/src/pools/vecs.rs index 678e3367c..a13e8be72 100644 --- a/crates/brk_computer/src/pools/vecs.rs +++ b/crates/brk_computer/src/pools/vecs.rs @@ -1,18 +1,21 @@ use brk_error::Result; use brk_traversable::Traversable; use brk_types::{Height, PoolSlug, Sats, StoredF32, StoredU16, StoredU32}; -use vecdb::{Database, Exit, GenericStoredVec, IterableCloneableVec, IterableVec, VecIndex, Version}; +use vecdb::{ + Database, Exit, GenericStoredVec, IterableCloneableVec, IterableVec, LazyVecFrom2, VecIndex, + Version, +}; use crate::{ chain, grouped::{ ComputedValueVecsFromHeight, ComputedVecsFromDateIndex, ComputedVecsFromHeight, - LazyVecsFrom2FromDateIndex, LazyVecsFrom2FromHeight, PercentageU32F32, Source, + DollarsPlus, LazyValueVecsFrom2FromHeight, LazyVecsFrom2FromDateIndex, + LazyVecsFrom2FromHeight, MaskSats, PercentageU32F32, SatsPlus, SatsPlusToBitcoin, Source, VecBuilderOptions, }, indexes::{self, Indexes}, price, - utils::OptionExt, }; #[derive(Clone, Traversable)] @@ -23,9 +26,11 @@ pub struct Vecs { pub indexes_to_1w_blocks_mined: ComputedVecsFromDateIndex, pub indexes_to_1m_blocks_mined: ComputedVecsFromDateIndex, pub indexes_to_1y_blocks_mined: ComputedVecsFromDateIndex, + pub height_to_subsidy: LazyVecFrom2, + pub height_to_fee: LazyVecFrom2, pub indexes_to_subsidy: ComputedValueVecsFromHeight, pub indexes_to_fee: ComputedValueVecsFromHeight, - pub indexes_to_coinbase: ComputedValueVecsFromHeight, + pub indexes_to_coinbase: LazyValueVecsFrom2FromHeight, pub indexes_to_dominance: LazyVecsFrom2FromHeight, pub indexes_to_1d_dominance: LazyVecsFrom2FromHeight, pub indexes_to_1w_dominance: LazyVecsFrom2FromDateIndex, @@ -76,20 +81,86 @@ impl Vecs { let indexes_to_1m_blocks_mined = import_di!("1m_blocks_mined"); let indexes_to_1y_blocks_mined = import_di!("1y_blocks_mined"); + let height_to_subsidy = LazyVecFrom2::transformed::( + &suffix("subsidy"), + version, + indexes_to_blocks_mined + .height + .as_ref() + .unwrap() + .boxed_clone(), + chain + .indexes_to_subsidy + .sats + .height + .as_ref() + .unwrap() + .boxed_clone(), + ); + + let indexes_to_subsidy = ComputedValueVecsFromHeight::forced_import( + db, + &suffix("subsidy"), + Source::Vec(height_to_subsidy.boxed_clone()), + version, + sum_cum, + compute_dollars, + indexes, + )?; + + let height_to_fee = LazyVecFrom2::transformed::( + &suffix("fee"), + version, + indexes_to_blocks_mined + .height + .as_ref() + .unwrap() + .boxed_clone(), + chain.indexes_to_fee.sats.height.unwrap_sum().boxed_clone(), + ); + + let indexes_to_fee = ComputedValueVecsFromHeight::forced_import( + db, + &suffix("fee"), + Source::Vec(height_to_fee.boxed_clone()), + version, + sum_cum, + compute_dollars, + indexes, + )?; + Ok(Self { indexes_to_dominance: LazyVecsFrom2FromHeight::from_computed::( &suffix("dominance"), version, - indexes_to_blocks_mined.height.as_ref().unwrap().boxed_clone(), - chain.indexes_to_block_count.height.as_ref().unwrap().boxed_clone(), + indexes_to_blocks_mined + .height + .as_ref() + .unwrap() + .boxed_clone(), + chain + .indexes_to_block_count + .height + .as_ref() + .unwrap() + .boxed_clone(), &indexes_to_blocks_mined, &chain.indexes_to_block_count, ), indexes_to_1d_dominance: LazyVecsFrom2FromHeight::from_computed::( &suffix("1d_dominance"), version, - indexes_to_blocks_mined.height.as_ref().unwrap().boxed_clone(), - chain.indexes_to_block_count.height.as_ref().unwrap().boxed_clone(), + indexes_to_blocks_mined + .height + .as_ref() + .unwrap() + .boxed_clone(), + chain + .indexes_to_block_count + .height + .as_ref() + .unwrap() + .boxed_clone(), &indexes_to_blocks_mined, &chain.indexes_to_block_count, ), @@ -116,33 +187,22 @@ impl Vecs { indexes_to_1w_blocks_mined, indexes_to_1m_blocks_mined, indexes_to_1y_blocks_mined, - indexes_to_subsidy: ComputedValueVecsFromHeight::forced_import( - db, - &suffix("subsidy"), - Source::Compute, - version, - sum_cum, - compute_dollars, - indexes, - )?, - indexes_to_fee: ComputedValueVecsFromHeight::forced_import( - db, - &suffix("fee"), - Source::Compute, - version, - sum_cum, - compute_dollars, - indexes, - )?, - indexes_to_coinbase: ComputedValueVecsFromHeight::forced_import( - db, + indexes_to_coinbase: LazyValueVecsFrom2FromHeight::from_computed::< + SatsPlus, + SatsPlusToBitcoin, + DollarsPlus, + >( &suffix("coinbase"), - Source::Compute, version, - sum_cum, - compute_dollars, - indexes, - )?, + height_to_subsidy.boxed_clone(), + height_to_fee.boxed_clone(), + &indexes_to_subsidy, + &indexes_to_fee, + ), + height_to_subsidy, + height_to_fee, + indexes_to_subsidy, + indexes_to_fee, indexes_to_days_since_block: import_di!("days_since_block"), }) } @@ -153,7 +213,6 @@ impl Vecs { indexes: &indexes::Vecs, starting_indexes: &Indexes, height_to_pool: &impl IterableVec, - chain: &chain::Vecs, price: Option<&price::Vecs>, exit: &Exit, ) -> Result<()> { @@ -210,70 +269,21 @@ impl Vecs { Ok(()) })?; - let height_to_blocks_mined = self.indexes_to_blocks_mined.height.u(); + self.indexes_to_subsidy.compute_rest( + indexes, + price, + starting_indexes, + exit, + Some(&self.height_to_subsidy), + )?; - self.indexes_to_subsidy - .compute_all(indexes, price, starting_indexes, exit, |vec| { - vec.compute_transform2( - starting_indexes.height, - height_to_blocks_mined, - chain.indexes_to_subsidy.sats.height.u(), - |(h, mined, sats, ..)| { - ( - h, - if mined == StoredU32::ONE { - sats - } else { - Sats::ZERO - }, - ) - }, - exit, - )?; - Ok(()) - })?; - - self.indexes_to_fee - .compute_all(indexes, price, starting_indexes, exit, |vec| { - vec.compute_transform2( - starting_indexes.height, - height_to_blocks_mined, - chain.indexes_to_fee.sats.height.unwrap_sum(), - |(h, mined, sats, ..)| { - ( - h, - if mined == StoredU32::ONE { - sats - } else { - Sats::ZERO - }, - ) - }, - exit, - )?; - Ok(()) - })?; - - self.indexes_to_coinbase - .compute_all(indexes, price, starting_indexes, exit, |vec| { - vec.compute_transform2( - starting_indexes.height, - height_to_blocks_mined, - chain.indexes_to_coinbase.sats.height.u(), - |(h, mined, sats, ..)| { - ( - h, - if mined == StoredU32::ONE { - sats - } else { - Sats::ZERO - }, - ) - }, - exit, - )?; - Ok(()) - })?; + self.indexes_to_fee.compute_rest( + indexes, + price, + starting_indexes, + exit, + Some(&self.height_to_fee), + )?; self.indexes_to_days_since_block .compute_all(starting_indexes, exit, |v| { diff --git a/crates/brk_indexer/examples/indexer.rs b/crates/brk_indexer/examples/indexer.rs index 5f4f96acb..c74cc8c05 100644 --- a/crates/brk_indexer/examples/indexer.rs +++ b/crates/brk_indexer/examples/indexer.rs @@ -49,7 +49,7 @@ fn main() -> color_eyre::Result<()> { indexer.checked_index(&blocks, &client, &exit)?; info!("Done in {:?}", i.elapsed()); - Mimalloc::collect_if_wasted_above(500); + Mimalloc::collect(); sleep(Duration::from_secs(60)); } diff --git a/crates/brk_indexer/examples/indexer_bench.rs b/crates/brk_indexer/examples/indexer_bench.rs index 02440ce07..87915ed66 100644 --- a/crates/brk_indexer/examples/indexer_bench.rs +++ b/crates/brk_indexer/examples/indexer_bench.rs @@ -58,7 +58,7 @@ fn main() -> Result<()> { sleep(Duration::from_secs(10)); - Mimalloc::collect_if_wasted_above(500); + Mimalloc::collect(); sleep(Duration::from_secs(10)); diff --git a/crates/brk_indexer/examples/indexer_bench2.rs b/crates/brk_indexer/examples/indexer_bench2.rs index 7d2b739e1..d49d7f3a7 100644 --- a/crates/brk_indexer/examples/indexer_bench2.rs +++ b/crates/brk_indexer/examples/indexer_bench2.rs @@ -56,7 +56,7 @@ fn main() -> Result<()> { indexer.index(&blocks, &client, &exit)?; info!("Done in {:?}", i.elapsed()); - Mimalloc::collect_if_wasted_above(500); + Mimalloc::collect(); sleep(Duration::from_secs(60)); } diff --git a/modules/brk-resources/index.js b/modules/brk-resources/index.js index 56d96299d..c1cb6d3f8 100644 --- a/modules/brk-resources/index.js +++ b/modules/brk-resources/index.js @@ -1,132 +1,128 @@ /** * @import { Signal, Signals } from "../brk-signals/index"; - * @import { BRK } from '../brk-client/index' - * @import { Metric } from '../brk-client/metrics' - * @import { IndexName } from '../brk-client/generated/metrics' + * @import { MetricNode } from "../brk-client/index"; + */ + +/** + * @template T + * @typedef {Object} Resource + * @property {Signal} data + * @property {Signal} loading + * @property {Signal} error + * @property {(...args: any[]) => Promise} fetch + */ + +/** + * @template T + * @typedef {Object} RangeState + * @property {Signal} data + * @property {Signal} loading + */ + +/** + * @template T + * @typedef {Object} MetricResource + * @property {string} path + * @property {(from?: number, to?: number) => RangeState} range + * @property {(from?: number, to?: number) => Promise} fetch */ /** * @typedef {ReturnType} Resources - * @typedef {ReturnType} MetricResource */ /** - * @param {BRK} brk * @param {Signals} signals */ -export function createResources(brk, signals) { +export function createResources(signals) { const owner = signals.getOwner(); - const defaultFrom = -10_000; - const defaultTo = undefined; - - /** - * @param {Object} [args] - * @param {number} [args.from] - * @param {number} [args.to] - */ - function genKey(args) { - return `${args?.from ?? defaultFrom}-${args?.to ?? ""}`; - } - /** + * Create a generic reactive resource wrapper for any async fetcher * @template T - * @param {Metric} metric - * @param {IndexName} index + * @template {any[]} Args + * @param {(...args: Args) => Promise} fetcher + * @returns {Resource} */ - function createMetricResource(metric, index) { - if (!brk.hasMetric(metric)) { - throw Error(`${metric} is invalid`); - } - + function createResource(fetcher) { return signals.runWithOwner(owner, () => { - const fetchedRecord = signals.createSignal( - /** @type {Map}>} */ ( - new Map() - ), - ); + const data = signals.createSignal(/** @type {T | null} */ (null)); + const loading = signals.createSignal(false); + const error = signals.createSignal(/** @type {Error | null} */ (null)); return { - url: brk.genMetricURL(metric, index, defaultFrom), - fetched: fetchedRecord, + data, + loading, + error, /** - * Defaults - * - from: -10_000 - * - to: undefined - * - * @param {Object} [args] - * @param {number} [args.from] - * @param {number} [args.to] + * @param {Args} args */ - async fetch(args) { - const from = args?.from ?? defaultFrom; - const to = args?.to ?? defaultTo; - const fetchedKey = genKey({ from, to }); - if (!fetchedRecord().has(fetchedKey)) { - fetchedRecord.set((map) => { - map.set(fetchedKey, { - loading: false, - at: null, - data: signals.createSignal(/** @type {T[] | null} */ (null), { - equals: false, - }), - }); - return map; - }); + async fetch(...args) { + loading.set(true); + error.set(null); + try { + const result = await fetcher(...args); + data.set(result); + return result; + } catch (e) { + error.set(e instanceof Error ? e : new Error(String(e))); + return null; + } finally { + loading.set(false); } - const fetched = fetchedRecord().get(fetchedKey); - if (!fetched) throw Error("Unreachable"); - if (fetched.loading) return fetched.data(); - if (fetched.at) { - const diff = new Date().getTime() - fetched.at.getTime(); - const ONE_MINUTE_IN_MS = 60_000; - if (diff < ONE_MINUTE_IN_MS) return fetched.data(); - } - fetched.loading = true; - const res = /** @type {T[] | null} */ ( - await brk.fetchMetric( - (data) => { - if (data.length || !fetched.data()) { - fetched.data.set(data); - } - }, - index, - metric, - from, - to, - ) - ); - fetched.at = new Date(); - fetched.loading = false; - return res; }, }; }); } - /** @type {Map>>} */ - const map = new Map(); + /** + * Create a reactive resource wrapper for a MetricNode with multi-range support + * @template T + * @param {MetricNode} node + * @returns {MetricResource} + */ + function useMetricNode(node) { + return signals.runWithOwner(owner, () => { + /** @type {Map>} */ + const ranges = new Map(); - const metrics = { - /** - * @template T - * @param {Metric} metric - * @param {IndexName} index - */ - getOrCreate(metric, index) { - const key = `${metric}/${index}`; - const found = map.get(key); - if (found) { - return found; + /** + * Get or create range state + * @param {number} [from=-10000] + * @param {number} [to] + */ + function range(from = -10000, to) { + const key = `${from}-${to ?? ""}`; + if (!ranges.has(key)) { + ranges.set(key, { + data: signals.createSignal(/** @type {T[] | null} */ (null)), + loading: signals.createSignal(false), + }); + } + return /** @type {RangeState} */ (ranges.get(key)); } - const resource = createMetricResource(metric, index); - if (!resource) throw Error("metric is undefined"); - map.set(key, /** @type {any} */ (resource)); - return resource; - }, - genKey, - }; + return { + path: node._path, + range, + /** + * Fetch data for a range + * @param {number} [from=-10000] + * @param {number} [to] + */ + async fetch(from = -10000, to) { + const r = range(from, to); + r.loading.set(true); + try { + const result = await node.getRange(from, to, r.data.set); + return result; + } finally { + r.loading.set(false); + } + }, + }; + }); + } - return { metrics }; + return { createResource, useMetricNode }; } diff --git a/websites/bitview/scripts/main.js b/websites/bitview/scripts/main.js index 984902be0..8fce549c7 100644 --- a/websites/bitview/scripts/main.js +++ b/websites/bitview/scripts/main.js @@ -114,7 +114,7 @@ Promise.all([ ]).then(([signals, { BrkClient, VERSION }, { createResources }, { initOptions }]) => signals.createRoot(() => { const brk = new BrkClient("/"); - const resources = createResources(brk, signals); + const resources = createResources(signals); const owner = signals.getOwner(); console.log(`VERSION = ${VERSION}`);