finlib_ta/indicators/
slow_stochastic.rs

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