Skip to main content

quantwave_core/indicators/
overlap.rs

1talib_1_in_1_out!(DEMA, talib_rs::overlap::dema, timeperiod: usize);
2impl From<usize> for DEMA {
3    fn from(p: usize) -> Self {
4        Self::new(p)
5    }
6}
7talib_1_in_1_out!(TRIMA, talib_rs::overlap::trima, timeperiod: usize);
8impl From<usize> for TRIMA {
9    fn from(p: usize) -> Self {
10        Self::new(p)
11    }
12}
13talib_1_in_1_out!(KAMA, talib_rs::overlap::kama, timeperiod: usize);
14impl From<usize> for KAMA {
15    fn from(p: usize) -> Self {
16        Self::new(p)
17    }
18}
19talib_1_in_1_out!(T3, talib_rs::overlap::t3, timeperiod: usize, v_factor: f64);
20talib_1_in_2_out!(MAMA, talib_rs::overlap::mama, fastlimit: f64, slowlimit: f64);
21talib_1_in_3_out!(BBANDS, talib_rs::overlap::bbands, timeperiod: usize, nbdevup: f64, nbdevdn: f64, matype: talib_rs::MaType);
22talib_2_in_1_out!(SAR, talib_rs::overlap::sar, acceleration: f64, maximum: f64);
23talib_2_in_1_out!(SAREXT, talib_rs::overlap::sar_ext, startvalue: f64, offsetonreverse: f64, accelerationinitlong: f64, accelerationlong: f64, accelerationmaxlong: f64, accelerationinitshort: f64, accelerationshort: f64, accelerationmaxshort: f64);
24talib_1_in_1_out!(MIDPOINT, talib_rs::overlap::midpoint, timeperiod: usize);
25impl From<usize> for MIDPOINT {
26    fn from(p: usize) -> Self {
27        Self::new(p)
28    }
29}
30talib_2_in_1_out!(MIDPRICE, talib_rs::overlap::midprice, timeperiod: usize);
31impl From<usize> for MIDPRICE {
32    fn from(p: usize) -> Self {
33        Self::new(p)
34    }
35}
36talib_2_in_1_out!(MAVP, talib_rs::overlap::mavp, minperiod: usize, maxperiod: usize, matype: talib_rs::MaType);
37talib_1_in_1_out!(HT_TRENDLINE, talib_rs::overlap::ht_trendline);
38impl Default for HT_TRENDLINE {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use crate::traits::Next;
48    use proptest::prelude::*;
49
50    proptest! {
51        #[test]
52        fn test_dema_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
53            let period = 10;
54            let mut dema = DEMA::new(period);
55            let streaming_results: Vec<f64> = input.iter().map(|&x| dema.next(x)).collect();
56            let batch_results = talib_rs::overlap::dema(&input, period).unwrap_or_else(|_| vec![f64::NAN; input.len()]);
57
58            for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
59                if s.is_nan() {
60                    assert!(b.is_nan());
61                } else {
62                    approx::assert_relative_eq!(s, b, epsilon = 1e-6);
63                }
64            }
65        }
66
67        #[test]
68        fn test_trima_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
69            let period = 10;
70            let mut trima = TRIMA::new(period);
71            let streaming_results: Vec<f64> = input.iter().map(|&x| trima.next(x)).collect();
72            let batch_results = talib_rs::overlap::trima(&input, period).unwrap_or_else(|_| vec![f64::NAN; input.len()]);
73
74            for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
75                if s.is_nan() {
76                    assert!(b.is_nan());
77                } else {
78                    approx::assert_relative_eq!(s, b, epsilon = 1e-6);
79                }
80            }
81        }
82
83        #[test]
84        fn test_kama_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
85            let period = 10;
86            let mut kama = KAMA::new(period);
87            let streaming_results: Vec<f64> = input.iter().map(|&x| kama.next(x)).collect();
88            let batch_results = talib_rs::overlap::kama(&input, period).unwrap_or_else(|_| vec![f64::NAN; input.len()]);
89
90            for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
91                if s.is_nan() {
92                    assert!(b.is_nan());
93                } else {
94                    approx::assert_relative_eq!(s, b, epsilon = 1e-6);
95                }
96            }
97        }
98
99        #[test]
100        fn test_t3_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
101            let period = 10;
102            let v_factor = 0.7;
103            let mut t3 = T3::new(period, v_factor);
104            let streaming_results: Vec<f64> = input.iter().map(|&x| t3.next(x)).collect();
105            let batch_results = talib_rs::overlap::t3(&input, period, v_factor).unwrap_or_else(|_| vec![f64::NAN; input.len()]);
106
107            for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
108                if s.is_nan() {
109                    assert!(b.is_nan());
110                } else {
111                    approx::assert_relative_eq!(s, b, epsilon = 1e-6);
112                }
113            }
114        }
115
116        #[test]
117        fn test_bbands_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
118            let period = 10;
119            let nbdevup = 2.0;
120            let nbdevdn = 2.0;
121            let matype = talib_rs::MaType::Sma;
122            let mut bbands = BBANDS::new(period, nbdevup, nbdevdn, matype);
123            let streaming_results: Vec<(f64, f64, f64)> = input.iter().map(|&x| bbands.next(x)).collect();
124            let (b_upper, b_middle, b_lower) = talib_rs::overlap::bbands(&input, period, nbdevup, nbdevdn, matype).unwrap_or_else(|_| {
125                (vec![f64::NAN; input.len()], vec![f64::NAN; input.len()], vec![f64::NAN; input.len()])
126            });
127
128            for (i, (s_upper, s_middle, s_lower)) in streaming_results.into_iter().enumerate() {
129                if s_upper.is_nan() {
130                    assert!(b_upper[i].is_nan());
131                } else {
132                    approx::assert_relative_eq!(s_upper, b_upper[i], epsilon = 1e-6);
133                }
134                if s_middle.is_nan() {
135                    assert!(b_middle[i].is_nan());
136                } else {
137                    approx::assert_relative_eq!(s_middle, b_middle[i], epsilon = 1e-6);
138                }
139                if s_lower.is_nan() {
140                    assert!(b_lower[i].is_nan());
141                } else {
142                    approx::assert_relative_eq!(s_lower, b_lower[i], epsilon = 1e-6);
143                }
144            }
145        }
146
147        #[test]
148        fn test_sar_parity(
149            h in prop::collection::vec(10.0..100.0, 1..100),
150            l in prop::collection::vec(10.0..100.0, 1..100)
151        ) {
152            let len = h.len().min(l.len());
153            if len == 0 { return Ok(()); }
154            let mut high = Vec::with_capacity(len);
155            let mut low = Vec::with_capacity(len);
156            for i in 0..len {
157                let v_h: f64 = h[i];
158                let v_l: f64 = l[i];
159                high.push(v_h.max(v_l));
160                low.push(v_h.min(v_l));
161            }
162
163            let accel = 0.02;
164            let max = 0.2;
165            let mut sar = SAR::new(accel, max);
166            let streaming_results: Vec<f64> = (0..len).map(|i| sar.next((high[i], low[i]))).collect();
167            let batch_results = talib_rs::overlap::sar(&high, &low, accel, max).unwrap_or_else(|_| vec![f64::NAN; len]);
168
169            for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
170                if s.is_nan() {
171                    assert!(b.is_nan());
172                } else {
173                    approx::assert_relative_eq!(s, b, epsilon = 1e-6);
174                }
175            }
176        }
177    }
178}