ux_indicators/indicators/
wma.rs1#![allow(dead_code)]
2#![allow(unused_imports)]
3
4use std::fmt;
5
6use rust_decimal_macros::*;
7use rust_decimal::prelude::*;
8use std::collections::VecDeque;
9
10use crate::errors::{ErrorKind, Error};
11use crate::{Close, Next, Reset};
12
13use crate::{Factory};
14use crate::indicators::SimpleMovingAverage;
15
16pub struct WmaFactory {
17}
18
19impl WmaFactory {
20 pub fn new() -> Self {
21 Self{}
22 }
23}
24
25impl Factory for WmaFactory {
26 fn create() -> Box<dyn Next<f64, Output = Box<[f64]>>> {
27 Box::new(SimpleMovingAverage::default())
28 }
29}
30
31#[derive(Debug, Clone)]
63pub struct WeightedMovingAverage {
64 period: u32,
65 index: usize,
66 count: u32,
67 sum: Decimal, weight_sum: Decimal, vec: VecDeque<f64>,
70}
71
72impl WeightedMovingAverage {
73 pub fn new(period: u32) -> Result<WeightedMovingAverage, Error> {
75 match period {
76 _ => {
78 let indicator = WeightedMovingAverage {
79 period: period,
80 index: 0,
81 count: 0,
82 sum: Decimal::zero(),
83 weight_sum: Decimal::zero(),
84 vec: VecDeque::with_capacity(period as usize),
85 };
86 Ok(indicator)
87 }
88 }
89 }
90}
91
92impl Next<f64> for WeightedMovingAverage {
93 type Output = f64;
94
95 fn next(&mut self, input: f64) -> Self::Output {
96 if self.count < self.period - 1 {
103 self.count += 1;
104 self.vec.push_back(input);
105 self.weight_sum += Decimal::from_f64(input).unwrap() * Decimal::from_u32(self.count).unwrap();
106 self.sum += Decimal::from_f64(input).unwrap();
107 return f64::INFINITY;
108 }
109
110 let weights: Decimal = Decimal::from_u32(self.period).unwrap() * (Decimal::from_u32(self.period).unwrap() + Decimal::from_u32(1).unwrap()) / Decimal::from_u32(2).unwrap();
111
112 self.weight_sum += Decimal::from_f64(input).unwrap() * Decimal::from_u32(self.period).unwrap();
114 self.sum += Decimal::from_f64(input).unwrap();
115
116 let output: Decimal = self.weight_sum / weights;
117
118 self.vec.push_back(input);
119 self.weight_sum -= self.sum;
120 self.sum -= Decimal::from_f64(self.vec.pop_front().unwrap()).unwrap();
121
122 output.round_dp_with_strategy(3, RoundingStrategy::RoundHalfUp).to_f64().unwrap()
123 }
124}
125
126impl<'a, T: Close> Next<&'a T> for WeightedMovingAverage {
127 type Output = f64;
128
129 fn next(&mut self, input: &'a T) -> Self::Output {
130 self.next(input.close())
131 }
132}
133
134impl Reset for WeightedMovingAverage {
135
136 fn reset(&mut self) {
137 self.index = 0;
138 self.count = 0;
139 self.sum = Decimal::from_f64(0.0).unwrap();
140 for idx in 0..(self.period as usize) {
141 self.vec[idx] = 0.0;
142 }
143 }
144}
145
146impl Default for WeightedMovingAverage {
147 fn default() -> Self {
148 Self::new(9).unwrap()
149 }
150}
151
152impl fmt::Display for WeightedMovingAverage {
153 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154 write!(f, "WMA({})", self.period)
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use crate::test_helper::*;
162
163 #[test]
166 fn test_new() {
167 initialize();
168 assert!(WeightedMovingAverage::new(1).is_ok());
170 }
171
172 #[test]
173 fn test_next() {
174 initialize();
175 let _params = &TESTS["wma"];
176 println!("");
177 for _test in _params.tests.iter() {
178 let _period = _test.options[0];
179 let _input = &_test.inputs[0];
180 let _output = &_test.outputs[0];
181
182 println!("WMA WITH PERIOD {}", _period);
183 let mut indicator = WeightedMovingAverage::new(_period as u32).unwrap();
184 for val in _input.iter() {
185 let res = indicator.next(*val);
186 println!("INPUT {} OUTPUT {}", val, res);
187 }
188 }
189 }
198
199 #[test]
224 fn test_default() {
225 WeightedMovingAverage::default();
226 }
227
228 #[test]
229 fn test_display() {
230 let indicator = WeightedMovingAverage::new(5).unwrap();
231 assert_eq!(format!("{}", indicator), "WMA(5)");
232 }
233}