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