quantaxis_rs/indicators/
slow_stochastic.rs

1use std::fmt;
2
3use crate::errors::Result;
4use crate::indicators::{ExponentialMovingAverage, FastStochastic};
5use crate::{Close, High, Low, Next, Reset};
6
7/// Slow stochastic oscillator.
8///
9/// Basically it is a fast stochastic oscillator smoothed with exponential moving average.
10///
11/// # Parameters
12///
13/// * _stochastic_n_ - number of periods for fast stochastic (integer greater than 0). Default is 14.
14/// *_ema_n_ - length for EMA (integer greater than 0). Default is 3.
15///
16/// # Example
17///
18/// ```
19/// use quantaxis_rs::indicators::SlowStochastic;
20/// use quantaxis_rs::Next;
21///
22/// let mut stoch = SlowStochastic::new(3, 2).unwrap();
23/// assert_eq!(stoch.next(10.0), 50.0);
24/// assert_eq!(stoch.next(50.0).round(), 83.0);
25/// assert_eq!(stoch.next(50.0).round(), 94.0);
26/// assert_eq!(stoch.next(30.0).round(), 31.0);
27/// assert_eq!(stoch.next(55.0).round(), 77.0);
28/// ```
29#[derive(Clone, Debug)]
30pub struct SlowStochastic {
31    fast_stochastic: FastStochastic,
32    ema: ExponentialMovingAverage,
33}
34
35impl SlowStochastic {
36    pub fn new(stochastic_n: u32, ema_n: u32) -> Result<Self> {
37        let indicator = Self {
38            fast_stochastic: FastStochastic::new(stochastic_n)?,
39            ema: ExponentialMovingAverage::new(ema_n)?,
40        };
41        Ok(indicator)
42    }
43}
44
45impl Next<f64> for SlowStochastic {
46    type Output = f64;
47
48    fn next(&mut self, input: f64) -> Self::Output {
49        self.ema.next(self.fast_stochastic.next(input))
50    }
51}
52
53impl<'a, T: High + Low + Close> Next<&'a T> for SlowStochastic {
54    type Output = f64;
55
56    fn next(&mut self, input: &'a T) -> Self::Output {
57        self.ema.next(self.fast_stochastic.next(input))
58    }
59}
60
61impl Reset for SlowStochastic {
62    fn reset(&mut self) {
63        self.fast_stochastic.reset();
64        self.ema.reset();
65    }
66}
67
68impl Default for SlowStochastic {
69    fn default() -> Self {
70        Self::new(14, 3).unwrap()
71    }
72}
73
74impl fmt::Display for SlowStochastic {
75    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76        write!(
77            f,
78            "SLOW_STOCH({}, {})",
79            self.fast_stochastic.length(),
80            self.ema.length()
81        )
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use crate::test_helper::*;
89    macro_rules! test_indicator {
90        ($i:tt) => {
91            #[test]
92            fn test_indicator() {
93                let bar = Bar::new();
94
95                // ensure Default trait is implemented
96                let mut indicator = $i::default();
97
98                // ensure Next<f64> is implemented
99                let first_output = indicator.next(12.3);
100
101                // ensure next accepts &DataItem as well
102                indicator.next(&bar);
103
104                // ensure Reset is implemented and works correctly
105                indicator.reset();
106                assert_eq!(indicator.next(12.3), first_output);
107
108                // ensure Display is implemented
109                format!("{}", indicator);
110            }
111        };
112    }
113    test_indicator!(SlowStochastic);
114
115    #[test]
116    fn test_new() {
117        assert!(SlowStochastic::new(0, 1).is_err());
118        assert!(SlowStochastic::new(1, 0).is_err());
119        assert!(SlowStochastic::new(1, 1).is_ok());
120    }
121
122    #[test]
123    fn test_next_with_f64() {
124        let mut stoch = SlowStochastic::new(3, 2).unwrap();
125        assert_eq!(stoch.next(10.0), 50.0);
126        assert_eq!(stoch.next(50.0).round(), 83.0);
127        assert_eq!(stoch.next(50.0).round(), 94.0);
128        assert_eq!(stoch.next(30.0).round(), 31.0);
129        assert_eq!(stoch.next(55.0).round(), 77.0);
130    }
131
132    #[test]
133    fn test_next_with_bars() {
134        let test_data = vec![
135            // high, low , close, expected
136            (30.0, 10.0, 25.0, 75.0),
137            (20.0, 20.0, 20.0, 58.0),
138            (40.0, 20.0, 16.0, 33.0),
139            (35.0, 15.0, 19.0, 22.0),
140            (30.0, 20.0, 25.0, 34.0),
141            (35.0, 25.0, 30.0, 61.0),
142        ];
143
144        let mut stoch = SlowStochastic::new(3, 2).unwrap();
145
146        for (high, low, close, expected) in test_data {
147            let input_bar = Bar::new().high(high).low(low).close(close);
148            assert_eq!(stoch.next(&input_bar).round(), expected);
149        }
150    }
151
152    #[test]
153    fn test_reset() {
154        let mut stoch = SlowStochastic::new(3, 2).unwrap();
155        assert_eq!(stoch.next(10.0), 50.0);
156        assert_eq!(stoch.next(50.0).round(), 83.0);
157        assert_eq!(stoch.next(50.0).round(), 94.0);
158
159        stoch.reset();
160        assert_eq!(stoch.next(10.0), 50.0);
161    }
162
163    #[test]
164    fn test_default() {
165        SlowStochastic::default();
166    }
167
168    #[test]
169    fn test_display() {
170        let indicator = SlowStochastic::new(10, 2).unwrap();
171        assert_eq!(format!("{}", indicator), "SLOW_STOCH(10, 2)");
172    }
173}