egui_charts/studies/builtin/
volatility_stop.rs1use super::clamp_param;
31use crate::model::Bar;
32use crate::studies::stepper::Stepper;
33use crate::studies::{ATR, Indicator, IndicatorValue, IndicatorValues};
34use egui::Color32;
35
36#[derive(Clone)]
38pub struct VolatilityStop {
39 length: usize,
40 multiplier: f64,
41 values: IndicatorValues,
42 stepper: Stepper<State>,
43 uptrend: Vec<bool>,
45 colors: [Color32; 2],
46 visible: bool,
47}
48
49#[derive(Clone, Default)]
50struct State {
51 usable: usize,
52 previous: f64,
53 atr: f64,
54 max: f64,
55 min: f64,
56 stop: f64,
57 downtrend: bool,
58}
59
60impl VolatilityStop {
61 pub fn new(length: usize, multiplier: f64) -> Self {
64 Self {
65 length: length.max(1),
66 multiplier: clamp_param(multiplier, 0.0, f64::MAX),
67 values: IndicatorValues::new(),
68 stepper: Stepper::default(),
69 uptrend: Vec::new(),
70 colors: [
71 crate::studies::palette::DEEP_TEAL,
72 crate::studies::palette::BRIGHT_RED,
73 ],
74 visible: true,
75 }
76 }
77}
78
79impl Default for VolatilityStop {
81 fn default() -> Self {
82 Self::new(20, 2.0)
83 }
84}
85
86impl Indicator for VolatilityStop {
87 crate::studies::inputs::study_inputs!(self;
88 length: length("Length", 1, usize::MAX),
89 multiplier: number("Multiplier", 0.0, f64::MAX)
90 ; {});
91
92 fn plot_style(&self, _line: usize) -> crate::studies::PlotStyle {
93 crate::studies::PlotStyle {
94 color: self.colors[0],
95 width: 1.0,
96 pattern: crate::model::LineStyle::Solid,
97 plot_type: crate::studies::PlotType::Crosses,
98 visible: true,
99 }
100 }
101
102 fn plot_color(&self, _line: usize, bar: usize) -> Option<Color32> {
103 self.values.value(0, bar)?;
104 let up = *self.uptrend.get(bar)?;
105 Some(self.colors[usize::from(!up)])
106 }
107
108 fn name(&self) -> &str {
109 "Volatility Stop"
110 }
111
112 fn desc(&self) -> &str {
113 "Volatility Stop - ATR trailing stop and reverse"
114 }
115
116 fn calculate(&mut self, data: &[Bar]) {
117 self.update(data, 0);
118 }
119
120 fn update(&mut self, data: &[Bar], from: usize) {
121 let (length, multiplier) = (self.length, self.multiplier);
122 let mut trends = Vec::new();
123 self.stepper
124 .run(data, from, &mut self.values, |state, bar| {
125 if ![bar.high, bar.low, bar.close].iter().all(|p| p.is_finite()) {
126 trends.push(false);
127 return IndicatorValue::None;
128 }
129 let source = bar.close;
130 let first = state.usable == 0;
131 let tr = if first {
132 bar.high - bar.low
133 } else {
134 ATR::true_range(bar, state.previous)
135 };
136 state.previous = bar.close;
137 state.usable += 1;
138 if state.usable <= length {
139 state.atr += tr;
140 if state.usable == length {
141 state.atr /= length as f64;
142 }
143 } else {
144 state.atr += (tr - state.atr) / length as f64;
145 }
146 let band = if state.usable >= length {
147 multiplier * state.atr
148 } else {
149 tr
150 };
151 if first {
152 (state.max, state.min, state.stop) = (source, source, source);
153 } else {
154 state.max = state.max.max(source);
155 state.min = state.min.min(source);
156 state.stop = if state.downtrend {
157 state.stop.min(state.min + band)
158 } else {
159 state.stop.max(state.max - band)
160 };
161 }
162 let downtrend = source < state.stop;
163 if downtrend != state.downtrend {
164 state.downtrend = downtrend;
165 (state.max, state.min) = (source, source);
166 state.stop = if downtrend {
167 source + band
168 } else {
169 source - band
170 };
171 }
172 trends.push(!state.downtrend);
173 IndicatorValue::Single(state.stop)
174 });
175 self.uptrend.truncate(data.len() - trends.len());
176 self.uptrend.extend(trends);
177 }
178
179 fn is_incremental(&self) -> bool {
180 true
181 }
182
183 fn values(&self) -> &IndicatorValues {
184 &self.values
185 }
186
187 fn colors(&self) -> Vec<Color32> {
188 self.colors.to_vec()
189 }
190
191 fn set_colors(&mut self, colors: Vec<Color32>) {
192 for (old, new) in self.colors.iter_mut().zip(colors) {
193 *old = new;
194 }
195 }
196
197 fn is_visible(&self) -> bool {
198 self.visible
199 }
200
201 fn set_visible(&mut self, visible: bool) {
202 self.visible = visible;
203 }
204
205 fn clone_box(&self) -> Box<dyn Indicator> {
206 Box::new(self.clone())
207 }
208
209 fn line_names(&self) -> Vec<String> {
210 vec!["Volatility Stop".into()]
211 }
212}