quantwave_core/indicators/
math.rs1talib_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#[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 && let Some(old) = self.history.pop_front() {
120 self.sum_sq -= old;
121 }
122
123 if self.history.is_empty() {
124 0.0
125 } else {
126 (self.sum_sq / self.history.len() as f64).sqrt()
127 }
128 }
129}
130
131#[derive(Debug, Clone)]
136pub struct AGC {
137 peak: f64,
138 decay: f64,
139}
140
141impl AGC {
142 pub fn new(decay: f64) -> Self {
143 Self {
144 peak: 0.0000001,
145 decay,
146 }
147 }
148}
149
150impl crate::traits::Next<f64> for AGC {
151 type Output = f64;
152
153 fn next(&mut self, input: f64) -> Self::Output {
154 self.peak *= self.decay;
155 let abs_input = input.abs();
156 if abs_input > self.peak {
157 self.peak = abs_input;
158 }
159
160 if self.peak != 0.0 {
161 input / self.peak
162 } else {
163 0.0
164 }
165 }
166}
167
168talib_2_in_1_out!(ADD, talib_rs::math_operator::add);
170impl Default for ADD {
171 fn default() -> Self {
172 Self::new()
173 }
174}
175talib_2_in_1_out!(SUB, talib_rs::math_operator::sub);
176impl Default for SUB {
177 fn default() -> Self {
178 Self::new()
179 }
180}
181talib_2_in_1_out!(MULT, talib_rs::math_operator::mult);
182impl Default for MULT {
183 fn default() -> Self {
184 Self::new()
185 }
186}
187talib_2_in_1_out!(DIV, talib_rs::math_operator::div);
188impl Default for DIV {
189 fn default() -> Self {
190 Self::new()
191 }
192}
193talib_1_in_1_out!(MAX, talib_rs::math_operator::max, timeperiod: usize);
194impl From<usize> for MAX {
195 fn from(p: usize) -> Self {
196 Self::new(p)
197 }
198}
199
200talib_1_in_1_out!(MAXINDEX, talib_rs::math_operator::maxindex, timeperiod: usize);
201impl From<usize> for MAXINDEX {
202 fn from(p: usize) -> Self {
203 Self::new(p)
204 }
205}
206
207talib_1_in_1_out!(MIN, talib_rs::math_operator::min, timeperiod: usize);
208impl From<usize> for MIN {
209 fn from(p: usize) -> Self {
210 Self::new(p)
211 }
212}
213
214talib_1_in_1_out!(MININDEX, talib_rs::math_operator::minindex, timeperiod: usize);
215impl From<usize> for MININDEX {
216 fn from(p: usize) -> Self {
217 Self::new(p)
218 }
219}
220
221talib_1_in_1_out!(SUM, talib_rs::math_operator::sum, timeperiod: usize);
222impl From<usize> for SUM {
223 fn from(p: usize) -> Self {
224 Self::new(p)
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231 use crate::traits::Next;
232 use proptest::prelude::*;
233
234 proptest! {
235 #[test]
236 fn test_sqrt_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
237 let mut sqrt = SQRT::new();
238 let streaming_results: Vec<f64> = input.iter().map(|&x| sqrt.next(x)).collect();
239 let batch_results = talib_rs::math_transform::sqrt(&input);
240
241 for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
242 if s.is_nan() {
243 assert!(b.is_nan());
244 } else {
245 approx::assert_relative_eq!(s, b, epsilon = 1e-6);
246 }
247 }
248 }
249
250 #[test]
251 fn test_add_parity(
252 in1 in prop::collection::vec(0.1..100.0, 1..100),
253 in2 in prop::collection::vec(0.1..100.0, 1..100)
254 ) {
255 let len = in1.len().min(in2.len());
256 if len == 0 { return Ok(()); }
257
258 let mut add = ADD::new();
259 let streaming_results: Vec<f64> = (0..len).map(|i| add.next((in1[i], in2[i]))).collect();
260 let batch_results = talib_rs::math_operator::add(&in1[..len], &in2[..len]).unwrap_or_else(|_| vec![f64::NAN; len]);
261
262 for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
263 if s.is_nan() {
264 assert!(b.is_nan());
265 } else {
266 approx::assert_relative_eq!(s, b, epsilon = 1e-6);
267 }
268 }
269 }
270
271 #[test]
272 fn test_rms_parity(input in prop::collection::vec(0.1..100.0, 10..100)) {
273 let period = 10;
274 let mut rms = RMS::new(period);
275 let streaming_results: Vec<f64> = input.iter().map(|&x| rms.next(x)).collect();
276
277 let mut batch_results = Vec::with_capacity(input.len());
278 for i in 0..input.len() {
279 let start = if i + 1 > period { i + 1 - period } else { 0 };
280 let window = &input[start..i+1];
281 let sum_sq: f64 = window.iter().map(|&x| x*x).sum();
282 batch_results.push((sum_sq / window.len() as f64).sqrt());
283 }
284
285 for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
286 approx::assert_relative_eq!(s, b, epsilon = 1e-10);
287 }
288 }
289 }
290}