use super::{ChartSeries, check_period, true_range};
use crate::errors::QlResult;
use crate::require;
use crate::types::Real;
#[derive(Clone, Debug, PartialEq)]
pub struct Adx {
pub plus_di: ChartSeries,
pub minus_di: ChartSeries,
pub dx: ChartSeries,
pub adx: ChartSeries,
}
fn wilder(values: &[Real], period: usize, start: usize) -> QlResult<ChartSeries> {
let first = start.saturating_add(period - 1);
let mut result = ChartSeries::zeroed(values.len(), first)?;
if first >= values.len() {
return Ok(result);
}
let mut mean = 0.0;
for (index, value) in values[start..=first].iter().enumerate() {
mean += (*value - mean) / (index + 1) as Real;
require!(mean.is_finite(), "nonfinite Wilder seed");
}
result.values[first] = mean;
for (index, value) in values.iter().enumerate().skip(first + 1) {
mean = if period == 1 {
*value
} else {
mean + (*value - mean) / period as Real
};
require!(mean.is_finite(), "nonfinite Wilder value at index {index}");
result.values[index] = mean;
}
Ok(result)
}
pub fn adx(high: &[Real], low: &[Real], close: &[Real], period: usize) -> QlResult<Adx> {
check_period(period)?;
let ranges = true_range(high, low, close)?;
let mut plus = ChartSeries::zeroed(close.len(), 0)?;
let mut minus = ChartSeries::zeroed(close.len(), 0)?;
for index in 1..close.len() {
let up = high[index] - high[index - 1];
let down = low[index - 1] - low[index];
require!(
up.is_finite() && down.is_finite(),
"nonfinite directional movement at index {index}"
);
if up > 0.0 && up > down {
plus.values[index] = up;
} else if down > 0.0 && down > up {
minus.values[index] = down;
}
}
let ranges = wilder(&ranges.values, period, 1)?;
let mut plus_di = wilder(&plus.values, period, 1)?;
let mut minus_di = wilder(&minus.values, period, 1)?;
let mut dx = ChartSeries::zeroed(close.len(), period)?;
for index in dx.first_valid..close.len() {
let range = ranges.values[index];
plus_di.values[index] = if range == 0.0 {
0.0
} else {
(plus_di.values[index] / range) * 100.0
};
minus_di.values[index] = if range == 0.0 {
0.0
} else {
(minus_di.values[index] / range) * 100.0
};
let sum = plus_di.values[index] + minus_di.values[index];
let value = if sum == 0.0 {
0.0
} else {
((plus_di.values[index] - minus_di.values[index]).abs() / sum) * 100.0
};
require!(
plus_di.values[index].is_finite()
&& minus_di.values[index].is_finite()
&& sum.is_finite()
&& value.is_finite(),
"nonfinite directional index at index {index}"
);
dx.values[index] = value;
}
let smoothed = wilder(&dx.values, period, period)?;
Ok(Adx {
plus_di,
minus_di,
dx,
adx: smoothed,
})
}
pub fn adx_default(high: &[Real], low: &[Real], close: &[Real]) -> QlResult<Adx> {
adx(high, low, close, 14)
}
#[cfg(test)]
#[path = "adx_tests.rs"]
mod tests;