indexes_rs/v1/ema/
main.rs

1//! # Exponential Moving Average (EMA) Module
2//!
3//! This module implements an Exponential Moving Average (EMA) indicator.
4//! The EMA applies a smoothing factor to give more weight to recent prices.
5//! The smoothing factor (alpha) is computed as:
6//!
7//! \[\alpha = \frac{2}{\text{period} + 1}\]
8//!
9//! On the first data point, the EMA is simply set to the price. On subsequent calls,
10//! the EMA is updated using the formula:
11//!
12//! \[\text{EMA}_{\text{new}} = \text{price} \times \alpha + \text{EMA}_{\text{prev}} \times (1 - \alpha)\]
13//!
14//! # Examples
15//!
16//! ```rust
17//! use indexes_rs::v1::ema::main::ExponentialMovingAverage;
18//!
19//! // Create an EMA indicator with a period of 10.
20//! let mut ema = ExponentialMovingAverage::new(10);
21//!
22//! // The first value sets the EMA to the price itself.
23//! assert_eq!(ema.add_value(100.0).unwrap(), 100.0);
24//!
25//! // Subsequent values update the EMA using the smoothing factor.
26//! let second = ema.add_value(105.0).unwrap();
27//! println!("Updated EMA: {:.2}", second);
28//!
29//! // Get the current EMA value.
30//! assert_eq!(ema.get_current_value().unwrap(), second);
31//! ```
32
33/// An Exponential Moving Average (EMA) indicator.
34pub struct ExponentialMovingAverage {
35    /// The smoothing factor (alpha).
36    pub alpha: f64,
37    /// The current EMA value.
38    pub current_ema: Option<f64>,
39}
40
41impl ExponentialMovingAverage {
42    /// Creates a new `ExponentialMovingAverage` indicator with the specified period.
43    ///
44    /// # Arguments
45    ///
46    /// * `period` - The number of periods for the EMA calculation.
47    ///
48    /// # Examples
49    ///
50    /// ```rust
51    /// use indexes_rs::v1::ema::main::ExponentialMovingAverage;
52    ///
53    /// let ema = ExponentialMovingAverage::new(10);
54    /// ```
55    pub fn new(period: usize) -> Self {
56        ExponentialMovingAverage {
57            alpha: 2.0 / (period as f64 + 1.0),
58            current_ema: None,
59        }
60    }
61
62    /// Adds a new price value to update the EMA.
63    ///
64    /// If no previous EMA exists, the current price is used as the initial EMA.
65    /// Otherwise, the EMA is updated using:
66    ///
67    /// \[\text{EMA}_{\text{new}} = \text{price} \times \alpha + \text{EMA}_{\text{prev}} \times (1 - \alpha)\]
68    ///
69    /// # Arguments
70    ///
71    /// * `price` - The latest price.
72    ///
73    /// # Returns
74    ///
75    /// * `Some(f64)` containing the updated EMA value.
76    ///
77    /// # Examples
78    ///
79    /// ```rust
80    /// use indexes_rs::v1::ema::main::ExponentialMovingAverage;
81    ///
82    /// let mut ema = ExponentialMovingAverage::new(10);
83    /// let first = ema.add_value(100.0).unwrap();
84    /// // first EMA is equal to 100.0
85    /// assert_eq!(first, 100.0);
86    /// let second = ema.add_value(105.0).unwrap();
87    /// println!("Updated EMA: {:.2}", second);
88    /// ```
89    pub fn add_value(&mut self, price: f64) -> Option<f64> {
90        self.current_ema = Some(match self.current_ema {
91            Some(ema) => price * self.alpha + ema * (1.0 - self.alpha),
92            None => price,
93        });
94        self.current_ema
95    }
96
97    /// Returns the current EMA value.
98    ///
99    /// # Returns
100    ///
101    /// * `Some(f64)` if the EMA has been computed.
102    /// * `None` if no values have been added yet.
103    ///
104    /// # Examples
105    ///
106    /// ```rust
107    /// use indexes_rs::v1::ema::main::ExponentialMovingAverage;
108    ///
109    /// let mut ema = ExponentialMovingAverage::new(10);
110    /// ema.add_value(100.0);
111    /// assert_eq!(ema.get_current_value().unwrap(), 100.0);
112    /// ```
113    pub fn get_current_value(&self) -> Option<f64> {
114        self.current_ema
115    }
116}