ux_indicators/indicators/
macd.rs

1#![allow(dead_code)]
2
3use std::fmt;
4
5use crate::errors::*;
6use crate::indicators::ExponentialMovingAverage as Ema;
7use crate::{Close, Next, Reset};
8
9use crate::{Factory};
10use crate::indicators::SimpleMovingAverage;
11
12pub struct MacdFactory {
13}
14
15impl MacdFactory {
16    pub fn new() -> Self {
17        Self{}
18    }
19}
20
21impl Factory for MacdFactory {
22    fn create() -> Box<dyn Next<f64, Output = Box<[f64]>>> {
23        Box::new(SimpleMovingAverage::default())
24    }
25}
26
27/// Moving average converge divergence (MACD).
28///
29/// The MACD indicator (or "oscillator") is a collection of three time series
30/// calculated from historical price data, most often the closing price.
31/// These three series are:
32///
33/// * The MACD series proper
34/// * The "signal" or "average" series
35/// * The "divergence" series which is the difference between the two
36///
37/// The MACD series is the difference between a "fast" (short period) exponential
38/// moving average (EMA), and a "slow" (longer period) EMA of the price series.
39/// The average series is an EMA of the MACD series itself.
40///
41/// # Formula
42///
43/// # Parameters
44///
45/// * _fast_length_ - length for the fast EMA. Default is 12.
46/// * _slow_length_ - length for the slow EMA. Default is 26.
47/// * _signal_length_ - length for the signal EMA. Default is 9.
48///
49/// # Example
50///
51/// ```
52/// use core::indicators::MovingAverageConvergenceDivergence as Macd;
53/// use core::Next;
54///
55/// let mut macd = Macd::new(3, 6, 4).unwrap();
56///
57/// assert_eq!(round(macd.next(2.0)), (0.0, 0.0, 0.0));
58/// assert_eq!(round(macd.next(3.0)), (0.21, 0.09, 0.13));
59/// assert_eq!(round(macd.next(4.2)), (0.52, 0.26, 0.26));
60/// assert_eq!(round(macd.next(7.0)), (1.15, 0.62, 0.54));
61/// assert_eq!(round(macd.next(6.7)), (1.15, 0.83, 0.32));
62/// assert_eq!(round(macd.next(6.5)), (0.94, 0.87, 0.07));
63///
64/// fn round(nums: (f64, f64, f64)) -> (f64, f64, f64) {
65///     let n0 = (nums.0 * 100.0).round() / 100.0;
66///     let n1 = (nums.1 * 100.0).round() / 100.0;
67///     let n2 = (nums.2 * 100.0).round() / 100.0;
68///     (n0, n1, n2)
69/// }
70/// ```
71
72#[derive(Debug, Clone)]
73pub struct MovingAverageConvergenceDivergence {
74    fast_ema: Ema,
75    slow_ema: Ema,
76    signal_ema: Ema,
77}
78
79impl MovingAverageConvergenceDivergence {
80    pub fn new(fast_length: u32, slow_length: u32, signal_length: u32) -> Result<Self> {
81        let indicator = Self {
82            fast_ema: Ema::new(fast_length)?,
83            slow_ema: Ema::new(slow_length)?,
84            signal_ema: Ema::new(signal_length)?,
85        };
86        Ok(indicator)
87    }
88}
89
90impl Next<f64> for MovingAverageConvergenceDivergence {
91    type Output = (f64, f64, f64);
92
93    fn next(&mut self, input: f64) -> Self::Output {
94        let fast_val = self.fast_ema.next(input);
95        let slow_val = self.slow_ema.next(input);
96
97        let macd = fast_val - slow_val;
98        let signal = self.signal_ema.next(macd);
99        let histogram = macd - signal;
100
101        (macd, signal, histogram)
102    }
103}
104
105impl<'a, T: Close> Next<&'a T> for MovingAverageConvergenceDivergence {
106    type Output = (f64, f64, f64);
107
108    fn next(&mut self, input: &'a T) -> Self::Output {
109        self.next(input.close())
110    }
111}
112
113impl Reset for MovingAverageConvergenceDivergence {
114    fn reset(&mut self) {
115        self.fast_ema.reset();
116        self.slow_ema.reset();
117        self.signal_ema.reset();
118    }
119}
120
121impl Default for MovingAverageConvergenceDivergence {
122    fn default() -> Self {
123        Self::new(12, 26, 9).unwrap()
124    }
125}
126
127impl fmt::Display for MovingAverageConvergenceDivergence {
128    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129        write!(
130            f,
131            "MACD({}, {}, {})",
132            self.fast_ema.length(),
133            self.slow_ema.length(),
134            self.signal_ema.length()
135        )
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142    // use crate::test_helper::*;
143    type Macd = MovingAverageConvergenceDivergence;
144
145    // test_indicator!(Macd);
146
147    fn round(nums: (f64, f64, f64)) -> (f64, f64, f64) {
148        let n0 = (nums.0 * 100.0).round() / 100.0;
149        let n1 = (nums.1 * 100.0).round() / 100.0;
150        let n2 = (nums.2 * 100.0).round() / 100.0;
151        (n0, n1, n2)
152    }
153
154    #[test]
155    fn test_new() {
156        assert!(Macd::new(0, 1, 1).is_err());
157        assert!(Macd::new(1, 0, 1).is_err());
158        assert!(Macd::new(1, 1, 0).is_err());
159        assert!(Macd::new(1, 1, 1).is_ok());
160    }
161
162    #[test]
163    fn test_macd() {
164        let mut macd = Macd::new(3, 6, 4).unwrap();
165
166        assert_eq!(round(macd.next(2.0)), (0.0, 0.0, 0.0));
167        assert_eq!(round(macd.next(3.0)), (0.21, 0.09, 0.13));
168        assert_eq!(round(macd.next(4.2)), (0.52, 0.26, 0.26));
169        assert_eq!(round(macd.next(7.0)), (1.15, 0.62, 0.54));
170        assert_eq!(round(macd.next(6.7)), (1.15, 0.83, 0.32));
171        assert_eq!(round(macd.next(6.5)), (0.94, 0.87, 0.07));
172    }
173
174    #[test]
175    fn test_reset() {
176        let mut macd = Macd::new(3, 6, 4).unwrap();
177
178        assert_eq!(round(macd.next(2.0)), (0.0, 0.0, 0.0));
179        assert_eq!(round(macd.next(3.0)), (0.21, 0.09, 0.13));
180
181        macd.reset();
182
183        assert_eq!(round(macd.next(2.0)), (0.0, 0.0, 0.0));
184        assert_eq!(round(macd.next(3.0)), (0.21, 0.09, 0.13));
185    }
186
187    #[test]
188    fn test_default() {
189        Macd::default();
190    }
191
192    #[test]
193    fn test_display() {
194        let indicator = Macd::new(13, 30, 10).unwrap();
195        assert_eq!(format!("{}", indicator), "MACD(13, 30, 10)");
196    }
197}