use super::{ChartSeries, check_hlc, check_period};
use crate::errors::QlResult;
use crate::require;
use crate::types::Real;
pub fn true_range(high: &[Real], low: &[Real], close: &[Real]) -> QlResult<ChartSeries> {
check_hlc(high, low, close)?;
let mut result = ChartSeries::zeroed(close.len(), 0)?;
for index in 0..close.len() {
let span = high[index] - low[index];
require!(span.is_finite(), "nonfinite true range at index {index}");
let value = if index == 0 {
span
} else {
let high_gap = (high[index] - close[index - 1]).abs();
let low_gap = (low[index] - close[index - 1]).abs();
require!(
high_gap.is_finite() && low_gap.is_finite(),
"nonfinite true range at index {index}"
);
span.max(high_gap).max(low_gap)
};
result.values[index] = value;
}
Ok(result)
}
pub fn atr(high: &[Real], low: &[Real], close: &[Real], period: usize) -> QlResult<ChartSeries> {
check_period(period)?;
let mut result = true_range(high, low, close)?;
if period == 1 {
return Ok(result);
}
result.first_valid = (period - 1).min(close.len());
if close.len() < period {
result.values.fill(0.0);
return Ok(result);
}
let mut mean = 0.0;
for (index, value) in result.values[..period].iter().enumerate() {
mean += (value - mean) / (index + 1) as Real;
}
require!(mean.is_finite(), "nonfinite ATR seed");
result.values[..period - 1].fill(0.0);
result.values[period - 1] = mean;
for (index, value) in result.values.iter_mut().enumerate().skip(period) {
mean += (*value - mean) / period as Real;
require!(mean.is_finite(), "nonfinite ATR at index {index}");
*value = mean;
}
Ok(result)
}
pub fn atr_default(high: &[Real], low: &[Real], close: &[Real]) -> QlResult<ChartSeries> {
atr(high, low, close, 14)
}
#[cfg(test)]
#[path = "true_range_atr_tests.rs"]
mod tests;