use crate::indicators::validate_data_length;
use crate::Candle;
use crate::Indicator;
use crate::IndicatorError;
#[derive(Debug)]
pub struct Adl {
current_ad: f64,
}
impl Adl {
pub fn new() -> Self {
Self { current_ad: 0.0 }
}
fn money_flow_multiplier(candle: &Candle) -> Result<f64, IndicatorError> {
let high = candle.high;
let low = candle.low;
let close = candle.close;
let range = high - low;
if range == 0.0 {
return Err(IndicatorError::CalculationError(
"Division by zero: high and low prices are equal".to_string(),
));
}
Ok((2.0 * close - high - low) / range)
}
fn money_flow_volume(candle: &Candle) -> Result<f64, IndicatorError> {
let mfm = Self::money_flow_multiplier(candle)?;
let volume = candle.volume;
Ok(mfm * volume)
}
}
impl Default for Adl {
fn default() -> Self {
Self::new()
}
}
impl Indicator<Candle, f64> for Adl {
fn calculate(&mut self, data: &[Candle]) -> Result<Vec<f64>, IndicatorError> {
validate_data_length(data, 1)?;
let n = data.len();
let mut result = Vec::with_capacity(n);
self.reset();
let mut ad_line = 0.0;
for candle in data {
let money_flow_volume = Self::money_flow_volume(candle)?;
ad_line += money_flow_volume;
result.push(ad_line);
}
self.current_ad = ad_line;
Ok(result)
}
fn next(&mut self, value: Candle) -> Result<Option<f64>, IndicatorError> {
let money_flow_volume = Self::money_flow_volume(&value)?;
self.current_ad += money_flow_volume;
Ok(Some(self.current_ad))
}
fn reset(&mut self) {
self.current_ad = 0.0;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::indicators::Candle;
#[test]
fn test_adl_new() {
let adl = Adl::new();
assert_eq!(adl.current_ad, 0.0);
}
#[test]
fn test_adl_calculation() {
let mut adl = Adl::new();
let candles = vec![
Candle {
timestamp: 1,
open: 10.0,
high: 12.0,
low: 8.0,
close: 11.0,
volume: 1000.0,
},
Candle {
timestamp: 2,
open: 11.0,
high: 13.0,
low: 9.0,
close: 12.0,
volume: 1200.0,
},
Candle {
timestamp: 3,
open: 12.0,
high: 14.0,
low: 10.0,
close: 11.0,
volume: 800.0,
},
];
let result = adl.calculate(&candles).unwrap();
assert_eq!(result.len(), 3);
assert!((result[0] - 500.0).abs() < 0.01);
assert!((result[1] - 1100.0).abs() < 0.01);
}
#[test]
fn test_adl_next() {
let mut adl = Adl::new();
let candle1 = Candle {
timestamp: 1,
open: 10.0,
high: 12.0,
low: 8.0,
close: 11.0,
volume: 1000.0,
};
let result = adl.next(candle1).unwrap();
assert!(result.is_some());
assert!((result.unwrap() - 500.0).abs() < 0.01);
let candle2 = Candle {
timestamp: 2,
open: 11.0,
high: 13.0,
low: 9.0,
close: 12.0,
volume: 1200.0,
};
let result = adl.next(candle2).unwrap();
assert!((result.unwrap() - 1100.0).abs() < 0.01);
}
#[test]
fn test_adl_reset() {
let mut adl = Adl::new();
let candle = Candle {
timestamp: 1,
open: 10.0,
high: 12.0,
low: 8.0,
close: 11.0,
volume: 1000.0,
};
adl.next(candle).unwrap();
adl.reset();
assert_eq!(adl.current_ad, 0.0);
let candle2 = Candle {
timestamp: 2,
open: 11.0,
high: 13.0,
low: 9.0,
close: 12.0,
volume: 1200.0,
};
let result = adl.next(candle2).unwrap();
assert!((result.unwrap() - 600.0).abs() < 0.01);
}
#[test]
fn test_adl_error_handling() {
let mut adl = Adl::new();
let candle = Candle {
timestamp: 1,
open: 10.0,
high: 10.0,
low: 10.0,
close: 10.0,
volume: 1000.0,
};
let result = adl.next(candle);
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
IndicatorError::CalculationError(
"Division by zero: high and low prices are equal".to_string()
)
);
}
#[test]
fn test_adl_money_flow_multiplier_zero_range() {
let candle = Candle {
timestamp: 1,
open: 10.0,
high: 10.0, low: 10.0, close: 10.0,
volume: 1000.0,
};
let result = Adl::money_flow_multiplier(&candle);
assert!(result.is_err());
if let Err(IndicatorError::CalculationError(msg)) = result {
assert!(
msg.contains("division by zero") || msg.contains("high and low prices are equal")
);
} else {
panic!("Expected CalculationError for zero range");
}
let mut adl = Adl::new();
let result = adl.calculate(&[candle]);
assert!(result.is_err());
let mut adl = Adl::new();
let result = adl.next(candle);
assert!(result.is_err());
}
}