Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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

Expand Down
6 changes: 3 additions & 3 deletions benches/my_benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ fn criterion_benchmark(c: &mut Criterion) {
b.iter(|| black_box(smooth::wma(&stats.close, 16).collect::<Vec<f64>>()))
});
c.bench_function("ma-wilder", |b| {
b.iter(|| black_box(smooth::wilder(&stats.close, 16).collect::<Vec<f64>>()))
b.iter(|| black_box(smooth::wilder(stats.close.iter().copied(), 16).collect::<Vec<f64>>()))
});
c.bench_function("ma-hull", |b| {
b.iter(|| black_box(smooth::hull(&stats.close, 16).collect::<Vec<f64>>()))
Expand All @@ -276,13 +276,13 @@ fn criterion_benchmark(c: &mut Criterion) {
b.iter(|| black_box(smooth::vma(&stats.close, 16).collect::<Vec<f64>>()))
});
c.bench_function("ma-lrf", |b| {
b.iter(|| black_box(smooth::lrf(&stats.close, 16).collect::<Vec<f64>>()))
b.iter(|| black_box(smooth::lrf(stats.close.iter().copied(), 16).collect::<Vec<f64>>()))
});
c.bench_function("ma-trima", |b| {
b.iter(|| black_box(smooth::trima(&stats.close, 16).collect::<Vec<f64>>()))
});
c.bench_function("ma-zlma", |b| {
b.iter(|| black_box(smooth::zlma(&stats.close, 16).collect::<Vec<f64>>()))
b.iter(|| black_box(smooth::zlma(stats.close.iter().copied(), 16).collect::<Vec<f64>>()))
});
}

Expand Down
28 changes: 13 additions & 15 deletions src/indicator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,20 @@ pub fn adx(
(dm_pos, dm_neg, tr)
}),
);
let atr = smooth::wilder(&tr, period).collect::<Vec<f64>>();
let di_pos = izip!(smooth::wilder(&dm_pos, period), &atr)
let atr = smooth::wilder(tr, period).collect::<Vec<f64>>();
let di_pos = izip!(smooth::wilder(dm_pos, period), &atr)
.map(|(di, tr)| di / tr * 100.0)
.collect::<Vec<f64>>();
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::<Vec<f64>>();
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::<Vec<f64>>();
(
di_pos,
di_neg,
smooth::wilder(&dx, smoothing).collect::<Vec<f64>>(),
dx
)
}

Expand All @@ -167,8 +167,8 @@ pub fn rsi(data: &[f64], window: usize) -> Vec<f64> {
.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::<Vec<f64>>()
}
Expand Down Expand Up @@ -653,8 +653,7 @@ pub fn supertrend(
multiplier: f64,
) -> Vec<f64> {
// TODO: needs a test for when it actually flips to use upper band line
let tr = _true_range(high, low, close).collect::<Vec<f64>>();
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),
Expand Down Expand Up @@ -710,7 +709,7 @@ pub fn stochastic(high: &[f64], low: &[f64], close: &[f64], window: usize) -> (V

fn _stc(series: &[f64], window: usize) -> Vec<f64> {
smooth::wilder(
&series
series
.windows(window)
.map(|w| {
let mut hh = f64::NAN;
Expand All @@ -720,8 +719,7 @@ fn _stc(series: &[f64], window: usize) -> Vec<f64> {
ll = ll.min(*x);
}
100.0 * (w.last().unwrap() - ll) / (hh - ll)
})
.collect::<Vec<f64>>(),
}),
2,
)
.collect::<Vec<f64>>()
Expand Down Expand Up @@ -754,8 +752,8 @@ pub fn relative_vol(close: &[f64], window: usize, smoothing: usize) -> Vec<f64>
)
})
.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::<Vec<f64>>()
}
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<f64>>());
dbg!(smooth::dema(&stats.close, 16).collect::<Vec<f64>>());
}
31 changes: 21 additions & 10 deletions src/smooth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,13 @@ pub fn wma(data: &[f64], window: usize) -> impl Iterator<Item = f64> + '_ {
}

/// welles wilder's moving average
pub fn wilder(data: &[f64], window: usize) -> impl Iterator<Item = f64> + '_ {
let initial = data[..window - 1].iter().sum::<f64>() / (window - 1) as f64;
data[window - 1..].iter().scan(initial, move |state, x| {
pub fn wilder<I>(data: I, window: usize) -> impl Iterator<Item = f64>
where
I: IntoIterator<Item = f64>,
{
let mut data_iter = data.into_iter();
let initial = data_iter.by_ref().take(window - 1).sum::<f64>() / (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)
Expand Down Expand Up @@ -145,13 +149,16 @@ pub fn vma(data: &[f64], window: usize) -> impl Iterator<Item = f64> + '_ {

/// 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<Item = f64> + '_ {
pub fn lrf<I>(data: I, window: usize) -> impl Iterator<Item = f64>
where
I: IntoIterator<Item = f64>,
{
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<f64> = (1..=window).map(|x| x as f64).collect();

data.windows(window).map(move |w| {
data.into_iter().collect::<Vec<f64>>().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()) {
Expand All @@ -162,6 +169,7 @@ pub fn lrf(data: &[f64], window: usize) -> impl Iterator<Item = f64> + '_ {
let b = (y_sum * x2_sum - x_sum * xy_sum) / divisor;
m * window as f64 + b
})
.collect::<Vec<f64>>().into_iter()
}

/// triangular moving average
Expand All @@ -176,16 +184,19 @@ pub fn trima(data: &[f64], window: usize) -> impl Iterator<Item = f64> + '_ {

/// Zero Lag Moving Average
/// https://en.wikipedia.org/wiki/Zero_lag_exponential_moving_average
pub fn zlma(data: &[f64], window: usize) -> impl Iterator<Item = f64> + '_ {
pub fn zlma<I>(data: I, window: usize) -> impl Iterator<Item = f64>
where
I: IntoIterator<Item = f64>,
{
let lag = (window - 1) / 2;
let data_vec = data.into_iter().collect::<Vec<f64>>();
ewma(
&data
&data_vec
.iter()
.zip(data[lag..].iter())
.zip(data_vec[lag..].iter())
.map(|(prev, curr)| 2.0 * curr - prev)
.collect::<Vec<f64>>(),
window,
)
.collect::<Vec<f64>>()
.into_iter()
.collect::<Vec<f64>>().into_iter()
}
2 changes: 1 addition & 1 deletion tests/ma_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down