quantwave_core/indicators/incremental/
sar.rs1use crate::traits::Next;
4
5#[derive(Debug, Clone)]
7#[allow(non_camel_case_types)]
8pub struct SAR {
9 pub acceleration: f64,
10 pub maximum: f64,
11 bar: usize,
12 is_long: bool,
13 sar_val: f64,
14 ep: f64,
15 af: f64,
16 prev_low: f64,
17 prev_high: f64,
18 low0: f64,
19 high0: f64,
20 high1: f64,
21 low1: f64,
22}
23
24impl SAR {
25 pub fn new(acceleration: f64, maximum: f64) -> Self {
26 Self {
27 acceleration,
28 maximum,
29 bar: 0,
30 is_long: false,
31 sar_val: 0.0,
32 ep: 0.0,
33 af: acceleration,
34 prev_low: 0.0,
35 prev_high: 0.0,
36 low0: 0.0,
37 high0: 0.0,
38 high1: 0.0,
39 low1: 0.0,
40 }
41 }
42
43 fn init_direction(&self) -> bool {
44 let diff_m = self.low0 - self.low1;
45 let diff_p = self.high1 - self.high0;
46 !(diff_m > 0.0 && diff_m > diff_p)
47 }
48}
49
50impl Next<(f64, f64)> for SAR {
51 type Output = f64;
52
53 fn next(&mut self, (high, low): (f64, f64)) -> Self::Output {
54 let i = self.bar;
55 self.bar += 1;
56
57 if i == 0 {
58 self.high0 = high;
59 self.low0 = low;
60 return f64::NAN;
61 }
62
63 if i == 1 {
64 self.high1 = high;
65 self.low1 = low;
66 self.is_long = self.init_direction();
67 self.af = self.acceleration;
68 if self.is_long {
69 self.ep = high;
70 self.sar_val = self.low0;
71 } else {
72 self.ep = low;
73 self.sar_val = self.high0;
74 }
75 let out = self.step_bar1(high, low);
76 self.prev_low = low;
77 self.prev_high = high;
78 return out;
79 }
80
81 let p_low = self.prev_low;
82 let p_high = self.prev_high;
83 self.prev_low = low;
84 self.prev_high = high;
85 self.step_main(high, low, p_low, p_high)
86 }
87}
88
89impl SAR {
90 fn step_bar1(&mut self, new_high: f64, new_low: f64) -> f64 {
91 let p_low = new_low;
92 let p_high = new_high;
93
94 if self.is_long {
95 if new_low <= self.sar_val {
96 self.is_long = false;
97 self.sar_val = self.ep;
98 self.sar_val = self.sar_val.max(p_high).max(new_high);
99 let out = self.sar_val;
100 self.af = self.acceleration;
101 self.ep = new_low;
102 self.sar_val += self.af * (self.ep - self.sar_val);
103 self.sar_val = self.sar_val.max(p_high).max(new_high);
104 out
105 } else {
106 let out = self.sar_val;
107 if new_high > self.ep {
108 self.ep = new_high;
109 self.af = (self.af + self.acceleration).min(self.maximum);
110 }
111 self.sar_val += self.af * (self.ep - self.sar_val);
112 self.sar_val = self.sar_val.min(p_low).min(new_low);
113 out
114 }
115 } else if new_high >= self.sar_val {
116 self.is_long = true;
117 self.sar_val = self.ep;
118 self.sar_val = self.sar_val.min(p_low).min(new_low);
119 let out = self.sar_val;
120 self.af = self.acceleration;
121 self.ep = new_high;
122 self.sar_val += self.af * (self.ep - self.sar_val);
123 self.sar_val = self.sar_val.min(p_low).min(new_low);
124 out
125 } else {
126 let out = self.sar_val;
127 if new_low < self.ep {
128 self.ep = new_low;
129 self.af = (self.af + self.acceleration).min(self.maximum);
130 }
131 self.sar_val += self.af * (self.ep - self.sar_val);
132 self.sar_val = self.sar_val.max(p_high).max(new_high);
133 out
134 }
135 }
136
137 fn step_main(&mut self, new_high: f64, new_low: f64, p_low: f64, p_high: f64) -> f64 {
138 if self.is_long {
139 if new_low <= self.sar_val {
140 self.is_long = false;
141 self.sar_val = self.ep;
142 self.sar_val = self.sar_val.max(p_high).max(new_high);
143 let out = self.sar_val;
144 self.af = self.acceleration;
145 self.ep = new_low;
146 self.sar_val += self.af * (self.ep - self.sar_val);
147 self.sar_val = self.sar_val.max(p_high).max(new_high);
148 out
149 } else {
150 let out = self.sar_val;
151 if new_high > self.ep {
152 self.ep = new_high;
153 self.af = (self.af + self.acceleration).min(self.maximum);
154 }
155 self.sar_val += self.af * (self.ep - self.sar_val);
156 self.sar_val = self.sar_val.min(p_low).min(new_low);
157 out
158 }
159 } else if new_high >= self.sar_val {
160 self.is_long = true;
161 self.sar_val = self.ep;
162 self.sar_val = self.sar_val.min(p_low).min(new_low);
163 let out = self.sar_val;
164 self.af = self.acceleration;
165 self.ep = new_high;
166 self.sar_val += self.af * (self.ep - self.sar_val);
167 self.sar_val = self.sar_val.min(p_low).min(new_low);
168 out
169 } else {
170 let out = self.sar_val;
171 if new_low < self.ep {
172 self.ep = new_low;
173 self.af = (self.af + self.acceleration).min(self.maximum);
174 }
175 self.sar_val += self.af * (self.ep - self.sar_val);
176 self.sar_val = self.sar_val.max(p_high).max(new_high);
177 out
178 }
179 }
180}
181
182#[derive(Debug, Clone)]
184#[allow(non_camel_case_types)]
185pub struct SAREXT {
186 pub startvalue: f64,
187 pub offsetonreverse: f64,
188 pub accelerationinitlong: f64,
189 pub accelerationlong: f64,
190 pub accelerationmaxlong: f64,
191 pub accelerationinitshort: f64,
192 pub accelerationshort: f64,
193 pub accelerationmaxshort: f64,
194 bar: usize,
195 is_long: bool,
196 sar_val: f64,
197 ep: f64,
198 af_long: f64,
199 af_short: f64,
200 prev_low: f64,
201 prev_high: f64,
202 low0: f64,
203 high0: f64,
204 high1: f64,
205 low1: f64,
206}
207
208impl SAREXT {
209 #[allow(clippy::too_many_arguments)]
210 pub fn new(
211 startvalue: f64,
212 offsetonreverse: f64,
213 accelerationinitlong: f64,
214 accelerationlong: f64,
215 accelerationmaxlong: f64,
216 accelerationinitshort: f64,
217 accelerationshort: f64,
218 accelerationmaxshort: f64,
219 ) -> Self {
220 Self {
221 startvalue,
222 offsetonreverse,
223 accelerationinitlong,
224 accelerationlong,
225 accelerationmaxlong,
226 accelerationinitshort,
227 accelerationshort,
228 accelerationmaxshort,
229 bar: 0,
230 is_long: false,
231 sar_val: 0.0,
232 ep: 0.0,
233 af_long: accelerationinitlong,
234 af_short: accelerationinitshort,
235 prev_low: 0.0,
236 prev_high: 0.0,
237 low0: 0.0,
238 high0: 0.0,
239 high1: 0.0,
240 low1: 0.0,
241 }
242 }
243
244 fn init_direction(&self) -> bool {
245 let diff_m = self.low0 - self.low1;
246 let diff_p = self.high1 - self.high0;
247 !(diff_m > 0.0 && diff_m > diff_p)
248 }
249
250 fn init_state(&mut self) {
251 if self.startvalue == 0.0 {
252 self.is_long = self.init_direction();
253 if self.is_long {
254 self.ep = self.high1;
255 self.sar_val = self.low0;
256 } else {
257 self.ep = self.low1;
258 self.sar_val = self.high0;
259 }
260 } else if self.startvalue > 0.0 {
261 self.is_long = true;
262 self.ep = self.high1;
263 self.sar_val = self.startvalue;
264 } else {
265 self.is_long = false;
266 self.ep = self.low1;
267 self.sar_val = self.startvalue.abs();
268 }
269 self.af_long = self.accelerationinitlong;
270 self.af_short = self.accelerationinitshort;
271 }
272}
273
274impl Next<(f64, f64)> for SAREXT {
275 type Output = f64;
276
277 fn next(&mut self, (high, low): (f64, f64)) -> Self::Output {
278 let i = self.bar;
279 self.bar += 1;
280
281 if i == 0 {
282 self.high0 = high;
283 self.low0 = low;
284 return f64::NAN;
285 }
286
287 if i == 1 {
288 self.high1 = high;
289 self.low1 = low;
290 self.init_state();
291 let out = self.step_bar1(high, low);
292 self.prev_low = low;
293 self.prev_high = high;
294 return out;
295 }
296
297 let p_low = self.prev_low;
298 let p_high = self.prev_high;
299 self.prev_low = low;
300 self.prev_high = high;
301 self.step_main(high, low, p_low, p_high)
302 }
303}
304
305impl SAREXT {
306 fn step_bar1(&mut self, new_high: f64, new_low: f64) -> f64 {
307 let p_low = new_low;
308 let p_high = new_high;
309
310 if self.is_long {
311 if new_low <= self.sar_val {
312 self.is_long = false;
313 self.sar_val = self.ep;
314 self.sar_val = self.sar_val.max(p_high).max(new_high);
315 if self.offsetonreverse != 0.0 {
316 self.sar_val += self.sar_val * self.offsetonreverse;
317 }
318 let out = -self.sar_val;
319 self.af_short = self.accelerationinitshort;
320 self.ep = new_low;
321 self.sar_val += self.af_short * (self.ep - self.sar_val);
322 self.sar_val = self.sar_val.max(p_high).max(new_high);
323 out
324 } else {
325 let out = self.sar_val;
326 if new_high > self.ep {
327 self.ep = new_high;
328 self.af_long = (self.af_long + self.accelerationlong)
329 .min(self.accelerationmaxlong);
330 }
331 self.sar_val += self.af_long * (self.ep - self.sar_val);
332 self.sar_val = self.sar_val.min(p_low).min(new_low);
333 out
334 }
335 } else if new_high >= self.sar_val {
336 self.is_long = true;
337 self.sar_val = self.ep;
338 self.sar_val = self.sar_val.min(p_low).min(new_low);
339 if self.offsetonreverse != 0.0 {
340 self.sar_val -= self.sar_val * self.offsetonreverse;
341 }
342 let out = self.sar_val;
343 self.af_long = self.accelerationinitlong;
344 self.ep = new_high;
345 self.sar_val += self.af_long * (self.ep - self.sar_val);
346 self.sar_val = self.sar_val.min(p_low).min(new_low);
347 out
348 } else {
349 let out = -self.sar_val;
350 if new_low < self.ep {
351 self.ep = new_low;
352 self.af_short = (self.af_short + self.accelerationshort)
353 .min(self.accelerationmaxshort);
354 }
355 self.sar_val += self.af_short * (self.ep - self.sar_val);
356 self.sar_val = self.sar_val.max(p_high).max(new_high);
357 out
358 }
359 }
360
361 fn step_main(&mut self, new_high: f64, new_low: f64, p_low: f64, p_high: f64) -> f64 {
362 if self.is_long {
363 if new_low <= self.sar_val {
364 self.is_long = false;
365 self.sar_val = self.ep;
366 self.sar_val = self.sar_val.max(p_high).max(new_high);
367 if self.offsetonreverse != 0.0 {
368 self.sar_val += self.sar_val * self.offsetonreverse;
369 }
370 let out = -self.sar_val;
371 self.af_short = self.accelerationinitshort;
372 self.ep = new_low;
373 self.sar_val += self.af_short * (self.ep - self.sar_val);
374 self.sar_val = self.sar_val.max(p_high).max(new_high);
375 out
376 } else {
377 let out = self.sar_val;
378 if new_high > self.ep {
379 self.ep = new_high;
380 self.af_long = (self.af_long + self.accelerationlong)
381 .min(self.accelerationmaxlong);
382 }
383 self.sar_val += self.af_long * (self.ep - self.sar_val);
384 self.sar_val = self.sar_val.min(p_low).min(new_low);
385 out
386 }
387 } else if new_high >= self.sar_val {
388 self.is_long = true;
389 self.sar_val = self.ep;
390 self.sar_val = self.sar_val.min(p_low).min(new_low);
391 if self.offsetonreverse != 0.0 {
392 self.sar_val -= self.sar_val * self.offsetonreverse;
393 }
394 let out = self.sar_val;
395 self.af_long = self.accelerationinitlong;
396 self.ep = new_high;
397 self.sar_val += self.af_long * (self.ep - self.sar_val);
398 self.sar_val = self.sar_val.min(p_low).min(new_low);
399 out
400 } else {
401 let out = -self.sar_val;
402 if new_low < self.ep {
403 self.ep = new_low;
404 self.af_short = (self.af_short + self.accelerationshort)
405 .min(self.accelerationmaxshort);
406 }
407 self.sar_val += self.af_short * (self.ep - self.sar_val);
408 self.sar_val = self.sar_val.max(p_high).max(new_high);
409 out
410 }
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use super::*;
417 use proptest::prelude::*;
418
419 proptest! {
420 #[test]
421 fn test_sar_parity(
422 h in prop::collection::vec(10.0..100.0, 2..100),
423 l in prop::collection::vec(10.0..100.0, 2..100)
424 ) {
425 let len = h.len().min(l.len());
426 let mut high = Vec::with_capacity(len);
427 let mut low = Vec::with_capacity(len);
428 for i in 0..len {
429 let hi: f64 = h[i];
430 let lo: f64 = l[i];
431 high.push(hi.max(lo));
432 low.push(hi.min(lo));
433 }
434 let accel = 0.02;
435 let max = 0.2;
436 let mut sar = SAR::new(accel, max);
437 let streaming: Vec<f64> = (0..len).map(|i| sar.next((high[i], low[i]))).collect();
438 let batch = talib_rs::overlap::sar(&high, &low, accel, max)
439 .unwrap_or_else(|_| vec![f64::NAN; len]);
440 for (s, b) in streaming.iter().zip(batch.iter()) {
441 if s.is_nan() { assert!(b.is_nan()); }
442 else { approx::assert_relative_eq!(s, b, epsilon = 1e-6); }
443 }
444 }
445 }
446}