use super::*;
use crate::test_helpers::helpers::{make_candle, ts};
use rust_decimal_macros::dec;
#[test]
fn bollinger_constant_prices_zero_width() {
let candles: Vec<Candle> = (0..10).map(|i| make_candle(dec!(100), ts(i))).collect();
let bb = BollingerBands::new(5, dec!(2)).expect("valid BB params");
let result = bb.compute(&candles).expect("sufficient data for BB");
assert_eq!(result.len(), 6);
for (_, v) in result.middle.values() {
assert_eq!(*v, dec!(100));
}
for (_, v) in result.upper.values() {
assert_eq!(*v, dec!(100));
}
for (_, v) in result.lower.values() {
assert_eq!(*v, dec!(100));
}
}
#[test]
fn bollinger_basic() {
let candles: Vec<Candle> = vec![
make_candle(dec!(20), ts(0)),
make_candle(dec!(22), ts(1)),
make_candle(dec!(21), ts(2)),
make_candle(dec!(23), ts(3)),
make_candle(dec!(22), ts(4)),
];
let bb = BollingerBands::new(3, dec!(2)).expect("valid BB params");
let result = bb.compute(&candles).expect("sufficient data for BB");
assert_eq!(result.len(), 3);
for i in 0..result.len() {
let upper = result.upper.get(i).expect("upper band value exists").1;
let middle = result.middle.get(i).expect("middle band value exists").1;
let lower = result.lower.get(i).expect("lower band value exists").1;
assert!(upper >= middle, "upper {} >= middle {}", upper, middle);
assert!(lower <= middle, "lower {} <= middle {}", lower, middle);
}
}
#[test]
fn bollinger_symmetric_bands() {
let candles: Vec<Candle> = vec![
make_candle(dec!(10), ts(0)),
make_candle(dec!(12), ts(1)),
make_candle(dec!(14), ts(2)),
make_candle(dec!(11), ts(3)),
make_candle(dec!(13), ts(4)),
];
let bb = BollingerBands::new(3, dec!(2)).expect("valid BB params");
let result = bb.compute(&candles).expect("sufficient data for BB");
for i in 0..result.len() {
let upper = result.upper.get(i).expect("upper band value exists").1;
let middle = result.middle.get(i).expect("middle band value exists").1;
let lower = result.lower.get(i).expect("lower band value exists").1;
let upper_diff = upper - middle;
let lower_diff = middle - lower;
assert_eq!(
upper_diff, lower_diff,
"Bands should be symmetric: upper_diff {} != lower_diff {}",
upper_diff, lower_diff
);
}
}
#[test]
fn bollinger_insufficient_data() {
let candles: Vec<Candle> = vec![
make_candle(dec!(100), ts(0)),
make_candle(dec!(102), ts(1)),
make_candle(dec!(103), ts(2)),
];
let bb = BollingerBands::new(5, dec!(2)).expect("valid BB params");
let result = bb.compute(&candles);
assert!(matches!(
result,
Err(IndicatorError::InsufficientData {
required: 5,
actual: 3
})
));
}
#[test]
fn bollinger_period_zero() {
let result = BollingerBands::new(0, dec!(2));
assert!(matches!(
result,
Err(IndicatorError::InvalidParameter { .. })
));
}
#[test]
fn bollinger_negative_multiplier() {
let result = BollingerBands::new(20, dec!(-1));
assert!(matches!(
result,
Err(IndicatorError::InvalidParameter { .. })
));
}
#[test]
fn bollinger_name() {
let bb = BollingerBands::new(20, dec!(2)).expect("valid BB params");
assert_eq!(bb.name(), "BB(20,2)");
}
#[test]
fn bollinger_warmup() {
let bb = BollingerBands::new(20, dec!(2)).expect("valid BB params");
assert_eq!(bb.warmup_period(), 20);
}
#[test]
fn bollinger_standard() {
let bb = BollingerBands::standard().expect("valid BB standard params");
assert_eq!(bb.warmup_period(), 20);
}
#[test]
fn bollinger_zero_multiplier() {
let candles: Vec<Candle> = vec![
make_candle(dec!(10), ts(0)),
make_candle(dec!(20), ts(1)),
make_candle(dec!(15), ts(2)),
];
let bb = BollingerBands::new(3, dec!(0)).expect("valid BB params");
let result = bb.compute(&candles).expect("sufficient data for BB");
for i in 0..result.len() {
let upper = result.upper.get(i).expect("upper band value exists").1;
let middle = result.middle.get(i).expect("middle band value exists").1;
let lower = result.lower.get(i).expect("lower band value exists").1;
assert_eq!(upper, middle);
assert_eq!(lower, middle);
}
}