use super::{Indicator, IncrementalIndicator, IndicatorResult, IndicatorError, PriceData}; /// Exponential Moving Average (EMA) Indicator /// /// Calculates the exponentially weighted moving average /// giving more weight to recent prices pub struct EMA { period: usize, alpha: f64, value: Option, initialized: bool, } impl EMA { pub fn new(period: usize) -> Result { if period == 0 { return Err(IndicatorError::InvalidParameter( "Period must be greater than 0".to_string() )); } // Calculate smoothing factor (alpha) // Common formula: 2 / (period + 1) let alpha = 2.0 / (period as f64 + 1.0); Ok(Self { period, alpha, value: None, initialized: false, }) } /// Create EMA with custom smoothing factor pub fn with_alpha(alpha: f64) -> Result { if alpha <= 0.0 || alpha > 1.0 { return Err(IndicatorError::InvalidParameter( "Alpha must be between 0 and 1".to_string() )); } // Calculate equivalent period for reference let period = ((2.0 / alpha) - 1.0) as usize; Ok(Self { period, alpha, value: None, initialized: false, }) } /// Calculate EMA for a series of values pub fn calculate_series(values: &[f64], period: usize) -> Result, IndicatorError> { if period == 0 { return Err(IndicatorError::InvalidParameter( "Period must be greater than 0".to_string() )); } if values.is_empty() { return Ok(vec![]); } let alpha = 2.0 / (period as f64 + 1.0); let mut result = Vec::with_capacity(values.len()); // Start with first value as initial EMA let mut ema = values[0]; result.push(ema); // Calculate EMA for remaining values for i in 1..values.len() { ema = alpha * values[i] + (1.0 - alpha) * ema; result.push(ema); } Ok(result) } /// Alternative initialization using SMA of first N values pub fn calculate_series_sma_init(values: &[f64], period: usize) -> Result, IndicatorError> { if period == 0 { return Err(IndicatorError::InvalidParameter( "Period must be greater than 0".to_string() )); } if values.len() < period { return Err(IndicatorError::InsufficientData { required: period, actual: values.len(), }); } let alpha = 2.0 / (period as f64 + 1.0); let mut result = Vec::with_capacity(values.len() - period + 1); // Calculate initial SMA let initial_sma: f64 = values[0..period].iter().sum::() / period as f64; let mut ema = initial_sma; result.push(ema); // Calculate EMA for remaining values for i in period..values.len() { ema = alpha * values[i] + (1.0 - alpha) * ema; result.push(ema); } Ok(result) } } impl Indicator for EMA { fn calculate(&mut self, data: &PriceData) -> Result { let values = &data.close; if values.is_empty() { return Err(IndicatorError::InsufficientData { required: 1, actual: 0, }); } // Reset and calculate from scratch self.reset(); let ema_values = Self::calculate_series(values, self.period)?; // Update internal state with last value if let Some(&last) = ema_values.last() { self.value = Some(last); self.initialized = true; } Ok(IndicatorResult::Series(ema_values)) } fn reset(&mut self) { self.value = None; self.initialized = false; } fn is_ready(&self) -> bool { self.initialized && self.value.is_some() } } impl IncrementalIndicator for EMA { fn update(&mut self, value: f64) -> Result, IndicatorError> { match self.value { Some(prev_ema) => { // Update EMA: EMA = α × Price + (1 - α) × Previous EMA let new_ema = self.alpha * value + (1.0 - self.alpha) * prev_ema; self.value = Some(new_ema); self.initialized = true; Ok(Some(new_ema)) } None => { // First value becomes the initial EMA self.value = Some(value); self.initialized = true; Ok(Some(value)) } } } fn current(&self) -> Option { self.value } } #[cfg(test)] mod tests { use super::*; #[test] fn test_ema_calculation() { let values = vec![10.0, 11.0, 12.0, 13.0, 14.0, 15.0]; let result = EMA::calculate_series(&values, 3).unwrap(); assert_eq!(result.len(), 6); assert!((result[0] - 10.0).abs() < 1e-10); // First value // Verify EMA calculation let alpha = 2.0 / 4.0; // 0.5 let expected_ema2 = alpha * 11.0 + (1.0 - alpha) * 10.0; // 10.5 assert!((result[1] - expected_ema2).abs() < 1e-10); } #[test] fn test_incremental_ema() { let mut ema = EMA::new(3).unwrap(); // First value assert_eq!(ema.update(10.0).unwrap(), Some(10.0)); // Second value: EMA = 0.5 * 12 + 0.5 * 10 = 11 assert_eq!(ema.update(12.0).unwrap(), Some(11.0)); // Third value: EMA = 0.5 * 14 + 0.5 * 11 = 12.5 assert_eq!(ema.update(14.0).unwrap(), Some(12.5)); } #[test] fn test_ema_with_sma_init() { let values = vec![2.0, 4.0, 6.0, 8.0, 10.0, 12.0]; let result = EMA::calculate_series_sma_init(&values, 3).unwrap(); // Initial SMA = (2 + 4 + 6) / 3 = 4 assert_eq!(result.len(), 4); assert!((result[0] - 4.0).abs() < 1e-10); } }