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}