egui_charts/studies/builtin/
bollinger_bands.rs1use super::{AverageType, clamp_param};
55use crate::model::Bar;
56use crate::studies::palette;
57use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
58use egui::Color32;
59
60#[derive(Clone)]
65pub struct BollingerBands {
66 period: usize,
67 basis: AverageType,
68 std_dev: f64,
69 average: Option<Box<dyn Indicator>>,
71 values: IndicatorValues,
72 colors: Vec<Color32>,
73 visible: bool,
74}
75
76impl BollingerBands {
77 pub fn new(period: usize, std_dev: f64) -> Self {
79 Self {
80 period: period.max(1),
81 basis: AverageType::Simple,
82 std_dev: clamp_param(std_dev, 0.0, f64::MAX),
83 average: None,
84 values: IndicatorValues::new(),
85 colors: vec![
86 palette::RED, palette::BB_MIDDLE, palette::TEAL, palette::LIGHT_BLUE.gamma_multiply_u8(13),
90 ],
91 visible: true,
92 }
93 }
94
95 pub fn with_basis(mut self, basis: AverageType) -> Self {
98 if basis != AverageType::Hull {
99 self.basis = basis;
100 }
101 self
102 }
103
104 fn row(&self, data: &[Bar], i: usize) -> IndicatorValue {
106 if i + 1 < self.period {
107 return IndicatorValue::None;
108 }
109 let prices: Vec<f64> = data[i + 1 - self.period..=i]
110 .iter()
111 .map(|bar| bar.close)
112 .collect();
113
114 let sma = prices.iter().sum::<f64>() / self.period as f64;
115 let variance = prices.iter().map(|p| (p - sma).powi(2)).sum::<f64>() / self.period as f64;
116 let std = variance.sqrt();
117 let middle = self.average.as_ref().map_or(sma, |average| {
118 average.values().value(0, i).unwrap_or(f64::NAN)
119 });
120 if !std.is_finite() {
121 return IndicatorValue::None;
122 }
123
124 IndicatorValue::Multiple(vec![
125 middle + (std * self.std_dev),
126 middle,
127 middle - (std * self.std_dev),
128 ])
129 }
130}
131
132impl Default for BollingerBands {
134 fn default() -> Self {
135 Self::new(20, 2.0)
136 }
137}
138
139impl Indicator for BollingerBands {
140 fn source(&self) -> Option<crate::studies::StudySource> {
141 Some(crate::studies::StudySource::Price(
142 crate::model::PriceSource::Close,
143 ))
144 }
145
146 crate::studies::inputs::study_inputs!(self;
147 period: length("Period", 1, usize::MAX),
148 basis: choice("Basis MA type", [AverageType::Simple, AverageType::Exponential, AverageType::Weighted, AverageType::Smoothed, AverageType::VolumeWeighted]),
149 std_dev: number("Std dev", 0.0, f64::MAX)
150 ; {});
151
152 fn fills(&self) -> Vec<crate::studies::StudyFill> {
153 vec![crate::studies::StudyFill {
154 first: crate::studies::FillBoundary::Plot(0),
155 second: crate::studies::FillBoundary::Plot(2),
156 color: self.colors[3],
157 inverted_color: None,
158 }]
159 }
160
161 fn name(&self) -> &str {
162 "BB"
163 }
164
165 fn desc(&self) -> &str {
166 "Bollinger Bands - Volatility indicator with upper and lower bands"
167 }
168
169 fn calculate(&mut self, data: &[Bar]) {
170 self.update(data, 0);
171 }
172
173 fn update(&mut self, data: &[Bar], from: usize) {
174 let from = from.min(self.values.len()).min(data.len());
175 if from == 0 {
176 self.average =
177 (self.basis != AverageType::Simple).then(|| self.basis.build(self.period));
178 }
179 if let Some(average) = &mut self.average {
180 average.update(data, from);
181 }
182 self.values.truncate(from);
183 for i in from..data.len() {
184 let row = self.row(data, i);
185 self.values.push(row);
186 }
187 }
188
189 fn is_incremental(&self) -> bool {
190 true
191 }
192
193 fn values(&self) -> &IndicatorValues {
194 &self.values
195 }
196
197 fn colors(&self) -> Vec<Color32> {
198 self.colors.clone()
199 }
200
201 fn set_colors(&mut self, colors: Vec<Color32>) {
202 if colors.len() >= 3 {
203 self.colors = colors;
204 } else if !colors.is_empty() {
205 self.colors = vec![colors[0], colors[0], colors[0]];
207 }
208 if self.colors.len() == 3 {
209 let color = self.colors[0];
210 self.colors.push(Color32::from_rgba_unmultiplied(
211 color.r(),
212 color.g(),
213 color.b(),
214 16,
215 ));
216 }
217 }
218
219 fn is_overlay(&self) -> bool {
220 true
221 }
222
223 fn line_count(&self) -> usize {
224 3
225 }
226
227 fn line_names(&self) -> Vec<String> {
228 vec![
229 format!("BB Upper({}, {})", self.period, self.std_dev),
230 format!("BB Middle({}, {})", self.period, self.std_dev),
231 format!("BB Lower({}, {})", self.period, self.std_dev),
232 ]
233 }
234
235 fn is_visible(&self) -> bool {
236 self.visible
237 }
238
239 fn set_visible(&mut self, visible: bool) {
240 self.visible = visible;
241 }
242
243 fn clone_box(&self) -> Box<dyn Indicator> {
244 Box::new(self.clone())
245 }
246}