#[inline]
fn dm1(high: &[f64], low: &[f64], i: usize) -> (f64, f64) {
let diff_p = high[i] - high[i - 1];
let diff_m = low[i - 1] - low[i];
let plus = if diff_p > 0.0 && diff_p > diff_m {
diff_p
} else {
0.0
};
let minus = if diff_m > 0.0 && diff_p < diff_m {
diff_m
} else {
0.0
};
(plus, minus)
}
#[inline]
fn tr1(high: &[f64], low: &[f64], close: &[f64], i: usize) -> f64 {
let hl = high[i] - low[i];
let hc = (high[i] - close[i - 1]).abs();
let lc = (low[i] - close[i - 1]).abs();
hl.max(hc).max(lc)
}
fn wilder_sum(n: usize, period: usize, term: impl Fn(usize) -> f64) -> Vec<f64> {
let mut out = vec![f64::NAN; n];
if period == 0 || period >= n {
return out;
}
let mut s = 0.0;
for i in 1..period {
s += term(i);
}
out[period - 1] = s;
let a = 1.0 - 1.0 / period as f64;
for i in period..n {
s = s.mul_add(a, term(i));
out[i] = s;
}
out
}
pub fn plus_dm(high: &[f64], low: &[f64], period: usize) -> Vec<f64> {
wilder_sum(high.len(), period, |i| dm1(high, low, i).0)
}
pub fn minus_dm(high: &[f64], low: &[f64], period: usize) -> Vec<f64> {
wilder_sum(high.len(), period, |i| dm1(high, low, i).1)
}
fn di(dm_sm: &[f64], tr_sm: &[f64], period: usize) -> Vec<f64> {
let n = dm_sm.len();
let mut out = vec![f64::NAN; n];
for i in period..n {
let t = tr_sm[i];
out[i] = if t.abs() < 1e-14 {
0.0
} else {
100.0 * dm_sm[i] / t
};
}
out
}
pub fn plus_di(high: &[f64], low: &[f64], close: &[f64], period: usize) -> Vec<f64> {
let sp = wilder_sum(high.len(), period, |i| dm1(high, low, i).0);
let st = wilder_sum(high.len(), period, |i| tr1(high, low, close, i));
di(&sp, &st, period)
}
pub fn minus_di(high: &[f64], low: &[f64], close: &[f64], period: usize) -> Vec<f64> {
let sm = wilder_sum(high.len(), period, |i| dm1(high, low, i).1);
let st = wilder_sum(high.len(), period, |i| tr1(high, low, close, i));
di(&sm, &st, period)
}
fn dm_tr_sums(
high: &[f64],
low: &[f64],
close: &[f64],
period: usize,
) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
let n = high.len();
let (mut sp, mut sm, mut st) = (vec![f64::NAN; n], vec![f64::NAN; n], vec![f64::NAN; n]);
if period == 0 || period >= n {
return (sp, sm, st);
}
let (mut p, mut m, mut t) = (0.0, 0.0, 0.0);
for i in 1..period {
let (dp, dm) = dm1(high, low, i);
p += dp;
m += dm;
t += tr1(high, low, close, i);
}
sp[period - 1] = p;
sm[period - 1] = m;
st[period - 1] = t;
let a = 1.0 - 1.0 / period as f64;
for i in period..n {
let (dp, dm) = dm1(high, low, i);
p = p.mul_add(a, dp);
m = m.mul_add(a, dm);
t = t.mul_add(a, tr1(high, low, close, i));
sp[i] = p;
sm[i] = m;
st[i] = t;
}
(sp, sm, st)
}
pub fn dx(high: &[f64], low: &[f64], close: &[f64], period: usize) -> Vec<f64> {
let (sp, sm, st) = dm_tr_sums(high, low, close, period);
let n = sp.len();
let mut out = vec![f64::NAN; n];
for i in period..n {
let t = st[i];
out[i] = if t.abs() < 1e-14 {
0.0
} else {
let plus_di = 100.0 * sp[i] / t;
let minus_di = 100.0 * sm[i] / t;
let sum = plus_di + minus_di;
if sum.abs() < 1e-14 {
0.0
} else {
100.0 * (minus_di - plus_di).abs() / sum
}
};
}
out
}
pub fn adx(high: &[f64], low: &[f64], close: &[f64], period: usize) -> Vec<f64> {
let dxv = dx(high, low, close, period);
let n = dxv.len();
let mut out = vec![f64::NAN; n];
if period == 0 {
return out;
}
let first = 2 * period - 1;
if first >= n {
return out;
}
let pf = period as f64;
let mut sum = 0.0;
for v in dxv.iter().take(2 * period).skip(period) {
sum += v;
}
let mut prev = sum / pf;
out[first] = prev;
let a = (pf - 1.0) / pf;
let b = 1.0 / pf;
for i in (2 * period)..n {
prev = prev.mul_add(a, dxv[i] * b);
out[i] = prev;
}
out
}
pub fn adxr(high: &[f64], low: &[f64], close: &[f64], period: usize) -> Vec<f64> {
let a = adx(high, low, close, period);
let n = a.len();
let mut out = vec![f64::NAN; n];
if period == 0 {
return out;
}
let off = period - 1;
for i in (3 * period - 2)..n {
out[i] = (a[i] + a[i - off]) / 2.0;
}
out
}
fn wilder_sum_final(n: usize, period: usize, term: impl Fn(usize) -> f64) -> Option<f64> {
if period == 0 || period >= n {
return None;
}
let mut s = 0.0;
for i in 1..period {
s += term(i);
}
let a = 1.0 - 1.0 / period as f64;
for i in period..n {
s = s.mul_add(a, term(i));
}
Some(s)
}
fn wilder_sum_resume(
n: usize,
period: usize,
from: usize,
state: f64,
term: impl Fn(usize) -> f64,
) -> (Vec<f64>, f64) {
let a = 1.0 - 1.0 / period as f64;
let mut s = state;
let mut out = Vec::with_capacity(n.saturating_sub(from));
for i in from..n {
s = s.mul_add(a, term(i));
out.push(s);
}
(out, s)
}
pub fn plus_dm_final_state(high: &[f64], low: &[f64], period: usize) -> Option<Vec<f64>> {
wilder_sum_final(high.len(), period, |i| dm1(high, low, i).0).map(|s| vec![s])
}
pub fn plus_dm_resume(
high: &[f64],
low: &[f64],
period: usize,
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
if from == 0 {
return None;
}
let (out, s) = wilder_sum_resume(high.len(), period, from, state[0], |i| dm1(high, low, i).0);
Some((out, vec![s]))
}
pub fn minus_dm_final_state(high: &[f64], low: &[f64], period: usize) -> Option<Vec<f64>> {
wilder_sum_final(high.len(), period, |i| dm1(high, low, i).1).map(|s| vec![s])
}
pub fn minus_dm_resume(
high: &[f64],
low: &[f64],
period: usize,
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
if from == 0 {
return None;
}
let (out, s) = wilder_sum_resume(high.len(), period, from, state[0], |i| dm1(high, low, i).1);
Some((out, vec![s]))
}
fn dm_tr_sums_final(
high: &[f64],
low: &[f64],
close: &[f64],
period: usize,
) -> Option<(f64, f64, f64)> {
let n = high.len();
if period == 0 || period >= n {
return None;
}
let (mut p, mut m, mut t) = (0.0, 0.0, 0.0);
for i in 1..period {
let (dp, dm) = dm1(high, low, i);
p += dp;
m += dm;
t += tr1(high, low, close, i);
}
let a = 1.0 - 1.0 / period as f64;
for i in period..n {
let (dp, dm) = dm1(high, low, i);
p = p.mul_add(a, dp);
m = m.mul_add(a, dm);
t = t.mul_add(a, tr1(high, low, close, i));
}
Some((p, m, t))
}
#[inline]
fn dm_tr_step(high: &[f64], low: &[f64], close: &[f64], i: usize, a: f64, s: &mut [f64; 3]) {
let (dp, dm) = dm1(high, low, i);
s[0] = s[0].mul_add(a, dp);
s[1] = s[1].mul_add(a, dm);
s[2] = s[2].mul_add(a, tr1(high, low, close, i));
}
#[inline]
fn di_val(dm_sm: f64, tr_sm: f64) -> f64 {
if tr_sm.abs() < 1e-14 {
0.0
} else {
100.0 * dm_sm / tr_sm
}
}
pub fn plus_di_final_state(
high: &[f64],
low: &[f64],
close: &[f64],
period: usize,
) -> Option<Vec<f64>> {
let (sp, _sm, st) = dm_tr_sums_final(high, low, close, period)?;
Some(vec![sp, st])
}
pub fn plus_di_resume(
high: &[f64],
low: &[f64],
close: &[f64],
period: usize,
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
if from == 0 {
return None;
}
let n = high.len();
let a = 1.0 - 1.0 / period as f64;
let mut s = [state[0], 0.0, state[1]];
let mut out = Vec::with_capacity(n.saturating_sub(from));
for i in from..n {
dm_tr_step(high, low, close, i, a, &mut s);
out.push(di_val(s[0], s[2]));
}
Some((out, vec![s[0], s[2]]))
}
pub fn minus_di_final_state(
high: &[f64],
low: &[f64],
close: &[f64],
period: usize,
) -> Option<Vec<f64>> {
let (_sp, sm, st) = dm_tr_sums_final(high, low, close, period)?;
Some(vec![sm, st])
}
pub fn minus_di_resume(
high: &[f64],
low: &[f64],
close: &[f64],
period: usize,
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
if from == 0 {
return None;
}
let n = high.len();
let a = 1.0 - 1.0 / period as f64;
let mut s = [0.0, state[0], state[1]];
let mut out = Vec::with_capacity(n.saturating_sub(from));
for i in from..n {
dm_tr_step(high, low, close, i, a, &mut s);
out.push(di_val(s[1], s[2]));
}
Some((out, vec![s[1], s[2]]))
}
#[inline]
fn dx_val(sp: f64, sm: f64, st: f64) -> f64 {
if st.abs() < 1e-14 {
0.0
} else {
let plus_di = 100.0 * sp / st;
let minus_di = 100.0 * sm / st;
let sum = plus_di + minus_di;
if sum.abs() < 1e-14 {
0.0
} else {
100.0 * (minus_di - plus_di).abs() / sum
}
}
}
pub fn dx_final_state(high: &[f64], low: &[f64], close: &[f64], period: usize) -> Option<Vec<f64>> {
let (sp, sm, st) = dm_tr_sums_final(high, low, close, period)?;
Some(vec![sp, sm, st])
}
pub fn dx_resume(
high: &[f64],
low: &[f64],
close: &[f64],
period: usize,
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
if from == 0 {
return None;
}
let n = high.len();
let a = 1.0 - 1.0 / period as f64;
let mut s = [state[0], state[1], state[2]];
let mut out = Vec::with_capacity(n.saturating_sub(from));
for i in from..n {
dm_tr_step(high, low, close, i, a, &mut s);
out.push(dx_val(s[0], s[1], s[2]));
}
Some((out, s.to_vec()))
}
pub fn adx_final_state(
high: &[f64],
low: &[f64],
close: &[f64],
period: usize,
) -> Option<Vec<f64>> {
let n = high.len();
if period == 0 {
return None;
}
let first = 2 * period - 1;
if first >= n {
return None;
}
let a_sum = 1.0 - 1.0 / period as f64;
let mut s = [0.0, 0.0, 0.0];
for i in 1..period {
let (dp, dm) = dm1(high, low, i);
s[0] += dp;
s[1] += dm;
s[2] += tr1(high, low, close, i);
}
let pf = period as f64;
let mut dx_seed_sum = 0.0;
let mut adx = f64::NAN;
let (a_avg, b_avg) = ((pf - 1.0) / pf, 1.0 / pf);
for i in period..n {
dm_tr_step(high, low, close, i, a_sum, &mut s);
let dxv = dx_val(s[0], s[1], s[2]);
if i < 2 * period {
dx_seed_sum += dxv;
if i == 2 * period - 1 {
adx = dx_seed_sum / pf;
}
} else {
adx = adx.mul_add(a_avg, dxv * b_avg);
}
}
Some(vec![s[0], s[1], s[2], adx])
}
pub fn adx_resume(
high: &[f64],
low: &[f64],
close: &[f64],
period: usize,
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
if from == 0 {
return None;
}
let n = high.len();
let pf = period as f64;
let a_sum = 1.0 - 1.0 / pf;
let (a_avg, b_avg) = ((pf - 1.0) / pf, 1.0 / pf);
let mut s = [state[0], state[1], state[2]];
let mut adx = state[3];
let mut out = Vec::with_capacity(n.saturating_sub(from));
for i in from..n {
dm_tr_step(high, low, close, i, a_sum, &mut s);
let dxv = dx_val(s[0], s[1], s[2]);
adx = adx.mul_add(a_avg, dxv * b_avg);
out.push(adx);
}
Some((out, vec![s[0], s[1], s[2], adx]))
}
pub fn adxr_final_state(
high: &[f64],
low: &[f64],
close: &[f64],
period: usize,
) -> Option<Vec<f64>> {
let n = high.len();
if period == 0 {
return None;
}
if 3 * period - 2 >= n {
return None;
}
let a_sum = 1.0 - 1.0 / period as f64;
let mut s = [0.0, 0.0, 0.0];
for i in 1..period {
let (dp, dm) = dm1(high, low, i);
s[0] += dp;
s[1] += dm;
s[2] += tr1(high, low, close, i);
}
let pf = period as f64;
let (a_avg, b_avg) = ((pf - 1.0) / pf, 1.0 / pf);
let mut adxv = vec![f64::NAN; n];
let mut dx_seed_sum = 0.0;
let mut adx = 0.0;
for i in period..n {
dm_tr_step(high, low, close, i, a_sum, &mut s);
let dxv = dx_val(s[0], s[1], s[2]);
if i < 2 * period {
dx_seed_sum += dxv;
if i == 2 * period - 1 {
adx = dx_seed_sum / pf;
adxv[i] = adx;
}
} else {
adx = adx.mul_add(a_avg, dxv * b_avg);
adxv[i] = adx;
}
}
let mut state = vec![s[0], s[1], s[2]];
state.extend_from_slice(&adxv[n - period..n]);
Some(state)
}
pub fn adxr_resume(
high: &[f64],
low: &[f64],
close: &[f64],
period: usize,
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
if from == 0 {
return None;
}
let n = high.len();
let pf = period as f64;
let a_sum = 1.0 - 1.0 / pf;
let (a_avg, b_avg) = ((pf - 1.0) / pf, 1.0 / pf);
let mut s = [state[0], state[1], state[2]];
let mut win: Vec<f64> = state[3..].to_vec();
let mut adx = win[period - 1]; let mut out = Vec::with_capacity(n.saturating_sub(from));
for i in from..n {
dm_tr_step(high, low, close, i, a_sum, &mut s);
let dxv = dx_val(s[0], s[1], s[2]);
adx = adx.mul_add(a_avg, dxv * b_avg);
win.remove(0);
win.push(adx);
out.push((adx + win[0]) / 2.0);
}
Some((out, {
let mut st = vec![s[0], s[1], s[2]];
st.extend_from_slice(&win);
st
}))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::indicators::test_support::*;
#[test]
fn directional_resume_is_bit_identical_to_full() {
let (high, low, close) = ohlc(120);
let p = 14usize;
for &from in &[41usize, 45, 60, 90, 119] {
let h = &high[..from];
let l = &low[..from];
let c = &close[..from];
let st = plus_dm_final_state(h, l, p).unwrap();
let (tail, _) = plus_dm_resume(&high, &low, p, from, &st).unwrap();
assert_bits(&tail, &plus_dm(&high, &low, p)[from..], "plus_dm");
let st = minus_dm_final_state(h, l, p).unwrap();
let (tail, _) = minus_dm_resume(&high, &low, p, from, &st).unwrap();
assert_bits(&tail, &minus_dm(&high, &low, p)[from..], "minus_dm");
let st = plus_di_final_state(h, l, c, p).unwrap();
let (tail, _) = plus_di_resume(&high, &low, &close, p, from, &st).unwrap();
assert_bits(&tail, &plus_di(&high, &low, &close, p)[from..], "plus_di");
let st = minus_di_final_state(h, l, c, p).unwrap();
let (tail, _) = minus_di_resume(&high, &low, &close, p, from, &st).unwrap();
assert_bits(&tail, &minus_di(&high, &low, &close, p)[from..], "minus_di");
let st = dx_final_state(h, l, c, p).unwrap();
let (tail, _) = dx_resume(&high, &low, &close, p, from, &st).unwrap();
assert_bits(&tail, &dx(&high, &low, &close, p)[from..], "dx");
let st = adx_final_state(h, l, c, p).unwrap();
let (tail, _) = adx_resume(&high, &low, &close, p, from, &st).unwrap();
assert_bits(&tail, &adx(&high, &low, &close, p)[from..], "adx");
let st = adxr_final_state(h, l, c, p).unwrap();
let (tail, _) = adxr_resume(&high, &low, &close, p, from, &st).unwrap();
assert_bits(&tail, &adxr(&high, &low, &close, p)[from..], "adxr");
}
}
#[test]
fn directional_resume_declines_at_from_zero() {
let (high, low, close) = ohlc(60);
let p = 14usize;
let dummy = vec![0.0; 8]; assert!(plus_dm_resume(&high, &low, p, 0, &dummy).is_none()); assert!(minus_dm_resume(&high, &low, p, 0, &dummy).is_none()); assert!(plus_di_resume(&high, &low, &close, p, 0, &dummy).is_none()); assert!(minus_di_resume(&high, &low, &close, p, 0, &dummy).is_none()); assert!(dx_resume(&high, &low, &close, p, 0, &dummy).is_none()); assert!(adx_resume(&high, &low, &close, p, 0, &dummy).is_none()); assert!(adxr_resume(&high, &low, &close, p, 0, &dummy).is_none()); }
#[test]
fn directional_final_state_declines_before_seed() {
let (high, low, close) = ohlc(120);
assert!(plus_dm_final_state(&high, &low, 200).is_none()); assert!(minus_dm_final_state(&high, &low, 200).is_none());
assert!(plus_di_final_state(&high, &low, &close, 200).is_none()); assert!(minus_di_final_state(&high, &low, &close, 200).is_none());
assert!(dx_final_state(&high, &low, &close, 200).is_none());
assert!(adx_final_state(&high, &low, &close, 0).is_none());
assert!(adx_final_state(&high, &low, &close, 70).is_none());
assert!(adxr_final_state(&high, &low, &close, 0).is_none());
assert!(adxr_final_state(&high, &low, &close, 50).is_none());
}
#[test]
fn di_dx_zero_divisor_guards() {
let p = 14usize;
let flat = vec![50.0; 40];
let from = 20usize;
let st = plus_di_final_state(&flat[..from], &flat[..from], &flat[..from], p).unwrap();
let (tail, _) = plus_di_resume(&flat, &flat, &flat, p, from, &st).unwrap();
assert!(
tail.iter().all(|&x| x == 0.0),
"plus_di resume flat TR -> 0"
); let st = minus_di_final_state(&flat[..from], &flat[..from], &flat[..from], p).unwrap();
let (tail, _) = minus_di_resume(&flat, &flat, &flat, p, from, &st).unwrap();
assert!(
tail.iter().all(|&x| x == 0.0),
"minus_di resume flat TR -> 0"
);
let st = dx_final_state(&flat[..from], &flat[..from], &flat[..from], p).unwrap();
let (tail, _) = dx_resume(&flat, &flat, &flat, p, from, &st).unwrap();
assert!(tail.iter().all(|&x| x == 0.0), "dx resume flat TR -> 0");
let n = 40usize;
let high: Vec<f64> = (0..n).map(|i| 100.0 - i as f64 * 0.5).collect();
let low: Vec<f64> = (0..n).map(|i| 60.0 + i as f64 * 0.5).collect();
let close: Vec<f64> = (0..n).map(|i| 80.0 + (i as f64 * 0.3).sin()).collect();
for i in 1..n {
assert!(high[i] < high[i - 1] && low[i] > low[i - 1] && high[i] > low[i]);
}
let st = dx_final_state(&high[..from], &low[..from], &close[..from], p).unwrap();
let (tail, _) = dx_resume(&high, &low, &close, p, from, &st).unwrap();
assert!(tail.iter().all(|&x| x == 0.0), "dx resume zero DI-sum -> 0"); }
}