finlib_ta/indicators/
efficiency_ratio.rs1use alloc::boxed::Box;
2use alloc::vec;
3use core::fmt;
4
5use crate::errors::{Result, TaError};
6use crate::traits::{Close, Next, Period, Reset};
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10#[doc(alias = "ER")]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36#[derive(Debug, Clone)]
37pub struct EfficiencyRatio {
38 period: usize,
39 index: usize,
40 count: usize,
41 deque: Box<[f64]>,
42}
43
44impl EfficiencyRatio {
45 pub fn new(period: usize) -> Result<Self> {
46 match period {
47 0 => Err(TaError::InvalidParameter),
48 _ => Ok(Self {
49 period,
50 index: 0,
51 count: 0,
52 deque: vec![0.0; period].into_boxed_slice(),
53 }),
54 }
55 }
56}
57
58impl Period for EfficiencyRatio {
59 fn period(&self) -> usize {
60 self.period
61 }
62}
63
64impl Next<f64> for EfficiencyRatio {
65 type Output = f64;
66
67 fn next(&mut self, input: f64) -> f64 {
68 let first = if self.count >= self.period {
69 self.deque[self.index]
70 } else {
71 self.count += 1;
72 self.deque[0]
73 };
74 self.deque[self.index] = input;
75
76 self.index = if self.index + 1 < self.period {
77 self.index + 1
78 } else {
79 0
80 };
81
82 let mut volatility = 0.0;
83 let mut previous = first;
84 for n in &self.deque[self.index..self.count] {
85 volatility += (previous - n).abs();
86 previous = *n;
87 }
88 for n in &self.deque[0..self.index] {
89 volatility += (previous - n).abs();
90 previous = *n;
91 }
92
93 (first - input).abs() / volatility
94 }
95}
96
97impl<T: Close> Next<&T> for EfficiencyRatio {
98 type Output = f64;
99
100 fn next(&mut self, input: &T) -> f64 {
101 self.next(input.close())
102 }
103}
104
105impl Reset for EfficiencyRatio {
106 fn reset(&mut self) {
107 self.index = 0;
108 self.count = 0;
109 for i in 0..self.period {
110 self.deque[i] = 0.0;
111 }
112 }
113}
114
115impl Default for EfficiencyRatio {
116 fn default() -> Self {
117 Self::new(14).unwrap()
118 }
119}
120
121impl fmt::Display for EfficiencyRatio {
122 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123 write!(f, "ER({})", self.period)
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use crate::test_helper::*;
131 use alloc::format;
132
133 test_indicator!(EfficiencyRatio);
134
135 #[test]
136 fn test_new() {
137 assert!(EfficiencyRatio::new(0).is_err());
138 assert!(EfficiencyRatio::new(1).is_ok());
139 }
140
141 #[test]
142 fn test_next() {
143 let mut er = EfficiencyRatio::new(3).unwrap();
144
145 assert_eq!(round(er.next(3.0)), 1.0);
146 assert_eq!(round(er.next(5.0)), 1.0);
147 assert_eq!(round(er.next(2.0)), 0.2);
148 assert_eq!(round(er.next(3.0)), 0.0);
149 assert_eq!(round(er.next(1.0)), 0.667);
150 assert_eq!(round(er.next(3.0)), 0.2);
151 assert_eq!(round(er.next(4.0)), 0.2);
152 assert_eq!(round(er.next(6.0)), 1.0);
153 }
154
155 #[test]
156 fn test_reset() {
157 let mut er = EfficiencyRatio::new(3).unwrap();
158
159 er.next(3.0);
160 er.next(5.0);
161
162 er.reset();
163
164 assert_eq!(round(er.next(3.0)), 1.0);
165 assert_eq!(round(er.next(5.0)), 1.0);
166 assert_eq!(round(er.next(2.0)), 0.2);
167 assert_eq!(round(er.next(3.0)), 0.0);
168 }
169
170 #[test]
171 fn test_display() {
172 let er = EfficiencyRatio::new(17).unwrap();
173 assert_eq!(format!("{}", er), "ER(17)");
174 }
175}