Skip to main content

quantwave_core/indicators/
momentum.rs

1talib_1_in_1_out!(RSI, talib_rs::momentum::rsi, timeperiod: usize);
2impl From<usize> for RSI {
3    fn from(p: usize) -> Self {
4        Self::new(p)
5    }
6}
7talib_1_in_3_out!(MACD, talib_rs::momentum::macd, fastperiod: usize, slowperiod: usize, signalperiod: usize);
8talib_1_in_3_out!(MACDEXT, talib_rs::momentum::macd_ext, fastperiod: usize, fastmatype: talib_rs::MaType, slowperiod: usize, slowmatype: talib_rs::MaType, signalperiod: usize, signalmatype: talib_rs::MaType);
9talib_1_in_3_out!(MACDFIX, talib_rs::momentum::macd_fix, signalperiod: usize);
10
11talib_3_in_2_out!(STOCH, talib_rs::momentum::stoch, fastk_period: usize, slowk_period: usize, slowk_matype: talib_rs::MaType, slowd_period: usize, slowd_matype: talib_rs::MaType);
12talib_3_in_2_out!(STOCHF, talib_rs::momentum::stochf, fastk_period: usize, fastd_period: usize, fastd_matype: talib_rs::MaType);
13talib_1_in_2_out!(STOCHRSI, talib_rs::momentum::stochrsi, timeperiod: usize, fastk_period: usize, fastd_period: usize, fastd_matype: talib_rs::MaType);
14
15talib_3_in_1_out!(ADX, talib_rs::momentum::adx, timeperiod: usize);
16impl From<usize> for ADX {
17    fn from(p: usize) -> Self {
18        Self::new(p)
19    }
20}
21talib_3_in_1_out!(ADXR, talib_rs::momentum::adxr, timeperiod: usize);
22impl From<usize> for ADXR {
23    fn from(p: usize) -> Self {
24        Self::new(p)
25    }
26}
27talib_3_in_1_out!(CCI, talib_rs::momentum::cci, timeperiod: usize);
28impl From<usize> for CCI {
29    fn from(p: usize) -> Self {
30        Self::new(p)
31    }
32}
33talib_1_in_1_out!(MOM, talib_rs::momentum::mom, timeperiod: usize);
34impl From<usize> for MOM {
35    fn from(p: usize) -> Self {
36        Self::new(p)
37    }
38}
39talib_1_in_1_out!(ROC, talib_rs::momentum::roc, timeperiod: usize);
40impl From<usize> for ROC {
41    fn from(p: usize) -> Self {
42        Self::new(p)
43    }
44}
45talib_1_in_1_out!(ROCP, talib_rs::momentum::rocp, timeperiod: usize);
46impl From<usize> for ROCP {
47    fn from(p: usize) -> Self {
48        Self::new(p)
49    }
50}
51talib_1_in_1_out!(ROCR, talib_rs::momentum::rocr, timeperiod: usize);
52impl From<usize> for ROCR {
53    fn from(p: usize) -> Self {
54        Self::new(p)
55    }
56}
57talib_1_in_1_out!(ROCR100, talib_rs::momentum::rocr100, timeperiod: usize);
58impl From<usize> for ROCR100 {
59    fn from(p: usize) -> Self {
60        Self::new(p)
61    }
62}
63talib_3_in_1_out!(WILLR, talib_rs::momentum::willr, timeperiod: usize);
64impl From<usize> for WILLR {
65    fn from(p: usize) -> Self {
66        Self::new(p)
67    }
68}
69talib_1_in_1_out!(APO, talib_rs::momentum::apo, fastperiod: usize, slowperiod: usize, matype: talib_rs::MaType);
70talib_1_in_1_out!(PPO, talib_rs::momentum::ppo, fastperiod: usize, slowperiod: usize, matype: talib_rs::MaType);
71talib_4_in_1_out!(BOP, talib_rs::momentum::bop);
72impl Default for BOP {
73    fn default() -> Self {
74        Self::new()
75    }
76}
77talib_1_in_1_out!(CMO, talib_rs::momentum::cmo, timeperiod: usize);
78impl From<usize> for CMO {
79    fn from(p: usize) -> Self {
80        Self::new(p)
81    }
82}
83talib_2_in_2_out!(AROON, talib_rs::momentum::aroon, timeperiod: usize);
84talib_2_in_1_out!(AROONOSC, talib_rs::momentum::aroon_osc, timeperiod: usize);
85talib_4_in_1_out!(MFI, talib_rs::momentum::mfi, timeperiod: usize);
86talib_1_in_1_out!(TRIX, talib_rs::momentum::trix, timeperiod: usize);
87impl From<usize> for TRIX {
88    fn from(p: usize) -> Self {
89        Self::new(p)
90    }
91}
92talib_3_in_1_out!(ULTOSC, talib_rs::momentum::ultosc, timeperiod1: usize, timeperiod2: usize, timeperiod3: usize);
93talib_3_in_1_out!(DX, talib_rs::momentum::dx, timeperiod: usize);
94impl From<usize> for DX {
95    fn from(p: usize) -> Self {
96        Self::new(p)
97    }
98}
99talib_3_in_1_out!(PLUS_DI, talib_rs::momentum::plus_di, timeperiod: usize);
100impl From<usize> for PLUS_DI {
101    fn from(p: usize) -> Self {
102        Self::new(p)
103    }
104}
105talib_3_in_1_out!(MINUS_DI, talib_rs::momentum::minus_di, timeperiod: usize);
106impl From<usize> for MINUS_DI {
107    fn from(p: usize) -> Self {
108        Self::new(p)
109    }
110}
111talib_2_in_1_out!(PLUS_DM, talib_rs::momentum::plus_dm, timeperiod: usize);
112talib_2_in_1_out!(MINUS_DM, talib_rs::momentum::minus_dm, timeperiod: usize);
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use crate::traits::Next;
118    use proptest::prelude::*;
119
120    proptest! {
121        #[test]
122        fn test_rsi_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
123            let period = 14;
124            let mut rsi = RSI::new(period);
125            let streaming_results: Vec<f64> = input.iter().map(|&x| rsi.next(x)).collect();
126            let batch_results = talib_rs::momentum::rsi(&input, period).unwrap_or_else(|_| vec![f64::NAN; input.len()]);
127
128            for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
129                if s.is_nan() {
130                    assert!(b.is_nan());
131                } else {
132                    approx::assert_relative_eq!(s, b, epsilon = 1e-6);
133                }
134            }
135        }
136
137        #[test]
138        fn test_macd_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
139            let fast = 12;
140            let slow = 26;
141            let signal = 9;
142            let mut macd = MACD::new(fast, slow, signal);
143            let streaming_results: Vec<(f64, f64, f64)> = input.iter().map(|&x| macd.next(x)).collect();
144            let (b_macd, b_signal, b_hist) = talib_rs::momentum::macd(&input, fast, slow, signal).unwrap_or_else(|_| {
145                (vec![f64::NAN; input.len()], vec![f64::NAN; input.len()], vec![f64::NAN; input.len()])
146            });
147
148            for (i, (s_macd, s_signal, s_hist)) in streaming_results.into_iter().enumerate() {
149                if s_macd.is_nan() {
150                    assert!(b_macd[i].is_nan());
151                } else {
152                    approx::assert_relative_eq!(s_macd, b_macd[i], epsilon = 1e-6);
153                }
154                if s_signal.is_nan() {
155                    assert!(b_signal[i].is_nan());
156                } else {
157                    approx::assert_relative_eq!(s_signal, b_signal[i], epsilon = 1e-6);
158                }
159                if s_hist.is_nan() {
160                    assert!(b_hist[i].is_nan());
161                } else {
162                    approx::assert_relative_eq!(s_hist, b_hist[i], epsilon = 1e-6);
163                }
164            }
165        }
166
167        #[test]
168        fn test_stoch_parity(
169            h in prop::collection::vec(1.0..100.0, 1..100),
170            l in prop::collection::vec(1.0..100.0, 1..100),
171            c in prop::collection::vec(1.0..100.0, 1..100)
172        ) {
173            let len = h.len().min(l.len()).min(c.len());
174            if len == 0 { return Ok(()); }
175            let mut high = Vec::with_capacity(len);
176            let mut low = Vec::with_capacity(len);
177            let mut close = Vec::with_capacity(len);
178            for i in 0..len {
179                let val_h: f64 = h[i];
180                let val_l: f64 = l[i];
181                let val_c: f64 = c[i];
182                let max: f64 = val_h.max(val_l).max(val_c);
183                let min: f64 = val_h.min(val_l).min(val_c);
184                high.push(max);
185                low.push(min);
186                close.push(val_c);
187            }
188
189            let fastk = 5;
190            let slowk = 3;
191            let slowk_ma = talib_rs::MaType::Sma;
192            let slowd = 3;
193            let slowd_ma = talib_rs::MaType::Sma;
194
195            let mut stoch = STOCH::new(fastk, slowk, slowk_ma, slowd, slowd_ma);
196            let streaming_results: Vec<(f64, f64)> = (0..len).map(|i| stoch.next((high[i], low[i], close[i]))).collect();
197            let (b_k, b_d) = talib_rs::momentum::stoch(&high, &low, &close, fastk, slowk, slowk_ma, slowd, slowd_ma).unwrap_or_else(|_| {
198                (vec![f64::NAN; len], vec![f64::NAN; len])
199            });
200
201            for (i, (s_k, s_d)) in streaming_results.into_iter().enumerate() {
202                if s_k.is_nan() {
203                    assert!(b_k[i].is_nan());
204                } else {
205                    approx::assert_relative_eq!(s_k, b_k[i], epsilon = 1e-6);
206                }
207                if s_d.is_nan() {
208                    assert!(b_d[i].is_nan());
209                } else {
210                    approx::assert_relative_eq!(s_d, b_d[i], epsilon = 1e-6);
211                }
212            }
213        }
214
215        #[test]
216        fn test_adx_parity(
217            h in prop::collection::vec(1.0..100.0, 1..100),
218            l in prop::collection::vec(1.0..100.0, 1..100),
219            c in prop::collection::vec(1.0..100.0, 1..100)
220        ) {
221            let len = h.len().min(l.len()).min(c.len());
222            if len == 0 { return Ok(()); }
223            let mut high = Vec::with_capacity(len);
224            let mut low = Vec::with_capacity(len);
225            let mut close = Vec::with_capacity(len);
226            for i in 0..len {
227                let val_h: f64 = h[i];
228                let val_l: f64 = l[i];
229                let val_c: f64 = c[i];
230                let max: f64 = val_h.max(val_l).max(val_c);
231                let min: f64 = val_h.min(val_l).min(val_c);
232                high.push(max);
233                low.push(min);
234                close.push(val_c);
235            }
236
237            let period = 14;
238            let mut adx = ADX::new(period);
239            let streaming_results: Vec<f64> = (0..len).map(|i| adx.next((high[i], low[i], close[i]))).collect();
240            let batch_results = talib_rs::momentum::adx(&high, &low, &close, period).unwrap_or_else(|_| vec![f64::NAN; len]);
241
242            for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
243                if s.is_nan() {
244                    assert!(b.is_nan());
245                } else {
246                    approx::assert_relative_eq!(s, b, epsilon = 1e-6);
247                }
248            }
249        }
250    }
251}