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> {
55 self.check_len(candles)?;
56
57 let mut adl = 0.0f64;
58 let values: Vec<f64> = candles
59 .iter()
60 .map(|c| {
61 let range = c.high - c.low;
62 let mfm = if range == 0.0 {
63 0.0
64 } else {
65 ((c.close - c.low) - (c.high - c.close)) / range
66 };
67 adl += mfm * c.volume;
68 adl
69 })
70 .collect();
71
72 Ok(IndicatorOutput::from_pairs([("ADL".to_string(), values)]))
73 }
74}
75
76pub fn factory<S: ::std::hash::BuildHasher>(
79 _params: &HashMap<String, String, S>,
80) -> Result<Box<dyn Indicator>, IndicatorError> {
81 Ok(Box::new(Adl::new()))
82}
83
84#[cfg(test)]
87mod tests {
88 use super::*;
89
90 fn candle(h: f64, l: f64, c: f64, v: f64) -> Candle {
91 Candle {
92 time: 0,
93 open: c,
94 high: h,
95 low: l,
96 close: c,
97 volume: v,
98 }
99 }
100
101 #[test]
102 fn adl_zero_range_no_panic() {
103 let bars = vec![candle(5.0, 5.0, 5.0, 1000.0)];
105 let out = Adl::new().calculate(&bars).unwrap();
106 let vals = out.get("ADL").unwrap();
107 assert_eq!(vals[0], 0.0);
108 }
109
110 #[test]
111 fn adl_full_positive_bar() {
112 let bars = vec![candle(10.0, 8.0, 10.0, 500.0)];
114 let out = Adl::new().calculate(&bars).unwrap();
115 let vals = out.get("ADL").unwrap();
116 assert!((vals[0] - 500.0).abs() < 1e-9, "got {}", vals[0]);
118 }
119
120 #[test]
121 fn adl_is_cumulative() {
122 let bars = vec![candle(10.0, 8.0, 9.0, 100.0); 2];
124 let out = Adl::new().calculate(&bars).unwrap();
125 let vals = out.get("ADL").unwrap();
126 assert!((vals[1] - 2.0 * vals[0]).abs() < 1e-9);
127 }
128
129 #[test]
130 fn factory_creates_adl() {
131 let ind = factory(&HashMap::new()).unwrap();
132 assert_eq!(ind.name(), "ADL");
133 }
134}