use super::moving_averages::SimpleMovingAverage;
use super::{MathError, Result};
use crate::advanced_optimizations::{simd_math, AlignedBuffer};
use std::collections::VecDeque;
#[derive(Debug, Clone)]
pub struct BollingerBands {
period: usize,
std_dev_multiplier: f64,
prices: VecDeque<f64>,
sma: SimpleMovingAverage,
cached_std_dev: Option<f64>, values_seen: usize,
}
impl BollingerBands {
pub fn new(period: usize, std_dev_multiplier: f64) -> Result<Self> {
if period == 0 {
return Err(MathError::InvalidInput(
"Period must be greater than zero".to_string(),
));
}
if std_dev_multiplier <= 0.0 {
return Err(MathError::InvalidInput(
"Standard deviation multiplier must be greater than zero".to_string(),
));
}
Ok(Self {
period,
std_dev_multiplier,
prices: VecDeque::with_capacity(period), sma: SimpleMovingAverage::new(period)?,
cached_std_dev: None,
values_seen: 0,
})
}
#[inline]
pub fn update(&mut self, price: f64) -> Result<()> {
self.prices.push_back(price);
self.sma.update(price)?;
self.values_seen += 1;
if self.values_seen > self.period {
self.prices.pop_front();
self.values_seen -= 1;
}
self.cached_std_dev = None;
Ok(())
}
#[inline]
pub fn middle_band(&self) -> Result<f64> {
self.sma.value()
}
fn calculate_std_dev(&mut self) -> Result<f64> {
if let Some(cached) = self.cached_std_dev {
return Ok(cached);
}
if self.values_seen < self.period {
return Err(MathError::InsufficientData(format!(
"Not enough data to calculate standard deviation. Need {} values, have {}.",
self.period, self.values_seen
)));
}
let sma = self.sma.value()?;
let variance: f64 = self
.prices
.iter()
.map(|&price| {
let diff = price - sma;
diff * diff
})
.sum::<f64>()
/ self.period as f64;
let std_dev = variance.sqrt();
self.cached_std_dev = Some(std_dev);
Ok(std_dev)
}
pub fn upper_band(&mut self) -> Result<f64> {
let middle = self.middle_band()?;
let std_dev = self.calculate_std_dev()?;
Ok(middle + (std_dev * self.std_dev_multiplier))
}
pub fn lower_band(&mut self) -> Result<f64> {
let middle = self.middle_band()?;
let std_dev = self.calculate_std_dev()?;
Ok(middle - (std_dev * self.std_dev_multiplier))
}
pub fn bands(&mut self) -> Result<(f64, f64, f64)> {
let middle = self.middle_band()?;
let std_dev = self.calculate_std_dev()?;
let band_offset = std_dev * self.std_dev_multiplier;
Ok((
middle + band_offset, middle, middle - band_offset, ))
}
pub fn band_width(&mut self) -> Result<f64> {
let (upper, middle, lower) = self.bands()?;
Ok((upper - lower) / middle * 100.0) }
pub fn percent_b(&mut self, price: f64) -> Result<f64> {
let (upper, _, lower) = self.bands()?;
if (upper - lower).abs() < f64::EPSILON {
return Err(MathError::CalculationError(
"Upper and lower bands are equal, cannot calculate %B".to_string(),
));
}
Ok((price - lower) / (upper - lower))
}
#[inline]
pub fn period(&self) -> usize {
self.period
}
#[inline]
pub fn std_dev_multiplier(&self) -> f64 {
self.std_dev_multiplier
}
#[inline]
pub fn is_ready(&self) -> bool {
self.values_seen >= self.period
}
#[inline]
pub fn values_seen(&self) -> usize {
self.values_seen
}
pub fn reset(&mut self) {
self.prices.clear();
self.sma = SimpleMovingAverage::new(self.period).unwrap();
self.cached_std_dev = None;
self.values_seen = 0;
}
}
#[derive(Debug, Clone)]
pub struct AverageTrueRange {
period: usize,
tr_values: VecDeque<f64>,
previous_close: Option<f64>,
current_atr: Option<f64>,
values_seen: usize,
}
impl AverageTrueRange {
pub fn new(period: usize) -> Result<Self> {
if period == 0 {
return Err(MathError::InvalidInput(
"Period must be greater than zero".to_string(),
));
}
Ok(Self {
period,
tr_values: VecDeque::with_capacity(period),
previous_close: None,
current_atr: None,
values_seen: 0,
})
}
pub fn update(&mut self, high: f64, low: f64, close: f64) -> Result<()> {
if low > high {
return Err(MathError::InvalidInput(
"Low price cannot be greater than high price".to_string(),
));
}
self.values_seen += 1;
let true_range = if let Some(prev_close) = self.previous_close {
let high_low = high - low;
let high_pc = (high - prev_close).abs();
let low_pc = (low - prev_close).abs();
high_low.max(high_pc).max(low_pc)
} else {
high - low
};
self.tr_values.push_back(true_range);
if self.values_seen <= self.period {
if self.values_seen == self.period {
let sum = self.tr_values.iter().sum::<f64>();
self.current_atr = Some(sum / self.period as f64);
}
} else {
if let Some(prior_atr) = self.current_atr {
let new_atr =
(prior_atr * (self.period as f64 - 1.0) + true_range) / self.period as f64;
self.current_atr = Some(new_atr);
if self.tr_values.len() > self.period {
self.tr_values.pop_front();
}
}
}
self.previous_close = Some(close);
Ok(())
}
pub fn value(&self) -> Result<f64> {
match self.current_atr {
Some(atr) => Ok(atr),
None => Err(MathError::InsufficientData(format!(
"Not enough data for ATR calculation. Need {} values, have {}.",
self.period, self.values_seen
))),
}
}
pub fn period(&self) -> usize {
self.period
}
pub fn reset(&mut self) {
self.tr_values.clear();
self.previous_close = None;
self.current_atr = None;
self.values_seen = 0;
}
}
#[derive(Debug, Clone)]
pub struct StandardDeviation {
period: usize,
values: VecDeque<f64>,
mean: Option<f64>,
}
impl StandardDeviation {
pub fn new(period: usize) -> Result<Self> {
if period == 0 {
return Err(MathError::InvalidInput(
"Period must be greater than zero".to_string(),
));
}
Ok(Self {
period,
values: VecDeque::with_capacity(period),
mean: None,
})
}
pub fn update(&mut self, value: f64) -> Result<()> {
self.values.push_back(value);
if self.values.len() >= self.period {
let sum: f64 = self.values.iter().sum();
self.mean = Some(sum / self.values.len() as f64);
if self.values.len() > self.period {
self.values.pop_front();
}
}
Ok(())
}
pub fn value(&self) -> Result<f64> {
if self.values.len() < self.period {
return Err(MathError::InsufficientData(format!(
"Not enough data for standard deviation calculation. Need {} values, have {}.",
self.period,
self.values.len()
)));
}
if let Some(mean) = self.mean {
let variance: f64 = self
.values
.iter()
.map(|&value| {
let diff = value - mean;
diff * diff
})
.sum::<f64>()
/ self.values.len() as f64;
Ok(variance.sqrt())
} else {
Err(MathError::CalculationError(
"Mean not calculated".to_string(),
))
}
}
pub fn period(&self) -> usize {
self.period
}
pub fn reset(&mut self) {
self.values.clear();
self.mean = None;
}
}
#[derive(Debug, Clone)]
pub struct FastBollingerBands {
period: usize,
std_dev_multiplier: f64,
prices: AlignedBuffer<f64>,
sum: f64,
cached_mean: Option<f64>,
cached_std_dev: Option<f64>,
values_seen: usize,
}
impl FastBollingerBands {
pub fn new(period: usize, std_dev_multiplier: f64) -> Result<Self> {
if period == 0 {
return Err(MathError::InvalidInput(
"Period must be greater than zero".to_string(),
));
}
if std_dev_multiplier <= 0.0 {
return Err(MathError::InvalidInput(
"Standard deviation multiplier must be greater than zero".to_string(),
));
}
Ok(Self {
period,
std_dev_multiplier,
prices: AlignedBuffer::new(period),
sum: 0.0,
cached_mean: None,
cached_std_dev: None,
values_seen: 0,
})
}
#[inline]
pub fn update_fast(&mut self, price: f64) -> Result<()> {
if self.prices.is_full() {
let old_slice = self.prices.as_slice();
let oldest = old_slice[self.values_seen % self.period];
self.sum -= oldest;
}
self.prices.push(price);
self.sum += price;
if self.values_seen < self.period {
self.values_seen += 1;
}
self.cached_mean = None;
self.cached_std_dev = None;
Ok(())
}
#[inline]
fn mean(&mut self) -> Result<f64> {
if let Some(cached) = self.cached_mean {
return Ok(cached);
}
if self.values_seen < self.period {
return Err(MathError::InsufficientData(format!(
"Not enough data. Need {} values, have {}.",
self.period, self.values_seen
)));
}
let mean = self.sum / self.period as f64;
self.cached_mean = Some(mean);
Ok(mean)
}
fn std_dev(&mut self) -> Result<f64> {
if let Some(cached) = self.cached_std_dev {
return Ok(cached);
}
let mean = self.mean()?;
let slice = self.prices.as_slice();
let variance = simd_math::variance_f64_optimized(slice, mean);
let std_dev = variance.sqrt();
self.cached_std_dev = Some(std_dev);
Ok(std_dev)
}
pub fn bands_fast(&mut self) -> Result<(f64, f64, f64)> {
let mean = self.mean()?;
let std_dev = self.std_dev()?;
let offset = std_dev * self.std_dev_multiplier;
Ok((
mean + offset, mean, mean - offset, ))
}
#[inline]
pub fn is_ready(&self) -> bool {
self.values_seen >= self.period
}
#[inline]
pub fn period(&self) -> usize {
self.period
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bollinger_bands_calculation() {
let mut bb = BollingerBands::new(3, 2.0).unwrap();
bb.update(10.0).unwrap();
bb.update(11.0).unwrap();
bb.update(9.0).unwrap();
let middle = bb.middle_band().unwrap();
assert!((middle - 10.0).abs() < 0.001);
let upper = bb.upper_band().unwrap();
let lower = bb.lower_band().unwrap();
assert!(upper > middle);
assert!(lower < middle);
let percent_b_upper = bb.percent_b(upper).unwrap();
assert!((percent_b_upper - 1.0).abs() < 0.001);
let percent_b_lower = bb.percent_b(lower).unwrap();
assert!(percent_b_lower.abs() < 0.001);
}
#[test]
fn test_atr_calculation() {
let mut atr = AverageTrueRange::new(3).unwrap();
atr.update(110.0, 100.0, 105.0).unwrap();
atr.update(115.0, 103.0, 110.0).unwrap();
atr.update(112.0, 106.0, 107.0).unwrap();
let atr_value = atr.value().unwrap();
assert!(atr_value > 0.0);
atr.update(108.0, 106.0, 107.0).unwrap(); let new_atr_value = atr.value().unwrap();
assert!(new_atr_value > 0.0 && new_atr_value.is_finite());
}
#[test]
fn test_standard_deviation_calculation() {
let mut std_dev = StandardDeviation::new(3).unwrap();
std_dev.update(10.0).unwrap();
std_dev.update(20.0).unwrap();
std_dev.update(30.0).unwrap();
let expected =
((10.0f64 - 20.0).powi(2) + (20.0f64 - 20.0).powi(2) + (30.0f64 - 20.0).powi(2)) / 3.0;
let expected = expected.sqrt();
let std_dev_value = std_dev.value().unwrap();
assert!((std_dev_value - expected).abs() < 0.001);
}
#[test]
fn test_fast_bollinger_bands() {
let mut bb = FastBollingerBands::new(3, 2.0).unwrap();
bb.update_fast(10.0).unwrap();
bb.update_fast(11.0).unwrap();
bb.update_fast(9.0).unwrap();
assert!(bb.is_ready());
let (upper, middle, lower) = bb.bands_fast().unwrap();
assert!((middle - 10.0).abs() < 0.001);
assert!(upper > middle);
assert!(lower < middle);
}
}