indexes_rs/v1/bollinger/main.rs
1//! # Bollinger Bands Module
2//!
3//! This module implements Bollinger Bands using a Simple Moving Average (SMA) for the middle band
4//! and the standard deviation of prices for the band width. The upper band is defined as the middle
5//! band plus a specified multiplier times the standard deviation, and the lower band is defined as the
6//! middle band minus that value.
7//!
8//! # Examples
9//!
10//! ```rust
11//! use indexes_rs::v1::bollinger::main::BollingerBands;
12//! use indexes_rs::v1::bollinger::types::BBResult;
13//!
14//! // Create a BollingerBands indicator with a period of 20 and multiplier of 2.0
15//! let mut bb = BollingerBands::new(20, 2.0).unwrap();
16//!
17//! // Feed in prices (e.g., closing prices)
18//! let prices = vec![
19//! 100.0, 101.0, 102.0, 101.5, 100.5, 102.0, 103.0, 102.5, 104.0, 105.0,
20//! 104.5, 105.5, 106.0, 107.0, 106.5, 108.0, 107.5, 108.5, 109.0, 110.0,
21//! ];
22//!
23//! if let Some(result) = prices.into_iter().fold(None, |_, price| bb.calculate(price)) {
24//! println!("Upper Band: {:.2}", result.upper);
25//! println!("Middle Band: {:.2}", result.middle);
26//! println!("Lower Band: {:.2}", result.lower);
27//! }
28//! ```
29
30use super::types::BBResult;
31use crate::v1::sma::main::{SMAError, SimpleMovingAverage};
32use std::collections::VecDeque;
33
34/// Bollinger Bands indicator.
35pub struct BollingerBands {
36 sma: SimpleMovingAverage,
37 period: usize,
38 multiplier: f64,
39 values: VecDeque<f64>,
40}
41
42impl BollingerBands {
43 /// Creates a new BollingerBands indicator.
44 ///
45 /// # Arguments
46 ///
47 /// * `period` - The number of values for the moving average and standard deviation.
48 /// * `multiplier` - The multiplier applied to the standard deviation to determine band width.
49 ///
50 /// # Returns
51 ///
52 /// * `Ok(BollingerBands)` on success, or `Err(SMAError)` if the underlying SMA creation fails.
53 ///
54 /// # Examples
55 ///
56 /// ```rust
57 /// use indexes_rs::v1::bollinger::main::BollingerBands;
58 ///
59 /// let bb = BollingerBands::new(20, 2.0);
60 /// assert!(bb.is_ok());
61 /// ```
62 pub fn new(period: usize, multiplier: f64) -> Result<Self, SMAError> {
63 Ok(BollingerBands {
64 sma: SimpleMovingAverage::new(period)?,
65 period,
66 multiplier,
67 values: VecDeque::with_capacity(period),
68 })
69 }
70
71 /// Calculates the Bollinger Bands for the given price.
72 ///
73 /// The method updates the internal SMA and sliding window of prices.
74 /// It returns a `BBResult` containing the upper, middle, and lower bands when enough data is available.
75 ///
76 /// # Arguments
77 ///
78 /// * `price` - The latest price.
79 ///
80 /// # Returns
81 ///
82 /// * `Some(BBResult)` if enough data is available.
83 /// * `None` if not enough data has been collected.
84 pub fn calculate(&mut self, price: f64) -> Option<BBResult> {
85 self.sma.add_value(price);
86 self.values.push_back(price);
87 if self.values.len() > self.period {
88 self.values.pop_front();
89 }
90 let middle = self.sma.calculate()?;
91 let std_dev = self.calculate_std_dev(middle.value)?;
92 let band_width = std_dev * self.multiplier;
93 Some(BBResult {
94 upper: middle.value + band_width,
95 middle: middle.value,
96 lower: middle.value - band_width,
97 })
98 }
99
100 /// Calculates the standard deviation of the prices in the current window.
101 ///
102 /// # Arguments
103 ///
104 /// * `mean` - The mean (middle band) value.
105 ///
106 /// # Returns
107 ///
108 /// * `Some(f64)` containing the standard deviation if the window is full.
109 /// * `None` if the window does not yet contain enough values.
110 fn calculate_std_dev(&self, mean: f64) -> Option<f64> {
111 if self.values.len() < self.period {
112 return None;
113 }
114 let variance = self
115 .values
116 .iter()
117 .map(|&value| {
118 let diff = mean - value;
119 diff * diff
120 })
121 .sum::<f64>()
122 / self.period as f64;
123 Some(variance.sqrt())
124 }
125}