diff --git a/ta_lib/volatility/Cargo.toml b/ta_lib/volatility/Cargo.toml new file mode 100644 index 00000000..cffb9c66 --- /dev/null +++ b/ta_lib/volatility/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "volatility" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +statistics = { path = "../statistics" } +overlap = { path = "../overlap" } \ No newline at end of file diff --git a/ta_lib/volatility/src/bbands.rs b/ta_lib/volatility/src/bbands.rs new file mode 100644 index 00000000..d7e9d012 --- /dev/null +++ b/ta_lib/volatility/src/bbands.rs @@ -0,0 +1,110 @@ +use overlap::sma::sma; +use statistics::stddev::std_dev; + +pub fn bbands( + data: &[f64], + period: usize, + factor: usize, +) -> (Vec>, Vec>, Vec>) { + let len = data.len(); + let stddev = std_dev(data, period); + let middle_band = sma(data, period); + + let mut upper_band = vec![None; len]; + let mut lower_band = vec![None; len]; + + for i in 0..len { + if let (Some(middle), Some(std_dev)) = (middle_band[i], stddev[i]) { + upper_band[i] = Some(middle + (std_dev * factor as f64)); + lower_band[i] = Some(middle - (std_dev * factor as f64)); + } + } + + (upper_band, middle_band, lower_band) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bbands() { + let data = vec![2.0, 4.0, 6.0, 8.0, 10.0, 9.0, 8.0, 7.0, 6.0, 5.0]; + let period = 3; + let factor = 2; + let epsilon = 0.001; + let expected_upper_band = vec![ + None, + None, + Some(7.265986), + Some(9.265986), + Some(11.265986), + Some(10.632993), + Some(10.632993), + Some(9.632993), + Some(8.632993), + Some(7.632993), + ]; + let expected_middle_band = vec![ + None, + None, + Some(4.0), + Some(6.0), + Some(8.0), + Some(9.0), + Some(9.0), + Some(8.0), + Some(7.0), + Some(6.0), + ]; + let expected_lower_band = vec![ + None, + None, + Some(0.734014), + Some(2.734014), + Some(4.734014), + Some(7.367007), + Some(7.367007), + Some(6.367007), + Some(5.367007), + Some(4.367007), + ]; + + let (upper_band, middle_band, lower_band) = bbands(&data, period, factor); + + for i in 0..data.len() { + match (upper_band[i], expected_upper_band[i]) { + (Some(a), Some(b)) => { + assert!((a - b).abs() < epsilon, "at position {}: {} != {}", i, a, b) + } + (None, None) => {} + _ => panic!( + "at position {}: {:?} != {:?}", + i, upper_band[i], expected_upper_band[i] + ), + } + + match (middle_band[i], expected_middle_band[i]) { + (Some(a), Some(b)) => { + assert!((a - b).abs() < epsilon, "at position {}: {} != {}", i, a, b) + } + (None, None) => {} + _ => panic!( + "at position {}: {:?} != {:?}", + i, middle_band[i], expected_middle_band[i] + ), + } + + match (lower_band[i], expected_lower_band[i]) { + (Some(a), Some(b)) => { + assert!((a - b).abs() < epsilon, "at position {}: {} != {}", i, a, b) + } + (None, None) => {} + _ => panic!( + "at position {}: {:?} != {:?}", + i, lower_band[i], expected_lower_band[i] + ), + } + } + } +} diff --git a/ta_lib/volatility/src/lib.rs b/ta_lib/volatility/src/lib.rs new file mode 100644 index 00000000..317ba665 --- /dev/null +++ b/ta_lib/volatility/src/lib.rs @@ -0,0 +1 @@ +pub mod bbands;