finlib_ta/indicators/
exponential_moving_average.rs

1use core::fmt;
2
3use crate::errors::{Result, TaError};
4use crate::{Close, Next, Period, Reset};
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8/// An exponential moving average (EMA), also known as an exponentially weighted moving average
9/// (EWMA).
10///
11/// It is a type of infinite impulse response filter that applies weighting factors which decrease exponentially.
12/// The weighting for each older datum decreases exponentially, never reaching zero.
13///
14/// # Formula
15///
16/// ![EMA formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/05d06bdbee2c14031fd91ead6f5f772aec1ec964)
17///
18/// Where:
19///
20/// * _EMA<sub>t</sub>_ - is the value of the EMA at any time period _t_.
21/// * _EMA<sub>t-1</sub>_ - is the value of the EMA at the previous period _t-1_.
22/// * _p<sub>t</sub>_ - is the input value at a time period t.
23/// * _α_ - is the coefficient that represents the degree of weighting decrease, a constant smoothing factor between 0 and 1.
24///
25/// _α_ is calculated with the following formula:
26///
27/// ![alpha formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/d9f6258e152db0644af548972bd6c50a8becf7ee)
28///
29/// Where:
30///
31/// * _period_ - number of periods
32///
33/// # Parameters
34///
35/// * _period_ - number of periods (integer greater than 0)
36///
37/// # Example
38///
39/// ```
40/// use finlib_ta::indicators::ExponentialMovingAverage;
41/// use finlib_ta::Next;
42///
43/// let mut ema = ExponentialMovingAverage::new(3).unwrap();
44/// assert_eq!(ema.next(2.0), 2.0);
45/// assert_eq!(ema.next(5.0), 3.5);
46/// assert_eq!(ema.next(1.0), 2.25);
47/// assert_eq!(ema.next(6.25), 4.25);
48/// ```
49///
50/// # Links
51///
52/// * [Exponential moving average, Wikipedia](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average)
53///
54
55#[doc(alias = "EMA")]
56#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
57#[derive(Debug, Clone)]
58pub struct ExponentialMovingAverage {
59    period: usize,
60    k: f64,
61    current: f64,
62    is_new: bool,
63}
64
65impl ExponentialMovingAverage {
66    pub fn new(period: usize) -> Result<Self> {
67        match period {
68            0 => Err(TaError::InvalidParameter),
69            _ => Ok(Self {
70                period,
71                k: 2.0 / (period + 1) as f64,
72                current: 0.0,
73                is_new: true,
74            }),
75        }
76    }
77}
78
79impl Period for ExponentialMovingAverage {
80    fn period(&self) -> usize {
81        self.period
82    }
83}
84
85impl Next<f64> for ExponentialMovingAverage {
86    type Output = f64;
87
88    fn next(&mut self, input: f64) -> Self::Output {
89        if self.is_new {
90            self.is_new = false;
91            self.current = input;
92        } else {
93            self.current = self.k * input + (1.0 - self.k) * self.current;
94        }
95        self.current
96    }
97}
98
99impl<T: Close> Next<&T> for ExponentialMovingAverage {
100    type Output = f64;
101
102    fn next(&mut self, input: &T) -> Self::Output {
103        self.next(input.close())
104    }
105}
106
107impl Reset for ExponentialMovingAverage {
108    fn reset(&mut self) {
109        self.current = 0.0;
110        self.is_new = true;
111    }
112}
113
114impl Default for ExponentialMovingAverage {
115    fn default() -> Self {
116        Self::new(9).unwrap()
117    }
118}
119
120impl fmt::Display for ExponentialMovingAverage {
121    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122        write!(f, "EMA({})", self.period)
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use crate::test_helper::*;
130    use alloc::format;
131
132    test_indicator!(ExponentialMovingAverage);
133
134    #[test]
135    fn test_new() {
136        assert!(ExponentialMovingAverage::new(0).is_err());
137        assert!(ExponentialMovingAverage::new(1).is_ok());
138    }
139
140    #[test]
141    fn test_next() {
142        let mut ema = ExponentialMovingAverage::new(3).unwrap();
143
144        assert_eq!(ema.next(2.0), 2.0);
145        assert_eq!(ema.next(5.0), 3.5);
146        assert_eq!(ema.next(1.0), 2.25);
147        assert_eq!(ema.next(6.25), 4.25);
148
149        let mut ema = ExponentialMovingAverage::new(3).unwrap();
150        let bar1 = Bar::new().close(2);
151        let bar2 = Bar::new().close(5);
152        assert_eq!(ema.next(&bar1), 2.0);
153        assert_eq!(ema.next(&bar2), 3.5);
154    }
155
156    #[test]
157    fn test_reset() {
158        let mut ema = ExponentialMovingAverage::new(5).unwrap();
159
160        assert_eq!(ema.next(4.0), 4.0);
161        ema.next(10.0);
162        ema.next(15.0);
163        ema.next(20.0);
164        assert_ne!(ema.next(4.0), 4.0);
165
166        ema.reset();
167        assert_eq!(ema.next(4.0), 4.0);
168    }
169
170    #[test]
171    fn test_default() {
172        ExponentialMovingAverage::default();
173    }
174
175    #[test]
176    fn test_display() {
177        let ema = ExponentialMovingAverage::new(7).unwrap();
178        assert_eq!(format!("{}", ema), "EMA(7)");
179    }
180}