1use std::collections::HashMap;
21
22use crate::error::IndicatorError;
23use crate::indicator::{Indicator, IndicatorOutput};
24use crate::types::Candle;
25
26#[derive(Debug, Clone, Default)]
30pub struct Adl;
31
32impl Adl {
33 pub fn new() -> Self {
34 Self
35 }
36}
37
38impl Indicator for Adl {
39 fn name(&self) -> &'static str {
40 "ADL"
41 }
42 fn required_len(&self) -> usize {
43 1
44 }
45 fn required_columns(&self) -> &[&'static str] {
46 &["high", "low", "close", "volume"]
47 }
48
49 fn calculate(&self, candles: &[Candle]) -> Result<IndicatorOutput, IndicatorError> {
51 self.check_len(candles)?;
52
53 let mut adl = 0.0f64;
54 let values: Vec<f64> = candles
55 .iter()
56 .map(|c| {
57 let range = c.high - c.low;
58 let mfm = if range == 0.0 {
60 0.0
61 } else {
62 ((c.close - c.low) - (c.high - c.close)) / range
63 };
64 adl += mfm * c.volume;
65 adl
66 })
67 .collect();
68
69 Ok(IndicatorOutput::from_pairs([("ADL".to_string(), values)]))
70 }
71}
72
73pub fn factory<S: ::std::hash::BuildHasher>(_params: &HashMap<String, String, S>) -> Result<Box<dyn Indicator>, IndicatorError> {
76 Ok(Box::new(Adl::new()))
77}
78
79#[cfg(test)]
82mod tests {
83 use super::*;
84
85 fn candle(h: f64, l: f64, c: f64, v: f64) -> Candle {
86 Candle {
87 time: 0,
88 open: c,
89 high: h,
90 low: l,
91 close: c,
92 volume: v,
93 }
94 }
95
96 #[test]
97 fn adl_zero_range_no_panic() {
98 let bars = vec![candle(5.0, 5.0, 5.0, 1000.0)];
100 let out = Adl::new().calculate(&bars).unwrap();
101 let vals = out.get("ADL").unwrap();
102 assert_eq!(vals[0], 0.0);
103 }
104
105 #[test]
106 fn adl_full_positive_bar() {
107 let bars = vec![candle(10.0, 8.0, 10.0, 500.0)];
109 let out = Adl::new().calculate(&bars).unwrap();
110 let vals = out.get("ADL").unwrap();
111 assert!((vals[0] - 500.0).abs() < 1e-9, "got {}", vals[0]);
113 }
114
115 #[test]
116 fn adl_is_cumulative() {
117 let bars = vec![candle(10.0, 8.0, 9.0, 100.0); 2];
119 let out = Adl::new().calculate(&bars).unwrap();
120 let vals = out.get("ADL").unwrap();
121 assert!((vals[1] - 2.0 * vals[0]).abs() < 1e-9);
122 }
123
124 #[test]
125 fn factory_creates_adl() {
126 let ind = factory(&HashMap::new()).unwrap();
127 assert_eq!(ind.name(), "ADL");
128 }
129}