use std::iter;
use itertools::{izip, multiunzip};
use num_traits::cast::ToPrimitive;
use crate::momentum::_swing;
use crate::smooth;
use crate::volatility::{_true_range, atr};
pub fn shinohara<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
window: usize,
) -> impl Iterator<Item = (f64, f64)> + 'a {
let high_win = high
.windows(window)
.map(|w| w.iter().filter_map(|x| x.to_f64()).sum())
.collect::<Vec<f64>>();
let low_win = low
.windows(window)
.map(|w| w.iter().filter_map(|x| x.to_f64()).sum())
.collect::<Vec<f64>>();
let close_win = close
.windows(window)
.map(|w| w.iter().filter_map(|x| x.to_f64()).sum())
.collect::<Vec<f64>>();
let weak_ratio = izip!(&high_win, &low_win, &close_win)
.map(|(h, l, c)| 100.0 * (h - c) / (c - l))
.collect::<Vec<f64>>();
let strong_ratio = izip!(
&high_win[1..],
&low_win[1..],
&close_win[..close_win.len() - 1]
)
.map(|(h, l, c)| 100.0 * (h - c) / (c - l))
.collect::<Vec<f64>>();
iter::repeat(f64::NAN)
.take(window)
.chain(strong_ratio)
.zip(iter::repeat(f64::NAN).take(window - 1).chain(weak_ratio))
}
pub fn adx<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
window: usize,
smoothing: usize,
) -> impl Iterator<Item = (f64, f64, f64)> + 'a {
let (dm_pos, dm_neg, tr): (Vec<_>, Vec<_>, Vec<_>) = multiunzip(
izip!(
&high[..high.len() - 1],
&high[1..],
&low[..low.len() - 1],
&low[1..],
&close[..close.len() - 1],
)
.map(|(prevh, h, prevl, l, prevc)| {
let (prevh, h, prevl, l, prevc) = (
prevh.to_f64().unwrap(),
h.to_f64().unwrap(),
prevl.to_f64().unwrap(),
l.to_f64().unwrap(),
prevc.to_f64().unwrap(),
);
let dm_pos = if h - prevh > prevl - l {
f64::max(0.0, h - prevh)
} else {
0.0
};
let dm_neg = if prevl - l > h - prevh {
f64::max(0.0, prevl - l)
} else {
0.0
};
let tr = (h - l).max(f64::abs(h - prevc)).max(f64::abs(l - prevc));
(dm_pos, dm_neg, tr)
}),
);
let atr = smooth::wilder(&tr, window).collect::<Vec<f64>>();
let di_pos = izip!(smooth::wilder(&dm_pos, window), &atr)
.map(|(di, tr)| di / tr * 100.0)
.collect::<Vec<f64>>();
let di_neg = izip!(smooth::wilder(&dm_neg, window), &atr)
.map(|(di, tr)| di / tr * 100.0)
.collect::<Vec<f64>>();
let dx = izip!(&di_pos[window - 1..], &di_neg[window - 1..])
.map(|(pos, neg)| f64::abs(pos - neg) / (pos + neg) * 100.0)
.collect::<Vec<f64>>();
iter::once((f64::NAN, f64::NAN, f64::NAN)).chain(izip!(
di_pos,
di_neg,
iter::repeat(f64::NAN)
.take(window - 1)
.chain(smooth::wilder(&dx, smoothing).collect::<Vec<f64>>()),
))
}
pub fn vortex<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
window: usize,
) -> impl Iterator<Item = (f64, f64)> + 'a {
iter::repeat((f64::NAN, f64::NAN)).take(window).chain(
izip!(
&high[..high.len() - 1],
&high[1..],
&low[..low.len() - 1],
&low[1..],
&close[..close.len() - 1],
)
.map(|(prevh, h, prevl, l, prevc)| {
let (prevh, h, prevl, l, prevc) = (
prevh.to_f64().unwrap(),
h.to_f64().unwrap(),
prevl.to_f64().unwrap(),
l.to_f64().unwrap(),
prevc.to_f64().unwrap(),
);
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)
.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)
})
.collect::<Vec<(f64, f64)>>(),
)
}
pub fn asi<'a, T: ToPrimitive>(
open: &'a [T],
high: &'a [T],
low: &'a [T],
close: &'a [T],
limit: f64,
) -> impl Iterator<Item = f64> + 'a {
iter::once(f64::NAN).chain(_swing(open, high, low, close, limit).scan(0.0, |acc, x| {
*acc += x;
Some(*acc)
}))
}
pub fn ulcer<T: ToPrimitive>(data: &[T], window: usize) -> impl Iterator<Item = f64> + '_ {
let highest = data.windows(window).map(|w| {
w.iter()
.fold(f64::NAN, |state, x| state.max(x.to_f64().unwrap()))
});
iter::repeat(f64::NAN).take(window - 1).chain(
smooth::sma(
&highest
.zip(data.iter().skip(window - 1))
.map(|(high, c)| (100.0 * (c.to_f64().unwrap() - high) / high).powi(2))
.collect::<Vec<f64>>(),
window,
)
.map(|x| x.sqrt())
.collect::<Vec<f64>>(),
)
}
pub fn supertrend<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
window: usize,
multiplier: f64,
) -> impl Iterator<Item = f64> + 'a {
let tr = _true_range(high, low, close).collect::<Vec<f64>>();
let atr = iter::once(f64::NAN).chain(smooth::wilder(&tr, window));
izip!(high, low, close, atr)
.scan(
(f64::NAN, f64::NAN, f64::MIN_POSITIVE, 1),
|state, (h, l, c, tr)| {
let (h, l, c) = (
h.to_f64().unwrap(),
l.to_f64().unwrap(),
c.to_f64().unwrap(),
);
let (prevlower, prevupper, prevc, prevdir) = state;
let mut lower = (h + l) / 2.0 - multiplier * tr;
let mut upper = (h + l) / 2.0 + multiplier * tr;
if prevc > prevlower && *prevlower > lower {
lower = *prevlower;
}
if prevc < prevupper && *prevupper < upper {
upper = *prevupper;
}
let dir = if c > *prevupper {
1
} else if c < *prevlower {
-1
} else {
*prevdir
};
*state = (lower, upper, c, dir);
if dir > 0 {
Some(lower)
} else {
Some(upper)
}
},
)
.collect::<Vec<f64>>()
.into_iter()
}
pub fn rwi<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
window: usize,
) -> impl Iterator<Item = (f64, f64)> + 'a {
iter::repeat((f64::NAN, f64::NAN)).take(window).chain(
izip!(
high[1..].windows(window),
low[1..].windows(window),
_true_range(high, low, close)
.collect::<Vec<f64>>()
.windows(window),
)
.map(|(h, l, tr)| {
let mut rwi_high: f64 = 0.0;
let mut rwi_low: f64 = 0.0;
let mut tr_sum = 0.0;
for i in 2..=window {
tr_sum += tr[window - i];
let denom = (tr_sum / (i - 1) as f64) * ((i - 1) as f64).sqrt();
rwi_high = rwi_high.max(
(h[window - 1].to_f64().unwrap() - l[window - i].to_f64().unwrap()) / denom,
);
rwi_low = rwi_low.max(
(h[window - i].to_f64().unwrap() - l[window - 1].to_f64().unwrap()) / denom,
);
}
(rwi_high, rwi_low)
})
.collect::<Vec<(f64, f64)>>(),
)
}
pub fn psar<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
af: Option<f64>,
af_max: Option<f64>,
) -> impl Iterator<Item = f64> + 'a {
let af_inc = af.unwrap_or(0.02);
let mut af = af_inc;
let af_max = af_max.unwrap_or(0.2);
let mut fall = true;
let mut sar = high[0].to_f64().unwrap();
let mut ep = low[0].to_f64().unwrap();
let mut flip: bool;
let mut result = Vec::with_capacity(high.len() - 1);
for i in 0..high.len() - 1 {
sar = sar + af * (ep - sar);
if fall {
flip = high[i + 1].to_f64().unwrap() > sar;
if low[i + 1].to_f64().unwrap() < ep {
ep = low[i + 1].to_f64().unwrap();
af = f64::min(af + af_inc, af_max);
}
sar = f64::max(
f64::max(
high[std::cmp::max(i, 1) - 1].to_f64().unwrap(),
high[i + 1].to_f64().unwrap(),
),
sar,
)
} else {
flip = low[i + 1].to_f64().unwrap() < sar;
if high[i + 1].to_f64().unwrap() > ep {
ep = high[i + 1].to_f64().unwrap();
af = f64::min(af + af_inc, af_max);
}
sar = f64::min(
f64::min(
low[std::cmp::max(i, 1) - 1].to_f64().unwrap(),
low[i + 1].to_f64().unwrap(),
),
sar,
)
}
if flip {
sar = ep;
af = af_inc;
fall = !fall;
ep = if fall {
low[i + 1].to_f64().unwrap()
} else {
high[i + 1].to_f64().unwrap()
};
}
result.push(sar);
}
iter::once(f64::NAN).chain(result)
}
pub fn dpo<T: ToPrimitive>(
data: &[T],
window: usize,
mamode: Option<smooth::MaMode>,
) -> impl Iterator<Item = f64> + '_ {
let ma = smooth::ma(data, window, mamode.unwrap_or(smooth::MaMode::SMA));
let lag = window / 2 + 1;
iter::repeat(f64::NAN).take(window - 1).chain(
data[window - lag - 1..]
.iter()
.zip(ma.skip(window - 1))
.map(|(x, y)| x.to_f64().unwrap() - y),
)
}
pub fn aroon<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
window: usize,
) -> impl Iterator<Item = (f64, f64)> + 'a {
iter::repeat((f64::NAN, f64::NAN)).take(window).chain(
high.windows(window + 1)
.zip(low.windows(window + 1))
.map(move |(h, l)| {
let (hh, _) =
h.iter()
.enumerate()
.fold((0, h[0].to_f64().unwrap()), |state, (idx, x)| {
if x.to_f64().unwrap() >= state.1 {
(idx, x.to_f64().unwrap())
} else {
state
}
});
let (ll, _) =
l.iter()
.enumerate()
.fold((0, l[0].to_f64().unwrap()), |state, (idx, x)| {
if x.to_f64().unwrap() <= state.1 {
(idx, x.to_f64().unwrap())
} else {
state
}
});
(
hh as f64 / window as f64 * 100.0,
ll as f64 / window as f64 * 100.0,
)
}),
)
}
pub fn chandelier<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
window: usize,
multiplier: Option<f64>,
) -> impl Iterator<Item = (f64, f64)> + 'a {
let multiplier = multiplier.unwrap_or(3.0);
iter::repeat((f64::NAN, f64::NAN)).take(window).chain(
izip!(
high.windows(window).skip(1),
low.windows(window).skip(1),
atr(high, low, close, window).skip(window)
)
.map(move |(h, l, atr)| {
let hh = h
.iter()
.fold(f64::NAN, |state, x| state.max(x.to_f64().unwrap()));
let ll = l
.iter()
.fold(f64::NAN, |state, x| state.min(x.to_f64().unwrap()));
(hh - multiplier * atr, ll + multiplier * atr)
}),
)
}
pub fn zigzag<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
distance: Option<f64>,
) -> impl Iterator<Item = f64> + 'a {
let distance = distance.unwrap_or(5.0);
let mut result: Vec<f64> = vec![f64::NAN; high.len()];
let mut startxy: (usize, f64) = (0, low[0].to_f64().unwrap());
let mut endxy: (usize, f64) = (high.len() - 1, high.last().unwrap().to_f64().unwrap());
result[0] = low[0].to_f64().unwrap();
for idx in 1..high.len() {
if (high[0].to_f64().unwrap() - low[idx].to_f64().unwrap()) / high[0].to_f64().unwrap()
* 100.0
>= distance
{
result[0] = high[0].to_f64().unwrap();
startxy = (0, high[0].to_f64().unwrap());
endxy = (idx, low[idx].to_f64().unwrap());
break;
} else if (high[idx].to_f64().unwrap() - low[0].to_f64().unwrap())
/ low[0].to_f64().unwrap()
* 100.0
>= distance
{
result[0] = low[0].to_f64().unwrap();
startxy = (0, low[0].to_f64().unwrap());
endxy = (idx, high[idx].to_f64().unwrap());
break;
}
}
if endxy.0 < high.len() - 1 {
for i in endxy.0..high.len() {
if endxy.1 > startxy.1 && high[i].to_f64().unwrap() > endxy.1 {
endxy = (i, high[i].to_f64().unwrap());
} else if endxy.1 < startxy.1
&& (high[i].to_f64().unwrap() - endxy.1) / high[i].to_f64().unwrap() * 100.0
>= distance
{
result[endxy.0] = endxy.1;
startxy = endxy;
endxy = (i, high[i].to_f64().unwrap());
} else if endxy.1 < startxy.1 && low[i].to_f64().unwrap() < endxy.1 {
endxy = (i, low[i].to_f64().unwrap());
} else if endxy.1 > startxy.1
&& (endxy.1 - low[i].to_f64().unwrap()) / low[i].to_f64().unwrap() * 100.0
>= distance
{
result[endxy.0] = endxy.1;
startxy = endxy;
endxy = (i, low[i].to_f64().unwrap());
}
}
}
if result.last().unwrap().is_nan() {
let result_size = result.len();
if startxy.1 > endxy.1 {
result[result_size - 1] = high.last().unwrap().to_f64().unwrap();
} else {
result[result_size - 1] = low.last().unwrap().to_f64().unwrap();
}
}
result.into_iter()
}
pub fn decay<T: ToPrimitive>(data: &[T], window: usize) -> impl Iterator<Item = f64> + '_ {
data.iter().scan(0.0, move |state, x| {
*state = (*state - 1.0 / window as f64)
.max(x.to_f64().unwrap())
.max(0.0);
Some(*state)
})
}
pub fn cks<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
win1: usize,
win2: usize,
multiplier: Option<f64>,
) -> impl Iterator<Item = (f64, f64)> + 'a {
let multiplier = multiplier.unwrap_or(1.0);
iter::repeat((f64::NAN, f64::NAN))
.take(win1 + (win2 - 1))
.chain(
izip!(
high[1..].windows(win1),
low[1..].windows(win1),
atr(high, low, close, win1).skip(win1)
)
.map(|(h, l, tr)| {
(
h.iter()
.fold(f64::NAN, |state, x| state.max(x.to_f64().unwrap()))
- multiplier * tr,
l.iter()
.fold(f64::NAN, |state, x| state.min(x.to_f64().unwrap()))
+ multiplier * tr,
)
})
.collect::<Vec<(f64, f64)>>()
.windows(win2)
.map(|w| {
let mut hh: f64 = 0.0;
let mut ll: f64 = f64::MAX;
for (h, l) in w {
hh = hh.max(*h);
ll = ll.min(*l);
}
(hh, ll)
})
.collect::<Vec<(f64, f64)>>(),
)
}
pub fn atr_stop<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
window: usize,
multiplier: Option<f64>,
) -> impl Iterator<Item = f64> + 'a {
let multiplier = multiplier.unwrap_or(3.0);
iter::repeat(f64::NAN).take(window).chain(
izip!(
close.windows(window).skip(1),
atr(high, low, close, window).skip(window)
)
.scan((true, 0.0), move |state, (c, atr)| {
let upper = c[window - 1].to_f64().unwrap() + multiplier * atr;
let lower = c[window - 1].to_f64().unwrap() - multiplier * atr;
let mut band: f64 = state.1;
if state.0 {
band = band.max(lower);
*state = if c[window - 1].to_f64().unwrap() < band {
(false, upper)
} else {
(state.0, band)
};
} else {
band = band.min(upper);
*state = if c[window - 1].to_f64().unwrap() > band {
(true, lower)
} else {
(state.0, band)
};
}
Some(state.1)
}),
)
}
pub fn alligator<T: ToPrimitive>(
data: &[T],
jaw_win: usize,
jaw_offset: usize,
teeth_win: usize,
teeth_offset: usize,
lips_win: usize,
lips_offset: usize,
) -> impl Iterator<Item = (f64, f64, f64)> + '_ {
let max_offset = jaw_offset.max(teeth_offset).max(lips_offset);
let jaw = iter::repeat(f64::NAN)
.take(jaw_offset)
.chain(smooth::wilder(data, jaw_win))
.chain(iter::repeat(f64::NAN).take(max_offset - jaw_offset));
let teeth = iter::repeat(f64::NAN)
.take(teeth_offset)
.chain(smooth::wilder(data, teeth_win))
.chain(iter::repeat(f64::NAN).take(max_offset - teeth_offset));
let lips = iter::repeat(f64::NAN)
.take(lips_offset)
.chain(smooth::wilder(data, lips_win))
.chain(iter::repeat(f64::NAN).take(max_offset - lips_offset));
izip!(jaw, teeth, lips)
}
pub fn ichimoku<'a, T: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
conversion_win: usize,
base_win: usize,
lead_win: usize,
lag_win: usize,
) -> impl Iterator<Item = (f64, f64, f64, f64, f64)> + 'a {
let c_line = iter::repeat(f64::NAN)
.take(conversion_win - 1)
.chain(
high.windows(conversion_win)
.zip(low.windows(conversion_win))
.map(|(h, l)| {
let hh = h
.iter()
.fold(f64::NAN, |state, x| state.max(x.to_f64().unwrap()));
let ll = l
.iter()
.fold(f64::NAN, |state, x| state.min(x.to_f64().unwrap()));
(hh + ll) / 2.0
}),
)
.collect::<Vec<f64>>();
let b_line = iter::repeat(f64::NAN)
.take(base_win - 1)
.chain(
high.windows(base_win)
.zip(low.windows(base_win))
.map(|(h, l)| {
let hh = h
.iter()
.fold(f64::NAN, |state, x| state.max(x.to_f64().unwrap()));
let ll = l
.iter()
.fold(f64::NAN, |state, x| state.min(x.to_f64().unwrap()));
(hh + ll) / 2.0
}),
)
.collect::<Vec<f64>>();
let lead_b = iter::repeat(f64::NAN).take(lead_win - 1 + base_win).chain(
high.windows(lead_win)
.zip(low.windows(lead_win))
.map(|(h, l)| {
let hh = h
.iter()
.fold(f64::NAN, |state, x| state.max(x.to_f64().unwrap()));
let ll = l
.iter()
.fold(f64::NAN, |state, x| state.min(x.to_f64().unwrap()));
(hh + ll) / 2.0
}),
);
let lead_a = iter::repeat(f64::NAN)
.take(base_win)
.chain(c_line.iter().zip(&b_line).map(|(c, b)| (c + b) / 2.0))
.collect::<Vec<f64>>();
let lag = close
.iter()
.skip(lag_win)
.filter_map(|x| x.to_f64())
.chain(iter::repeat(f64::NAN).take(lag_win + base_win));
izip!(
c_line
.into_iter()
.chain(iter::repeat(f64::NAN).take(base_win)),
b_line
.into_iter()
.chain(iter::repeat(f64::NAN).take(base_win)),
lead_a,
lead_b,
lag
)
}
fn hurst_rs<T: ToPrimitive>(data: &[T]) -> f64 {
let avg = data.iter().filter_map(|x| x.to_f64()).sum::<f64>() / data.len() as f64;
let mut max_z = f64::MIN;
let mut min_z = f64::MAX;
let mut dev_cumsum = 0.0;
let mut var = 0.0;
for x in data {
let dev = x.to_f64().unwrap() - avg;
var += dev * dev;
dev_cumsum += dev;
max_z = max_z.max(dev_cumsum);
min_z = min_z.min(dev_cumsum);
}
let r = max_z - min_z;
let s = (var / (data.len() - 1) as f64).sqrt();
r / s
}
fn linreg(x: &[f64], y: &[f64]) -> f64 {
let x_avg = x.iter().fold(0.0, |acc, &x| acc + x) / x.len() as f64;
let y_avg = y.iter().fold(0.0, |acc, &y| acc + y) / y.len() as f64;
let mut covar = 0.0;
let mut var = 0.0;
for (&xi, &yi) in x.iter().zip(y) {
covar += (xi - x_avg) * (yi - y_avg);
var += (xi - x_avg).powi(2);
}
covar / var
}
pub fn hurst<T: ToPrimitive>(
data: &[T],
window: usize,
min_win: Option<usize>,
) -> impl Iterator<Item = f64> + '_ {
let mut win_sizes = Vec::new();
let mut win = (min_win.unwrap_or(4) as f64).log10();
loop {
let win_size = 10.0_f64.powf(win) as usize;
if win_size >= window {
break;
}
win_sizes.push(win_size);
win += 0.25;
}
win_sizes.push(window);
let win_size_log10 = win_sizes
.iter()
.map(|&x| (x as f64).log10())
.collect::<Vec<_>>();
iter::repeat(f64::NAN)
.take(window - 1)
.chain(data.windows(window).map(move |w| {
let mut rs_vals = Vec::new();
for &x in win_sizes.iter() {
let chunks = w.chunks_exact(x);
let cnt = chunks.len();
rs_vals.push((chunks.fold(0.0, |acc, x| acc + hurst_rs(x)) / cnt as f64).log10());
}
linreg(&win_size_log10, &rs_vals)
}))
}