1use core::fmt;
2
3use crate::errors::Result;
4use crate::indicators::ExponentialMovingAverage as Ema;
5use crate::{Close, Next, Period, Reset};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9#[doc(alias = "MACD")]
54#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
55#[derive(Debug, Clone)]
56pub struct MovingAverageConvergenceDivergence {
57 fast_ema: Ema,
58 slow_ema: Ema,
59 signal_ema: Ema,
60}
61
62impl MovingAverageConvergenceDivergence {
63 pub fn new(fast_period: usize, slow_period: usize, signal_period: usize) -> Result<Self> {
64 Ok(Self {
65 fast_ema: Ema::new(fast_period)?,
66 slow_ema: Ema::new(slow_period)?,
67 signal_ema: Ema::new(signal_period)?,
68 })
69 }
70}
71
72#[derive(Debug, Clone, PartialEq)]
73pub struct MovingAverageConvergenceDivergenceOutput {
74 pub macd: f64,
75 pub signal: f64,
76 pub histogram: f64,
77}
78
79impl From<MovingAverageConvergenceDivergenceOutput> for (f64, f64, f64) {
80 fn from(mo: MovingAverageConvergenceDivergenceOutput) -> Self {
81 (mo.macd, mo.signal, mo.histogram)
82 }
83}
84
85impl Next<f64> for MovingAverageConvergenceDivergence {
86 type Output = MovingAverageConvergenceDivergenceOutput;
87
88 fn next(&mut self, input: f64) -> Self::Output {
89 let fast_val = self.fast_ema.next(input);
90 let slow_val = self.slow_ema.next(input);
91
92 let macd = fast_val - slow_val;
93 let signal = self.signal_ema.next(macd);
94 let histogram = macd - signal;
95
96 MovingAverageConvergenceDivergenceOutput {
97 macd,
98 signal,
99 histogram,
100 }
101 }
102}
103
104impl<T: Close> Next<&T> for MovingAverageConvergenceDivergence {
105 type Output = MovingAverageConvergenceDivergenceOutput;
106
107 fn next(&mut self, input: &T) -> Self::Output {
108 self.next(input.close())
109 }
110}
111
112impl Reset for MovingAverageConvergenceDivergence {
113 fn reset(&mut self) {
114 self.fast_ema.reset();
115 self.slow_ema.reset();
116 self.signal_ema.reset();
117 }
118}
119
120impl Default for MovingAverageConvergenceDivergence {
121 fn default() -> Self {
122 Self::new(12, 26, 9).unwrap()
123 }
124}
125
126impl fmt::Display for MovingAverageConvergenceDivergence {
127 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128 write!(
129 f,
130 "MACD({}, {}, {})",
131 self.fast_ema.period(),
132 self.slow_ema.period(),
133 self.signal_ema.period()
134 )
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use crate::test_helper::*;
142 use alloc::format;
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).into()), (0.0, 0.0, 0.0));
167 assert_eq!(round(macd.next(3.0).into()), (0.21, 0.09, 0.13));
168 assert_eq!(round(macd.next(4.2).into()), (0.52, 0.26, 0.26));
169 assert_eq!(round(macd.next(7.0).into()), (1.15, 0.62, 0.54));
170 assert_eq!(round(macd.next(6.7).into()), (1.15, 0.83, 0.32));
171 assert_eq!(round(macd.next(6.5).into()), (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).into()), (0.0, 0.0, 0.0));
179 assert_eq!(round(macd.next(3.0).into()), (0.21, 0.09, 0.13));
180
181 macd.reset();
182
183 assert_eq!(round(macd.next(2.0).into()), (0.0, 0.0, 0.0));
184 assert_eq!(round(macd.next(3.0).into()), (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}