Skip to main content

egui_charts/studies/builtin/
chande_kroll_stop.rs

1//! Chande Kroll Stop
2//!
3//! Tushar Chande and Stanley Kroll (The New Technical Trader, 1994) place trailing stops a
4//! multiple of the average true range beyond the recent extremes, then take the extreme of
5//! those preliminary stops so that a stop only moves after the market has.
6//!
7//! # Formula
8//! - first high stop = highest(high, p) - x * ATR(p)
9//! - first low stop = lowest(low, p) + x * ATR(p)
10//! - stop short = highest(first high stop, q)
11//! - stop long = lowest(first low stop, q)
12//!
13//! ATR is Wilder's average true range. The defaults are p = 10, x = 1 and q = 9, so the
14//! first stops appear on bar p + q - 2, counting from 0. The short stop is drawn red, the long stop green.
15//!
16//! # Example
17//! ```
18//! # let bars: Vec<egui_charts::model::Bar> = (0..60).map(|i| { let p = 100.0 + (i as f64 * 0.3).sin() * 5.0; egui_charts::model::Bar::new(chrono::Utc::now(), p, p + 1.0, p - 1.0, p + 0.5, 1_000.0) }).collect();
19//! use egui_charts::studies::{ChandeKrollStop, Indicator};
20//!
21//! let mut stops = ChandeKrollStop::new(10, 1.0, 9);
22//! stops.calculate(&bars);
23//! ```
24
25use 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/// Chande Kroll Stop: a short (upper) and a long (lower) trailing stop.
33#[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    /// Create the stops from the ATR and extreme length `p` (typically 10), the ATR multiplier
55    /// `x` (typically 1, clamped into 0 to `f64::MAX`, NaN to 0) and the stop length `q`
56    /// (typically 9).
57    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
71/// Construct with the conventional default parameters.
72impl 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}