#![allow(dead_code)]
use std::collections::VecDeque;
pub struct SimpleMovingAverage {
window: VecDeque<f64>,
size: usize,
sum: f64,
}
pub fn new_sma(window_size: usize) -> SimpleMovingAverage {
SimpleMovingAverage {
window: VecDeque::new(),
size: window_size.max(1),
sum: 0.0,
}
}
impl SimpleMovingAverage {
pub fn push(&mut self, x: f64) -> f64 {
self.window.push_back(x);
self.sum += x;
if self.window.len() > self.size {
self.sum -= self.window.pop_front().unwrap_or(0.0);
}
self.current()
}
pub fn current(&self) -> f64 {
if self.window.is_empty() {
0.0
} else {
self.sum / self.window.len() as f64
}
}
pub fn len(&self) -> usize {
self.window.len()
}
pub fn is_empty(&self) -> bool {
self.window.is_empty()
}
pub fn is_full(&self) -> bool {
self.window.len() == self.size
}
pub fn reset(&mut self) {
self.window.clear();
self.sum = 0.0;
}
}
pub struct ExponentialMovingAverage {
alpha: f64,
value: Option<f64>,
}
pub fn new_ema(alpha: f64) -> ExponentialMovingAverage {
let alpha = alpha.clamp(1e-6, 1.0);
ExponentialMovingAverage { alpha, value: None }
}
impl ExponentialMovingAverage {
pub fn push(&mut self, x: f64) -> f64 {
self.value = Some(match self.value {
None => x,
Some(prev) => self.alpha * x + (1.0 - self.alpha) * prev,
});
self.value.unwrap_or_default()
}
pub fn current(&self) -> Option<f64> {
self.value
}
pub fn alpha(&self) -> f64 {
self.alpha
}
pub fn reset(&mut self) {
self.value = None;
}
}
pub struct WeightedMovingAverage {
window: VecDeque<f64>,
size: usize,
}
pub fn new_wma(window_size: usize) -> WeightedMovingAverage {
WeightedMovingAverage {
window: VecDeque::new(),
size: window_size.max(1),
}
}
impl WeightedMovingAverage {
pub fn push(&mut self, x: f64) -> f64 {
self.window.push_back(x);
if self.window.len() > self.size {
self.window.pop_front();
}
self.current()
}
pub fn current(&self) -> f64 {
let n = self.window.len();
if n == 0 {
return 0.0;
}
let denom = (n * (n + 1)) as f64 / 2.0;
self.window
.iter()
.enumerate()
.map(|(i, &v)| v * (i + 1) as f64)
.sum::<f64>()
/ denom
}
pub fn reset(&mut self) {
self.window.clear();
}
}
pub fn apply_sma(data: &[f64], window_size: usize) -> Vec<f64> {
let mut sma = new_sma(window_size);
data.iter().map(|&x| sma.push(x)).collect()
}
pub fn apply_ema(data: &[f64], alpha: f64) -> Vec<f64> {
let mut ema = new_ema(alpha);
data.iter().map(|&x| ema.push(x)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sma_constant() {
let mut sma = new_sma(3);
sma.push(5.0);
sma.push(5.0);
let v = sma.push(5.0);
assert!((v - 5.0).abs() < 1e-12);
}
#[test]
fn test_sma_window_full() {
let mut sma = new_sma(3);
sma.push(1.0);
sma.push(2.0);
assert!(!sma.is_full());
sma.push(3.0);
assert!(sma.is_full());
}
#[test]
fn test_sma_sliding() {
let mut sma = new_sma(2);
sma.push(10.0);
sma.push(20.0);
let v = sma.push(30.0);
assert!((v - 25.0).abs() < 1e-12, "v={v}");
}
#[test]
fn test_ema_first_value() {
let mut ema = new_ema(0.5);
let v = ema.push(10.0);
assert!((v - 10.0).abs() < 1e-12);
}
#[test]
fn test_ema_smoothing() {
let mut ema = new_ema(0.1);
for _ in 0..100 {
ema.push(0.0);
}
let v = ema.push(100.0);
assert!(v < 20.0, "v={v}");
}
#[test]
fn test_wma_current() {
let mut wma = new_wma(3);
wma.push(1.0);
wma.push(2.0);
let v = wma.push(3.0);
assert!((v - 14.0 / 6.0).abs() < 1e-10, "v={v}");
}
#[test]
fn test_apply_sma_length() {
let data: Vec<f64> = (0..10).map(|i| i as f64).collect();
let out = apply_sma(&data, 3);
assert_eq!(out.len(), 10);
}
#[test]
fn test_apply_ema_length() {
let data = vec![1.0f64; 5];
assert_eq!(apply_ema(&data, 0.3).len(), 5);
}
#[test]
fn test_sma_reset() {
let mut sma = new_sma(3);
sma.push(1.0);
sma.reset();
assert_eq!(sma.len(), 0);
}
}