ux_indicators/indicators/
ker.rs1#![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
27pub 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 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 let last_index = self.prices.len() - 1;
94 let direction = (first - self.prices[last_index]).abs();
95
96 if self.prices.len() > (self.length as usize) {
98 self.prices.pop_front();
99 }
100
101 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]
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}