quantwave_core/indicators/incremental/
macd_ext.rs1use crate::indicators::incremental::ma_stream::MaStream;
4use crate::indicators::incremental::macd::MACD;
5use crate::traits::Next;
6use talib_rs::MaType;
7
8const NAN_TRIPLE: (f64, f64, f64) = (f64::NAN, f64::NAN, f64::NAN);
9
10#[derive(Debug, Clone)]
12#[allow(non_camel_case_types)]
13pub struct MACDEXT {
14 pub fastperiod: usize,
15 pub fastmatype: MaType,
16 pub slowperiod: usize,
17 pub slowmatype: MaType,
18 pub signalperiod: usize,
19 pub signalmatype: MaType,
20 ema_macd: Option<MACD>,
21 fast_ma: MaStream,
22 slow_ma: MaStream,
23 signal_ma: MaStream,
24 macd_valid_count: usize,
25}
26
27impl MACDEXT {
28 pub fn new(
29 fastperiod: usize,
30 fastmatype: MaType,
31 slowperiod: usize,
32 slowmatype: MaType,
33 signalperiod: usize,
34 signalmatype: MaType,
35 ) -> Self {
36 let ema_macd = if fastmatype == MaType::Ema
37 && slowmatype == MaType::Ema
38 && signalmatype == MaType::Ema
39 {
40 Some(MACD::new(fastperiod, slowperiod, signalperiod))
41 } else {
42 None
43 };
44 Self {
45 fastperiod,
46 fastmatype,
47 slowperiod,
48 slowmatype,
49 signalperiod,
50 signalmatype,
51 ema_macd,
52 fast_ma: MaStream::new(fastperiod, fastmatype),
53 slow_ma: MaStream::new(slowperiod, slowmatype),
54 signal_ma: MaStream::new(signalperiod, signalmatype),
55 macd_valid_count: 0,
56 }
57 }
58}
59
60impl Next<f64> for MACDEXT {
61 type Output = (f64, f64, f64);
62
63 fn next(&mut self, input: f64) -> Self::Output {
64 if let Some(ref mut macd) = self.ema_macd {
65 return macd.next(input);
66 }
67
68 let fast = self.fast_ma.next(input);
69 let slow = self.slow_ma.next(input);
70 if fast.is_nan() || slow.is_nan() {
71 return NAN_TRIPLE;
72 }
73
74 let macd_line = fast - slow;
75 self.macd_valid_count += 1;
76 let signal = self.signal_ma.next(macd_line);
77 if signal.is_nan() {
78 return NAN_TRIPLE;
79 }
80
81 (macd_line, signal, macd_line - signal)
82 }
83}
84
85#[derive(Debug, Clone)]
87#[allow(non_camel_case_types)]
88pub struct MACDFIX {
89 pub signalperiod: usize,
90 fp: usize,
91 sp: usize,
92 k_fast: f64,
93 k_slow: f64,
94 k_signal: f64,
95 out_start: usize,
96 bars_seen: usize,
97 seed_closes: Vec<f64>,
98 slow_ema: f64,
99 fast_ema: f64,
100 macd_values: Vec<f64>,
101 signal_ema: f64,
102}
103
104impl MACDFIX {
105 pub fn new(signalperiod: usize) -> Self {
106 let fp = 12usize;
107 let sp = 26usize;
108 Self {
109 signalperiod,
110 fp,
111 sp,
112 k_fast: 0.15,
113 k_slow: 0.075,
114 k_signal: 2.0 / (signalperiod as f64 + 1.0),
115 out_start: sp - 1 + signalperiod - 1,
116 bars_seen: 0,
117 seed_closes: Vec::with_capacity(sp),
118 slow_ema: 0.0,
119 fast_ema: 0.0,
120 macd_values: Vec::new(),
121 signal_ema: 0.0,
122 }
123 }
124
125 #[inline]
126 fn update_emas(&mut self, input: f64) {
127 self.slow_ema = self.k_slow.mul_add(input - self.slow_ema, self.slow_ema);
128 self.fast_ema = self.k_fast.mul_add(input - self.fast_ema, self.fast_ema);
129 self.macd_values.push(self.fast_ema - self.slow_ema);
130 }
131}
132
133impl Next<f64> for MACDFIX {
134 type Output = (f64, f64, f64);
135
136 fn next(&mut self, input: f64) -> Self::Output {
137 let i = self.bars_seen;
138 self.bars_seen += 1;
139
140 if i < self.sp - 1 {
141 self.seed_closes.push(input);
142 return NAN_TRIPLE;
143 }
144
145 if i == self.sp - 1 {
146 self.seed_closes.push(input);
147 let slow_seed: f64 = self.seed_closes.iter().sum::<f64>() / self.sp as f64;
148 let fast_seed: f64 = self.seed_closes[self.sp - self.fp..self.sp]
149 .iter()
150 .sum::<f64>()
151 / self.fp as f64;
152 self.slow_ema = slow_seed;
153 self.fast_ema = fast_seed;
154 let macd0 = fast_seed - slow_seed;
155 self.macd_values.push(macd0);
156 if self.out_start == self.sp - 1 {
157 let signal_seed = macd0;
158 self.signal_ema = signal_seed;
159 return (macd0, signal_seed, 0.0);
160 }
161 return NAN_TRIPLE;
162 }
163
164 if i < self.out_start {
165 self.update_emas(input);
166 return NAN_TRIPLE;
167 }
168
169 if i == self.out_start {
170 if i >= self.sp {
171 self.update_emas(input);
172 }
173 let signal_seed: f64 = self.macd_values[..self.signalperiod]
174 .iter()
175 .sum::<f64>()
176 / self.signalperiod as f64;
177 self.signal_ema = signal_seed;
178 let macd = self.macd_values[self.signalperiod - 1];
179 return (macd, signal_seed, macd - signal_seed);
180 }
181
182 self.update_emas(input);
183 let macd = *self.macd_values.last().unwrap_or(&f64::NAN);
184 self.signal_ema = self
185 .k_signal
186 .mul_add(macd - self.signal_ema, self.signal_ema);
187 (macd, self.signal_ema, macd - self.signal_ema)
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194 use proptest::prelude::*;
195
196 proptest! {
197 #[test]
198 fn test_macdext_ema_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
199 let fast = 12usize;
200 let slow = 26usize;
201 let signal = 9usize;
202 let matype = MaType::Ema;
203 let mut ext = MACDEXT::new(fast, matype, slow, matype, signal, matype);
204 let streaming: Vec<_> = input.iter().map(|&x| ext.next(x)).collect();
205 let (b_macd, b_signal, b_hist) = talib_rs::momentum::macd_ext(
206 &input, fast, matype, slow, matype, signal, matype,
207 ).unwrap_or_else(|_| {
208 (vec![f64::NAN; input.len()], vec![f64::NAN; input.len()], vec![f64::NAN; input.len()])
209 });
210 for (i, (s_m, s_s, s_h)) in streaming.into_iter().enumerate() {
211 if s_m.is_nan() { assert!(b_macd[i].is_nan()); }
212 else { approx::assert_relative_eq!(s_m, b_macd[i], epsilon = 1e-6); }
213 if s_s.is_nan() { assert!(b_signal[i].is_nan()); }
214 else { approx::assert_relative_eq!(s_s, b_signal[i], epsilon = 1e-6); }
215 if s_h.is_nan() { assert!(b_hist[i].is_nan()); }
216 else { approx::assert_relative_eq!(s_h, b_hist[i], epsilon = 1e-6); }
217 }
218 }
219
220 #[test]
221 fn test_macdfix_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
222 let signal = 9usize;
223 let mut fix = MACDFIX::new(signal);
224 let streaming: Vec<_> = input.iter().map(|&x| fix.next(x)).collect();
225 let (b_macd, b_signal, b_hist) = talib_rs::momentum::macd_fix(&input, signal)
226 .unwrap_or_else(|_| {
227 (vec![f64::NAN; input.len()], vec![f64::NAN; input.len()], vec![f64::NAN; input.len()])
228 });
229 for (i, (s_m, s_s, s_h)) in streaming.into_iter().enumerate() {
230 if s_m.is_nan() { assert!(b_macd[i].is_nan()); }
231 else { approx::assert_relative_eq!(s_m, b_macd[i], epsilon = 1e-6); }
232 if s_s.is_nan() { assert!(b_signal[i].is_nan()); }
233 else { approx::assert_relative_eq!(s_s, b_signal[i], epsilon = 1e-6); }
234 if s_h.is_nan() { assert!(b_hist[i].is_nan()); }
235 else { approx::assert_relative_eq!(s_h, b_hist[i], epsilon = 1e-6); }
236 }
237 }
238 }
239}