use std::fmt;
use crate::errors::*;
use crate::indicators::{Maximum, Minimum};
use crate::{Close, High, Low, Next, Reset};
#[derive(Debug, Clone)]
pub struct FastStochastic {
length: u32,
minimum: Minimum,
maximum: Maximum,
}
impl FastStochastic {
pub fn new(length: u32) -> Result<Self> {
let indicator = Self {
length: length,
minimum: Minimum::new(length)?,
maximum: Maximum::new(length)?,
};
Ok(indicator)
}
pub fn length(&self) -> u32 {
self.length
}
}
impl Next<f64> for FastStochastic {
type Output = f64;
fn next(&mut self, input: f64) -> Self::Output {
let min = self.minimum.next(input);
let max = self.maximum.next(input);
if min == max {
50.0
} else {
(input - min) / (max - min) * 100.0
}
}
}
impl<'a, T: High + Low + Close> Next<&'a T> for FastStochastic {
type Output = f64;
fn next(&mut self, input: &'a T) -> Self::Output {
let highest = self.maximum.next(input.high());
let lowest = self.minimum.next(input.low());
let close = input.close();
if highest == lowest {
50.0
} else {
(close - lowest) / (highest - lowest) * 100.0
}
}
}
impl Reset for FastStochastic {
fn reset(&mut self) {
self.minimum.reset();
self.maximum.reset();
}
}
impl Default for FastStochastic {
fn default() -> Self {
Self::new(14).unwrap()
}
}
impl fmt::Display for FastStochastic {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "FAST_STOCH({})", self.length)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helper::*;
macro_rules! test_indicator {
($i:tt) => {
#[test]
fn test_indicator() {
let bar = Bar::new();
let mut indicator = $i::default();
let first_output = indicator.next(12.3);
indicator.next(&bar);
indicator.reset();
assert_eq!(indicator.next(12.3), first_output);
format!("{}", indicator);
}
};
}
test_indicator!(FastStochastic);
#[test]
fn test_new() {
assert!(FastStochastic::new(0).is_err());
assert!(FastStochastic::new(1).is_ok());
}
#[test]
fn test_next_with_f64() {
let mut stoch = FastStochastic::new(3).unwrap();
assert_eq!(stoch.next(0.0), 50.0);
assert_eq!(stoch.next(200.0), 100.0);
assert_eq!(stoch.next(100.0), 50.0);
assert_eq!(stoch.next(120.0), 20.0);
assert_eq!(stoch.next(115.0), 75.0);
}
#[test]
fn test_next_with_bars() {
let test_data = vec![
(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), ];
let mut stoch = FastStochastic::new(3).unwrap();
for (high, low, close, expected) in test_data {
let input_bar = Bar::new().high(high).low(low).close(close);
assert_eq!(stoch.next(&input_bar), expected);
}
}
#[test]
fn test_reset() {
let mut indicator = FastStochastic::new(10).unwrap();
assert_eq!(indicator.next(10.0), 50.0);
assert_eq!(indicator.next(210.0), 100.0);
assert_eq!(indicator.next(10.0), 0.0);
assert_eq!(indicator.next(60.0), 25.0);
indicator.reset();
assert_eq!(indicator.next(10.0), 50.0);
assert_eq!(indicator.next(20.0), 100.0);
assert_eq!(indicator.next(12.5), 25.0);
}
#[test]
fn test_default() {
FastStochastic::default();
}
#[test]
fn test_display() {
let indicator = FastStochastic::new(21).unwrap();
assert_eq!(format!("{}", indicator), "FAST_STOCH(21)");
}
}