egui_charts/studies/builtin/
vortex.rs1use super::over_finite_bars;
2use crate::model::Bar;
3use crate::studies::palette;
4use crate::studies::stepper::{update_window, usable_behind};
5use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
6use egui::Color32;
7
8#[derive(Clone)]
12pub struct VortexIndicator {
13 period: usize,
15 values: IndicatorValues,
16 colors: Vec<Color32>,
18 visible: bool,
19}
20
21impl VortexIndicator {
22 pub fn new(period: usize) -> Self {
24 Self {
25 period: period.max(1),
26 values: IndicatorValues::new(),
27 colors: vec![
28 palette::BLUE, palette::PINK, ],
31 visible: true,
32 }
33 }
34
35 pub fn with_colors(mut self, colors: Vec<Color32>) -> Self {
37 self.colors = colors;
38 self
39 }
40}
41
42impl Default for VortexIndicator {
44 fn default() -> Self {
45 Self::new(14)
46 }
47}
48
49impl VortexIndicator {
50 fn compute(&self, data: &[Bar]) -> IndicatorValues {
52 let mut values = IndicatorValues::new();
53 if data.len() < self.period.saturating_add(1) {
54 for _ in 0..data.len() {
55 values.push(IndicatorValue::None);
56 }
57 return values;
58 }
59
60 let mut tr_values = Vec::with_capacity(data.len());
62 let mut vm_plus = Vec::with_capacity(data.len());
63 let mut vm_minus = Vec::with_capacity(data.len());
64
65 tr_values.push(0.0);
66 vm_plus.push(0.0);
67 vm_minus.push(0.0);
68
69 for i in 1..data.len() {
70 let current = &data[i];
71 let prev = &data[i - 1];
72
73 let tr = (current.high - current.low)
75 .max((current.high - prev.close).abs())
76 .max((current.low - prev.close).abs());
77
78 let vm_p = (current.high - prev.low).abs();
80
81 let vm_m = (current.low - prev.high).abs();
83
84 tr_values.push(tr);
85 vm_plus.push(vm_p);
86 vm_minus.push(vm_m);
87 }
88
89 for i in 0..data.len() {
91 if i < self.period {
92 values.push(IndicatorValue::None);
93 } else {
94 let tr_sum: f64 = tr_values[i + 1 - self.period..=i].iter().sum();
96 let vm_plus_sum: f64 = vm_plus[i + 1 - self.period..=i].iter().sum();
97 let vm_minus_sum: f64 = vm_minus[i + 1 - self.period..=i].iter().sum();
98
99 if tr_sum == 0.0 {
100 values.push(IndicatorValue::None);
101 } else {
102 let vi_plus = vm_plus_sum / tr_sum;
103 let vi_minus = vm_minus_sum / tr_sum;
104 values.push_lines(&[vi_plus, vi_minus]);
105 }
106 }
107 }
108
109 values
110 }
111}
112
113impl Indicator for VortexIndicator {
114 crate::studies::inputs::study_inputs!(self;
115 period: length("Period", 1, usize::MAX)
116 ; {});
117
118 fn name(&self) -> &str {
119 "Vortex"
120 }
121
122 fn desc(&self) -> &str {
123 "Vortex Indicator - Identifies trend direction"
124 }
125
126 fn calculate(&mut self, data: &[Bar]) {
127 self.update(data, 0);
128 }
129
130 fn update(&mut self, data: &[Bar], from: usize) {
131 let mut values = std::mem::take(&mut self.values);
132 update_window(
133 &mut values,
134 data,
135 from,
136 usable_behind(self.period, |b| {
137 b.high.is_finite() && b.low.is_finite() && b.close.is_finite()
138 }),
139 |data| {
140 over_finite_bars(
141 data,
142 |b| b.high.is_finite() && b.low.is_finite() && b.close.is_finite(),
143 |data| self.compute(data),
144 )
145 },
146 );
147 self.values = values;
148 }
149
150 fn is_incremental(&self) -> bool {
151 true
152 }
153
154 fn values(&self) -> &IndicatorValues {
155 &self.values
156 }
157
158 fn colors(&self) -> Vec<Color32> {
159 self.colors.clone()
160 }
161
162 fn set_colors(&mut self, colors: Vec<Color32>) {
163 if colors.len() >= 2 {
164 self.colors = colors;
165 }
166 }
167
168 fn is_overlay(&self) -> bool {
169 false
170 }
171
172 fn is_visible(&self) -> bool {
173 self.visible
174 }
175
176 fn set_visible(&mut self, visible: bool) {
177 self.visible = visible;
178 }
179
180 fn clone_box(&self) -> Box<dyn Indicator> {
181 Box::new(self.clone())
182 }
183
184 fn line_names(&self) -> Vec<String> {
185 vec![
186 format!("+VI({})", self.period),
187 format!("-VI({})", self.period),
188 ]
189 }
190}