2026-03-23 23:34:28 +05:30
|
|
|
//! Statistic functions.
|
|
|
|
|
|
2026-03-30 12:45:52 +05:30
|
|
|
/// Compute the rolling population standard deviation, scaled by `nbdev`.
|
|
|
|
|
///
|
|
|
|
|
/// Uses population variance (`ddof = 0`). Returns `nbdev * stddev` for
|
|
|
|
|
/// each window. The first `timeperiod - 1` values are `NaN`.
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
/// * `real` - Input series.
|
|
|
|
|
/// * `timeperiod` - Rolling window size (must be >= 1).
|
|
|
|
|
/// * `nbdev` - Multiplier applied to the standard deviation (use 1.0 for raw stddev).
|
2026-03-23 23:34:28 +05:30
|
|
|
pub fn stddev(real: &[f64], timeperiod: usize, nbdev: f64) -> Vec<f64> {
|
|
|
|
|
let n = real.len();
|
|
|
|
|
let mut result = vec![f64::NAN; n];
|
|
|
|
|
if timeperiod < 1 || n < timeperiod {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
for i in (timeperiod - 1)..n {
|
|
|
|
|
let window = &real[i + 1 - timeperiod..=i];
|
|
|
|
|
let mean: f64 = window.iter().sum::<f64>() / timeperiod as f64;
|
|
|
|
|
let var: f64 = window.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / timeperiod as f64;
|
|
|
|
|
result[i] = var.sqrt() * nbdev;
|
|
|
|
|
}
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn stddev_constant() {
|
|
|
|
|
let prices = vec![5.0; 5];
|
|
|
|
|
let result = stddev(&prices, 3, 1.0);
|
|
|
|
|
for v in result.iter().filter(|v| !v.is_nan()) {
|
|
|
|
|
assert!(v.abs() < 1e-10);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|