use crate::core::{Error, PeriodType, ValueType};
use crate::core::{Method, MovingAverage};
use crate::helpers::Peekable;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RMA {
alpha: ValueType,
alpha_rev: ValueType,
prev_value: ValueType,
}
pub type MMA = RMA;
pub type SMMA = RMA;
impl Method for RMA {
type Params = PeriodType;
type Input = ValueType;
type Output = Self::Input;
fn new(length: Self::Params, &value: &Self::Input) -> Result<Self, Error> {
match length {
0 => Err(Error::WrongMethodParameters),
length => {
let alpha = (length as ValueType).recip();
Ok(Self {
alpha,
alpha_rev: 1. - alpha,
prev_value: value,
})
}
}
}
#[inline]
fn next(&mut self, &value: &Self::Input) -> Self::Output {
let value = self.alpha.mul_add(value, self.alpha_rev * self.prev_value);
self.prev_value = value;
value
}
}
impl MovingAverage for RMA {}
impl Peekable<<Self as Method>::Output> for RMA {
fn peek(&self) -> <Self as Method>::Output {
self.prev_value
}
}
#[cfg(test)]
#[allow(clippy::suboptimal_flops)]
mod tests {
use super::{Method, RMA as TestingMethod};
use crate::core::ValueType;
use crate::helpers::{assert_eq_float, RandomCandles};
#[test]
fn test_rma_const() {
use crate::methods::tests::test_const_float;
for i in 1..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_rma1() {
let mut candles = RandomCandles::default();
let mut ma = TestingMethod::new(1, &candles.first().close).unwrap();
candles.take(100).for_each(|x| {
assert_eq_float(x.close, ma.next(&x.close));
});
}
#[test]
fn test_rma() {
let candles = RandomCandles::default();
let src: Vec<ValueType> = candles.take(300).map(|x| x.close).collect();
(1..255).for_each(|length| {
let mut ma = TestingMethod::new(length, &src[0]).unwrap();
let mut value2 = src[0];
for &x in &src {
let value = ma.next(&x);
value2 = (x + (length - 1) as ValueType * value2) / (length as ValueType);
assert_eq_float(value2, value);
}
});
}
}