egui_charts/studies/builtin/
vidya.rs1use super::smoothing::Smoothing;
2use crate::model::Bar;
3use crate::studies::palette;
4use crate::studies::stepper::Stepper;
5use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
6use egui::Color32;
7
8#[derive(Clone)]
12pub struct VIDYA {
13 period: usize,
15 values: IndicatorValues,
16 stepper: Stepper<(std::collections::VecDeque<f64>, Option<f64>)>,
17 color: Color32,
18 visible: bool,
19}
20
21impl VIDYA {
22 pub fn new(period: usize) -> Self {
24 Self {
25 period: period.max(1),
26 values: IndicatorValues::new(),
27 stepper: Stepper::default(),
28 color: palette::BLUE,
29 visible: true,
30 }
31 }
32
33 pub fn with_color(mut self, color: Color32) -> Self {
35 self.color = color;
36 self
37 }
38}
39
40impl Default for VIDYA {
41 fn default() -> Self {
42 Self::new(14)
43 }
44}
45
46impl Indicator for VIDYA {
47 fn source(&self) -> Option<crate::studies::StudySource> {
48 Some(crate::studies::StudySource::Price(
49 crate::model::PriceSource::Close,
50 ))
51 }
52
53 crate::studies::inputs::study_inputs!(self;
54 period: length("Period", 1, usize::MAX)
55 ; {});
56
57 fn name(&self) -> &str {
58 "VIDYA"
59 }
60
61 fn desc(&self) -> &str {
62 "Variable Index Dynamic Avg - Volatility-adjusted MA"
63 }
64
65 fn calculate(&mut self, data: &[Bar]) {
66 self.update(data, 0);
67 }
68
69 fn update(&mut self, data: &[Bar], from: usize) {
70 self.stepper
71 .run(data, from, &mut self.values, |state, bar| {
72 if !bar.close.is_finite() {
73 return IndicatorValue::None;
74 }
75 let previous = state.1.get_or_insert(bar.close);
76 state.0.push_back(bar.close);
77 if state.0.len() <= self.period {
78 return IndicatorValue::None;
79 }
80 while state.0.len() - 1 > self.period {
81 state.0.pop_front();
82 }
83 let (mut up, mut down) = (0.0, 0.0);
84 for i in 1..state.0.len() {
85 let change = state.0[i] - state.0[i - 1];
86 if change > 0.0 {
87 up += change;
88 } else {
89 down += (-change).abs();
90 }
91 }
92 let vi = if up + down != 0.0 {
93 (up - down).abs() / (up + down)
94 } else {
95 0.0
96 };
97 let sc = 2.0 / (self.period as f64 + 1.0);
98 *previous = sc * vi * bar.close + (1.0 - sc * vi) * *previous;
99 IndicatorValue::Single(*previous)
100 });
101 }
102
103 fn is_incremental(&self) -> bool {
104 true
105 }
106
107 fn values(&self) -> &IndicatorValues {
108 &self.values
109 }
110
111 fn colors(&self) -> Vec<Color32> {
112 vec![self.color]
113 }
114
115 fn set_colors(&mut self, colors: Vec<Color32>) {
116 if !colors.is_empty() {
117 self.color = colors[0];
118 }
119 }
120
121 fn is_overlay(&self) -> bool {
122 true
123 }
124
125 fn is_visible(&self) -> bool {
126 self.visible
127 }
128
129 fn set_visible(&mut self, visible: bool) {
130 self.visible = visible;
131 }
132
133 fn clone_box(&self) -> Box<dyn Indicator> {
134 Box::new(self.clone())
135 }
136
137 fn line_names(&self) -> Vec<String> {
138 vec![format!("VIDYA({})", self.period)]
139 }
140}
141
142#[derive(Clone)]
145pub struct ZLEMA {
146 period: usize,
147 values: IndicatorValues,
148 stepper: Stepper<(std::collections::VecDeque<f64>, usize, Smoothing)>,
149 color: Color32,
150 visible: bool,
151}
152
153impl ZLEMA {
154 pub fn new(period: usize) -> Self {
156 Self {
157 period: period.max(1),
158 values: IndicatorValues::new(),
159 stepper: Stepper::default(),
160 color: palette::BLUE,
161 visible: true,
162 }
163 }
164
165 pub fn with_color(mut self, color: Color32) -> Self {
167 self.color = color;
168 self
169 }
170}
171
172impl Default for ZLEMA {
173 fn default() -> Self {
174 Self::new(14)
175 }
176}
177
178impl Indicator for ZLEMA {
179 fn source(&self) -> Option<crate::studies::StudySource> {
180 Some(crate::studies::StudySource::Price(
181 crate::model::PriceSource::Close,
182 ))
183 }
184
185 crate::studies::inputs::study_inputs!(self;
186 period: length("Period", 1, usize::MAX)
187 ; {});
188
189 fn name(&self) -> &str {
190 "ZLEMA"
191 }
192
193 fn desc(&self) -> &str {
194 "Zero Lag EMA - Reduced lag moving avg"
195 }
196
197 fn calculate(&mut self, data: &[Bar]) {
198 self.update(data, 0);
199 }
200
201 fn update(&mut self, data: &[Bar], from: usize) {
202 self.stepper
203 .run(data, from, &mut self.values, |state, bar| {
204 if !bar.close.is_finite() {
205 return IndicatorValue::None;
206 }
207 let lag = (self.period - 1) / 2;
208 state.0.push_back(bar.close);
209 let delagged = if state.0.len() > lag {
210 bar.close + (bar.close - state.0[state.0.len() - 1 - lag])
211 } else {
212 bar.close
213 };
214 if state.0.len() > lag {
215 state.0.pop_front();
216 }
217 let ema = state.2.next(delagged, 1, 2.0 / (self.period as f64 + 1.0));
218 state.1 += 1;
219 if state.1 < self.period {
220 IndicatorValue::None
221 } else {
222 IndicatorValue::Single(ema)
223 }
224 });
225 }
226
227 fn is_incremental(&self) -> bool {
228 true
229 }
230
231 fn values(&self) -> &IndicatorValues {
232 &self.values
233 }
234
235 fn colors(&self) -> Vec<Color32> {
236 vec![self.color]
237 }
238
239 fn set_colors(&mut self, colors: Vec<Color32>) {
240 if !colors.is_empty() {
241 self.color = colors[0];
242 }
243 }
244
245 fn is_overlay(&self) -> bool {
246 true
247 }
248
249 fn is_visible(&self) -> bool {
250 self.visible
251 }
252
253 fn set_visible(&mut self, visible: bool) {
254 self.visible = visible;
255 }
256
257 fn clone_box(&self) -> Box<dyn Indicator> {
258 Box::new(self.clone())
259 }
260
261 fn line_names(&self) -> Vec<String> {
262 vec![format!("ZLEMA({})", self.period)]
263 }
264}