quantwave_core/indicators/incremental/
apo.rs1use crate::indicators::incremental::ma_stream::MaStream;
4use crate::traits::Next;
5use talib_rs::MaType;
6
7#[derive(Debug, Clone)]
9#[allow(non_camel_case_types)]
10pub struct APO {
11 pub fastperiod: usize,
12 pub slowperiod: usize,
13 pub matype: MaType,
14 fast_ma: MaStream,
15 slow_ma: MaStream,
16}
17
18impl APO {
19 pub fn new(fastperiod: usize, slowperiod: usize, matype: MaType) -> Self {
20 Self {
21 fastperiod,
22 slowperiod,
23 matype,
24 fast_ma: MaStream::new(fastperiod, matype),
25 slow_ma: MaStream::new(slowperiod, matype),
26 }
27 }
28}
29
30impl Next<f64> for APO {
31 type Output = f64;
32
33 fn next(&mut self, input: f64) -> Self::Output {
34 let fast = self.fast_ma.next(input);
35 let slow = self.slow_ma.next(input);
36 if fast.is_nan() || slow.is_nan() {
37 f64::NAN
38 } else {
39 fast - slow
40 }
41 }
42}
43
44#[derive(Debug, Clone)]
46#[allow(non_camel_case_types)]
47pub struct PPO {
48 pub fastperiod: usize,
49 pub slowperiod: usize,
50 pub matype: MaType,
51 fast_ma: MaStream,
52 slow_ma: MaStream,
53}
54
55impl PPO {
56 pub fn new(fastperiod: usize, slowperiod: usize, matype: MaType) -> Self {
57 Self {
58 fastperiod,
59 slowperiod,
60 matype,
61 fast_ma: MaStream::new(fastperiod, matype),
62 slow_ma: MaStream::new(slowperiod, matype),
63 }
64 }
65}
66
67impl Next<f64> for PPO {
68 type Output = f64;
69
70 fn next(&mut self, input: f64) -> Self::Output {
71 let fast = self.fast_ma.next(input);
72 let slow = self.slow_ma.next(input);
73 if fast.is_nan() || slow.is_nan() || slow == 0.0 {
74 f64::NAN
75 } else {
76 (fast - slow) / slow * 100.0
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use proptest::prelude::*;
85
86 proptest! {
87 #[test]
88 fn test_apo_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
89 let fast = 12;
90 let slow = 26;
91 let matype = MaType::Ema;
92 let mut apo = APO::new(fast, slow, matype);
93 let streaming: Vec<f64> = input.iter().map(|&x| apo.next(x)).collect();
94 let batch = talib_rs::momentum::apo(&input, fast, slow, matype)
95 .unwrap_or_else(|_| vec![f64::NAN; input.len()]);
96 for (s, b) in streaming.iter().zip(batch.iter()) {
97 if s.is_nan() { assert!(b.is_nan()); }
98 else { approx::assert_relative_eq!(s, b, epsilon = 1e-6); }
99 }
100 }
101
102 #[test]
103 fn test_ppo_parity(input in prop::collection::vec(0.1..100.0, 1..100)) {
104 let fast = 12;
105 let slow = 26;
106 let matype = MaType::Ema;
107 let mut ppo = PPO::new(fast, slow, matype);
108 let streaming: Vec<f64> = input.iter().map(|&x| ppo.next(x)).collect();
109 let batch = talib_rs::momentum::ppo(&input, fast, slow, matype)
110 .unwrap_or_else(|_| vec![f64::NAN; input.len()]);
111 for (s, b) in streaming.iter().zip(batch.iter()) {
112 if s.is_nan() { assert!(b.is_nan()); }
113 else { approx::assert_relative_eq!(s, b, epsilon = 1e-6); }
114 }
115 }
116 }
117}