cxmr_ta_core/indicators/
slow_stochastic.rs

1use std::fmt;
2
3use crate::errors::Result;
4use crate::indicators::{ExponentialMovingAverage, FastStochastic};
5use crate::{Calculate, 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 ta::indicators::SlowStochastic;
20/// use ta::{Calculate, Next};
21///
22/// let mut stoch = SlowStochastic::new(3, 2).unwrap();
23/// assert_eq!(stoch.calc(10.0), 50.0);
24/// assert_eq!(stoch.calc(50.0).round(), 83.0);
25/// assert_eq!(stoch.calc(50.0).round(), 94.0);
26/// assert_eq!(stoch.calc(30.0).round(), 31.0);
27/// assert_eq!(stoch.calc(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 Calculate for SlowStochastic {
46    fn calc(&mut self, input: f64) -> f64 {
47        self.ema.calc(self.fast_stochastic.calc(input))
48    }
49}
50
51impl<T: High + Low + Close> Next<T> for SlowStochastic {
52    fn next(&mut self, input: &T) -> f64 {
53        self.ema.calc(self.fast_stochastic.next(input))
54    }
55}
56
57impl Reset for SlowStochastic {
58    fn reset(&mut self) {
59        self.fast_stochastic.reset();
60        self.ema.reset();
61    }
62}
63
64impl Default for SlowStochastic {
65    fn default() -> Self {
66        Self::new(14, 3).unwrap()
67    }
68}
69
70impl fmt::Display for SlowStochastic {
71    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72        write!(
73            f,
74            "SLOW_STOCH({}, {})",
75            self.fast_stochastic.length(),
76            self.ema.length()
77        )
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use crate::test_helper::*;
85
86    test_indicator!(SlowStochastic);
87
88    #[test]
89    fn test_new() {
90        assert!(SlowStochastic::new(0, 1).is_err());
91        assert!(SlowStochastic::new(1, 0).is_err());
92        assert!(SlowStochastic::new(1, 1).is_ok());
93    }
94
95    #[test]
96    fn test_next_with_f64() {
97        let mut stoch = SlowStochastic::new(3, 2).unwrap();
98        assert_eq!(stoch.calc(10.0), 50.0);
99        assert_eq!(stoch.calc(50.0).round(), 83.0);
100        assert_eq!(stoch.calc(50.0).round(), 94.0);
101        assert_eq!(stoch.calc(30.0).round(), 31.0);
102        assert_eq!(stoch.calc(55.0).round(), 77.0);
103    }
104
105    #[test]
106    fn test_next_with_bars() {
107        let test_data = vec![
108            // high, low , close, expected
109            (30.0, 10.0, 25.0, 75.0),
110            (20.0, 20.0, 20.0, 58.0),
111            (40.0, 20.0, 16.0, 33.0),
112            (35.0, 15.0, 19.0, 22.0),
113            (30.0, 20.0, 25.0, 34.0),
114            (35.0, 25.0, 30.0, 61.0),
115        ];
116
117        let mut stoch = SlowStochastic::new(3, 2).unwrap();
118
119        for (high, low, close, expected) in test_data {
120            let input_bar = Bar::new().high(high).low(low).close(close);
121            assert_eq!(stoch.next(&input_bar).round(), expected);
122        }
123    }
124
125    #[test]
126    fn test_reset() {
127        let mut stoch = SlowStochastic::new(3, 2).unwrap();
128        assert_eq!(stoch.calc(10.0), 50.0);
129        assert_eq!(stoch.calc(50.0).round(), 83.0);
130        assert_eq!(stoch.calc(50.0).round(), 94.0);
131
132        stoch.reset();
133        assert_eq!(stoch.calc(10.0), 50.0);
134    }
135
136    #[test]
137    fn test_default() {
138        SlowStochastic::default();
139    }
140
141    #[test]
142    fn test_display() {
143        let indicator = SlowStochastic::new(10, 2).unwrap();
144        assert_eq!(format!("{}", indicator), "SLOW_STOCH(10, 2)");
145    }
146}