use std::iter;
use itertools::izip;
use num_traits::cast::ToPrimitive;
use crate::smooth;
fn vforce<'a, T: ToPrimitive, U: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
volume: &'a [U],
) -> impl Iterator<Item = f64> + 'a {
izip!(&high[1..], &low[1..], &close[1..], &volume[1..]).scan(
(
high[0].to_f64().unwrap(),
low[0].to_f64().unwrap(),
close[0].to_f64().unwrap(),
0.0,
0.0,
0.0,
),
|state, (h, l, c, v)| {
let (h, l, c, v) = (
h.to_f64().unwrap(),
l.to_f64().unwrap(),
c.to_f64().unwrap(),
v.to_f64().unwrap(),
);
let trend = ((h + l + c) - (state.0 + state.1 + state.2)).signum();
let dm = h - l;
let cm = if trend == state.3 {
state.4 + dm
} else {
state.5 + dm
};
*state = (h, l, c, trend, cm, dm);
Some(v * ((dm / cm) - 1.0) * trend * 200.0)
},
)
}
fn vforce_alt<'a, T: ToPrimitive, U: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
volume: &'a [U],
) -> impl Iterator<Item = f64> + 'a {
izip!(&high[1..], &low[1..], &close[1..], &volume[1..]).scan(
(
high[0].to_f64().unwrap(),
low[0].to_f64().unwrap(),
close[0].to_f64().unwrap(),
0.0,
),
|state, (h, l, c, v)| {
let (h, l, c, v) = (
h.to_f64().unwrap(),
l.to_f64().unwrap(),
c.to_f64().unwrap(),
v.to_f64().unwrap(),
);
let trend = ((h + l + c) - (state.0 + state.1 + state.2)).signum();
*state = (h, l, c, trend);
Some(v * trend)
},
)
}
pub fn kvo<'a, T: ToPrimitive, U: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
volume: &'a [U],
short: usize,
long: usize,
alt: Option<bool>,
) -> impl Iterator<Item = f64> + 'a {
let vf = if alt.unwrap_or(true) {
vforce_alt(high, low, close, volume).collect::<Vec<f64>>()
} else {
vforce(high, low, close, volume).collect::<Vec<f64>>()
};
let short_ma = smooth::ewma(&vf, short);
let long_ma = smooth::ewma(&vf, long);
iter::once(f64::NAN).chain(
short_ma
.zip(long_ma)
.map(|(x, y)| x - y)
.collect::<Vec<f64>>(),
)
}
fn wilder_sum<T: ToPrimitive>(data: &[T], window: usize) -> impl Iterator<Item = f64> + '_ {
let initial = data[..(window - 1)]
.iter()
.filter_map(|x| x.to_f64())
.sum::<f64>();
data[(window - 1)..].iter().scan(initial, move |state, x| {
let ma = *state * (window - 1) as f64 / window as f64 + x.to_f64().unwrap();
*state = ma;
Some(ma)
})
}
pub fn twiggs<'a, T: ToPrimitive, U: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
volume: &'a [U],
window: usize,
) -> impl Iterator<Item = f64> + 'a {
let data = izip!(&high[1..], &low[1..], &close[1..], &volume[1..]);
iter::repeat(f64::NAN).take(window).chain(
wilder_sum(
&data
.scan(close[0].to_f64().unwrap(), |state, (h, l, c, vol)| {
let (h, l, c, vol) = (
h.to_f64().unwrap(),
l.to_f64().unwrap(),
c.to_f64().unwrap(),
vol.to_f64().unwrap(),
);
let range_vol = vol
* ((2.0 * c - f64::min(l, *state) - f64::max(h, *state))
/ (f64::max(h, *state) - f64::min(l, *state)));
*state = c;
Some(range_vol)
})
.collect::<Vec<f64>>(),
window,
)
.zip(wilder_sum(&volume[1..], window))
.map(|(range, vol)| range / vol)
.collect::<Vec<f64>>(),
)
}
pub fn ad<'a, T: ToPrimitive, U: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
volume: &'a [U],
alt: Option<bool>,
) -> Box<dyn Iterator<Item = f64> + 'a> {
if !alt.unwrap_or(false) {
Box::new(
izip!(high, low, close, volume).scan(0.0, |state, (h, l, c, vol)| {
let (h, l, c, vol) = (
h.to_f64().unwrap(),
l.to_f64().unwrap(),
c.to_f64().unwrap(),
vol.to_f64().unwrap(),
);
let mfm = ((c - l) - (h - c)) / (h - l);
let mfv = mfm * vol;
let adl = *state + mfv;
*state = adl;
Some(adl)
}),
)
} else {
Box::new(iter::once(f64::NAN).chain(
izip!(&high[1..], &low[1..], &close[1..], &volume[1..]).scan(
(close[0].to_f64().unwrap(), 0.0),
|state, (h, l, c, vol)| {
let (h, l, c, vol) = (
h.to_f64().unwrap(),
l.to_f64().unwrap(),
c.to_f64().unwrap(),
vol.to_f64().unwrap(),
);
let mfm = if c > state.0 {
c - f64::min(l, state.0)
} else {
c - f64::max(h, state.0)
};
let mfv = mfm * vol;
let adl = state.1 + mfv;
*state = (c, adl);
Some(adl)
},
),
))
}
}
pub fn elder_force<'a, T: ToPrimitive, U: ToPrimitive>(
close: &'a [T],
volume: &'a [U],
window: usize,
) -> impl Iterator<Item = f64> + 'a {
iter::once(f64::NAN).chain(
smooth::ewma(
&izip!(&close[..close.len() - 1], &close[1..], &volume[1..])
.map(|(prev, curr, vol)| {
(curr.to_f64().unwrap() - prev.to_f64().unwrap()) * vol.to_f64().unwrap()
})
.collect::<Vec<f64>>(),
window,
)
.collect::<Vec<f64>>(),
)
}
pub fn mfi<'a, T: ToPrimitive, U: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
volume: &'a [U],
window: usize,
) -> impl Iterator<Item = f64> + 'a {
let (pos_mf, neg_mf): (Vec<f64>, Vec<f64>) =
izip!(&high[1..], &low[1..], &close[1..], &volume[1..])
.scan(
(high[0].to_f64().unwrap() + low[0].to_f64().unwrap() + close[0].to_f64().unwrap())
/ 3.0,
|state, (h, l, c, vol)| {
let (h, l, c, vol) = (
h.to_f64().unwrap(),
l.to_f64().unwrap(),
c.to_f64().unwrap(),
vol.to_f64().unwrap(),
);
let hlc = (h + l + c) / 3.0;
let pos_mf = if hlc > *state { hlc * vol } else { 0.0 };
let neg_mf = if hlc < *state { hlc * vol } else { 0.0 };
*state = hlc;
Some((pos_mf, neg_mf))
},
)
.unzip();
iter::repeat(f64::NAN).take(window).chain(
pos_mf
.windows(window)
.zip(neg_mf.windows(window))
.map(|(pos, neg)| {
100.0 - (100.0 / (1.0 + pos.iter().sum::<f64>() / neg.iter().sum::<f64>()))
})
.collect::<Vec<f64>>(),
)
}
pub fn cmf<'a, T: ToPrimitive, U: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
volume: &'a [U],
window: usize,
) -> impl Iterator<Item = f64> + 'a {
iter::repeat(f64::NAN).take(window - 1).chain(
izip!(high, low, close, volume)
.map(|(h, l, c, vol)| {
let (h, l, c, vol) = (
h.to_f64().unwrap(),
l.to_f64().unwrap(),
c.to_f64().unwrap(),
vol.to_f64().unwrap(),
);
vol * ((c - l) - (h - c)) / (h - l)
})
.collect::<Vec<f64>>()
.windows(window)
.zip(volume.windows(window))
.map(|(mfv_win, v_win)| {
mfv_win.iter().sum::<f64>() / v_win.iter().filter_map(|x| x.to_f64()).sum::<f64>()
})
.collect::<Vec<f64>>(),
)
}
pub fn tvi<'a, T: ToPrimitive, U: ToPrimitive>(
close: &'a [T],
volume: &'a [U],
min_tick: f64,
) -> impl Iterator<Item = f64> + 'a {
iter::once(f64::NAN).chain(
izip!(&close[..close.len() - 1], &close[1..], &volume[1..],).scan(
(1, 0.0),
move |state, (prev, curr, vol)| {
let (prev, curr, vol) = (
prev.to_f64().unwrap(),
curr.to_f64().unwrap(),
vol.to_f64().unwrap(),
);
let direction = if curr - prev > min_tick {
1
} else if prev - curr > min_tick {
-1
} else {
state.0
};
let tvi = state.1 + direction as f64 * vol;
*state = (direction, tvi);
Some(tvi)
},
),
)
}
pub fn ease<'a, T: ToPrimitive, U: ToPrimitive>(
high: &'a [T],
low: &'a [T],
volume: &'a [U],
window: usize,
) -> impl Iterator<Item = f64> + 'a {
iter::once(f64::NAN).chain(
smooth::sma(
&(1..high.len())
.map(|i| {
(high[i].to_f64().unwrap() + low[i].to_f64().unwrap()
- high[i - 1].to_f64().unwrap()
- low[i - 1].to_f64().unwrap())
/ 2.0
/ (volume[i].to_f64().unwrap()
/ 100000000.0
/ (high[i].to_f64().unwrap() - low[i].to_f64().unwrap()))
})
.collect::<Vec<f64>>(),
window,
)
.collect::<Vec<f64>>(),
)
}
pub fn obv<'a, T: ToPrimitive, U: ToPrimitive>(
close: &'a [T],
volume: &'a [U],
) -> impl Iterator<Item = f64> + 'a {
iter::once(f64::NAN).chain(close.windows(2).enumerate().scan(0.0, |state, (i, pairs)| {
*state += (pairs[1].to_f64().unwrap() - pairs[0].to_f64().unwrap()).signum()
* volume[i + 1].to_f64().unwrap();
Some(*state)
}))
}
pub fn bw_mfi<'a, T: ToPrimitive, U: ToPrimitive>(
high: &'a [T],
low: &'a [T],
volume: &'a [U],
) -> impl Iterator<Item = f64> + 'a {
high.iter().zip(low).zip(volume).map(|((h, l), vol)| {
(h.to_f64().unwrap() - l.to_f64().unwrap()) / vol.to_f64().unwrap() * (10.0_f64).powi(6)
})
}
pub fn pvi<'a, T: ToPrimitive, U: ToPrimitive>(
data: &'a [T],
volume: &'a [U],
) -> impl Iterator<Item = f64> + 'a {
iter::once(f64::NAN).chain(data.windows(2).zip(volume.windows(2)).scan(
100.0,
|state, (c, vol)| {
if vol[1].to_f64().unwrap() > vol[0].to_f64().unwrap() {
*state *= c[1].to_f64().unwrap() / c[0].to_f64().unwrap();
}
Some(*state)
},
))
}
pub fn nvi<'a, T: ToPrimitive, U: ToPrimitive>(
data: &'a [T],
volume: &'a [U],
) -> impl Iterator<Item = f64> + 'a {
iter::once(f64::NAN).chain(data.windows(2).zip(volume.windows(2)).scan(
100.0,
|state, (c, vol)| {
if vol[1].to_f64().unwrap() < vol[0].to_f64().unwrap() {
*state *= c[1].to_f64().unwrap() / c[0].to_f64().unwrap();
}
Some(*state)
},
))
}
pub fn vwap<'a, T: ToPrimitive, U: ToPrimitive>(
high: &'a [T],
low: &'a [T],
close: &'a [T],
volume: &'a [U],
reset_idx: Option<&'a [usize]>,
) -> impl Iterator<Item = f64> + 'a {
let mut reset_idx = reset_idx.unwrap_or(&[close.len()]).to_vec();
izip!(high, low, close, volume).enumerate().scan(
(0.0, 0.0),
move |state, (idx, (h, l, c, vol))| {
let (h, l, c, vol) = (
h.to_f64().unwrap(),
l.to_f64().unwrap(),
c.to_f64().unwrap(),
vol.to_f64().unwrap(),
);
let (mut tpv_sum, mut vol_sum) = state;
if idx == reset_idx[0] {
tpv_sum = 0.0;
vol_sum = 0.0;
reset_idx.rotate_left(1);
}
tpv_sum += (h + l + c) / 3.0 * vol;
vol_sum += vol;
*state = (tpv_sum, vol_sum);
Some(tpv_sum / vol_sum)
},
)
}
pub fn vwma<'a, T: ToPrimitive, U: ToPrimitive>(
data: &'a [T],
volume: &'a [U],
window: usize,
) -> impl Iterator<Item = f64> + 'a {
iter::repeat(f64::NAN).take(window - 1).chain(
data.windows(window)
.zip(volume.windows(window))
.map(|(data_w, vol_w)| {
data_w.iter().zip(vol_w).fold(0.0, |acc, (x, v)| {
acc + x.to_f64().unwrap() * v.to_f64().unwrap()
}) / vol_w.iter().filter_map(|x| x.to_f64()).sum::<f64>()
}),
)
}
pub fn vpt<'a, T: ToPrimitive, U: ToPrimitive>(
data: &'a [T],
volume: &'a [U],
) -> impl Iterator<Item = f64> + 'a {
iter::once(f64::NAN).chain(
data.windows(2)
.zip(&volume[1..])
.scan(0.0, |state, (w, v)| {
*state += v.to_f64().unwrap() * (w[1].to_f64().unwrap() - w[0].to_f64().unwrap())
/ w[0].to_f64().unwrap();
Some(*state)
}),
)
}