global: snapshot

This commit is contained in:
nym21
2025-12-29 13:20:52 +01:00
parent 647f177f31
commit 445959f5b9
22 changed files with 794 additions and 650 deletions

View File

@@ -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()
)
}
}

View File

@@ -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<T[]>}}
*/
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<T>}} {}", 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<T>}} {}", field_name).unwrap();
}
writeln!(output, " * @property {{{}<T>}} 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],

View File

@@ -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)
"#
)

View File

@@ -158,8 +158,15 @@ impl<T: DeserializeOwned> MetricNode<T> {{
}}
/// Fetch data points within a range.
pub fn get_range(&self, from: &str, to: &str) -> Result<Vec<T>> {{
let path = format!("{{}}?from={{}}&to={{}}", self.path, from, to);
pub fn get_range(&self, from: Option<&str>, to: Option<&str>) -> Result<Vec<T>> {{
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)
}}
}}

View File

@@ -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)?;

View File

@@ -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());

View File

@@ -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)?;

View File

@@ -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)?;

View File

@@ -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<Sats, Sats, Sats>,
pub bitcoin: LazyVecsFrom2FromHeight<Bitcoin, Sats, Sats>,
pub dollars: Option<LazyVecsFrom2FromHeight<Dollars, Dollars, Dollars>>,
}
impl LazyValueVecsFrom2FromHeight {
pub fn from_computed<SatsF, BitcoinF, DollarsF>(
name: &str,
version: Version,
height_source1: IterableBoxedVec<Height, Sats>,
height_source2: IterableBoxedVec<Height, Sats>,
source1: &ComputedValueVecsFromHeight,
source2: &ComputedValueVecsFromHeight,
) -> Self
where
SatsF: BinaryTransform<Sats, Sats, Sats>,
BitcoinF: BinaryTransform<Sats, Sats, Bitcoin>,
DollarsF: BinaryTransform<Dollars, Dollars, Dollars>,
{
let sats = LazyVecsFrom2FromHeight::from_computed::<SatsF>(
name,
version,
height_source1.boxed_clone(),
height_source2.boxed_clone(),
&source1.sats,
&source2.sats,
);
let bitcoin = LazyVecsFrom2FromHeight::from_computed::<BitcoinF>(
&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::<DollarsF>(
&format!("{name}_usd"),
version,
d1.height.as_ref().unwrap().boxed_clone(),
d2.height.as_ref().unwrap().boxed_clone(),
d1,
d2,
)
});
Self {
sats,
bitcoin,
dollars,
}
}
}

View File

@@ -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::*;

View File

@@ -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<Dollars, Dollars, Dollars> for DollarsMinus {
}
}
/// (Sats, Sats) -> Sats addition
/// Used for computing coinbase = subsidy + fee
pub struct SatsPlus;
impl BinaryTransform<Sats, Sats, Sats> 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<Sats, Sats, Bitcoin> 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<StoredU32, Sats, Sats> 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<StoredU32, StoredU32, StoredF32> 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<StoredF32, StoredF32> 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<StoredF32, StoredF32> 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<StoredF32, StoredF32> 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<StoredU16, StoredF32> for StoredU16ToYears {
#[inline(always)]
fn apply(v: StoredU16) -> StoredF32 {
StoredF32::from(*v as f64 / 365.0)
}
}
// === Percentage Difference Transforms ===
/// (Close<Dollars>, 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<Close<Dollars>, Dollars, StoredF32> for PercentageDiffCloseDollars {
#[inline(always)]
fn apply(close: Close<Dollars>, base: Dollars) -> StoredF32 {
if base == Dollars::ZERO {
StoredF32::default()
} else {
StoredF32::from((**close / *base - 1.0) * 100.0)
}
}
}
/// (High<Dollars>, Dollars) -> StoredF32 percentage difference ((a/b - 1) × 100)
/// Used for drawdown calculation from high prices
pub struct PercentageDiffHighDollars;
impl BinaryTransform<High<Dollars>, Dollars, StoredF32> for PercentageDiffHighDollars {
#[inline(always)]
fn apply(high: High<Dollars>, base: Dollars) -> StoredF32 {
if base == Dollars::ZERO {
StoredF32::default()
} else {
StoredF32::from((**high / *base - 1.0) * 100.0)
}
}
}

View File

@@ -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,
)?;

View File

@@ -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(),

View File

@@ -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<Self> {
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::<StoredF32TimesSqrt7>(
"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::<StoredF32TimesSqrt30>(
"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::<StoredF32TimesSqrt365>(
"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::<StoredU16ToYears>(
"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::<PercentageDiffCloseDollars>(
"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::<PercentageDiffCloseDollars>(
$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::<PercentageDiffCloseDollars>(
$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,

View File

@@ -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<PcoVec<Height, Dollars>>,
pub height_to_price_drawdown: EagerVec<PcoVec<Height, StoredF32>>,
pub indexes_to_price_ath: ComputedVecsFromDateIndex<Dollars>,
pub indexes_to_price_drawdown: ComputedVecsFromDateIndex<StoredF32>,
pub indexes_to_price_drawdown: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub indexes_to_days_since_price_ath: ComputedVecsFromDateIndex<StoredU16>,
pub indexes_to_max_days_between_price_aths: ComputedVecsFromDateIndex<StoredU16>,
pub indexes_to_max_years_between_price_aths: ComputedVecsFromDateIndex<StoredF32>,
pub indexes_to_max_years_between_price_aths: LazyVecsFromDateIndex<StoredF32, StoredU16>,
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<StoredF32>,
pub indexes_to_price_1m_volatility: ComputedVecsFromDateIndex<StoredF32>,
pub indexes_to_price_1y_volatility: ComputedVecsFromDateIndex<StoredF32>,
pub indexes_to_price_1w_volatility: LazyVecsFromDateIndex<StoredF32>,
pub indexes_to_price_1m_volatility: LazyVecsFromDateIndex<StoredF32>,
pub indexes_to_price_1y_volatility: LazyVecsFromDateIndex<StoredF32>,
pub indexes_to_price_1w_min: ComputedVecsFromDateIndex<Dollars>,
pub indexes_to_price_1w_max: ComputedVecsFromDateIndex<Dollars>,
@@ -91,19 +91,19 @@ pub struct Vecs {
pub price_8y_ago: ComputedVecsFromDateIndex<Dollars>,
pub price_10y_ago: ComputedVecsFromDateIndex<Dollars>,
pub _1d_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _1w_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _1m_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _3m_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _6m_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _1y_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _2y_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _3y_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _4y_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _5y_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _6y_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _8y_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _10y_price_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _1d_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _1w_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _1m_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _3m_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _6m_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _1y_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _2y_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _3y_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _4y_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _5y_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _6y_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _8y_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _10y_price_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _2y_cagr: ComputedVecsFromDateIndex<StoredF32>,
pub _3y_cagr: ComputedVecsFromDateIndex<StoredF32>,
pub _4y_cagr: ComputedVecsFromDateIndex<StoredF32>,
@@ -136,18 +136,18 @@ pub struct Vecs {
pub _6y_dca_avg_price: ComputedVecsFromDateIndex<Dollars>,
pub _8y_dca_avg_price: ComputedVecsFromDateIndex<Dollars>,
pub _10y_dca_avg_price: ComputedVecsFromDateIndex<Dollars>,
pub _1w_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _1m_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _3m_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _6m_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _1y_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _2y_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _3y_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _4y_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _5y_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _6y_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _8y_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _10y_dca_returns: ComputedVecsFromDateIndex<StoredF32>,
pub _1w_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _1m_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _3m_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _6m_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _1y_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _2y_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _3y_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _4y_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _5y_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _6y_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _8y_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _10y_dca_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub _2y_dca_cagr: ComputedVecsFromDateIndex<StoredF32>,
pub _3y_dca_cagr: ComputedVecsFromDateIndex<StoredF32>,
pub _4y_dca_cagr: ComputedVecsFromDateIndex<StoredF32>,
@@ -180,15 +180,15 @@ pub struct Vecs {
pub dca_class_2016_avg_price: ComputedVecsFromDateIndex<Dollars>,
pub dca_class_2015_avg_price: ComputedVecsFromDateIndex<Dollars>,
pub dca_class_2025_returns: ComputedVecsFromDateIndex<StoredF32>,
pub dca_class_2024_returns: ComputedVecsFromDateIndex<StoredF32>,
pub dca_class_2023_returns: ComputedVecsFromDateIndex<StoredF32>,
pub dca_class_2022_returns: ComputedVecsFromDateIndex<StoredF32>,
pub dca_class_2021_returns: ComputedVecsFromDateIndex<StoredF32>,
pub dca_class_2020_returns: ComputedVecsFromDateIndex<StoredF32>,
pub dca_class_2019_returns: ComputedVecsFromDateIndex<StoredF32>,
pub dca_class_2018_returns: ComputedVecsFromDateIndex<StoredF32>,
pub dca_class_2017_returns: ComputedVecsFromDateIndex<StoredF32>,
pub dca_class_2016_returns: ComputedVecsFromDateIndex<StoredF32>,
pub dca_class_2015_returns: ComputedVecsFromDateIndex<StoredF32>,
pub dca_class_2025_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub dca_class_2024_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub dca_class_2023_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub dca_class_2022_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub dca_class_2021_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub dca_class_2020_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub dca_class_2019_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub dca_class_2018_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub dca_class_2017_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub dca_class_2016_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
pub dca_class_2015_returns: LazyVecsFrom2FromDateIndex<StoredF32, Close<Dollars>, Dollars>,
}

View File

@@ -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(())

View File

@@ -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<StoredU32>,
pub indexes_to_1m_blocks_mined: ComputedVecsFromDateIndex<StoredU32>,
pub indexes_to_1y_blocks_mined: ComputedVecsFromDateIndex<StoredU32>,
pub height_to_subsidy: LazyVecFrom2<Height, Sats, Height, StoredU32, Height, Sats>,
pub height_to_fee: LazyVecFrom2<Height, Sats, Height, StoredU32, Height, Sats>,
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<StoredF32, StoredU32, StoredU32>,
pub indexes_to_1d_dominance: LazyVecsFrom2FromHeight<StoredF32, StoredU32, StoredU32>,
pub indexes_to_1w_dominance: LazyVecsFrom2FromDateIndex<StoredF32, StoredU32, StoredU32>,
@@ -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::<MaskSats>(
&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::<MaskSats>(
&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::<PercentageU32F32>(
&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::<PercentageU32F32>(
&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<Height, PoolSlug>,
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| {

View File

@@ -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));
}

View File

@@ -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));

View File

@@ -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));
}

View File

@@ -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<T | null>} data
* @property {Signal<boolean>} loading
* @property {Signal<Error | null>} error
* @property {(...args: any[]) => Promise<T | null>} fetch
*/
/**
* @template T
* @typedef {Object} RangeState
* @property {Signal<T[] | null>} data
* @property {Signal<boolean>} loading
*/
/**
* @template T
* @typedef {Object} MetricResource
* @property {string} path
* @property {(from?: number, to?: number) => RangeState<T>} range
* @property {(from?: number, to?: number) => Promise<T[] | null>} fetch
*/
/**
* @typedef {ReturnType<typeof createResources>} Resources
* @typedef {ReturnType<Resources["metrics"]["getOrCreate"]>} 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<T>} fetcher
* @returns {Resource<T>}
*/
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<string, {loading: boolean, at: Date | null, data: Signal<T[] | null>}>} */ (
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<string, NonNullable<ReturnType<typeof createMetricResource>>>} */
const map = new Map();
/**
* Create a reactive resource wrapper for a MetricNode with multi-range support
* @template T
* @param {MetricNode<T>} node
* @returns {MetricResource<T>}
*/
function useMetricNode(node) {
return signals.runWithOwner(owner, () => {
/** @type {Map<string, RangeState<T>>} */
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<T>} */ (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 };
}

View File

@@ -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}`);