use super::WMA;
use crate::{
core::{Error, Method, MovingAverage, PeriodType, ValueType},
helpers::Peekable,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct HMA {
wma1: WMA,
wma2: WMA,
wma3: WMA,
}
impl Method for HMA {
type Params = PeriodType;
type Input = ValueType;
type Output = Self::Input;
fn new(length: Self::Params, value: &Self::Input) -> Result<Self, Error> {
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
match length {
0 | 1 => Err(Error::WrongMethodParameters),
length => Ok(Self {
wma1: WMA::new(length / 2, value)?,
wma2: WMA::new(length, value)?,
wma3: WMA::new((length as ValueType).sqrt() as PeriodType, value)?,
}),
}
}
#[inline]
fn next(&mut self, value: &Self::Input) -> Self::Output {
let w1 = self.wma1.next(value);
let w2 = self.wma2.next(value);
self.wma3.next(&w1.mul_add(2., -w2))
}
}
impl MovingAverage for HMA {}
impl Peekable<<Self as Method>::Output> for HMA {
fn peek(&self) -> <Self as Method>::Output {
self.wma3.peek()
}
}
#[cfg(test)]
mod tests {
use super::{HMA as TestingMethod, WMA};
use crate::core::Method;
use crate::core::{PeriodType, ValueType};
use crate::helpers::{assert_eq_float, RandomCandles};
use crate::methods::tests::test_const_float;
#[test]
fn test_hma_const() {
for i in 2..255 {
let input = (i as ValueType + 56.0) / 16.3251;
let mut method = TestingMethod::new(i, &input).unwrap();
let output = method.next(&input);
test_const_float(&mut method, &input, output);
}
}
#[test]
fn test_hma() {
let candles = RandomCandles::default();
let src: Vec<ValueType> = candles.take(300).map(|x| x.close).collect();
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
(2..255).for_each(|length| {
let mut wma1 = WMA::new(length, &src[0]).unwrap();
let mut wma2 = WMA::new(length / 2, &src[0]).unwrap();
let mut wma3 = WMA::new((length as ValueType).sqrt() as PeriodType, &src[0]).unwrap();
let mut ma = TestingMethod::new(length, &src[0]).unwrap();
for x in &src {
let value1 = ma.next(x);
#[allow(clippy::suboptimal_flops)]
let value2 = wma3.next(&(2. * wma2.next(x) - wma1.next(x)));
assert_eq_float(value2, value1);
}
});
}
}