quantwave_core/indicators/
momentum.rs1talib_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}