diff --git a/src/indicator.rs b/src/indicator.rs index a3c7c1d..ea01e6b 100644 --- a/src/indicator.rs +++ b/src/indicator.rs @@ -372,9 +372,9 @@ pub fn vortex(high: &[f64], low: &[f64], close: &[f64], window: usize) -> (Vec Vec { +pub fn ppo(data: &[f64], short: usize, long: usize) -> Vec { let short_ma = smooth::ewma(data, short); let long_ma = smooth::ewma(data, long); short_ma[short_ma.len() - long_ma.len()..] @@ -384,6 +384,30 @@ pub fn po(data: &[f64], short: usize, long: usize) -> Vec { .collect::>() } +/// Absolute Price Oscillator +/// https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/apo +pub fn apo(data: &[f64], short: usize, long: usize) -> Vec { + let short_ma = smooth::ewma(data, short); + let long_ma = smooth::ewma(data, long); + short_ma[short_ma.len() - long_ma.len()..] + .iter() + .zip(long_ma) + .map(|(x, y)| x - y) + .collect::>() +} + +/// Detrended Price Oscillator +pub fn dpo(data: &[f64], window: usize) -> Vec { + let ma = smooth::sma(data, window); + let lag = window / 2 + 1; + dbg!(&ma); + data[window - lag - 1..] + .iter() + .zip(ma) + .map(|(x, y)| x - y) + .collect::>() +} + /// vertical horizontal filter /// https://www.upcomingtrader.com/blog/the-vertical-horizontal-filter-a-traders-guide-to-market-phases/ pub fn vhf(high: &[f64], low: &[f64], close: &[f64], window: usize) -> Vec { diff --git a/src/main.rs b/src/main.rs index 9dca34c..5e8b20f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,11 +16,5 @@ fn main() { let data = fs::read_to_string("./tests/rddt.input").expect("Unable to read file"); let stats: SecStats = serde_json::from_str(&data).expect("JSON does not have correct format."); - dbg!(indicator::relative_vigor( - &stats.open, - &stats.high, - &stats.low, - &stats.close, - 16 - )); + dbg!(indicator::dpo(&stats.close, 16)); } diff --git a/src/smooth.rs b/src/smooth.rs index a7fb096..4d6fe37 100644 --- a/src/smooth.rs +++ b/src/smooth.rs @@ -172,3 +172,17 @@ pub fn trima(data: &[f64], window: usize) -> Vec { let win2 = if window & 2 == 0 { win1 + 1 } else { win1 }; sma(&sma(data, win1), win2) } + +/// Zero Lag Moving Average +/// https://en.wikipedia.org/wiki/Zero_lag_exponential_moving_average +pub fn zlma(data: &[f64], window: usize) -> Vec { + let lag = (window - 1) / 2; + ewma( + &data + .iter() + .zip(data[lag..].iter()) + .map(|(prev, curr)| 2.0 * curr - prev) + .collect::>(), + window, + ) +} diff --git a/tests/indicator_test.rs b/tests/indicator_test.rs index 2932d4e..54e586e 100644 --- a/tests/indicator_test.rs +++ b/tests/indicator_test.rs @@ -648,9 +648,9 @@ fn test_vortex() { } #[test] -fn test_po() { +fn test_ppo() { let stats = common::test_data(); - let result = indicator::po(&stats.volume, 10, 16); + let result = indicator::ppo(&stats.volume, 10, 16); assert_eq!( vec![ -35.378723807894985, @@ -677,6 +677,66 @@ fn test_po() { ); } +#[test] +fn test_apo() { + let stats = common::test_data(); + let result = indicator::apo(&stats.close, 10, 16); + assert_eq!( + vec![ + -3.066861454520314, + -2.976616647434078, + -2.974212899316754, + -2.745221486406585, + -2.5750892679175763, + -2.3227459270799784, + -2.040835124550796, + -1.7724858153941057, + -1.5857634044718836, + -1.2258138130321825, + -0.885191183160849, + -0.7384274508922886, + -0.5242732104821357, + -0.24166405735277152, + -0.1004936005485888, + 0.10816949510991947, + 0.3290082798737828, + 0.6097419848129704, + 0.6642876636596782, + ], + result + ); +} + +#[test] +fn test_dpo() { + let stats = common::test_data(); + let result = indicator::dpo(&stats.close, 16); + assert_eq!( + vec![ + 2.0268754959106445, + -1.129373550415039, + -1.0500013828277588, + 2.191876173019409, + 1.8362512588500977, + 1.141249656677246, + -1.5718750953674316, + 1.324373483657837, + -0.6518747806549072, + -2.9000003337860107, + -1.6800007820129395, + -3.5431268215179443, + -1.048123836517334, + -2.2387490272521973, + -1.2106242179870605, + -0.8056254386901855, + -1.0631237030029297, + -2.404374599456787, + -0.057187557220458984, + ], + result + ); +} + #[test] fn test_vhf() { let stats = common::test_data(); diff --git a/tests/ma_test.rs b/tests/ma_test.rs index 77cfd30..d72975a 100644 --- a/tests/ma_test.rs +++ b/tests/ma_test.rs @@ -622,3 +622,26 @@ fn test_trima_odd() { result ); } + +#[test] +fn test_zlma() { + let stats = common::test_data(); + let result = smooth::zlma(&stats.close, 16); + assert_eq!( + vec![ + 37.98812532424927, + 38.66716965507059, + 40.19926762993361, + 41.45111806616384, + 42.2215743154743, + 43.10374218667769, + 44.21859626248675, + 44.91405520259631, + 45.993578137889436, + 46.861392797735625, + 47.99770003005625, + 48.72149975724747, + ], + result + ); +}