egui_charts/studies/builtin/
reversal.rs1use crate::model::Bar;
21use crate::studies::palette;
22use crate::studies::stepper::Stepper;
23use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
24use egui::Color32;
25use std::collections::VecDeque;
26
27#[derive(Clone)]
29pub struct Reversal {
30 lookback: usize,
31 values: IndicatorValues,
32 stepper: Stepper<VecDeque<[f64; 3]>>,
33 up_color: Color32,
34 down_color: Color32,
35 visible: bool,
36}
37
38impl Reversal {
39 pub fn new(lookback: usize) -> Self {
41 Self {
42 lookback: lookback.max(1),
43 values: IndicatorValues::new(),
44 stepper: Stepper::default(),
45 up_color: palette::GREEN,
46 down_color: palette::RED,
47 visible: true,
48 }
49 }
50}
51
52impl Default for Reversal {
54 fn default() -> Self {
55 Self::new(20)
56 }
57}
58
59impl Indicator for Reversal {
60 crate::studies::inputs::study_inputs!(self;
61 lookback: length("Lookback", 1, usize::MAX)
62 ; {});
63
64 fn name(&self) -> &str {
65 "Reversal"
66 }
67
68 fn desc(&self) -> &str {
69 "Reversal - New extreme of the lookback that closes beyond the previous close"
70 }
71
72 fn calculate(&mut self, data: &[Bar]) {
73 self.update(data, 0);
74 }
75
76 fn update(&mut self, data: &[Bar], from: usize) {
77 let lookback = self.lookback;
78 self.stepper
79 .run(data, from, &mut self.values, |window, bar| {
80 if ![bar.high, bar.low, bar.close].iter().all(|v| v.is_finite()) {
81 return IndicatorValue::None;
82 }
83 let mut row = [f64::NAN; 2];
84 if let (Some(&[_, _, close]), true) = (window.back(), window.len() == lookback) {
85 let lowest = window.iter().map(|b| b[1]).fold(f64::INFINITY, f64::min);
88 let highest = window
89 .iter()
90 .map(|b| b[0])
91 .fold(f64::NEG_INFINITY, f64::max);
92 if bar.low < lowest && bar.close > close {
93 row[0] = bar.low;
94 }
95 if bar.high > highest && bar.close < close {
96 row[1] = bar.high;
97 }
98 }
99 window.push_back([bar.high, bar.low, bar.close]);
100 if window.len() > lookback {
101 window.pop_front();
102 }
103 IndicatorValue::Multiple(row.to_vec())
104 });
105 }
106
107 fn is_incremental(&self) -> bool {
108 true
109 }
110
111 fn plot_style(&self, line: usize) -> crate::studies::PlotStyle {
112 crate::studies::PlotStyle {
113 color: if line == 0 {
114 self.up_color
115 } else {
116 self.down_color
117 },
118 width: 1.0,
119 pattern: crate::model::LineStyle::Solid,
120 plot_type: if line == 0 {
121 crate::studies::PlotType::TriangleUp
122 } else {
123 crate::studies::PlotType::TriangleDown
124 },
125 visible: true,
126 }
127 }
128
129 fn values(&self) -> &IndicatorValues {
130 &self.values
131 }
132
133 fn colors(&self) -> Vec<Color32> {
134 vec![self.up_color, self.down_color]
135 }
136
137 fn set_colors(&mut self, colors: Vec<Color32>) {
138 if let Some(&color) = colors.first() {
139 self.up_color = color;
140 }
141 if let Some(&color) = colors.get(1) {
142 self.down_color = color;
143 }
144 }
145
146 fn is_visible(&self) -> bool {
147 self.visible
148 }
149
150 fn set_visible(&mut self, visible: bool) {
151 self.visible = visible;
152 }
153
154 fn clone_box(&self) -> Box<dyn Indicator> {
155 Box::new(self.clone())
156 }
157
158 fn line_names(&self) -> Vec<String> {
159 vec![
160 "Upward Reversal".to_string(),
161 "Downward Reversal".to_string(),
162 ]
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
175 fn key_reversals_need_a_new_extreme_and_a_close_beyond_the_previous_close() {
176 let bars = super::super::hlc_bars(&[
177 (10.0, 8.0, 9.0),
178 (9.5, 7.5, 8.0),
179 (8.5, 7.0, 7.5),
180 (8.0, 6.5, 7.0),
181 (8.5, 6.0, 8.2),
182 (9.0, 7.8, 8.8),
183 (11.0, 8.5, 10.5),
184 (12.0, 8.0, 7.5),
185 ]);
186 let mut study = Reversal::new(3);
187 super::super::assert_replay_matches_batch(&study, &bars);
188 study.calculate(&bars);
189 let line = |line: usize| -> Vec<Option<f64>> {
190 (0..bars.len())
191 .map(|at| study.values().value(line, at))
192 .collect()
193 };
194 let none = None;
195 assert_eq!(
196 line(0),
197 [none, none, none, none, Some(6.0), none, none, none]
198 );
199 assert_eq!(
200 line(1),
201 [none, none, none, none, none, none, none, Some(12.0)]
202 );
203 }
204}