diff --git a/benches/my_benchmark.rs b/benches/my_benchmark.rs index e410c64..d2de307 100644 --- a/benches/my_benchmark.rs +++ b/benches/my_benchmark.rs @@ -3,7 +3,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use serde::{Deserialize, Serialize}; use std::fs; -use traquer::indicator; +use traquer::*; #[derive(Deserialize, Serialize, Debug)] struct SecStats { @@ -18,16 +18,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { let data = fs::read_to_string("./aapl.input").expect("Unable to read file"); let stats: SecStats = serde_json::from_str(&data).expect("JSON does not have correct format."); c.bench_function("fib 20", |b| { - b.iter(|| { - black_box(indicator::ultimate( - &stats.high, - &stats.low, - &stats.close, - 6, - 12, - 24, - )) - }) + b.iter(|| black_box(smooth::wilder(&stats.close, 16).collect::>())) }); } diff --git a/src/indicator.rs b/src/indicator.rs index ea01e6b..51b2e9a 100644 --- a/src/indicator.rs +++ b/src/indicator.rs @@ -31,8 +31,8 @@ pub fn kvo( let vf = vforce(high, low, close, volume); let short_ma = smooth::ewma(&vf, short); let long_ma = smooth::ewma(&vf, long); - short_ma[short_ma.len() - long_ma.len()..] - .iter() + short_ma + .skip(long - short) .zip(long_ma) .map(|(x, y)| x - y) .collect::>() @@ -46,7 +46,7 @@ pub fn qstick(open: &[f64], close: &[f64], window: usize) -> Vec { .zip(open.iter()) .map(|(c, o)| c - o) .collect::>(); - smooth::ewma(&q, window) + smooth::ewma(&q, window).collect::>() } fn wilder_sum(data: &[f64], window: usize) -> Vec { @@ -146,7 +146,7 @@ pub fn adx( (dm_pos, dm_neg, tr) }), ); - let atr = smooth::wilder(&tr, period); + 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::>(); @@ -156,7 +156,11 @@ pub fn adx( let dx = izip!(&di_pos, &di_neg) .map(|(pos, neg)| f64::abs(pos - neg) / (pos + neg) * 100.0) .collect::>(); - (di_pos, di_neg, smooth::wilder(&dx, smoothing)) + ( + di_pos, + di_neg, + smooth::wilder(&dx, smoothing).collect::>(), + ) } /// relative strength index @@ -168,8 +172,7 @@ pub fn rsi(data: &[f64], window: usize) -> Vec { .map(|(curr, prev)| (f64::max(0.0, curr - prev), f64::min(0.0, curr - prev).abs())) .unzip(); smooth::wilder(&gain, window) - .iter() - .zip(smooth::wilder(&loss, window).iter()) + .zip(smooth::wilder(&loss, window)) .map(|(g, l)| 100.0 * g / (g + l)) .collect::>() } @@ -179,8 +182,8 @@ pub fn rsi(data: &[f64], window: usize) -> Vec { pub fn macd(close: &[f64], short: usize, long: usize) -> Vec { let short_ma = smooth::ewma(close, short); let long_ma = smooth::ewma(close, long); - short_ma[short_ma.len() - long_ma.len()..] - .iter() + short_ma + .skip(long - short) .zip(long_ma) .map(|(x, y)| x - y) .collect::>() @@ -190,7 +193,6 @@ pub fn macd(close: &[f64], short: usize, long: usize) -> Vec { /// https://www.investopedia.com/terms/c/chandemomentumoscillator.asp pub fn cmo(data: &[f64], window: usize) -> Vec { smooth::_cmo(data, window) - .iter() .map(|x| x * 100.0) .collect::>() } @@ -198,12 +200,13 @@ pub fn cmo(data: &[f64], window: usize) -> Vec { /// centre of gravity /// https://www.stockmaniacs.net/center-of-gravity-indicator/ pub fn cog(data: &[f64], window: usize) -> Vec { + let weights: Vec = (1..=window).map(|x| x as f64).collect(); data.windows(window) .map(|w| { -w.iter() .rev() - .enumerate() - .map(|(i, e)| (e * (i + 1) as f64)) + .zip(weights.iter()) + .map(|(e, i)| e * i) .sum::() / w.iter().sum::() }) @@ -248,8 +251,8 @@ pub fn ad_yahoo(high: &[f64], low: &[f64], close: &[f64], volume: &[f64]) -> Vec pub fn elder_ray(high: &[f64], low: &[f64], close: &[f64], window: usize) -> (Vec, Vec) { let close_ma = smooth::ewma(close, window); izip!( - &high[high.len() - close_ma.len()..], - &low[low.len() - close_ma.len()..], + high.iter().skip(window - 1), + low.iter().skip(window - 1), close_ma ) .map(|(h, l, c)| (h - c, l - c)) @@ -265,6 +268,7 @@ pub fn elder_force(close: &[f64], volume: &[f64], window: usize) -> Vec { .collect::>(), window, ) + .collect::>() } /// williams alligator @@ -320,6 +324,7 @@ pub fn cvi(high: &[f64], low: &[f64], window: usize, rate_of_change: usize) -> V .collect::>(), window, ) + .collect::>() .windows(rate_of_change + 1) .map(|w| 100.0 * (w.last().unwrap() / w.first().unwrap() - 1.0)) .collect::>() @@ -377,8 +382,8 @@ pub fn vortex(high: &[f64], low: &[f64], close: &[f64], window: usize) -> (Vec Vec { let short_ma = smooth::ewma(data, short); let long_ma = smooth::ewma(data, long); - short_ma[short_ma.len() - long_ma.len()..] - .iter() + short_ma + .skip(long - short) .zip(long_ma) .map(|(x, y)| 100.0 * (x / y - 1.0)) .collect::>() @@ -389,8 +394,8 @@ pub fn ppo(data: &[f64], short: usize, long: usize) -> Vec { 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() + short_ma + .skip(long - short) .zip(long_ma) .map(|(x, y)| x - y) .collect::>() @@ -400,7 +405,6 @@ pub fn apo(data: &[f64], short: usize, long: usize) -> Vec { 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) @@ -411,10 +415,12 @@ pub fn dpo(data: &[f64], window: usize) -> Vec { /// 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 { - let diffs = &close[1..] - .iter() - .zip(&close[..close.len() - 1]) - .map(|(curr, prev)| (curr - prev).abs()) + let diffs = close + .windows(2) + .map(|pair| { + let (prev, curr) = (pair[0], pair[1]); + (curr - prev).abs() + }) .collect::>(); izip!( diffs.windows(window), @@ -475,15 +481,12 @@ pub fn ultimate( /// pretty good oscillator /// https://library.tradingtechnologies.com/trade/chrt-ti-pretty-good-oscillator.html pub fn pgo(high: &[f64], low: &[f64], close: &[f64], window: usize) -> Vec { - let atr = smooth::ewma(&_true_range(high, low, close).collect::>(), window); + let tr = _true_range(high, low, close).collect::>(); + let atr = smooth::ewma(&tr, window); let sma_close = smooth::sma(close, window); - izip!( - &close[close.len() - atr.len()..], - &sma_close[sma_close.len() - atr.len()..], - atr - ) - .map(|(c, c_ma, tr_ma)| (c - c_ma) / tr_ma) - .collect::>() + izip!(close.iter().skip(window), sma_close.skip(1), atr) + .map(|(c, c_ma, tr_ma)| (c - c_ma) / tr_ma) + .collect::>() } fn _swing<'a>( @@ -544,17 +547,14 @@ pub fn asi(open: &[f64], high: &[f64], low: &[f64], close: &[f64], limit: f64) - pub fn ulcer(data: &[f64], window: usize) -> Vec { let highest = data .windows(window) - .map(|w| w.iter().fold(f64::NAN, |state, &x| state.max(x))) - .collect::>(); + .map(|w| w.iter().fold(f64::NAN, |state, &x| state.max(x))); smooth::sma( &highest - .iter() - .zip(&data[data.len() - highest.len()..]) + .zip(data.iter().skip(window - 1)) .map(|(high, c)| (100.0 * (c - high) / high).powi(2)) .collect::>(), window, ) - .iter() .map(|x| x.sqrt()) .collect::>() } @@ -583,12 +583,18 @@ pub fn hlc3(high: &[f64], low: &[f64], close: &[f64], window: usize) -> Vec .collect::>(), window, ) + .collect::>() } /// Triple Exponential Average /// https://www.investopedia.com/terms/t/trix.asp pub fn trix(close: &[f64], window: usize) -> Vec { - let ema3 = smooth::ewma(&smooth::ewma(&smooth::ewma(close, window), window), window); + let ema3 = smooth::ewma( + &smooth::ewma(&smooth::ewma(close, window).collect::>(), window) + .collect::>(), + window, + ) + .collect::>(); ema3[..ema3.len() - 1] .iter() .zip(&ema3[1..]) @@ -600,7 +606,6 @@ pub fn trix(close: &[f64], window: usize) -> Vec { /// https://www.marketvolume.com/technicalanalysis/trendintensityindex.asp pub fn tii(data: &[f64], window: usize) -> Vec { smooth::sma(data, window) - .iter() .zip(&data[(window - 1)..]) .map(|(avg, actual)| { let dev: f64 = actual - avg; @@ -652,8 +657,9 @@ pub fn supertrend( multiplier: f64, ) -> Vec { // TODO: needs a test for when it actually flips to use upper band line - let atr = smooth::wilder(&_true_range(high, low, close).collect::>(), window); - izip!(&high[window..], &low[window..], &close[window..], &atr) + let tr = _true_range(high, low, close).collect::>(); + let atr = smooth::wilder(&tr, window); + izip!(&high[window..], &low[window..], &close[window..], atr) .scan( (f64::NAN, f64::NAN, f64::MIN_POSITIVE, 1), |state, (h, l, c, tr)| { @@ -700,8 +706,9 @@ pub fn stochastic(high: &[f64], low: &[f64], close: &[f64], window: usize) -> (V }) .collect::>(), 3, - ); - let k = smooth::sma(&fast_k, 3); + ) + .collect::>(); + let k = smooth::sma(&fast_k, 3).collect::>(); (fast_k, k) } @@ -721,6 +728,7 @@ fn _stc(series: &[f64], window: usize) -> Vec { .collect::>(), 2, ) + .collect::>() } /// Shaff Trend Cycle @@ -734,7 +742,7 @@ pub fn stc(close: &[f64], window: usize, short: usize, long: usize) -> Vec /// Relative Volatility /// https://www.tradingview.com/support/solutions/43000594684-relative-volatility-index/ pub fn relative_vol(close: &[f64], window: usize, smoothing: usize) -> Vec { - let stdev = smooth::std_dev(close, window); + let stdev = smooth::std_dev(close, window).collect::>(); let (gain, loss): (Vec, Vec) = izip!( &stdev, &close[close.len() - stdev.len()..], @@ -751,8 +759,7 @@ pub fn relative_vol(close: &[f64], window: usize, smoothing: usize) -> Vec }) .unzip(); smooth::wilder(&gain, smoothing) - .iter() - .zip(smooth::wilder(&loss, smoothing).iter()) + .zip(smooth::wilder(&loss, smoothing)) .map(|(g, l)| 100.0 * g / (g + l)) .collect::>() } @@ -786,6 +793,7 @@ pub fn relative_vigor( .map(|w| (w[3] + 2.0 * w[2] + 2.0 * w[1] + w[0]) / 6.0) .collect::>(); smooth::sma(&numerator, window) + .collect::>() .iter() .zip(smooth::sma(&denominator, window)) .map(|(n, d)| n / d) diff --git a/src/main.rs b/src/main.rs index 5e8b20f..e98dbe7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use std::fs; -use traquer::indicator; +use traquer::*; #[derive(Deserialize, Serialize, Debug)] struct SecStats { @@ -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::dpo(&stats.close, 16)); + dbg!(smooth::dema(&stats.close, 16).count()); } diff --git a/src/smooth.rs b/src/smooth.rs index 4d6fe37..11f265f 100644 --- a/src/smooth.rs +++ b/src/smooth.rs @@ -1,181 +1,182 @@ use itertools::izip; use std::iter; -/// exponentially weighted moving average -pub fn ewma(data: &[f64], window: usize) -> Vec { +// exponentially weighted moving average +pub fn ewma(data: &[f64], window: usize) -> impl Iterator + '_ { let initial = data[..window].iter().sum::() / window as f64; let alpha = 2.0 / (window + 1) as f64; - iter::once(initial) - .chain(data[window..].iter().scan(initial, |state, &x| { - *state = x * alpha + *state * (1.0 - alpha); - Some(*state) - })) - .collect::>() + iter::once(initial).chain(data[window..].iter().scan(initial, move |state, &x| { + *state = x * alpha + *state * (1.0 - alpha); + Some(*state) + })) } /// simple moving average -pub fn sma(data: &[f64], window: usize) -> Vec { +pub fn sma(data: &[f64], window: usize) -> impl Iterator + '_ { data.windows(window) - .map(|w| w.iter().sum::() / window as f64) - .collect::>() + .map(move |w| w.iter().sum::() / window as f64) } /// double exponential moving average /// https://www.investopedia.com/terms/d/double-exponential-moving-average.asp -pub fn dema(data: &[f64], window: usize) -> Vec { - let ma = ewma(data, window); - let mama = ewma(&ma, window); - ma[ma.len() - mama.len()..] - .iter() - .zip(mama.iter()) +pub fn dema(data: &[f64], window: usize) -> impl Iterator + '_ { + let ma = ewma(data, window).collect::>(); + let mama = ewma(&ma, window).collect::>(); + let offset = ma.len() - mama.len(); + ma.into_iter() + .skip(offset) + .zip(mama) .map(|(ma1, ma2)| 2.0 * ma1 - ma2) - .collect::>() } /// triple exponential moving average /// https://www.investopedia.com/terms/t/triple-exponential-moving-average.asp -pub fn tema(data: &[f64], window: usize) -> Vec { - let ma = ewma(data, window); - let ma2 = ewma(&ma, window); - let ma3 = ewma(&ma2, window); +pub fn tema(data: &[f64], window: usize) -> impl Iterator + '_ { + let ma = ewma(data, window).collect::>(); + let ma2 = ewma(&ma, window).collect::>(); + let ma3 = ewma(&ma2, window).collect::>(); + let ma_offset = ma.len() - ma3.len(); + let ma2_offset = ma2.len() - ma3.len(); izip!( - &ma[ma.len() - ma3.len()..], - &ma2[ma2.len() - ma3.len()..], - &ma3 + ma.into_iter().skip(ma_offset), + ma2.into_iter().skip(ma2_offset), + ma3 ) .map(|(ma1, ma2, ma3)| 3.0 * ma1 - 3.0 * ma2 + ma3) - .collect::>() } /// weighted moving average -pub fn wma(data: &[f64], window: usize) -> Vec { +pub fn wma(data: &[f64], window: usize) -> impl Iterator + '_ { let denom = (u64::pow(window as u64, 2) + window as u64) as f64 / 2.0; - data.windows(window) - .map(|w| { - w.iter() - .enumerate() - .map(|(i, e)| (e * (i + 1) as f64 / denom)) - .sum() - }) - .collect::>() + let weights: Vec = (1..=window).map(|i| i as f64 / denom).collect(); + data.windows(window).map(move |w| { + w.iter() + .zip(weights.iter()) + .map(|(value, weight)| value * weight) + .sum() + }) } /// welles wilder's moving average -pub fn wilder(data: &[f64], window: usize) -> Vec { - let initial = data[..window].iter().sum::() / window as f64; - iter::once(initial) - .chain(data[window..].iter().scan(initial, |state, x| { - let ma = (*state * (window - 1) as f64 + x) / window as f64; - *state = ma; - Some(ma) - })) - .collect::>() +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| { + let ma = (*state * (window - 1) as f64 + x) / window as f64; + *state = ma; + Some(ma) + }) } /// hull's moving average -pub fn hull(data: &[f64], window: usize) -> Vec { +pub fn hull(data: &[f64], window: usize) -> impl Iterator + '_ { let ma = wma(data, window); let ma2 = wma(data, window.div_ceil(2)); wma( - &ma2[(ma2.len() - ma.len())..] - .iter() - .zip(ma.iter()) + &ma2.skip(window / 2) + .zip(ma) .map(|(x, y)| 2.0 * x - y) .collect::>(), (window as f64).sqrt().floor() as usize, ) + .collect::>() + .into_iter() } /// standard deviation -pub(crate) fn std_dev(data: &[f64], window: usize) -> Vec { - data.windows(window) - .map(|w| { - let mean = w.iter().sum::() / window as f64; - (w.iter().map(|x| (x - mean).powi(2)).sum::() / window as f64).sqrt() - }) - .collect::>() +pub(crate) fn std_dev(data: &[f64], window: usize) -> impl Iterator + '_ { + data.windows(window).map(move |w| { + let mean = w.iter().sum::() / window as f64; + (w.iter().map(|x| (x - mean).powi(2)).sum::() / window as f64).sqrt() + }) } /// volatility index dynamic average (vidya) -pub fn vidya(data: &[f64], window: usize) -> Vec { +pub fn vidya(data: &[f64], window: usize) -> impl Iterator + '_ { let alpha = 2.0 / (window + 1) as f64; - let std5 = std_dev(data, 5); - let std20 = sma(&std5, 20); + let std5 = std_dev(data, 5).collect::>(); + let std20 = sma(&std5, 20).collect::>(); + let std5_offset = std5.len() - std20.len(); + let data_offset = data.len() - std20.len(); izip!( - &std20, - &std5[std5.len() - std20.len()..], - &data[data.len() - std20.len()..] + std20.into_iter(), + std5.into_iter().skip(std5_offset), + data.iter().skip(data_offset) ) - .scan(0.0, |state, (s20, s5, d)| { + .scan(0.0, move |state, (s20, s5, d)| { *state = alpha * (s5 / s20) * (d - *state) + *state; Some(*state) }) - .collect::>() } /// chande momentum oscillator -pub(crate) fn _cmo(data: &[f64], window: usize) -> Vec { - let (gain, loss): (Vec, Vec) = data[..data.len() - 1] - .iter() - .zip(data[1..].iter()) - .map(|(x, y)| (f64::max(0.0, y - x), f64::max(0.0, x - y))) - .unzip(); - gain.windows(window) - .zip(loss.windows(window)) - .map(|(g, l)| { - let gain_sum = g.iter().sum::(); - let loss_sum = l.iter().sum::(); +pub(crate) fn _cmo(data: &[f64], window: usize) -> impl Iterator + '_ { + data.windows(2) + .map(|pair| { + let (prev, curr) = (pair[0], pair[1]); + (f64::max(0.0, curr - prev), f64::max(0.0, prev - curr)) + }) + .collect::>() + .windows(window) + .map(|w| { + let mut gain_sum = 0.0; + let mut loss_sum = 0.0; + for (g, l) in w { + gain_sum += g; + loss_sum += l; + } (gain_sum - loss_sum) / (gain_sum + loss_sum) }) .collect::>() + .into_iter() } /// variable moving average (vma) -pub fn vma(data: &[f64], window: usize) -> Vec { +pub fn vma(data: &[f64], window: usize) -> impl Iterator + '_ { let alpha = 2.0 / (window + 1) as f64; - let vi = _cmo(data, 9); // maybe make this configurable? - izip!(&vi, &data[data.len() - vi.len()..]) - .scan(0.0, |state, (vi, d)| { + let cmo_win = 9; // maybe make this configurable? + let vi = _cmo(data, cmo_win); + izip!(vi, data.iter().skip(cmo_win)) + .scan(0.0, move |state, (vi, d)| { *state = alpha * vi.abs() * (d - *state) + *state; Some(*state) }) - .skip(window.max(9) - 9) - .collect::>() + .skip(window.max(cmo_win) - cmo_win) } /// 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) -> Vec { +pub fn lrf(data: &[f64], window: usize) -> impl Iterator + '_ { 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(|w| { - let mut y_sum = 0.0; - let mut xy_sum = 0.0; - for (count, val) in w.iter().enumerate() { - y_sum += val; - xy_sum += (count + 1) as f64 * val; - } - let m = (window as f64 * xy_sum - x_sum * y_sum) / divisor; - let b = (y_sum * x2_sum - x_sum * xy_sum) / divisor; - m * window as f64 + b - }) - .collect::>() + data.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()) { + y_sum += val; + xy_sum += count * val; + } + let m = (window as f64 * xy_sum - x_sum * y_sum) / divisor; + let b = (y_sum * x2_sum - x_sum * xy_sum) / divisor; + m * window as f64 + b + }) } /// triangular moving average /// computes sma(N/2) and then sma(N/2) again. -pub fn trima(data: &[f64], window: usize) -> Vec { +pub fn trima(data: &[f64], window: usize) -> impl Iterator + '_ { let win1 = window.div_ceil(2); let win2 = if window & 2 == 0 { win1 + 1 } else { win1 }; - sma(&sma(data, win1), win2) + sma(&sma(data, win1).collect::>(), win2) + .collect::>() + .into_iter() } /// Zero Lag Moving Average /// https://en.wikipedia.org/wiki/Zero_lag_exponential_moving_average -pub fn zlma(data: &[f64], window: usize) -> Vec { +pub fn zlma(data: &[f64], window: usize) -> impl Iterator + '_ { let lag = (window - 1) / 2; ewma( &data @@ -185,4 +186,6 @@ pub fn zlma(data: &[f64], window: usize) -> Vec { .collect::>(), window, ) + .collect::>() + .into_iter() } diff --git a/tests/ma_test.rs b/tests/ma_test.rs index d72975a..35d491a 100644 --- a/tests/ma_test.rs +++ b/tests/ma_test.rs @@ -28,7 +28,7 @@ fn test_ewma_even() { 46.717392383280725, 47.02064012277067, ], - results + results.collect::>() ); } @@ -59,7 +59,7 @@ fn test_ewma_odd() { 46.72808855464295, 47.048952256430745, ], - results + results.collect::>() ); } @@ -89,7 +89,7 @@ fn test_sma_even() { 44.85437536239624, 45.48718786239624, ], - results + results.collect::>() ); } @@ -120,7 +120,7 @@ fn test_sma_odd() { 45.233333841959634, 45.73833363850911, ], - results + results.collect::>() ); } @@ -142,7 +142,7 @@ fn test_vidya_even() { 31.240918773298006, 33.780320050418, ], - results + results.collect::>() ); } @@ -164,7 +164,7 @@ fn test_vidya_odd() { 32.40359132105506, 34.92794816477713, ], - results + results.collect::>() ); } @@ -174,7 +174,7 @@ fn test_wma_even() { let results = smooth::wma(&stats.close, 16); assert_eq!( vec![ - 45.75977967767154, + 45.759779677671546, 44.902941339156214, 43.85007344975192, 43.24889710370232, @@ -185,16 +185,16 @@ fn test_wma_even() { 42.18205917582793, 42.47713271309348, 42.87477958903593, - 43.06536764257094, + 43.06536764257095, 43.43794129876529, 44.018161970026355, 44.43242659288294, 45.022941336912275, 45.70227973601397, 46.547794594484216, - 47.0702208070194, + 47.0702208070194 ], - results + results.collect::>() ); } @@ -211,21 +211,21 @@ fn test_wma_odd() { 42.966333357493085, 42.50850013097127, 42.26600020726522, - 42.1537501335144, + 42.15375013351441, 42.146333630879724, - 42.083417034149164, + 42.08341703414917, 42.42075036366781, 42.88208351135254, 43.11233332951864, 43.527250130971275, - 44.13808355331422, - 44.59000012079874, + 44.1380835533142, + 44.59000012079875, 45.209250164031985, 45.900833670298255, 46.773583825429284, - 47.281291866302496, + 47.281291866302496 ], - results + results.collect::>() ); } @@ -254,7 +254,7 @@ fn test_vma_even() { 29.00800914486834, 29.672845402427125, ], - results + results.collect::>() ); } @@ -284,7 +284,7 @@ fn test_vma_odd() { 30.086715436149444, 30.75554364251794, ], - results + results.collect::>() ); } @@ -320,7 +320,7 @@ fn test_vma_small_window() { 41.49233065069245, 42.03570500577861, ], - results + results.collect::>() ); } @@ -332,22 +332,22 @@ fn test_hull_even() { vec![ 39.52407458124597, 39.376835387049155, - 39.55042884988722, + 39.550428849887226, 40.025559802148855, 40.71055601219726, 41.406150204527606, - 42.38044363688798, - 43.5624974082498, - 44.48106479582444, - 45.30848860242009, + 42.38044363688799, + 43.56249740824978, + 44.48106479582443, + 45.308488602420084, 46.159912574057486, - 46.82134468758029, - 47.52666904910717, - 48.29279342539171, - 49.237479216756384, - 49.964879527123145, + 46.82134468758028, + 47.52666904910716, + 48.29279342539169, + 49.23747921675638, + 49.964879527123145 ], - results + results.collect::>() ); } @@ -357,26 +357,26 @@ fn test_hull_odd() { let results = smooth::hull(&stats.close, 15); assert_eq!( vec![ - 40.843907204380734, + 40.84390720438073, 40.046115124667125, 39.721994749705, 39.586976522869534, 39.84200009593256, 40.35702341574209, 41.10171350373162, - 41.776297060648595, - 42.7662277398286, - 43.975824703993624, - 44.831069552456896, - 45.513768434524536, + 41.77629706064859, + 42.766227739828594, + 43.97582470399361, + 44.83106955245688, + 45.51376843452453, 46.301856447149206, - 46.939060115814215, - 47.59569436179268, - 48.33557422779225, - 49.34222279301396, - 50.0292250280027, + 46.9390601158142, + 47.59569436179266, + 48.335574227792236, + 49.34222279301395, + 50.0292250280027 ], - results + results.collect::>() ); } @@ -395,7 +395,7 @@ fn test_dema_even() { 48.0159797504427, 48.477344590672004, ], - results + results.collect::>() ); } @@ -416,7 +416,7 @@ fn test_dema_odd() { 48.245239835005584, 48.692560341186216, ], - results + results.collect::>() ); } @@ -440,7 +440,7 @@ fn test_tema_even() { 50.92541420046855, 50.65205563565898, ], - results + results.collect::>() ); } @@ -467,7 +467,7 @@ fn test_tema_odd() { 50.948867388256396, 50.52826712062671, ], - results + results.collect::>() ); } @@ -497,7 +497,7 @@ fn test_wilder_even() { 46.55729031195337, 46.72839705301537, ], - results + results.collect::>() ); } @@ -528,7 +528,7 @@ fn test_wilder_odd() { 46.523486444150215, 46.70825389246989, ], - results + results.collect::>() ); } @@ -558,7 +558,7 @@ fn test_lrf() { 49.934633058660175, 50.23628669626572, ], - result + result.collect::>() ); } @@ -588,7 +588,7 @@ fn test_trima_even() { 44.77319468392266, 45.403264098697235, ], - result + result.collect::>() ); } @@ -619,7 +619,7 @@ fn test_trima_odd() { 45.093906462192535, 45.69164079427719, ], - result + result.collect::>() ); } @@ -642,6 +642,6 @@ fn test_zlma() { 47.99770003005625, 48.72149975724747, ], - result + result.collect::>() ); }