finlib_ta/indicators/
slow_stochastic.rs1use 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#[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 (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}