finlib_ta/indicators/
fast_stochastic.rs1use core::fmt;
2
3use crate::errors::Result;
4use crate::indicators::{Maximum, Minimum};
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))]
44#[derive(Debug, Clone)]
45pub struct FastStochastic {
46 period: usize,
47 minimum: Minimum,
48 maximum: Maximum,
49}
50
51impl FastStochastic {
52 pub fn new(period: usize) -> Result<Self> {
53 Ok(Self {
54 period,
55 minimum: Minimum::new(period)?,
56 maximum: Maximum::new(period)?,
57 })
58 }
59}
60
61impl Period for FastStochastic {
62 fn period(&self) -> usize {
63 self.period
64 }
65}
66
67impl Next<f64> for FastStochastic {
68 type Output = f64;
69
70 fn next(&mut self, input: f64) -> Self::Output {
71 let min = self.minimum.next(input);
72 let max = self.maximum.next(input);
73
74 if min == max {
75 50.0
78 } else {
79 (input - min) / (max - min) * 100.0
80 }
81 }
82}
83
84impl<T: High + Low + Close> Next<&T> for FastStochastic {
85 type Output = f64;
86
87 fn next(&mut self, input: &T) -> Self::Output {
88 let highest = self.maximum.next(input.high());
89 let lowest = self.minimum.next(input.low());
90 let close = input.close();
91
92 if highest == lowest {
93 50.0
95 } else {
96 (close - lowest) / (highest - lowest) * 100.0
97 }
98 }
99}
100
101impl Reset for FastStochastic {
102 fn reset(&mut self) {
103 self.minimum.reset();
104 self.maximum.reset();
105 }
106}
107
108impl Default for FastStochastic {
109 fn default() -> Self {
110 Self::new(14).unwrap()
111 }
112}
113
114impl fmt::Display for FastStochastic {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 write!(f, "FAST_STOCH({})", self.period)
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use crate::test_helper::*;
124 use alloc::format;
125 use alloc::vec;
126
127 test_indicator!(FastStochastic);
128
129 #[test]
130 fn test_new() {
131 assert!(FastStochastic::new(0).is_err());
132 assert!(FastStochastic::new(1).is_ok());
133 }
134
135 #[test]
136 fn test_next_with_f64() {
137 let mut stoch = FastStochastic::new(3).unwrap();
138 assert_eq!(stoch.next(0.0), 50.0);
139 assert_eq!(stoch.next(200.0), 100.0);
140 assert_eq!(stoch.next(100.0), 50.0);
141 assert_eq!(stoch.next(120.0), 20.0);
142 assert_eq!(stoch.next(115.0), 75.0);
143 }
144
145 #[test]
146 fn test_next_with_bars() {
147 let test_data = vec![
148 (20.0, 20.0, 20.0, 50.0), (30.0, 10.0, 25.0, 75.0), (40.0, 20.0, 16.0, 20.0), (35.0, 15.0, 19.0, 30.0), (30.0, 20.0, 25.0, 40.0), (35.0, 25.0, 30.0, 75.0), ];
156
157 let mut stoch = FastStochastic::new(3).unwrap();
158
159 for (high, low, close, expected) in test_data {
160 let input_bar = Bar::new().high(high).low(low).close(close);
161 assert_eq!(stoch.next(&input_bar), expected);
162 }
163 }
164
165 #[test]
166 fn test_reset() {
167 let mut indicator = FastStochastic::new(10).unwrap();
168 assert_eq!(indicator.next(10.0), 50.0);
169 assert_eq!(indicator.next(210.0), 100.0);
170 assert_eq!(indicator.next(10.0), 0.0);
171 assert_eq!(indicator.next(60.0), 25.0);
172
173 indicator.reset();
174 assert_eq!(indicator.next(10.0), 50.0);
175 assert_eq!(indicator.next(20.0), 100.0);
176 assert_eq!(indicator.next(12.5), 25.0);
177 }
178
179 #[test]
180 fn test_default() {
181 FastStochastic::default();
182 }
183
184 #[test]
185 fn test_display() {
186 let indicator = FastStochastic::new(21).unwrap();
187 assert_eq!(format!("{}", indicator), "FAST_STOCH(21)");
188 }
189}