quant_indicators/
bollinger.rs1use quant_primitives::Candle;
4use rust_decimal::Decimal;
5
6use crate::error::IndicatorError;
7use crate::indicator::Indicator;
8use crate::series::Series;
9use crate::sma::Sma;
10use crate::stddev::StdDev;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct BollingerResult {
15 pub upper: Series,
17 pub middle: Series,
19 pub lower: Series,
21}
22
23impl BollingerResult {
24 #[must_use]
26 pub fn len(&self) -> usize {
27 self.middle.len()
28 }
29
30 #[must_use]
32 pub fn is_empty(&self) -> bool {
33 self.middle.is_empty()
34 }
35}
36
37#[derive(Debug, Clone)]
66pub struct BollingerBands {
67 period: usize,
68 multiplier: Decimal,
69 name: String,
70}
71
72impl BollingerBands {
73 pub fn new(period: usize, multiplier: Decimal) -> Result<Self, IndicatorError> {
84 if period == 0 {
85 return Err(IndicatorError::InvalidParameter {
86 message: "BollingerBands period must be > 0".to_string(),
87 });
88 }
89 if multiplier.is_sign_negative() {
90 return Err(IndicatorError::InvalidParameter {
91 message: "BollingerBands multiplier must be >= 0".to_string(),
92 });
93 }
94 Ok(Self {
95 period,
96 multiplier,
97 name: format!("BB({},{})", period, multiplier),
98 })
99 }
100
101 pub fn standard() -> Result<Self, IndicatorError> {
103 Self::new(20, Decimal::TWO)
104 }
105
106 pub fn name(&self) -> &str {
108 &self.name
109 }
110
111 pub fn warmup_period(&self) -> usize {
113 self.period
114 }
115
116 pub fn compute(&self, candles: &[Candle]) -> Result<BollingerResult, IndicatorError> {
120 if candles.len() < self.period {
121 return Err(IndicatorError::InsufficientData {
122 required: self.period,
123 actual: candles.len(),
124 });
125 }
126
127 let sma = Sma::new(self.period)?;
128 let stddev = StdDev::new(self.period)?;
129
130 let middle_series = sma.compute(candles)?;
131 let stddev_series = stddev.compute(candles)?;
132
133 let middle_values = middle_series.values();
134 let stddev_values = stddev_series.values();
135
136 let mut upper_values = Vec::with_capacity(middle_values.len());
137 let mut lower_values = Vec::with_capacity(middle_values.len());
138
139 for (i, (ts, middle)) in middle_values.iter().enumerate() {
140 let std = stddev_values[i].1;
141 let band_width = self.multiplier * std;
142 upper_values.push((*ts, *middle + band_width));
143 lower_values.push((*ts, *middle - band_width));
144 }
145
146 Ok(BollingerResult {
147 upper: Series::new(upper_values),
148 middle: middle_series,
149 lower: Series::new(lower_values),
150 })
151 }
152}
153
154#[cfg(test)]
155#[path = "bollinger_tests.rs"]
156mod tests;