quantaxis_rs/indicators/
efficiency_ratio.rs1use std::collections::VecDeque;
2use std::fmt;
3
4use crate::errors::*;
5use crate::traits::{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 Next<f64> for EfficiencyRatio {
51 type Output = f64;
52
53 fn next(&mut self, input: f64) -> f64 {
54 self.prices.push_back(input);
55
56 if self.prices.len() <= 2 {
57 return 1.0;
58 }
59
60 let first = self.prices[0];
61
62 let volatility = self
64 .prices
65 .iter()
66 .skip(1)
67 .fold((first, 0.0), |(prev, sum), &val| {
68 (val, sum + (prev - val).abs())
69 })
70 .1;
71
72 let last_index = self.prices.len() - 1;
74 let direction = (first - self.prices[last_index]).abs();
75
76 if self.prices.len() > (self.length as usize) {
78 self.prices.pop_front();
79 }
80
81 direction / volatility
83 }
84}
85
86impl<'a, T: Close> Next<&'a T> for EfficiencyRatio {
87 type Output = f64;
88
89 fn next(&mut self, input: &'a T) -> f64 {
90 self.next(input.close())
91 }
92}
93
94impl Reset for EfficiencyRatio {
95 fn reset(&mut self) {
96 self.prices.clear();
97 }
98}
99
100impl Default for EfficiencyRatio {
101 fn default() -> Self {
102 Self::new(14).unwrap()
103 }
104}
105
106impl fmt::Display for EfficiencyRatio {
107 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108 write!(f, "ER({})", self.length)
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use crate::test_helper::*;
116 macro_rules! test_indicator {
117 ($i:tt) => {
118 #[test]
119 fn test_indicator() {
120 let bar = Bar::new();
121
122 let mut indicator = $i::default();
124
125 let first_output = indicator.next(12.3);
127
128 indicator.next(&bar);
130
131 indicator.reset();
133 assert_eq!(indicator.next(12.3), first_output);
134
135 format!("{}", indicator);
137 }
138 };
139 }
140 test_indicator!(EfficiencyRatio);
141
142 #[test]
143 fn test_new() {
144 assert!(EfficiencyRatio::new(0).is_err());
145 assert!(EfficiencyRatio::new(1).is_ok());
146 }
147
148 #[test]
149 fn test_next_f64() {
150 let mut er = EfficiencyRatio::new(3).unwrap();
151
152 assert_eq!(round(er.next(3.0)), 1.0);
153 assert_eq!(round(er.next(5.0)), 1.0);
154 assert_eq!(round(er.next(2.0)), 0.2);
155 assert_eq!(round(er.next(3.0)), 0.0);
156 assert_eq!(round(er.next(1.0)), 0.667);
157 assert_eq!(round(er.next(3.0)), 0.2);
158 assert_eq!(round(er.next(4.0)), 0.2);
159 assert_eq!(round(er.next(6.0)), 1.0);
160
161 er.reset();
162 assert_eq!(round(er.next(3.0)), 1.0);
163 assert_eq!(round(er.next(5.0)), 1.0);
164 assert_eq!(round(er.next(2.0)), 0.2);
165 assert_eq!(round(er.next(3.0)), 0.0);
166 }
167
168 #[test]
169 fn test_display() {
170 let er = EfficiencyRatio::new(17).unwrap();
171 assert_eq!(format!("{}", er), "ER(17)");
172 }
173}