ux_indicators/indicators/
fsto.rs

1#![allow(dead_code)]
2
3use std::fmt;
4
5use crate::errors::*;
6use crate::indicators::{Maximum, Minimum};
7use crate::{Close, High, Low, Next, Reset};
8
9use crate::{Factory};
10use crate::indicators::SimpleMovingAverage;
11
12pub struct FstoFactory {
13}
14
15impl FstoFactory {
16    pub fn new() -> Self {
17        Self{}
18    }
19}
20
21impl Factory for FstoFactory {
22    fn create() -> Box<dyn Next<f64, Output = Box<[f64]>>> {
23        Box::new(SimpleMovingAverage::default())
24    }
25}
26
27/// Fast stochastic oscillator.
28///
29/// The stochastic oscillator is a momentum indicator comparing the closing price
30/// of a security to the range of its prices over a certain period of time.
31///
32/// # Formula
33///
34/// ![Fast stochastic oscillator formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/5a419041034a8044308c999f85661a08bcf91b1d)
35///
36/// Where:
37///
38/// * \%K<sub>t</sub> - value of fast stochastic oscillator
39/// * C<sub>t</sub> - close price of the current period
40/// * L<sub>n</sub> - lowest price for the last _n_ periods
41/// * H<sub>n</sub> - highest price for the last _n_ periods
42///
43///
44/// # Parameters
45///
46/// * _length_ - number of periods (integer greater than 0). Default is 14.
47///
48/// # Example
49///
50/// ```
51/// use core::indicators::FastStochastic;
52/// use core::Next;
53///
54/// let mut stoch = FastStochastic::new(5).unwrap();
55/// assert_eq!(stoch.next(20.0), 50.0);
56/// assert_eq!(stoch.next(30.0), 100.0);
57/// assert_eq!(stoch.next(40.0), 100.0);
58/// assert_eq!(stoch.next(35.0), 75.0);
59/// assert_eq!(stoch.next(15.0), 0.0);
60/// ```
61
62#[derive(Debug, Clone)]
63pub struct FastStochastic {
64    length: u32,
65    minimum: Minimum,
66    maximum: Maximum,
67}
68
69impl FastStochastic {
70    pub fn new(length: u32) -> Result<Self> {
71        let indicator = Self {
72            length: length,
73            minimum: Minimum::new(length)?,
74            maximum: Maximum::new(length)?,
75        };
76        Ok(indicator)
77    }
78
79    pub fn length(&self) -> u32 {
80        self.length
81    }
82}
83
84impl Next<f64> for FastStochastic {
85    type Output = f64;
86
87    fn next(&mut self, input: f64) -> Self::Output {
88        let min = self.minimum.next(input);
89        let max = self.maximum.next(input);
90
91        if min == max {
92            // When only 1 input was given, than min and max are the same,
93            // therefore it makes sense to return 50
94            50.0
95        } else {
96            (input - min) / (max - min) * 100.0
97        }
98    }
99}
100
101impl<'a, T: High + Low + Close> Next<&'a T> for FastStochastic {
102    type Output = f64;
103
104    fn next(&mut self, input: &'a T) -> Self::Output {
105        let highest = self.maximum.next(input.high());
106        let lowest = self.minimum.next(input.low());
107        let close = input.close();
108
109        if highest == lowest {
110            // To avoid division by zero, return 50.0
111            50.0
112        } else {
113            (close - lowest) / (highest - lowest) * 100.0
114        }
115    }
116}
117
118impl Reset for FastStochastic {
119    fn reset(&mut self) {
120        self.minimum.reset();
121        self.maximum.reset();
122    }
123}
124
125impl Default for FastStochastic {
126    fn default() -> Self {
127        Self::new(14).unwrap()
128    }
129}
130
131impl fmt::Display for FastStochastic {
132    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133        write!(f, "FAST_STOCH({})", self.length)
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use crate::test_helper::*;
141
142    // test_indicator!(FastStochastic);
143
144    #[test]
145    fn test_new() {
146        assert!(FastStochastic::new(0).is_err());
147        assert!(FastStochastic::new(1).is_ok());
148    }
149
150    #[test]
151    fn test_next_with_f64() {
152        let mut stoch = FastStochastic::new(3).unwrap();
153        assert_eq!(stoch.next(0.0), 50.0);
154        assert_eq!(stoch.next(200.0), 100.0);
155        assert_eq!(stoch.next(100.0), 50.0);
156        assert_eq!(stoch.next(120.0), 20.0);
157        assert_eq!(stoch.next(115.0), 75.0);
158    }
159
160    #[test]
161    fn test_next_with_bars() {
162        let test_data = vec![
163            // high, low , close, expected
164            (20.0, 20.0, 20.0, 50.0), // min = 20, max = 20
165            (30.0, 10.0, 25.0, 75.0), // min = 10, max = 30
166            (40.0, 20.0, 16.0, 20.0), // min = 10, max = 40
167            (35.0, 15.0, 19.0, 30.0), // min = 10, max = 40
168            (30.0, 20.0, 25.0, 40.0), // min = 15, max = 40
169            (35.0, 25.0, 30.0, 75.0), // min = 15, max = 35
170        ];
171
172        let mut stoch = FastStochastic::new(3).unwrap();
173
174        for (high, low, close, expected) in test_data {
175            let input_bar = Bar::new().high(high).low(low).close(close);
176            assert_eq!(stoch.next(&input_bar), expected);
177        }
178    }
179
180    #[test]
181    fn test_reset() {
182        let mut indicator = FastStochastic::new(10).unwrap();
183        assert_eq!(indicator.next(10.0), 50.0);
184        assert_eq!(indicator.next(210.0), 100.0);
185        assert_eq!(indicator.next(10.0), 0.0);
186        assert_eq!(indicator.next(60.0), 25.0);
187
188        indicator.reset();
189        assert_eq!(indicator.next(10.0), 50.0);
190        assert_eq!(indicator.next(20.0), 100.0);
191        assert_eq!(indicator.next(12.5), 25.0);
192    }
193
194    #[test]
195    fn test_default() {
196        FastStochastic::default();
197    }
198
199    #[test]
200    fn test_display() {
201        let indicator = FastStochastic::new(21).unwrap();
202        assert_eq!(format!("{}", indicator), "FAST_STOCH(21)");
203    }
204}