Skip to content

Commit

Permalink
add williams R, vortex and percent oscillator indicators
Browse files Browse the repository at this point in the history
  • Loading branch information
chungg committed May 13, 2024
1 parent 3878b3c commit 11eaeed
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 1 deletion.
59 changes: 59 additions & 0 deletions src/indicator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,62 @@ pub fn cvi(h: &[f64], l: &[f64], window: u8, rate_of_change: u8) -> Vec<f64> {
.map(|w| 100.0 * (w.last().unwrap() / w.first().unwrap() - 1.0))
.collect::<Vec<f64>>()
}

/// Williams Percent Range
/// https://www.investopedia.com/terms/w/williamsr.asp
pub fn wpr(h: &[f64], l: &[f64], c: &[f64], window: u8) -> Vec<f64> {
izip!(
h.windows(window.into()),
l.windows(window.into()),
&c[(window - 1).into()..]
)
.map(|(high, low, close)| {
let hh = high.iter().fold(f64::NAN, |state, &x| state.max(x));
let ll = low.iter().fold(f64::NAN, |state, &x| state.min(x));
-100.0 * ((hh - close) / (hh - ll))
})
.collect::<Vec<f64>>()
}

/// vortex
/// https://www.investopedia.com/terms/v/vortex-indicator-vi.asp
pub fn vortex(h: &[f64], l: &[f64], c: &[f64], window: u8) -> (Vec<f64>, Vec<f64>) {
izip!(
&h[..h.len() - 1],
&h[1..],
&l[..l.len() - 1],
&l[1..],
&c[..c.len() - 1],
)
.map(|(prevh, h, prevl, l, prevc)| {
let vm_pos = (h - prevl).abs();
let vm_neg = (l - prevh).abs();
let tr = (h - l).max(f64::abs(h - prevc)).max(f64::abs(l - prevc));
(vm_pos, vm_neg, tr)
})
.collect::<Vec<(f64, f64, f64)>>()
.windows(window.into())
.map(|w| {
let (vm_pos, vm_neg, tr) = w
.iter()
.copied()
.reduce(|(acc_pos, acc_neg, acc_tr), (pos, neg, tr)| {
(acc_pos + pos, acc_neg + neg, acc_tr + tr)
})
.unwrap();
(vm_pos / tr, vm_neg / tr)
})
.unzip()
}

/// percent oscillator
/// pass in any data (close, high, low, etc...), and two window ranges
pub fn po(data: &[f64], short: u8, long: u8) -> Vec<f64> {
let short_ma = smooth::ewma(&data, short);

Check failure on line 365 in src/indicator.rs

View workflow job for this annotation

GitHub Actions / analyze

this expression creates a reference which is immediately dereferenced by the compiler
let long_ma = smooth::ewma(&data, long);

Check failure on line 366 in src/indicator.rs

View workflow job for this annotation

GitHub Actions / analyze

this expression creates a reference which is immediately dereferenced by the compiler
short_ma[short_ma.len() - long_ma.len()..]
.iter()
.zip(long_ma)
.map(|(x, y)| 100.0 * (x / y - 1.0))
.collect::<Vec<f64>>()
}
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +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::cvi(&stats.high, &stats.low, 16, 2));
dbg!(indicator::po(&stats.volume, 10, 16));
}
112 changes: 112 additions & 0 deletions tests/indicator_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,3 +564,115 @@ fn test_cvi() {
result
);
}

#[test]
fn test_wpr() {
let stats = common::test_data();
let result = indicator::wpr(&stats.high, &stats.low, &stats.close, 16);
assert_eq!(
vec![
-99.09142622449394,
-94.88476784384058,
-98.7016646516565,
-83.45322691468988,
-80.33424822308433,
-66.49998256138393,
-60.92856270926339,
-58.24332819489061,
-56.88925190670342,
-31.699065254207774,
-24.51395669497002,
-38.50825188642196,
-26.019074805435594,
-15.778329765623885,
-24.403940042613893,
-12.779540060870623,
-7.164869527394986,
-21.111723928174033,
-32.930944560522704,
],
result
);
}

#[test]
fn test_vortex() {
let stats = common::test_data();
let (vi_pos, vi_neg) = indicator::vortex(&stats.high, &stats.low, &stats.close, 16);
assert_eq!(
vec![
0.8610723090930696,
0.8159089697456218,
0.5782198030623583,
0.7200793857247078,
0.8358118890986928,
0.9355813847576413,
0.9484126915125689,
0.8489016637989781,
0.9131108818882286,
0.9790387062979787,
0.9343265196480259,
0.9443134085341751,
1.0302488811514465,
1.0364553935724832,
1.0553301509762798,
1.1219923299032897,
1.161732264987062,
1.1478332770638693,
],
vi_pos
);
assert_eq!(
vec![
1.029701151945177,
1.1817046446451998,
1.3803206728528217,
1.238892593460365,
1.1850444607043855,
1.061167502645104,
1.111042759028416,
1.1313347365841422,
0.9951327158267141,
0.9280527122256887,
0.9901265627745084,
0.9313767804581332,
0.8402113281909229,
0.891932290028499,
0.8280623772231498,
0.7860652938018367,
0.6974842970716879,
0.7557323147066469,
],
vi_neg
);
}

#[test]
fn test_po() {
let stats = common::test_data();
let result = indicator::po(&stats.volume, 10, 16);
assert_eq!(
vec![
-35.378723807894985,
-37.993168058882375,
-39.524346350340444,
-40.41967020222986,
-40.51097271301698,
-42.086823387150005,
-42.30015209438188,
-43.383528771209576,
-43.81409605428357,
-40.648471039972534,
-37.7876496415686,
-37.39351505516741,
-37.136103488993875,
-34.28067604316157,
-35.12026222042619,
-32.44673522414948,
-18.294669010949182,
2.308566455542005,
-0.92080395315155,
],
result
);
}

0 comments on commit 11eaeed

Please sign in to comment.