egui_charts/studies/builtin/
chande_kroll_stop.rs1use super::clamp_param;
26use crate::model::Bar;
27use crate::studies::palette;
28use crate::studies::stepper::Stepper;
29use crate::studies::{ATR, Indicator, IndicatorValue, IndicatorValues};
30use egui::Color32;
31
32#[derive(Clone)]
34pub struct ChandeKrollStop {
35 atr_length: usize,
36 multiplier: f64,
37 stop_length: usize,
38 values: IndicatorValues,
39 atr: ATR,
40 stepper: Stepper<State>,
41 colors: [Color32; 2],
42 visible: bool,
43}
44
45#[derive(Clone, Default)]
46struct State {
47 index: usize,
48 count: usize,
49 prices: std::collections::VecDeque<[f64; 2]>,
50 stops: std::collections::VecDeque<[f64; 2]>,
51}
52
53impl ChandeKrollStop {
54 pub fn new(atr_length: usize, multiplier: f64, stop_length: usize) -> Self {
58 Self {
59 atr_length: atr_length.max(1),
60 multiplier: clamp_param(multiplier, 0.0, f64::MAX),
61 stop_length: stop_length.max(1),
62 values: IndicatorValues::new(),
63 atr: ATR::new(atr_length),
64 stepper: Stepper::default(),
65 colors: [palette::ORANGE, palette::BLUE],
66 visible: true,
67 }
68 }
69}
70
71impl Default for ChandeKrollStop {
73 fn default() -> Self {
74 Self::new(10, 1.0, 9)
75 }
76}
77
78impl Indicator for ChandeKrollStop {
79 crate::studies::inputs::study_inputs!(self;
80 atr_length: length("ATR length", 1, usize::MAX),
81 multiplier: number("ATR coefficient", 0.0, f64::MAX),
82 stop_length: length("Stop length", 1, usize::MAX)
83 ; {});
84
85 fn name(&self) -> &str {
86 "Chande Kroll Stop"
87 }
88
89 fn desc(&self) -> &str {
90 "Chande Kroll Stop - ATR trailing stops beyond recent extremes"
91 }
92
93 fn calculate(&mut self, data: &[Bar]) {
94 self.update(data, 0);
95 }
96
97 fn update(&mut self, data: &[Bar], from: usize) {
98 if from == 0 {
99 self.atr = ATR::new(self.atr_length);
100 }
101 self.atr.update(data, from);
102 self.stepper
103 .run(data, from, &mut self.values, |state, bar| {
104 let index = state.index;
105 state.index += 1;
106 if ![bar.high, bar.low, bar.close].iter().all(|x| x.is_finite()) {
107 return IndicatorValue::None;
108 }
109 state.count += 1;
110 state.prices.push_back([bar.high, bar.low]);
111 if state.prices.len() > self.atr_length {
112 state.prices.pop_front();
113 }
114 let high = state
115 .prices
116 .iter()
117 .map(|v| v[0])
118 .fold(f64::NEG_INFINITY, f64::max);
119 let low = state
120 .prices
121 .iter()
122 .map(|v| v[1])
123 .fold(f64::INFINITY, f64::min);
124 let band = self
125 .atr
126 .values()
127 .value(0, index)
128 .map(|atr| self.multiplier * atr);
129 state.stops.push_back([
130 band.map_or(f64::NAN, |band| high - band),
131 band.map_or(f64::NAN, |band| low + band),
132 ]);
133 if state.stops.len() > self.stop_length {
134 state.stops.pop_front();
135 }
136 if state.count - 1 < (self.atr_length - 1).saturating_add(self.stop_length - 1) {
137 return IndicatorValue::None;
138 }
139 let short = state
140 .stops
141 .iter()
142 .map(|v| v[0])
143 .fold(f64::NEG_INFINITY, f64::max);
144 let long = state
145 .stops
146 .iter()
147 .map(|v| v[1])
148 .fold(f64::INFINITY, f64::min);
149 IndicatorValue::Multiple(vec![short, long])
150 });
151 }
152
153 fn is_incremental(&self) -> bool {
154 true
155 }
156
157 fn values(&self) -> &IndicatorValues {
158 &self.values
159 }
160
161 fn colors(&self) -> Vec<Color32> {
162 self.colors.to_vec()
163 }
164
165 fn set_colors(&mut self, colors: Vec<Color32>) {
166 for (old, new) in self.colors.iter_mut().zip(colors) {
167 *old = new;
168 }
169 }
170
171 fn is_visible(&self) -> bool {
172 self.visible
173 }
174
175 fn set_visible(&mut self, visible: bool) {
176 self.visible = visible;
177 }
178
179 fn clone_box(&self) -> Box<dyn Indicator> {
180 Box::new(self.clone())
181 }
182
183 fn line_names(&self) -> Vec<String> {
184 vec!["Stop Short".into(), "Stop Long".into()]
185 }
186}