cxmr_ta_core/indicators/
efficiency_ratio.rs1use std::collections::VecDeque;
2use std::fmt;
3
4use crate::errors::*;
5use crate::traits::{Calculate, Close, Next, Reset};
6
7pub struct EfficiencyRatio {
32 length: u32,
33 prices: VecDeque<f64>,
34}
35
36impl EfficiencyRatio {
37 pub fn new(length: u32) -> Result<Self> {
38 if length == 0 {
39 Err(Error::from_kind(ErrorKind::InvalidParameter))
40 } else {
41 let indicator = Self {
42 length: length,
43 prices: VecDeque::with_capacity(length as usize + 1),
44 };
45 Ok(indicator)
46 }
47 }
48}
49
50impl Calculate for EfficiencyRatio {
51 fn calc(&mut self, input: f64) -> f64 {
52 self.prices.push_back(input);
53
54 if self.prices.len() <= 2 {
55 return 1.0;
56 }
57
58 let first = self.prices[0];
59
60 let volatility = self
62 .prices
63 .iter()
64 .skip(1)
65 .fold((first, 0.0), |(prev, sum), &val| {
66 (val, sum + (prev - val).abs())
67 })
68 .1;
69
70 let last_index = self.prices.len() - 1;
72 let direction = (first - self.prices[last_index]).abs();
73
74 if self.prices.len() > (self.length as usize) {
76 self.prices.pop_front();
77 }
78
79 direction / volatility
81 }
82}
83
84impl<T: Close> Next<T> for EfficiencyRatio {
85 fn next(&mut self, input: &T) -> f64 {
86 self.calc(input.close())
87 }
88}
89
90impl Reset for EfficiencyRatio {
91 fn reset(&mut self) {
92 self.prices.clear();
93 }
94}
95
96impl Default for EfficiencyRatio {
97 fn default() -> Self {
98 Self::new(14).unwrap()
99 }
100}
101
102impl fmt::Display for EfficiencyRatio {
103 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104 write!(f, "ER({})", self.length)
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::test_helper::*;
112
113 test_indicator!(EfficiencyRatio);
114
115 #[test]
116 fn test_new() {
117 assert!(EfficiencyRatio::new(0).is_err());
118 assert!(EfficiencyRatio::new(1).is_ok());
119 }
120
121 #[test]
122 fn test_next_f64() {
123 let mut er = EfficiencyRatio::new(3).unwrap();
124
125 assert_eq!(round(er.calc(3.0)), 1.0);
126 assert_eq!(round(er.calc(5.0)), 1.0);
127 assert_eq!(round(er.calc(2.0)), 0.2);
128 assert_eq!(round(er.calc(3.0)), 0.0);
129 assert_eq!(round(er.calc(1.0)), 0.667);
130 assert_eq!(round(er.calc(3.0)), 0.2);
131 assert_eq!(round(er.calc(4.0)), 0.2);
132 assert_eq!(round(er.calc(6.0)), 1.0);
133
134 er.reset();
135 assert_eq!(round(er.calc(3.0)), 1.0);
136 assert_eq!(round(er.calc(5.0)), 1.0);
137 assert_eq!(round(er.calc(2.0)), 0.2);
138 assert_eq!(round(er.calc(3.0)), 0.0);
139 }
140
141 #[test]
142 fn test_display() {
143 let er = EfficiencyRatio::new(17).unwrap();
144 assert_eq!(format!("{}", er), "ER(17)");
145 }
146}