Skip to main content

quantwave_core/indicators/
math.rs

1// Math Transform
2talib_1_in_1_out_no_result!(ACOS, talib_rs::math_transform::acos);
3impl Default for ACOS {
4    fn default() -> Self {
5        Self::new()
6    }
7}
8talib_1_in_1_out_no_result!(ASIN, talib_rs::math_transform::asin);
9impl Default for ASIN {
10    fn default() -> Self {
11        Self::new()
12    }
13}
14talib_1_in_1_out_no_result!(ATAN, talib_rs::math_transform::atan);
15impl Default for ATAN {
16    fn default() -> Self {
17        Self::new()
18    }
19}
20talib_1_in_1_out_no_result!(CEIL, talib_rs::math_transform::ceil);
21impl Default for CEIL {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26talib_1_in_1_out_no_result!(COS, talib_rs::math_transform::cos);
27impl Default for COS {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32talib_1_in_1_out_no_result!(COSH, talib_rs::math_transform::cosh);
33impl Default for COSH {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38talib_1_in_1_out_no_result!(EXP, talib_rs::math_transform::exp);
39impl Default for EXP {
40    fn default() -> Self {
41        Self::new()
42    }
43}
44talib_1_in_1_out_no_result!(FLOOR, talib_rs::math_transform::floor);
45impl Default for FLOOR {
46    fn default() -> Self {
47        Self::new()
48    }
49}
50talib_1_in_1_out_no_result!(LN, talib_rs::math_transform::ln);
51impl Default for LN {
52    fn default() -> Self {
53        Self::new()
54    }
55}
56talib_1_in_1_out_no_result!(LOG10, talib_rs::math_transform::log10);
57impl Default for LOG10 {
58    fn default() -> Self {
59        Self::new()
60    }
61}
62talib_1_in_1_out_no_result!(SIN, talib_rs::math_transform::sin);
63impl Default for SIN {
64    fn default() -> Self {
65        Self::new()
66    }
67}
68talib_1_in_1_out_no_result!(SINH, talib_rs::math_transform::sinh);
69impl Default for SINH {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74talib_1_in_1_out_no_result!(SQRT, talib_rs::math_transform::sqrt);
75impl Default for SQRT {
76    fn default() -> Self {
77        Self::new()
78    }
79}
80talib_1_in_1_out_no_result!(TAN, talib_rs::math_transform::tan);
81impl Default for TAN {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86talib_1_in_1_out_no_result!(TANH, talib_rs::math_transform::tanh);
87impl Default for TANH {
88    fn default() -> Self {
89        Self::new()
90    }
91}
92
93/// Root Mean Square (RMS)
94#[derive(Debug, Clone)]
95pub struct RMS {
96    period: usize,
97    history: std::collections::VecDeque<f64>,
98    sum_sq: f64,
99}
100
101impl RMS {
102    pub fn new(period: usize) -> Self {
103        Self {
104            period,
105            history: std::collections::VecDeque::with_capacity(period),
106            sum_sq: 0.0,
107        }
108    }
109}
110
111impl crate::traits::Next<f64> for RMS {
112    type Output = f64;
113
114    fn next(&mut self, input: f64) -> Self::Output {
115        let input_sq = input * input;
116        self.sum_sq += input_sq;
117        self.history.push_back(input_sq);
118
119        if self.history.len() > self.period {
120            if let Some(old) = self.history.pop_front() {
121                self.sum_sq -= old;
122            }
123        }
124
125        if self.history.is_empty() {
126            0.0
127        } else {
128            (self.sum_sq / self.history.len() as f64).sqrt()
129        }
130    }
131}
132
133// Math Operators
134talib_2_in_1_out!(ADD, talib_rs::math_operator::add);
135impl Default for ADD {
136    fn default() -> Self {
137        Self::new()
138    }
139}
140talib_2_in_1_out!(SUB, talib_rs::math_operator::sub);
141impl Default for SUB {
142    fn default() -> Self {
143        Self::new()
144    }
145}
146talib_2_in_1_out!(MULT, talib_rs::math_operator::mult);
147impl Default for MULT {
148    fn default() -> Self {
149        Self::new()
150    }
151}
152talib_2_in_1_out!(DIV, talib_rs::math_operator::div);
153impl Default for DIV {
154    fn default() -> Self {
155        Self::new()
156    }
157}
158talib_1_in_1_out!(MAX, talib_rs::math_operator::max, timeperiod: usize);
159impl From<usize> for MAX {
160    fn from(p: usize) -> Self {
161        Self::new(p)
162    }
163}
164
165talib_1_in_1_out!(MAXINDEX, talib_rs::math_operator::maxindex, timeperiod: usize);
166impl From<usize> for MAXINDEX {
167    fn from(p: usize) -> Self {
168        Self::new(p)
169    }
170}
171
172talib_1_in_1_out!(MIN, talib_rs::math_operator::min, timeperiod: usize);
173impl From<usize> for MIN {
174    fn from(p: usize) -> Self {
175        Self::new(p)
176    }
177}
178
179talib_1_in_1_out!(MININDEX, talib_rs::math_operator::minindex, timeperiod: usize);
180impl From<usize> for MININDEX {
181    fn from(p: usize) -> Self {
182        Self::new(p)
183    }
184}
185
186talib_1_in_1_out!(SUM, talib_rs::math_operator::sum, timeperiod: usize);
187impl From<usize> for SUM {
188    fn from(p: usize) -> Self {
189        Self::new(p)
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196    use crate::traits::Next;
197    use proptest::prelude::*;
198
199    proptest! {
200        #[test]
201        fn test_sqrt_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
202            let mut sqrt = SQRT::new();
203            let streaming_results: Vec<f64> = input.iter().map(|&x| sqrt.next(x)).collect();
204            let batch_results = talib_rs::math_transform::sqrt(&input);
205
206            for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
207                if s.is_nan() {
208                    assert!(b.is_nan());
209                } else {
210                    approx::assert_relative_eq!(s, b, epsilon = 1e-6);
211                }
212            }
213        }
214
215        #[test]
216        fn test_add_parity(
217            in1 in prop::collection::vec(0.1..100.0, 1..100),
218            in2 in prop::collection::vec(0.1..100.0, 1..100)
219        ) {
220            let len = in1.len().min(in2.len());
221            if len == 0 { return Ok(()); }
222
223            let mut add = ADD::new();
224            let streaming_results: Vec<f64> = (0..len).map(|i| add.next((in1[i], in2[i]))).collect();
225            let batch_results = talib_rs::math_operator::add(&in1[..len], &in2[..len]).unwrap_or_else(|_| vec![f64::NAN; len]);
226
227            for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
228                if s.is_nan() {
229                    assert!(b.is_nan());
230                } else {
231                    approx::assert_relative_eq!(s, b, epsilon = 1e-6);
232                }
233            }
234        }
235
236        #[test]
237        fn test_rms_parity(input in prop::collection::vec(0.1..100.0, 10..100)) {
238            let period = 10;
239            let mut rms = RMS::new(period);
240            let streaming_results: Vec<f64> = input.iter().map(|&x| rms.next(x)).collect();
241
242            let mut batch_results = Vec::with_capacity(input.len());
243            for i in 0..input.len() {
244                let start = if i + 1 > period { i + 1 - period } else { 0 };
245                let window = &input[start..i+1];
246                let sum_sq: f64 = window.iter().map(|&x| x*x).sum();
247                batch_results.push((sum_sq / window.len() as f64).sqrt());
248            }
249
250            for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
251                approx::assert_relative_eq!(s, b, epsilon = 1e-10);
252            }
253        }
254    }
255}