Skip to content

Commit

Permalink
add price oscillator indicators and zero lag ma (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
chungg authored May 18, 2024
1 parent 38ea0b5 commit c7c9990
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 11 deletions.
28 changes: 26 additions & 2 deletions src/indicator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,9 @@ pub fn vortex(high: &[f64], low: &[f64], close: &[f64], window: usize) -> (Vec<f
.unzip()
}

/// percent oscillator
/// percent price oscillator
/// pass in any data (close, high, low, etc...), and two window ranges
pub fn po(data: &[f64], short: usize, long: usize) -> Vec<f64> {
pub fn ppo(data: &[f64], short: usize, long: usize) -> Vec<f64> {
let short_ma = smooth::ewma(data, short);
let long_ma = smooth::ewma(data, long);
short_ma[short_ma.len() - long_ma.len()..]
Expand All @@ -384,6 +384,30 @@ pub fn po(data: &[f64], short: usize, long: usize) -> Vec<f64> {
.collect::<Vec<f64>>()
}

/// 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<f64> {
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::<Vec<f64>>()
}

/// Detrended Price Oscillator
pub fn dpo(data: &[f64], window: usize) -> Vec<f64> {
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::<Vec<f64>>()
}

/// 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<f64> {
Expand Down
8 changes: 1 addition & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
14 changes: 14 additions & 0 deletions src/smooth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,17 @@ pub fn trima(data: &[f64], window: usize) -> Vec<f64> {
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<f64> {
let lag = (window - 1) / 2;
ewma(
&data
.iter()
.zip(data[lag..].iter())
.map(|(prev, curr)| 2.0 * curr - prev)
.collect::<Vec<f64>>(),
window,
)
}
64 changes: 62 additions & 2 deletions tests/indicator_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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();
Expand Down
23 changes: 23 additions & 0 deletions tests/ma_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}

0 comments on commit c7c9990

Please sign in to comment.