ux_indicators/indicators/
macd.rs1#![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#[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 type Macd = MovingAverageConvergenceDivergence;
144
145 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}