use crate::indicators::trend::Ema;
use crate::indicators::utils::{validate_data_length, validate_period};
use crate::indicators::{Candle, Indicator, IndicatorError};
#[derive(Debug)]
pub struct Dema {
period: usize,
ema1: Ema,
ema2: Ema,
seen: usize,
}
impl Dema {
pub fn new(period: usize) -> Result<Self, IndicatorError> {
validate_period(period, 1)?;
Ok(Self {
period,
ema1: Ema::new(period)?,
ema2: Ema::new(period)?,
seen: 0,
})
}
pub fn reset_state(&mut self) {
self.ema1.reset_state();
self.ema2.reset_state();
self.seen = 0;
}
fn step(&mut self, value: f64) -> Result<Option<f64>, IndicatorError> {
self.seen += 1;
let e1 = <Ema as Indicator<f64, f64>>::next(&mut self.ema1, value)?
.expect("inner Ema always emits");
let e2 = <Ema as Indicator<f64, f64>>::next(&mut self.ema2, e1)?
.expect("inner Ema always emits");
if self.seen < 2 * self.period - 1 {
return Ok(None);
}
Ok(Some(2.0 * e1 - e2))
}
}
impl Indicator<f64, f64> for Dema {
fn calculate(&mut self, data: &[f64]) -> Result<Vec<f64>, IndicatorError> {
validate_data_length(data, 2 * self.period - 1)?;
self.reset_state();
let mut out = Vec::with_capacity(data.len() - 2 * (self.period - 1));
for &v in data {
if let Some(x) = self.step(v)? {
out.push(x);
}
}
Ok(out)
}
fn next(&mut self, value: f64) -> Result<Option<f64>, IndicatorError> {
self.step(value)
}
fn reset(&mut self) {
self.reset_state();
}
fn name(&self) -> &'static str {
"Dema"
}
fn period(&self) -> Option<usize> {
Some(self.period)
}
}
impl Indicator<Candle, f64> for Dema {
fn calculate(&mut self, data: &[Candle]) -> Result<Vec<f64>, IndicatorError> {
validate_data_length(data, 2 * self.period - 1)?;
self.reset_state();
let mut out = Vec::with_capacity(data.len() - 2 * (self.period - 1));
for c in data {
if let Some(x) = self.step(c.close)? {
out.push(x);
}
}
Ok(out)
}
fn next(&mut self, candle: Candle) -> Result<Option<f64>, IndicatorError> {
self.step(candle.close)
}
fn reset(&mut self) {
self.reset_state();
}
fn name(&self) -> &'static str {
"Dema"
}
fn period(&self) -> Option<usize> {
Some(self.period)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_emission_at_2p_minus_1() {
let mut dema = Dema::new(5).unwrap();
for v in 1..=8 {
assert!(
<Dema as Indicator<f64, f64>>::next(&mut dema, v as f64)
.unwrap()
.is_none(),
"premature emission at v={v}",
);
}
assert!(<Dema as Indicator<f64, f64>>::next(&mut dema, 9.0)
.unwrap()
.is_some());
}
}