ux_indicators/indicators/
ker.rs

1#![allow(dead_code)]
2
3use std::collections::VecDeque;
4use std::fmt;
5
6use crate::errors::*;
7use crate::traits::{Close, Next, Reset};
8
9use crate::{Factory};
10use crate::indicators::SimpleMovingAverage;
11
12pub struct KerFactory {
13}
14
15impl KerFactory {
16    pub fn new() -> Self {
17        Self{}
18    }
19}
20
21impl Factory for KerFactory {
22    fn create() -> Box<dyn Next<f64, Output = Box<[f64]>>> {
23        Box::new(SimpleMovingAverage::default())
24    }
25}
26
27/// Kaufman's Efficiency Ratio (ER).
28///
29/// It is calculated by dividing the price change over a period by the absolute sum of the price movements that occurred to achieve that change.
30/// The resulting ratio ranges between 0.0 and 1.0 with higher values representing a more efficient or trending market.
31///
32/// # Parameters
33///
34/// * _length_ - number of periods (integer greater than 0)
35///
36/// # Example
37///
38/// ```
39/// use core::indicators::EfficiencyRatio;
40/// use core::Next;
41///
42/// let mut er = EfficiencyRatio::new(4).unwrap();
43/// assert_eq!(er.next(10.0), 1.0);
44/// assert_eq!(er.next(13.0), 1.0);
45/// assert_eq!(er.next(12.0), 0.5);
46/// assert_eq!(er.next(13.0), 0.6);
47/// assert_eq!(er.next(18.0), 0.8);
48/// assert_eq!(er.next(19.0), 0.75);
49/// ```
50
51pub struct EfficiencyRatio {
52    length: u32,
53    prices: VecDeque<f64>,
54}
55
56impl EfficiencyRatio {
57    pub fn new(length: u32) -> Result<Self> {
58        if length == 0 {
59            Err(Error::from_kind(ErrorKind::InvalidParameter))
60        } else {
61            let indicator = Self {
62                length: length,
63                prices: VecDeque::with_capacity(length as usize + 1),
64            };
65            Ok(indicator)
66        }
67    }
68}
69
70impl Next<f64> for EfficiencyRatio {
71    type Output = f64;
72
73    fn next(&mut self, input: f64) -> f64 {
74        self.prices.push_back(input);
75
76        if self.prices.len() <= 2 {
77            return 1.0;
78        }
79
80        let first = self.prices[0];
81
82        // Calculate volatility
83        let volatility = self
84            .prices
85            .iter()
86            .skip(1)
87            .fold((first, 0.0), |(prev, sum), &val| {
88                (val, sum + (prev - val).abs())
89            })
90            .1;
91
92        // Calculate direction
93        let last_index = self.prices.len() - 1;
94        let direction = (first - self.prices[last_index]).abs();
95
96        // Get rid of the first element
97        if self.prices.len() > (self.length as usize) {
98            self.prices.pop_front();
99        }
100
101        // Return actual efficiency ratio
102        direction / volatility
103    }
104}
105
106impl<'a, T: Close> Next<&'a T> for EfficiencyRatio {
107    type Output = f64;
108
109    fn next(&mut self, input: &'a T) -> f64 {
110        self.next(input.close())
111    }
112}
113
114impl Reset for EfficiencyRatio {
115    fn reset(&mut self) {
116        self.prices.clear();
117    }
118}
119
120impl Default for EfficiencyRatio {
121    fn default() -> Self {
122        Self::new(14).unwrap()
123    }
124}
125
126impl fmt::Display for EfficiencyRatio {
127    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128        write!(f, "ER({})", self.length)
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use crate::test_helper::*;
136
137    // test_indicator!(EfficiencyRatio);
138
139    #[test]
140    fn test_new() {
141        assert!(EfficiencyRatio::new(0).is_err());
142        assert!(EfficiencyRatio::new(1).is_ok());
143    }
144
145    #[test]
146    fn test_next_f64() {
147        let mut er = EfficiencyRatio::new(3).unwrap();
148
149        assert_eq!(round(er.next(3.0)), 1.0);
150        assert_eq!(round(er.next(5.0)), 1.0);
151        assert_eq!(round(er.next(2.0)), 0.2);
152        assert_eq!(round(er.next(3.0)), 0.0);
153        assert_eq!(round(er.next(1.0)), 0.667);
154        assert_eq!(round(er.next(3.0)), 0.2);
155        assert_eq!(round(er.next(4.0)), 0.2);
156        assert_eq!(round(er.next(6.0)), 1.0);
157
158        er.reset();
159        assert_eq!(round(er.next(3.0)), 1.0);
160        assert_eq!(round(er.next(5.0)), 1.0);
161        assert_eq!(round(er.next(2.0)), 0.2);
162        assert_eq!(round(er.next(3.0)), 0.0);
163    }
164
165    #[test]
166    fn test_display() {
167        let er = EfficiencyRatio::new(17).unwrap();
168        assert_eq!(format!("{}", er), "ER(17)");
169    }
170}