yata/methods/
mean_abs_dev.rs1use crate::core::Method;
2use crate::core::{Error, PeriodType, ValueType};
3use crate::helpers::Peekable;
4use crate::methods::SMA;
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33pub struct MeanAbsDev(SMA);
34
35impl MeanAbsDev {
36	#[must_use]
38	pub const fn get_sma(&self) -> &SMA {
39		&self.0
40	}
41}
42
43impl Method for MeanAbsDev {
44	type Params = PeriodType;
45	type Input = ValueType;
46	type Output = Self::Input;
47
48	fn new(length: Self::Params, value: &Self::Input) -> Result<Self, Error> {
49		match length {
50			0 => Err(Error::WrongMethodParameters),
51			length => Ok(Self(SMA::new(length, value)?)),
52		}
53	}
54
55	#[inline]
56	fn next(&mut self, value: &Self::Input) -> Self::Output {
57		self.0.next(value);
58
59		self.peek()
60	}
61}
62
63impl Peekable<<Self as Method>::Output> for MeanAbsDev {
64	fn peek(&self) -> <Self as Method>::Output {
65		let mean = self.0.peek();
66		self.0
67			.get_window()
68			.as_slice()
69			.iter()
70			.map(|x| x - mean)
71			.map(ValueType::abs)
72			.sum::<ValueType>()
73			* self.0.get_divider()
74	}
75}
76
77#[cfg(test)]
78mod tests {
79	use super::{MeanAbsDev as TestingMethod, Method};
80	use crate::core::ValueType;
81	use crate::helpers::{assert_eq_float, RandomCandles};
82
83	#[test]
84	fn test_mean_abs_dev_const() {
85		for i in 2..255 {
86			let input = (i as ValueType + 56.0) / 16.3251;
87			let mut method = TestingMethod::new(i, &input).unwrap();
88
89			let output = method.next(&input);
90			assert_eq_float(0.0, output);
91		}
92	}
93
94	#[test]
95	fn test_mean_abs_dev1() {
96		let mut candles = RandomCandles::default();
97
98		let mut ma = TestingMethod::new(1, &candles.first().close).unwrap();
99
100		candles.take(100).for_each(|x| {
101			assert_eq_float(0.0, ma.next(&x.close));
102		});
103	}
104
105	#[test]
106	fn test_mean_abs_dev0() {
107		let candles = RandomCandles::default();
108
109		let src: Vec<ValueType> = candles.take(300).map(|x| x.close).collect();
110
111		(2..255).for_each(|length| {
112			let mut method = TestingMethod::new(length, &src[0]).unwrap();
113
114			src.iter().enumerate().for_each(|(i, x)| {
115				let mut sum = 0.0;
116
117				for j in 0..length {
118					sum += src[i.saturating_sub(j as usize)];
119				}
120
121				let sma = sum / length as ValueType;
122
123				let mut sum2 = 0.0;
124				for j in 0..length {
125					sum2 += (sma - src[i.saturating_sub(j as usize)]).abs();
126				}
127
128				let q = sum2 / length as ValueType;
129
130				let value = method.next(x);
131				assert_eq_float(q, value);
132			});
133		});
134	}
135}