egui_charts/studies/builtin/
supertrend.rs1use super::clamp_param;
28use crate::model::Bar;
29use crate::studies::palette;
30use crate::studies::stepper::Stepper;
31use crate::studies::{ATR, Indicator, IndicatorValue, IndicatorValues};
32use egui::Color32;
33
34#[derive(Clone)]
36pub struct SuperTrend {
37 atr_period: usize,
38 multiplier: f64,
39 values: IndicatorValues,
40 stepper: Stepper<State>,
41 bullish_color: Color32,
42 bearish_color: Color32,
43 visible: bool,
44}
45
46#[derive(Clone)]
47struct State {
48 usable: usize,
49 previous: f64,
50 atr: f64,
51 upper: f64,
52 lower: f64,
53 trend: f64,
54}
55
56impl Default for State {
57 fn default() -> Self {
58 Self {
59 usable: 0,
60 previous: 0.0,
61 atr: -0.0,
62 upper: 0.0,
63 lower: 0.0,
64 trend: 0.0,
65 }
66 }
67}
68
69impl SuperTrend {
70 pub fn new(atr_period: usize, multiplier: f64) -> Self {
76 Self {
77 atr_period: atr_period.max(1),
78 multiplier: clamp_param(multiplier, 0.0, f64::MAX),
79 values: IndicatorValues::new(),
80 stepper: Stepper::default(),
81 bullish_color: palette::GREEN, bearish_color: palette::RED, visible: true,
84 }
85 }
86
87 pub fn default_params() -> Self {
89 Self::new(10, 3.0)
90 }
91
92 pub fn with_colors(mut self, bullish: Color32, bearish: Color32) -> Self {
94 self.bullish_color = bullish;
95 self.bearish_color = bearish;
96 self
97 }
98}
99
100impl Default for SuperTrend {
102 fn default() -> Self {
103 Self::new(10, 3.0)
104 }
105}
106
107impl Indicator for SuperTrend {
108 crate::studies::inputs::study_inputs!(self;
109 atr_period: length("ATR period", 1, usize::MAX),
110 multiplier: number("Multiplier", 0.0, f64::MAX)
111 ; {});
112
113 fn name(&self) -> &str {
114 "SuperTrend"
115 }
116
117 fn desc(&self) -> &str {
118 "SuperTrend - Trend following indicator"
119 }
120
121 fn calculate(&mut self, data: &[Bar]) {
122 self.update(data, 0);
123 }
124
125 fn update(&mut self, data: &[Bar], from: usize) {
126 let period = self.atr_period;
127 let threshold = period.saturating_add(1);
128 let from = if self.stepper.state().usable < threshold {
130 0
131 } else {
132 from
133 };
134 let atr_mult = 1.0 / period as f64;
135 let multiplier = self.multiplier;
136 self.stepper
137 .run(data, from, &mut self.values, |state, bar| {
138 if !bar.high.is_finite() || !bar.low.is_finite() || !bar.close.is_finite() {
139 return IndicatorValue::None;
140 }
141 let tr = if state.usable == 0 {
142 bar.high - bar.low
143 } else {
144 ATR::true_range(bar, state.previous)
145 };
146 let previous = state.previous;
147 state.previous = bar.close;
148 state.usable += 1;
149 if state.usable <= period {
150 state.atr += tr;
151 if state.usable < period {
152 return IndicatorValue::None;
153 }
154 state.atr /= period as f64;
155 } else {
156 state.atr += atr_mult * (tr - state.atr);
157 }
158 let hl2 = (bar.high + bar.low) / 2.0;
159 let upper = hl2 + multiplier * state.atr;
160 let lower = hl2 - multiplier * state.atr;
161 let bullish = if state.usable == period {
162 state.upper = upper;
163 state.lower = lower;
164 true
165 } else {
166 let was_upper = state.trend == state.upper;
167 if upper < state.upper || previous > state.upper {
168 state.upper = upper;
169 }
170 if lower > state.lower || previous < state.lower {
171 state.lower = lower;
172 }
173 if was_upper {
174 bar.close > state.upper
175 } else {
176 bar.close.partial_cmp(&state.lower) != Some(std::cmp::Ordering::Less)
177 }
178 };
179 state.trend = if bullish { state.lower } else { state.upper };
180 if bullish {
181 IndicatorValue::Multiple(vec![state.trend, f64::NAN])
182 } else {
183 IndicatorValue::Multiple(vec![f64::NAN, state.trend])
184 }
185 });
186 if self.stepper.state().usable < threshold {
187 self.values.fill(IndicatorValue::None);
188 }
189 }
190
191 fn is_incremental(&self) -> bool {
192 true
193 }
194
195 fn values(&self) -> &IndicatorValues {
196 &self.values
197 }
198
199 fn colors(&self) -> Vec<Color32> {
200 vec![self.bullish_color, self.bearish_color]
201 }
202
203 fn set_colors(&mut self, colors: Vec<Color32>) {
204 if !colors.is_empty() {
205 self.bullish_color = colors[0];
206 }
207 if colors.len() > 1 {
208 self.bearish_color = colors[1];
209 }
210 }
211
212 fn is_overlay(&self) -> bool {
213 true }
215
216 fn is_visible(&self) -> bool {
217 self.visible
218 }
219
220 fn set_visible(&mut self, visible: bool) {
221 self.visible = visible;
222 }
223
224 fn clone_box(&self) -> Box<dyn Indicator> {
225 Box::new(self.clone())
226 }
227
228 fn line_names(&self) -> Vec<String> {
229 vec!["Up Trend".to_string(), "Down Trend".to_string()]
230 }
231}