mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-25 01:38:12 -07:00
global: snapshot
This commit is contained in:
@@ -89,6 +89,17 @@ where
|
||||
});
|
||||
|
||||
let start = index.to_usize();
|
||||
|
||||
// Truncate all vecs to start once, so the loop only pushes
|
||||
macro_rules! truncate_vec {
|
||||
($($vec:ident),*) => {
|
||||
$(if let Some(ref mut v) = $vec {
|
||||
v.truncate_if_needed_at(start)?;
|
||||
})*
|
||||
};
|
||||
}
|
||||
truncate_vec!(first, last, min, max, average, sum, cumulative, median, pct10, pct25, pct75, pct90);
|
||||
|
||||
let fi_len = first_indexes.len();
|
||||
let first_indexes_batch: Vec<A> = first_indexes.collect_range_at(start, fi_len);
|
||||
let count_indexes_batch: Vec<StoredU64> = count_indexes.collect_range_at(start, fi_len);
|
||||
@@ -97,8 +108,7 @@ where
|
||||
.into_iter()
|
||||
.zip(count_indexes_batch)
|
||||
.enumerate()
|
||||
.try_for_each(|(j, (first_index, count_index))| -> Result<()> {
|
||||
let idx = start + j;
|
||||
.try_for_each(|(_, (first_index, count_index))| -> Result<()> {
|
||||
let count = u64::from(count_index) as usize;
|
||||
|
||||
// Effective count after skipping (e.g., skip coinbase for fee calculations)
|
||||
@@ -113,17 +123,15 @@ where
|
||||
} else {
|
||||
T::from(0_usize)
|
||||
};
|
||||
first_vec.truncate_push_at(idx, f)?;
|
||||
first_vec.push(f);
|
||||
}
|
||||
|
||||
if let Some(ref mut last_vec) = last {
|
||||
if effective_count == 0 {
|
||||
// If all items skipped, use zero
|
||||
last_vec.truncate_push_at(idx, T::from(0_usize))?;
|
||||
last_vec.push(T::from(0_usize));
|
||||
} else {
|
||||
let last_index = first_index + (count - 1);
|
||||
let v = source.collect_one_at(last_index.to_usize()).unwrap();
|
||||
last_vec.truncate_push_at(idx, v)?;
|
||||
last_vec.push(source.collect_one_at(last_index.to_usize()).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,12 +151,10 @@ where
|
||||
});
|
||||
|
||||
if let Some(ref mut min_vec) = min {
|
||||
let v = min_val.or(max_val).unwrap_or_else(|| T::from(0_usize));
|
||||
min_vec.truncate_push_at(idx, v)?;
|
||||
min_vec.push(min_val.or(max_val).unwrap_or_else(|| T::from(0_usize)));
|
||||
}
|
||||
if let Some(ref mut max_vec) = max {
|
||||
let v = max_val.or(min_val).unwrap_or_else(|| T::from(0_usize));
|
||||
max_vec.truncate_push_at(idx, v)?;
|
||||
max_vec.push(max_val.or(min_val).unwrap_or_else(|| T::from(0_usize)));
|
||||
}
|
||||
} else if needs_percentiles || needs_minmax {
|
||||
let mut values: Vec<T> = source.collect_range_at(
|
||||
@@ -157,21 +163,18 @@ where
|
||||
);
|
||||
|
||||
if values.is_empty() {
|
||||
// Handle edge case where all items were skipped
|
||||
macro_rules! push_zero {
|
||||
($($vec:ident),*) => {
|
||||
$(if let Some(ref mut v) = $vec {
|
||||
v.truncate_push_at(idx, T::from(0_usize))?;
|
||||
v.push(T::from(0_usize));
|
||||
})*
|
||||
};
|
||||
}
|
||||
push_zero!(max, pct90, pct75, median, pct25, pct10, min, average, sum);
|
||||
if let Some(ref mut cumulative_vec) = cumulative {
|
||||
let t = cumulative_val.unwrap();
|
||||
cumulative_vec.truncate_push_at(idx, t)?;
|
||||
cumulative_vec.push(cumulative_val.unwrap());
|
||||
}
|
||||
} else if needs_percentiles {
|
||||
// Compute aggregates from unsorted values first to avoid clone
|
||||
let aggregate_result = if needs_aggregates {
|
||||
let len = values.len();
|
||||
let sum_val = values.iter().copied().fold(T::from(0), |a, b| a + b);
|
||||
@@ -180,53 +183,52 @@ where
|
||||
None
|
||||
};
|
||||
|
||||
// Sort in-place — no clone needed
|
||||
values.sort_unstable();
|
||||
|
||||
if let Some(ref mut max_vec) = max {
|
||||
max_vec.truncate_push_at(idx, *values.last().unwrap())?;
|
||||
max_vec.push(*values.last().unwrap());
|
||||
}
|
||||
if let Some(ref mut pct90_vec) = pct90 {
|
||||
pct90_vec.truncate_push_at(idx, get_percentile(&values, 0.90))?;
|
||||
pct90_vec.push(get_percentile(&values, 0.90));
|
||||
}
|
||||
if let Some(ref mut pct75_vec) = pct75 {
|
||||
pct75_vec.truncate_push_at(idx, get_percentile(&values, 0.75))?;
|
||||
pct75_vec.push(get_percentile(&values, 0.75));
|
||||
}
|
||||
if let Some(ref mut median_vec) = median {
|
||||
median_vec.truncate_push_at(idx, get_percentile(&values, 0.50))?;
|
||||
median_vec.push(get_percentile(&values, 0.50));
|
||||
}
|
||||
if let Some(ref mut pct25_vec) = pct25 {
|
||||
pct25_vec.truncate_push_at(idx, get_percentile(&values, 0.25))?;
|
||||
pct25_vec.push(get_percentile(&values, 0.25));
|
||||
}
|
||||
if let Some(ref mut pct10_vec) = pct10 {
|
||||
pct10_vec.truncate_push_at(idx, get_percentile(&values, 0.10))?;
|
||||
pct10_vec.push(get_percentile(&values, 0.10));
|
||||
}
|
||||
if let Some(ref mut min_vec) = min {
|
||||
min_vec.truncate_push_at(idx, *values.first().unwrap())?;
|
||||
min_vec.push(*values.first().unwrap());
|
||||
}
|
||||
|
||||
if let Some((len, sum_val)) = aggregate_result {
|
||||
if let Some(ref mut average_vec) = average {
|
||||
average_vec.truncate_push_at(idx, sum_val / len)?;
|
||||
average_vec.push(sum_val / len);
|
||||
}
|
||||
|
||||
if needs_sum_or_cumulative {
|
||||
if let Some(ref mut sum_vec) = sum {
|
||||
sum_vec.truncate_push_at(idx, sum_val)?;
|
||||
sum_vec.push(sum_val);
|
||||
}
|
||||
if let Some(ref mut cumulative_vec) = cumulative {
|
||||
let t = cumulative_val.unwrap() + sum_val;
|
||||
cumulative_val.replace(t);
|
||||
cumulative_vec.truncate_push_at(idx, t)?;
|
||||
cumulative_vec.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if needs_minmax {
|
||||
if let Some(ref mut min_vec) = min {
|
||||
min_vec.truncate_push_at(idx, *values.iter().min().unwrap())?;
|
||||
min_vec.push(*values.iter().min().unwrap());
|
||||
}
|
||||
if let Some(ref mut max_vec) = max {
|
||||
max_vec.truncate_push_at(idx, *values.iter().max().unwrap())?;
|
||||
max_vec.push(*values.iter().max().unwrap());
|
||||
}
|
||||
|
||||
if needs_aggregates {
|
||||
@@ -234,23 +236,22 @@ where
|
||||
let sum_val = values.into_iter().fold(T::from(0), |a, b| a + b);
|
||||
|
||||
if let Some(ref mut average_vec) = average {
|
||||
average_vec.truncate_push_at(idx, sum_val / len)?;
|
||||
average_vec.push(sum_val / len);
|
||||
}
|
||||
|
||||
if needs_sum_or_cumulative {
|
||||
if let Some(ref mut sum_vec) = sum {
|
||||
sum_vec.truncate_push_at(idx, sum_val)?;
|
||||
sum_vec.push(sum_val);
|
||||
}
|
||||
if let Some(ref mut cumulative_vec) = cumulative {
|
||||
let t = cumulative_val.unwrap() + sum_val;
|
||||
cumulative_val.replace(t);
|
||||
cumulative_vec.truncate_push_at(idx, t)?;
|
||||
cumulative_vec.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if needs_aggregates {
|
||||
// Aggregates only (sum/average/cumulative) — no Vec allocation needed
|
||||
let efi = effective_first_index.to_usize();
|
||||
let (sum_val, len) = source.fold_range_at(
|
||||
efi,
|
||||
@@ -265,17 +266,17 @@ where
|
||||
} else {
|
||||
T::from(0_usize)
|
||||
};
|
||||
average_vec.truncate_push_at(idx, avg)?;
|
||||
average_vec.push(avg);
|
||||
}
|
||||
|
||||
if needs_sum_or_cumulative {
|
||||
if let Some(ref mut sum_vec) = sum {
|
||||
sum_vec.truncate_push_at(idx, sum_val)?;
|
||||
sum_vec.push(sum_val);
|
||||
}
|
||||
if let Some(ref mut cumulative_vec) = cumulative {
|
||||
let t = cumulative_val.unwrap() + sum_val;
|
||||
cumulative_val.replace(t);
|
||||
cumulative_vec.truncate_push_at(idx, t)?;
|
||||
cumulative_vec.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,6 +349,19 @@ where
|
||||
let zero = T::from(0_usize);
|
||||
let mut values: Vec<T> = Vec::new();
|
||||
|
||||
for vec in [
|
||||
&mut *min,
|
||||
&mut *max,
|
||||
&mut *average,
|
||||
&mut *median,
|
||||
&mut *pct10,
|
||||
&mut *pct25,
|
||||
&mut *pct75,
|
||||
&mut *pct90,
|
||||
] {
|
||||
vec.truncate_if_needed_at(start)?;
|
||||
}
|
||||
|
||||
count_indexes_batch
|
||||
.iter()
|
||||
.enumerate()
|
||||
@@ -378,7 +392,7 @@ where
|
||||
&mut *pct75,
|
||||
&mut *pct90,
|
||||
] {
|
||||
vec.truncate_push_at(idx, zero)?;
|
||||
vec.push(zero);
|
||||
}
|
||||
} else {
|
||||
source.collect_range_into_at(range_start_usize, range_end_usize, &mut values);
|
||||
@@ -390,14 +404,14 @@ where
|
||||
|
||||
values.sort_unstable();
|
||||
|
||||
max.truncate_push_at(idx, *values.last().unwrap())?;
|
||||
pct90.truncate_push_at(idx, get_percentile(&values, 0.90))?;
|
||||
pct75.truncate_push_at(idx, get_percentile(&values, 0.75))?;
|
||||
median.truncate_push_at(idx, get_percentile(&values, 0.50))?;
|
||||
pct25.truncate_push_at(idx, get_percentile(&values, 0.25))?;
|
||||
pct10.truncate_push_at(idx, get_percentile(&values, 0.10))?;
|
||||
min.truncate_push_at(idx, *values.first().unwrap())?;
|
||||
average.truncate_push_at(idx, avg)?;
|
||||
max.push(*values.last().unwrap());
|
||||
pct90.push(get_percentile(&values, 0.90));
|
||||
pct75.push(get_percentile(&values, 0.75));
|
||||
median.push(get_percentile(&values, 0.50));
|
||||
pct25.push(get_percentile(&values, 0.25));
|
||||
pct10.push(get_percentile(&values, 0.10));
|
||||
min.push(*values.first().unwrap());
|
||||
average.push(avg);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -107,9 +107,21 @@ where
|
||||
|
||||
let starts_batch = window_starts.collect_range_at(skip, end);
|
||||
|
||||
for v in [
|
||||
&mut *average_out,
|
||||
&mut *min_out,
|
||||
&mut *max_out,
|
||||
&mut *p10_out,
|
||||
&mut *p25_out,
|
||||
&mut *median_out,
|
||||
&mut *p75_out,
|
||||
&mut *p90_out,
|
||||
] {
|
||||
v.truncate_if_needed_at(skip)?;
|
||||
}
|
||||
|
||||
for (j, start) in starts_batch.into_iter().enumerate() {
|
||||
let i = skip + j;
|
||||
let v = partial_values[i - range_start];
|
||||
let v = partial_values[skip + j - range_start];
|
||||
let start_usize = start.to_usize();
|
||||
window.advance(v, start_usize, partial_values, range_start);
|
||||
|
||||
@@ -125,19 +137,19 @@ where
|
||||
&mut *p75_out,
|
||||
&mut *p90_out,
|
||||
] {
|
||||
v.truncate_push_at(i, zero)?;
|
||||
v.push(zero);
|
||||
}
|
||||
} else {
|
||||
average_out.truncate_push_at(i, T::from(window.average()))?;
|
||||
min_out.truncate_push_at(i, T::from(window.min()))?;
|
||||
max_out.truncate_push_at(i, T::from(window.max()))?;
|
||||
average_out.push(T::from(window.average()));
|
||||
min_out.push(T::from(window.min()));
|
||||
max_out.push(T::from(window.max()));
|
||||
let [p10, p25, p50, p75, p90] =
|
||||
window.percentiles(&[0.10, 0.25, 0.50, 0.75, 0.90]);
|
||||
p10_out.truncate_push_at(i, T::from(p10))?;
|
||||
p25_out.truncate_push_at(i, T::from(p25))?;
|
||||
median_out.truncate_push_at(i, T::from(p50))?;
|
||||
p75_out.truncate_push_at(i, T::from(p75))?;
|
||||
p90_out.truncate_push_at(i, T::from(p90))?;
|
||||
p10_out.push(T::from(p10));
|
||||
p25_out.push(T::from(p25));
|
||||
median_out.push(T::from(p50));
|
||||
p75_out.push(T::from(p75));
|
||||
p90_out.push(T::from(p90));
|
||||
}
|
||||
|
||||
if average_out.batch_limit_reached() {
|
||||
|
||||
@@ -35,6 +35,7 @@ impl RollingDistributionSlot {
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::{Traversable, TreeNode};
|
||||
use brk_types::{Cents, Height, Version};
|
||||
use brk_types::{Cents, Version};
|
||||
use vecdb::{AnyExportableVec, Database, ReadOnlyClone, Ro, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::indexes;
|
||||
@@ -38,16 +38,12 @@ impl PercentilesVecs {
|
||||
Ok(Self { vecs })
|
||||
}
|
||||
|
||||
/// Push percentile prices at this height (in cents).
|
||||
pub(crate) fn truncate_push(
|
||||
&mut self,
|
||||
height: Height,
|
||||
percentile_prices: &[Cents; PERCENTILES_LEN],
|
||||
) -> Result<()> {
|
||||
/// Push percentile prices (in cents).
|
||||
#[inline(always)]
|
||||
pub(crate) fn push(&mut self, percentile_prices: &[Cents; PERCENTILES_LEN]) {
|
||||
for (i, v) in self.vecs.iter_mut().enumerate() {
|
||||
v.cents.height.truncate_push(height, percentile_prices[i])?;
|
||||
v.cents.height.push(percentile_prices[i]);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate computed versions or reset if mismatched.
|
||||
|
||||
@@ -46,12 +46,7 @@ impl RatioPerBlockPercentiles {
|
||||
|
||||
macro_rules! import_ratio {
|
||||
($suffix:expr) => {
|
||||
RatioPerBlock::forced_import_raw(
|
||||
db,
|
||||
&format!("{name}_{}", $suffix),
|
||||
v,
|
||||
indexes,
|
||||
)?
|
||||
RatioPerBlock::forced_import_raw(db, &format!("{name}_{}", $suffix), v, indexes)?
|
||||
};
|
||||
}
|
||||
|
||||
@@ -126,12 +121,15 @@ impl RatioPerBlockPercentiles {
|
||||
const PCTS: [f64; 6] = [0.01, 0.02, 0.05, 0.95, 0.98, 0.99];
|
||||
let mut out = [0u32; 6];
|
||||
|
||||
for (offset, &ratio) in new_ratios.iter().enumerate() {
|
||||
for vec in pct_vecs.iter_mut() {
|
||||
vec.truncate_if_needed_at(start)?;
|
||||
}
|
||||
|
||||
for &ratio in new_ratios.iter() {
|
||||
self.expanding_pct.add(*ratio);
|
||||
self.expanding_pct.quantiles(&PCTS, &mut out);
|
||||
let idx = start + offset;
|
||||
for (vec, &val) in pct_vecs.iter_mut().zip(out.iter()) {
|
||||
vec.truncate_push_at(idx, BasisPoints32::from(val))?;
|
||||
vec.push(BasisPoints32::from(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@ where
|
||||
let cached = cached_start.clone();
|
||||
let starts_version = cached.version();
|
||||
|
||||
// Change: source[h] - source[ago] as C (via f64)
|
||||
// Absolute change: source[h] - source[ago] as C (via f64)
|
||||
let change_vec = LazyDeltaVec::<Height, S, C, DeltaChange>::new(
|
||||
&format!("{full_name}_change"),
|
||||
&full_name,
|
||||
version,
|
||||
src.clone(),
|
||||
starts_version,
|
||||
@@ -91,7 +91,7 @@ where
|
||||
},
|
||||
);
|
||||
let change_resolutions = Resolutions::forced_import(
|
||||
&format!("{full_name}_change"),
|
||||
&full_name,
|
||||
change_vec.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
@@ -102,15 +102,16 @@ where
|
||||
};
|
||||
|
||||
// Rate BPS: (source[h] - source[ago]) / source[ago] as B (via f64)
|
||||
let rate_bps_name = format!("{full_name}_rate_bps");
|
||||
let rate_vec = LazyDeltaVec::<Height, S, B, DeltaRate>::new(
|
||||
&format!("{full_name}_rate_bps"),
|
||||
&rate_bps_name,
|
||||
version,
|
||||
src.clone(),
|
||||
starts_version,
|
||||
move || cached.get(),
|
||||
);
|
||||
let rate_resolutions = Resolutions::forced_import(
|
||||
&format!("{full_name}_rate_bps"),
|
||||
&rate_bps_name,
|
||||
rate_vec.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
@@ -121,28 +122,30 @@ where
|
||||
};
|
||||
|
||||
// Ratio: bps / 10000
|
||||
let rate_ratio_name = format!("{full_name}_rate_ratio");
|
||||
let ratio = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToRatio>(
|
||||
&format!("{full_name}_rate_ratio"),
|
||||
&rate_ratio_name,
|
||||
version,
|
||||
bps.height.read_only_boxed_clone(),
|
||||
),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<B::ToRatio>(
|
||||
&format!("{full_name}_rate_ratio"),
|
||||
&rate_ratio_name,
|
||||
version,
|
||||
&bps.resolutions,
|
||||
)),
|
||||
};
|
||||
|
||||
// Percent: bps / 100
|
||||
let rate_name = format!("{full_name}_rate");
|
||||
let percent = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToPercent>(
|
||||
&format!("{full_name}_rate"),
|
||||
&rate_name,
|
||||
version,
|
||||
bps.height.read_only_boxed_clone(),
|
||||
),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<B::ToPercent>(
|
||||
&format!("{full_name}_rate"),
|
||||
&rate_name,
|
||||
version,
|
||||
&bps.resolutions,
|
||||
)),
|
||||
@@ -214,9 +217,10 @@ where
|
||||
let cached = cached_start.clone();
|
||||
let starts_version = cached.version();
|
||||
|
||||
// Change cents: source[h] - source[ago] as C (via f64)
|
||||
// Absolute change (cents): source[h] - source[ago] as C (via f64)
|
||||
let cents_name = format!("{full_name}_cents");
|
||||
let change_vec = LazyDeltaVec::<Height, S, C, DeltaChange>::new(
|
||||
&format!("{full_name}_change"),
|
||||
¢s_name,
|
||||
version,
|
||||
src.clone(),
|
||||
starts_version,
|
||||
@@ -226,7 +230,7 @@ where
|
||||
},
|
||||
);
|
||||
let change_resolutions = Resolutions::forced_import(
|
||||
&format!("{full_name}_change"),
|
||||
¢s_name,
|
||||
change_vec.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
@@ -236,15 +240,15 @@ where
|
||||
resolutions: Box::new(change_resolutions),
|
||||
};
|
||||
|
||||
// Change USD: lazy from cents delta
|
||||
// Absolute change (usd): lazy from cents delta
|
||||
let usd = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<C::ToDollars>(
|
||||
&format!("{full_name}_change_usd"),
|
||||
&full_name,
|
||||
version,
|
||||
cents.height.read_only_boxed_clone(),
|
||||
),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<C::ToDollars>(
|
||||
&format!("{full_name}_change_usd"),
|
||||
&full_name,
|
||||
version,
|
||||
¢s.resolutions,
|
||||
)),
|
||||
@@ -253,15 +257,16 @@ where
|
||||
let absolute = LazyDeltaFiatFromHeight { usd, cents };
|
||||
|
||||
// Rate BPS: (source[h] - source[ago]) / source[ago] as B (via f64)
|
||||
let rate_bps_name = format!("{full_name}_rate_bps");
|
||||
let rate_vec = LazyDeltaVec::<Height, S, B, DeltaRate>::new(
|
||||
&format!("{full_name}_rate_bps"),
|
||||
&rate_bps_name,
|
||||
version,
|
||||
src.clone(),
|
||||
starts_version,
|
||||
move || cached.get(),
|
||||
);
|
||||
let rate_resolutions = Resolutions::forced_import(
|
||||
&format!("{full_name}_rate_bps"),
|
||||
&rate_bps_name,
|
||||
rate_vec.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
@@ -271,27 +276,29 @@ where
|
||||
resolutions: Box::new(rate_resolutions),
|
||||
};
|
||||
|
||||
let rate_ratio_name = format!("{full_name}_rate_ratio");
|
||||
let ratio = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToRatio>(
|
||||
&format!("{full_name}_rate_ratio"),
|
||||
&rate_ratio_name,
|
||||
version,
|
||||
bps.height.read_only_boxed_clone(),
|
||||
),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<B::ToRatio>(
|
||||
&format!("{full_name}_rate_ratio"),
|
||||
&rate_ratio_name,
|
||||
version,
|
||||
&bps.resolutions,
|
||||
)),
|
||||
};
|
||||
|
||||
let rate_name = format!("{full_name}_rate");
|
||||
let percent = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToPercent>(
|
||||
&format!("{full_name}_rate"),
|
||||
&rate_name,
|
||||
version,
|
||||
bps.height.read_only_boxed_clone(),
|
||||
),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<B::ToPercent>(
|
||||
&format!("{full_name}_rate"),
|
||||
&rate_name,
|
||||
version,
|
||||
&bps.resolutions,
|
||||
)),
|
||||
|
||||
@@ -163,11 +163,11 @@ impl StdDevPerBlockExtended {
|
||||
0.5, 1.0, 1.5, 2.0, 2.5, 3.0, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0,
|
||||
];
|
||||
for (vec, mult) in self.mut_band_height_vecs().zip(MULTIPLIERS) {
|
||||
vec.truncate_if_needed_at(start)?;
|
||||
for (offset, _) in source_data.iter().enumerate() {
|
||||
let index = start + offset;
|
||||
let average = sma_data[offset];
|
||||
let sd = sd_data[offset];
|
||||
vec.truncate_push_at(index, average + StoredF32::from(mult * *sd))?;
|
||||
vec.push(average + StoredF32::from(mult * *sd));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user