From 01874307b0f4747ff4a5046e68f990318fe63cb8 Mon Sep 17 00:00:00 2001 From: gord chung Date: Wed, 22 May 2024 10:03:51 -0400 Subject: [PATCH] test passing in iterable to avoid building collections --- Cargo.toml | 4 +++- benches/my_benchmark.rs | 6 +++--- src/indicator.rs | 28 +++++++++++++--------------- src/main.rs | 2 +- src/smooth.rs | 31 +++++++++++++++++++++---------- tests/ma_test.rs | 2 +- 6 files changed, 42 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4a0fdda..68c6d91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,9 @@ [package] name = "traquer" -version = "0.1.0" +version = "0.0.1" edition = "2021" +description = "statistical functions library" +license = "Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/benches/my_benchmark.rs b/benches/my_benchmark.rs index 52a4a54..fdb5db9 100644 --- a/benches/my_benchmark.rs +++ b/benches/my_benchmark.rs @@ -264,7 +264,7 @@ fn criterion_benchmark(c: &mut Criterion) { b.iter(|| black_box(smooth::wma(&stats.close, 16).collect::>())) }); c.bench_function("ma-wilder", |b| { - b.iter(|| black_box(smooth::wilder(&stats.close, 16).collect::>())) + b.iter(|| black_box(smooth::wilder(stats.close.iter().copied(), 16).collect::>())) }); c.bench_function("ma-hull", |b| { b.iter(|| black_box(smooth::hull(&stats.close, 16).collect::>())) @@ -276,13 +276,13 @@ fn criterion_benchmark(c: &mut Criterion) { b.iter(|| black_box(smooth::vma(&stats.close, 16).collect::>())) }); c.bench_function("ma-lrf", |b| { - b.iter(|| black_box(smooth::lrf(&stats.close, 16).collect::>())) + b.iter(|| black_box(smooth::lrf(stats.close.iter().copied(), 16).collect::>())) }); c.bench_function("ma-trima", |b| { b.iter(|| black_box(smooth::trima(&stats.close, 16).collect::>())) }); c.bench_function("ma-zlma", |b| { - b.iter(|| black_box(smooth::zlma(&stats.close, 16).collect::>())) + b.iter(|| black_box(smooth::zlma(stats.close.iter().copied(), 16).collect::>())) }); } diff --git a/src/indicator.rs b/src/indicator.rs index 2246037..1140725 100644 --- a/src/indicator.rs +++ b/src/indicator.rs @@ -142,20 +142,20 @@ pub fn adx( (dm_pos, dm_neg, tr) }), ); - let atr = smooth::wilder(&tr, period).collect::>(); - let di_pos = izip!(smooth::wilder(&dm_pos, period), &atr) + let atr = smooth::wilder(tr, period).collect::>(); + let di_pos = izip!(smooth::wilder(dm_pos, period), &atr) .map(|(di, tr)| di / tr * 100.0) .collect::>(); - let di_neg = izip!(smooth::wilder(&dm_neg, period), &atr) + let di_neg = izip!(smooth::wilder(dm_neg, period), &atr) .map(|(di, tr)| di / tr * 100.0) .collect::>(); - let dx = izip!(&di_pos, &di_neg) - .map(|(pos, neg)| f64::abs(pos - neg) / (pos + neg) * 100.0) + let dx = smooth::wilder(izip!(&di_pos, &di_neg) + .map(|(pos, neg)| f64::abs(pos - neg) / (pos + neg) * 100.0), smoothing) .collect::>(); ( di_pos, di_neg, - smooth::wilder(&dx, smoothing).collect::>(), + dx ) } @@ -167,8 +167,8 @@ pub fn rsi(data: &[f64], window: usize) -> Vec { .zip(data[..data.len() - 1].iter()) .map(|(curr, prev)| (f64::max(0.0, curr - prev), f64::min(0.0, curr - prev).abs())) .unzip(); - smooth::wilder(&gain, window) - .zip(smooth::wilder(&loss, window)) + smooth::wilder(gain, window) + .zip(smooth::wilder(loss, window)) .map(|(g, l)| 100.0 * g / (g + l)) .collect::>() } @@ -653,8 +653,7 @@ pub fn supertrend( multiplier: f64, ) -> Vec { // TODO: needs a test for when it actually flips to use upper band line - let tr = _true_range(high, low, close).collect::>(); - let atr = smooth::wilder(&tr, window); + let atr = smooth::wilder(_true_range(high, low, close), window); izip!(&high[window..], &low[window..], &close[window..], atr) .scan( (f64::NAN, f64::NAN, f64::MIN_POSITIVE, 1), @@ -710,7 +709,7 @@ pub fn stochastic(high: &[f64], low: &[f64], close: &[f64], window: usize) -> (V fn _stc(series: &[f64], window: usize) -> Vec { smooth::wilder( - &series + series .windows(window) .map(|w| { let mut hh = f64::NAN; @@ -720,8 +719,7 @@ fn _stc(series: &[f64], window: usize) -> Vec { ll = ll.min(*x); } 100.0 * (w.last().unwrap() - ll) / (hh - ll) - }) - .collect::>(), + }), 2, ) .collect::>() @@ -754,8 +752,8 @@ pub fn relative_vol(close: &[f64], window: usize, smoothing: usize) -> Vec ) }) .unzip(); - smooth::wilder(&gain, smoothing) - .zip(smooth::wilder(&loss, smoothing)) + smooth::wilder(gain, smoothing) + .zip(smooth::wilder(loss, smoothing)) .map(|(g, l)| 100.0 * g / (g + l)) .collect::>() } diff --git a/src/main.rs b/src/main.rs index 551834c..0783ed6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,5 +17,5 @@ fn main() { let stats: SecStats = serde_json::from_str(&data).expect("JSON does not have correct format."); //dbg!(indicator::rwi(&stats.high, &stats.low, &stats.close, 16)); - dbg!(smooth::sma(&stats.close, 16).collect::>()); + dbg!(smooth::dema(&stats.close, 16).collect::>()); } diff --git a/src/smooth.rs b/src/smooth.rs index 11f265f..7a4fb8d 100644 --- a/src/smooth.rs +++ b/src/smooth.rs @@ -58,9 +58,13 @@ pub fn wma(data: &[f64], window: usize) -> impl Iterator + '_ { } /// welles wilder's moving average -pub fn wilder(data: &[f64], window: usize) -> impl Iterator + '_ { - let initial = data[..window - 1].iter().sum::() / (window - 1) as f64; - data[window - 1..].iter().scan(initial, move |state, x| { +pub fn wilder(data: I, window: usize) -> impl Iterator +where + I: IntoIterator, +{ + let mut data_iter = data.into_iter(); + let initial = data_iter.by_ref().take(window - 1).sum::() / (window - 1) as f64; + data_iter.scan(initial, move |state, x| { let ma = (*state * (window - 1) as f64 + x) / window as f64; *state = ma; Some(ma) @@ -145,13 +149,16 @@ pub fn vma(data: &[f64], window: usize) -> impl Iterator + '_ { /// Linear Regression Forecast aka Time Series Forecast /// https://quantstrategy.io/blog/what-is-tsf-understanding-time-series-forecast-indicator/ -pub fn lrf(data: &[f64], window: usize) -> impl Iterator + '_ { +pub fn lrf(data: I, window: usize) -> impl Iterator +where + I: IntoIterator, +{ let x_sum = (window * (window + 1)) as f64 / 2.0; let x2_sum: f64 = x_sum * (2 * window + 1) as f64 / 3.0; let divisor = window as f64 * x2_sum - x_sum.powi(2); let indices: Vec = (1..=window).map(|x| x as f64).collect(); - data.windows(window).map(move |w| { + data.into_iter().collect::>().windows(window).map(move |w| { let mut y_sum = 0.0; let mut xy_sum = 0.0; for (count, val) in indices.iter().zip(w.iter()) { @@ -162,6 +169,7 @@ pub fn lrf(data: &[f64], window: usize) -> impl Iterator + '_ { let b = (y_sum * x2_sum - x_sum * xy_sum) / divisor; m * window as f64 + b }) + .collect::>().into_iter() } /// triangular moving average @@ -176,16 +184,19 @@ pub fn trima(data: &[f64], window: usize) -> impl Iterator + '_ { /// Zero Lag Moving Average /// https://en.wikipedia.org/wiki/Zero_lag_exponential_moving_average -pub fn zlma(data: &[f64], window: usize) -> impl Iterator + '_ { +pub fn zlma(data: I, window: usize) -> impl Iterator +where + I: IntoIterator, +{ let lag = (window - 1) / 2; + let data_vec = data.into_iter().collect::>(); ewma( - &data + &data_vec .iter() - .zip(data[lag..].iter()) + .zip(data_vec[lag..].iter()) .map(|(prev, curr)| 2.0 * curr - prev) .collect::>(), window, ) - .collect::>() - .into_iter() + .collect::>().into_iter() } diff --git a/tests/ma_test.rs b/tests/ma_test.rs index 35d491a..f7c4fce 100644 --- a/tests/ma_test.rs +++ b/tests/ma_test.rs @@ -535,7 +535,7 @@ fn test_wilder_odd() { #[test] fn test_lrf() { let stats = common::test_data(); - let result = smooth::lrf(&stats.close, 16); + let result = smooth::lrf(stats.close, 16); assert_eq!( vec![ 40.43308849895702,