wickra_core/indicators/
dema.rs1use crate::error::Result;
4use crate::indicators::ema::Ema;
5use crate::traits::Indicator;
6
7#[derive(Debug, Clone)]
25pub struct Dema {
26 ema1: Ema,
27 ema2: Ema,
28 period: usize,
29}
30
31impl Dema {
32 pub fn new(period: usize) -> Result<Self> {
35 Ok(Self {
36 ema1: Ema::new(period)?,
37 ema2: Ema::new(period)?,
38 period,
39 })
40 }
41
42 pub const fn period(&self) -> usize {
44 self.period
45 }
46}
47
48impl Indicator for Dema {
49 type Input = f64;
50 type Output = f64;
51
52 fn update(&mut self, input: f64) -> Option<f64> {
53 let e1 = self.ema1.update(input)?;
54 let e2 = self.ema2.update(e1)?;
55 Some(2.0 * e1 - e2)
56 }
57
58 fn reset(&mut self) {
59 self.ema1.reset();
60 self.ema2.reset();
61 }
62
63 fn warmup_period(&self) -> usize {
64 2 * self.period - 1
66 }
67
68 fn is_ready(&self) -> bool {
69 self.ema2.is_ready()
70 }
71
72 fn name(&self) -> &'static str {
73 "DEMA"
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use crate::traits::BatchExt;
81 use approx::assert_relative_eq;
82
83 #[test]
84 fn constant_series_yields_constant_dema() {
85 let mut dema = Dema::new(5).unwrap();
86 let out = dema.batch(&[100.0_f64; 60]);
87 let last = out.iter().rev().flatten().next().unwrap();
88 assert_relative_eq!(*last, 100.0, epsilon = 1e-9);
89 }
90
91 #[test]
92 fn linear_uptrend_dema_above_ema_eventually() {
93 let prices: Vec<f64> = (1..=200).map(f64::from).collect();
96 let mut dema = Dema::new(20).unwrap();
97 let mut ema = Ema::new(20).unwrap();
98 let dema_out = dema.batch(&prices);
99 let ema_out = ema.batch(&prices);
100 let d = dema_out.last().unwrap().unwrap();
102 let e = ema_out.last().unwrap().unwrap();
103 assert!(d > e, "DEMA={d} should exceed EMA={e} on uptrend");
104 }
105
106 #[test]
107 fn batch_equals_streaming() {
108 let prices: Vec<f64> = (1..=80).map(|i| f64::from(i) * 0.5).collect();
109 let mut a = Dema::new(7).unwrap();
110 let mut b = Dema::new(7).unwrap();
111 assert_eq!(
112 a.batch(&prices),
113 prices.iter().map(|p| b.update(*p)).collect::<Vec<_>>()
114 );
115 }
116
117 #[test]
118 fn reset_clears_state() {
119 let mut dema = Dema::new(5).unwrap();
120 dema.batch(&(1..=50).map(f64::from).collect::<Vec<_>>());
121 assert!(dema.is_ready());
122 dema.reset();
123 assert!(!dema.is_ready());
124 }
125
126 #[test]
127 fn rejects_zero_period() {
128 assert!(Dema::new(0).is_err());
129 }
130
131 #[test]
135 fn accessors_and_metadata() {
136 let dema = Dema::new(5).unwrap();
137 assert_eq!(dema.period(), 5);
138 assert_eq!(dema.warmup_period(), 9);
141 assert_eq!(dema.name(), "DEMA");
142 }
143}