1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::{Echo, View, EMA};
use std::collections::VecDeque;
#[derive(Clone)]
pub struct EhlersFisherTransform {
view: Box<dyn View>,
moving_average: Box<dyn View>,
window_len: usize,
q_vals: VecDeque<f64>,
high: f64,
low: f64,
q_out: VecDeque<f64>,
}
impl EhlersFisherTransform {
pub fn new(view: Box<dyn View>, window_len: usize) -> Box<Self> {
Self::with_ma(view, EMA::new_final(5), window_len)
}
pub fn new_final(window_len: usize) -> Box<Self> {
Self::new(Echo::new(), window_len)
}
pub fn with_ma(view: Box<dyn View>, ma: Box<dyn View>, window_len: usize) -> Box<Self> {
Box::new(Self {
view,
moving_average: ma,
window_len,
q_vals: VecDeque::new(),
high: 0.0,
low: 0.0,
q_out: VecDeque::new(),
})
}
}
impl View for EhlersFisherTransform {
fn update(&mut self, val: f64) {
self.view.update(val);
let val: f64 = self.view.last();
if self.q_vals.len() == 0 {
self.high = val;
self.low = val;
}
if self.q_vals.len() >= self.window_len {
let old_val = self.q_vals.pop_front().unwrap();
if old_val >= self.high {
self.high = *self
.q_vals
.iter()
.max_by(|x, y| x.partial_cmp(y).unwrap())
.unwrap();
}
if old_val <= self.low {
self.low = *self
.q_vals
.iter()
.min_by(|x, y| x.partial_cmp(y).unwrap())
.unwrap();
}
}
self.q_vals.push_back(val);
if val > self.high {
self.high = val;
} else if val < self.low {
self.low = val;
}
if self.high == self.low {
self.q_out.push_back(0.0);
return;
}
let val: f64 = 2.0 * ((val - self.low) / (self.high - self.low) - 0.5);
self.moving_average.update(val);
let mut smoothed = self.moving_average.last();
if smoothed > 0.99 {
smoothed = 0.99;
} else if smoothed < -0.99 {
smoothed = -0.99;
}
if self.q_out.len() == 0 {
self.q_out.push_back(0.0);
return;
}
let fish: f64 =
0.5 * ((1.0 + smoothed) / (1.0 - smoothed)).ln() + 0.5 * self.q_out.back().unwrap();
self.q_out.push_back(fish);
}
fn last(&self) -> f64 {
*self.q_out.back().unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::plot::plot_values;
use crate::test_data::TEST_DATA;
#[test]
fn ehlers_fisher_transform_plot() {
let mut eft = EhlersFisherTransform::new_final(16);
let mut out: Vec<f64> = vec![];
for v in &TEST_DATA {
eft.update(*v);
out.push(eft.last());
}
println!("out: {:?}", out);
let filename = "img/ehlers_fisher_transform.png";
plot_values(out, filename).unwrap();
}
}