finance_query/indicators/
dema.rs1use super::{IndicatorError, Result, ema::ema};
4
5pub fn dema(data: &[f64], period: usize) -> Result<Vec<Option<f64>>> {
28 if period == 0 {
29 return Err(IndicatorError::InvalidPeriod(
30 "Period must be greater than 0".to_string(),
31 ));
32 }
33 if data.len() < period {
34 return Err(IndicatorError::InsufficientData {
35 need: period,
36 got: data.len(),
37 });
38 }
39
40 let ema1 = ema(data, period);
41
42 let valid_ema1: Vec<f64> = ema1.iter().filter_map(|&x| x).collect();
44
45 if valid_ema1.len() < period {
46 return Err(IndicatorError::InsufficientData {
47 need: 2 * period - 1,
48 got: data.len(),
49 });
50 }
51
52 let ema2 = ema(&valid_ema1, period);
53
54 let mut result = vec![None; data.len()];
55
56 for i in 0..data.len() {
57 if i >= period - 1 {
58 let j = i - (period - 1);
59 if j < ema2.len()
60 && let (Some(e1), Some(e2)) = (ema1[i], ema2[j])
61 {
62 result[i] = Some(2.0 * e1 - e2);
63 }
64 }
65 }
66
67 Ok(result)
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn test_dema() {
76 let prices = vec![10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0];
77 let result = dema(&prices, 3).unwrap();
78
79 assert_eq!(result.len(), prices.len());
80 assert!(result[0].is_none());
82 assert!(result[1].is_none());
83 assert!(result[2].is_none());
84 assert!(result[3].is_none());
85 assert!(result[4].is_some());
86 }
87}