use std::collections::HashMap;
use crate::error::IndicatorError;
use crate::indicator::{Indicator, IndicatorOutput};
use crate::types::Candle;
#[derive(Debug, Clone, Default)]
pub struct Adl;
impl Adl {
pub fn new() -> Self {
Self
}
}
impl Indicator for Adl {
fn name(&self) -> &'static str {
"ADL"
}
fn required_len(&self) -> usize {
1
}
fn required_columns(&self) -> &[&'static str] {
&["high", "low", "close", "volume"]
}
fn calculate(&self, candles: &[Candle]) -> Result<IndicatorOutput, IndicatorError> {
self.check_len(candles)?;
let mut adl = 0.0f64;
let values: Vec<f64> = candles
.iter()
.map(|c| {
let range = c.high - c.low;
let mfm = if range == 0.0 {
0.0
} else {
((c.close - c.low) - (c.high - c.close)) / range
};
adl += mfm * c.volume;
adl
})
.collect();
Ok(IndicatorOutput::from_pairs([("ADL".to_string(), values)]))
}
}
pub fn factory<S: ::std::hash::BuildHasher>(
_params: &HashMap<String, String, S>,
) -> Result<Box<dyn Indicator>, IndicatorError> {
Ok(Box::new(Adl::new()))
}
#[cfg(test)]
mod tests {
use super::*;
fn candle(h: f64, l: f64, c: f64, v: f64) -> Candle {
Candle {
time: 0,
open: c,
high: h,
low: l,
close: c,
volume: v,
}
}
#[test]
fn adl_zero_range_no_panic() {
let bars = vec![candle(5.0, 5.0, 5.0, 1000.0)];
let out = Adl::new().calculate(&bars).unwrap();
let vals = out.get("ADL").unwrap();
assert_eq!(vals[0], 0.0);
}
#[test]
fn adl_full_positive_bar() {
let bars = vec![candle(10.0, 8.0, 10.0, 500.0)];
let out = Adl::new().calculate(&bars).unwrap();
let vals = out.get("ADL").unwrap();
assert!((vals[0] - 500.0).abs() < 1e-9, "got {}", vals[0]);
}
#[test]
fn adl_is_cumulative() {
let bars = vec![candle(10.0, 8.0, 9.0, 100.0); 2];
let out = Adl::new().calculate(&bars).unwrap();
let vals = out.get("ADL").unwrap();
assert!((vals[1] - 2.0 * vals[0]).abs() < 1e-9);
}
#[test]
fn factory_creates_adl() {
let ind = factory(&HashMap::new()).unwrap();
assert_eq!(ind.name(), "ADL");
}
}