quantwave_core/indicators/incremental/
rolling.rs1use crate::traits::Next;
4use crate::utils::RingBuffer;
5
6#[derive(Debug, Clone)]
7pub struct RollingMax {
8 pub timeperiod: usize,
9 window: RingBuffer<f64>,
10}
11
12impl RollingMax {
13 pub fn new(timeperiod: usize) -> Self {
14 Self {
15 timeperiod,
16 window: RingBuffer::with_capacity(timeperiod),
17 }
18 }
19
20 fn push(&mut self, v: f64) -> f64 {
21 let p = self.timeperiod;
22 if self.window.len() >= p {
23 let _ = self.window.pop_front();
24 }
25 self.window.push_back(v);
26 if self.window.len() < p {
27 return f64::NAN;
28 }
29 if self.window.iter().any(|x| x.is_nan()) {
30 return f64::NAN;
31 }
32 let mut m = f64::NEG_INFINITY;
33 for &x in self.window.iter() {
34 if x > m {
35 m = x;
36 }
37 }
38 m
39 }
40}
41
42#[derive(Debug, Clone)]
43#[allow(non_camel_case_types)]
44pub struct MAX {
45 pub timeperiod: usize,
46 inner: RollingMax,
47}
48
49impl MAX {
50 pub fn new(timeperiod: usize) -> Self {
51 Self {
52 timeperiod,
53 inner: RollingMax::new(timeperiod),
54 }
55 }
56}
57
58impl Next<f64> for MAX {
59 type Output = f64;
60 fn next(&mut self, input: f64) -> Self::Output {
61 self.inner.push(input)
62 }
63}
64
65#[derive(Debug, Clone)]
66pub struct RollingMin {
67 pub timeperiod: usize,
68 window: RingBuffer<f64>,
69}
70
71impl RollingMin {
72 pub fn new(timeperiod: usize) -> Self {
73 Self {
74 timeperiod,
75 window: RingBuffer::with_capacity(timeperiod),
76 }
77 }
78
79 fn push(&mut self, v: f64) -> f64 {
80 let p = self.timeperiod;
81 if self.window.len() >= p {
82 let _ = self.window.pop_front();
83 }
84 self.window.push_back(v);
85 if self.window.len() < p {
86 return f64::NAN;
87 }
88 if self.window.iter().any(|x| x.is_nan()) {
89 return f64::NAN;
90 }
91 let mut m = f64::INFINITY;
92 for &x in self.window.iter() {
93 if x < m {
94 m = x;
95 }
96 }
97 m
98 }
99}
100
101#[derive(Debug, Clone)]
102#[allow(non_camel_case_types)]
103pub struct MIN {
104 pub timeperiod: usize,
105 inner: RollingMin,
106}
107
108impl MIN {
109 pub fn new(timeperiod: usize) -> Self {
110 Self {
111 timeperiod,
112 inner: RollingMin::new(timeperiod),
113 }
114 }
115}
116
117impl Next<f64> for MIN {
118 type Output = f64;
119 fn next(&mut self, input: f64) -> Self::Output {
120 self.inner.push(input)
121 }
122}
123
124#[derive(Debug, Clone)]
125pub struct RollingSum {
126 pub timeperiod: usize,
127 window: RingBuffer<f64>,
128 sum: f64,
129}
130
131impl RollingSum {
132 pub fn new(timeperiod: usize) -> Self {
133 Self {
134 timeperiod,
135 window: RingBuffer::with_capacity(timeperiod),
136 sum: 0.0,
137 }
138 }
139
140 fn push(&mut self, v: f64) -> f64 {
141 let p = self.timeperiod;
142 if self.window.len() >= p
143 && let Some(old) = self.window.pop_front()
144 {
145 self.sum -= old;
146 }
147 self.window.push_back(v);
148 self.sum += v;
149 if self.window.len() < p {
150 return f64::NAN;
151 }
152 self.sum
153 }
154}
155
156#[derive(Debug, Clone)]
157#[allow(non_camel_case_types)]
158pub struct SUM {
159 pub timeperiod: usize,
160 inner: RollingSum,
161}
162
163impl SUM {
164 pub fn new(timeperiod: usize) -> Self {
165 Self {
166 timeperiod,
167 inner: RollingSum::new(timeperiod),
168 }
169 }
170}
171
172impl Next<f64> for SUM {
173 type Output = f64;
174 fn next(&mut self, input: f64) -> Self::Output {
175 self.inner.push(input)
176 }
177}
178
179#[derive(Debug, Clone)]
180pub struct RollingMaxIndex {
181 pub timeperiod: usize,
182 window: RingBuffer<f64>,
183 bar_index: usize,
184}
185
186impl RollingMaxIndex {
187 pub fn new(timeperiod: usize) -> Self {
188 Self {
189 timeperiod,
190 window: RingBuffer::with_capacity(timeperiod),
191 bar_index: 0,
192 }
193 }
194
195 fn push(&mut self, v: f64) -> f64 {
196 let p = self.timeperiod;
197 if self.window.len() >= p {
198 let _ = self.window.pop_front();
199 }
200 self.window.push_back(v);
201 self.bar_index += 1;
202 if self.window.len() < p {
203 return f64::NAN;
204 }
205 let mut best_idx = 0usize;
206 let mut best_val = f64::NEG_INFINITY;
207 for (i, &x) in self.window.iter().enumerate() {
208 if x > best_val {
209 best_val = x;
210 best_idx = i;
211 }
212 }
213 (self.bar_index - p + best_idx) as f64
214 }
215}
216
217#[derive(Debug, Clone)]
218#[allow(non_camel_case_types)]
219pub struct MAXINDEX {
220 pub timeperiod: usize,
221 inner: RollingMaxIndex,
222}
223
224impl MAXINDEX {
225 pub fn new(timeperiod: usize) -> Self {
226 Self {
227 timeperiod,
228 inner: RollingMaxIndex::new(timeperiod),
229 }
230 }
231}
232
233impl Next<f64> for MAXINDEX {
234 type Output = f64;
235 fn next(&mut self, input: f64) -> Self::Output {
236 self.inner.push(input)
237 }
238}
239
240#[derive(Debug, Clone)]
241pub struct RollingMinIndex {
242 pub timeperiod: usize,
243 window: RingBuffer<f64>,
244 bar_index: usize,
245}
246
247impl RollingMinIndex {
248 pub fn new(timeperiod: usize) -> Self {
249 Self {
250 timeperiod,
251 window: RingBuffer::with_capacity(timeperiod),
252 bar_index: 0,
253 }
254 }
255
256 fn push(&mut self, v: f64) -> f64 {
257 let p = self.timeperiod;
258 if self.window.len() >= p {
259 let _ = self.window.pop_front();
260 }
261 self.window.push_back(v);
262 self.bar_index += 1;
263 if self.window.len() < p {
264 return f64::NAN;
265 }
266 let mut best_idx = 0usize;
267 let mut best_val = f64::INFINITY;
268 for (i, &x) in self.window.iter().enumerate() {
269 if x < best_val {
270 best_val = x;
271 best_idx = i;
272 }
273 }
274 (self.bar_index - p + best_idx) as f64
275 }
276}
277
278#[derive(Debug, Clone)]
279#[allow(non_camel_case_types)]
280pub struct MININDEX {
281 pub timeperiod: usize,
282 inner: RollingMinIndex,
283}
284
285impl MININDEX {
286 pub fn new(timeperiod: usize) -> Self {
287 Self {
288 timeperiod,
289 inner: RollingMinIndex::new(timeperiod),
290 }
291 }
292}
293
294impl Next<f64> for MININDEX {
295 type Output = f64;
296 fn next(&mut self, input: f64) -> Self::Output {
297 self.inner.push(input)
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304 use proptest::prelude::*;
305
306 proptest! {
307 #[test]
308 fn test_max_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
309 let period = 10;
310 let mut max = MAX::new(period);
311 let streaming: Vec<f64> = input.iter().map(|&x| max.next(x)).collect();
312 let batch = talib_rs::math_operator::max(&input, period)
313 .unwrap_or_else(|_| vec![f64::NAN; input.len()]);
314 for (s, b) in streaming.iter().zip(batch.iter()) {
315 if s.is_nan() { assert!(b.is_nan()); }
316 else { approx::assert_relative_eq!(s, b, epsilon = 1e-6); }
317 }
318 }
319 }
320}