quantwave_core/indicators/incremental/
volume_ta.rs1use crate::indicators::incremental::talib_ema::TalibEma;
4use crate::traits::Next;
5
6#[derive(Debug, Clone)]
8#[allow(non_camel_case_types)]
9pub struct AD {
10 cumulative: f64,
11}
12
13impl AD {
14 pub fn new() -> Self {
15 Self { cumulative: 0.0 }
16 }
17}
18
19impl Next<(f64, f64, f64, f64)> for AD {
20 type Output = f64;
21
22 fn next(&mut self, (high, low, close, volume): (f64, f64, f64, f64)) -> Self::Output {
23 let hl = high - low;
24 let mfm = if hl > 0.0 {
25 ((close - low) - (high - close)) / hl
26 } else {
27 0.0
28 };
29 self.cumulative += mfm * volume;
30 self.cumulative
31 }
32}
33
34#[derive(Debug, Clone)]
36#[allow(non_camel_case_types)]
37pub struct ADOSC {
38 pub fastperiod: usize,
39 pub slowperiod: usize,
40 ad: AD,
41 fast_ema: TalibEma,
42 slow_ema: TalibEma,
43}
44
45impl ADOSC {
46 pub fn new(fastperiod: usize, slowperiod: usize) -> Self {
47 Self {
48 fastperiod,
49 slowperiod,
50 ad: AD::new(),
51 fast_ema: TalibEma::new(fastperiod),
52 slow_ema: TalibEma::new(slowperiod),
53 }
54 }
55}
56
57impl Next<(f64, f64, f64, f64)> for ADOSC {
58 type Output = f64;
59
60 fn next(&mut self, ohlcv: (f64, f64, f64, f64)) -> Self::Output {
61 let ad_val = self.ad.next(ohlcv);
62 let fast = self.fast_ema.next(ad_val);
63 let slow = self.slow_ema.next(ad_val);
64 if fast.is_nan() || slow.is_nan() {
65 f64::NAN
66 } else {
67 fast - slow
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use proptest::prelude::*;
76
77 proptest! {
78 #[test]
79 fn test_ad_parity(
80 h in prop::collection::vec(10.0..100.0, 1..100),
81 l in prop::collection::vec(10.0..100.0, 1..100),
82 c in prop::collection::vec(10.0..100.0, 1..100),
83 v in prop::collection::vec(1.0..1000.0, 1..100)
84 ) {
85 let len = h.len().min(l.len()).min(c.len()).min(v.len());
86 let mut ad = AD::new();
87 let mut high = Vec::with_capacity(len);
88 let mut low = Vec::with_capacity(len);
89 let mut close = Vec::with_capacity(len);
90 let mut vol = Vec::with_capacity(len);
91 for i in 0..len {
92 let v_h: f64 = h[i];
93 let v_l: f64 = l[i];
94 let v_c: f64 = c[i];
95 let v_v: f64 = v[i];
96 high.push(v_h.max(v_l).max(v_c));
97 low.push(v_h.min(v_l).min(v_c));
98 close.push(v_c);
99 vol.push(v_v);
100 }
101 let streaming: Vec<f64> = (0..len)
102 .map(|i| ad.next((high[i], low[i], close[i], vol[i])))
103 .collect();
104 let batch = talib_rs::volume::ad(&high, &low, &close, &vol)
105 .unwrap_or_else(|_| vec![f64::NAN; len]);
106 for (s, b) in streaming.iter().zip(batch.iter()) {
107 if s.is_nan() { assert!(b.is_nan()); }
108 else { approx::assert_relative_eq!(s, b, epsilon = 1e-6); }
109 }
110 }
111 }
112}