pub(crate) fn ema_raw(data: &[f64], period: usize) -> Vec<f64> {
if period == 0 || data.is_empty() || data.len() < period {
return Vec::new();
}
let multiplier = 2.0 / (period as f64 + 1.0);
let initial: f64 = data[..period].iter().sum::<f64>() / period as f64;
let mut result = Vec::with_capacity(data.len() - period + 1);
result.push(initial);
let mut prev = initial;
for &price in &data[period..] {
let val = (price - prev) * multiplier + prev;
result.push(val);
prev = val;
}
result
}
pub fn ema(data: &[f64], period: usize) -> Vec<Option<f64>> {
if period == 0 || data.is_empty() || data.len() < period {
return vec![None; data.len()];
}
let multiplier = 2.0 / (period as f64 + 1.0);
let mut result = Vec::with_capacity(data.len());
let initial: f64 = data[..period].iter().sum::<f64>() / period as f64;
result.extend(std::iter::repeat_n(None, period - 1));
result.push(Some(initial));
let mut prev = initial;
for &price in &data[period..] {
let val = (price - prev) * multiplier + prev;
result.push(Some(val));
prev = val;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ema_basic() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let result = ema(&data, 3);
assert_eq!(result.len(), 5);
assert_eq!(result[0], None);
assert_eq!(result[1], None);
assert!(result[2].is_some());
assert!(result[3].is_some());
assert!(result[4].is_some());
}
#[test]
fn test_ema_period_1() {
let data = vec![10.0, 20.0, 30.0];
let result = ema(&data, 1);
assert_eq!(result[0], Some(10.0));
assert_eq!(result[1], Some(20.0));
assert_eq!(result[2], Some(30.0));
}
#[test]
fn test_ema_insufficient_data() {
let data = vec![1.0, 2.0];
let result = ema(&data, 5);
assert_eq!(result.len(), 2);
assert_eq!(result[0], None);
assert_eq!(result[1], None);
}
}