finlib_ta/indicators/
hull_moving_average.rs1use core::fmt;
2
3use crate::errors::{Result, TaError};
4use crate::indicators::WeightedMovingAverage;
5use crate::{Close, Next, Period, Reset};
6#[cfg(feature = "std")]
7use core::f64;
8#[cfg(not(feature = "std"))]
9use libm::sqrt;
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13#[doc(alias = "HMA")]
36#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
37#[derive(Debug, Clone)]
38pub struct HullMovingAverage {
39 period: usize,
40 short_wma: WeightedMovingAverage,
41 regular_wma: WeightedMovingAverage,
42 wrapping_wma: WeightedMovingAverage,
43}
44
45impl HullMovingAverage {
46 pub fn new(period: usize) -> Result<Self> {
47 match period {
48 0 | 1 => Err(TaError::InvalidParameter),
49 _ => {
50 Ok(Self {
51 period,
52 short_wma: WeightedMovingAverage::new(period / 2)?,
53 regular_wma: WeightedMovingAverage::new(period)?,
54 wrapping_wma: WeightedMovingAverage::new(Self::sqrt(period))?,
55 })
56 }
57 }
58 }
59
60 fn sqrt(period: usize) -> usize {
61 #[cfg(feature = "std")]
62 {
63 return (period as f64).sqrt() as usize;
64 }
65 #[cfg(not(feature = "std"))]
66 {
67 return sqrt(period as f64) as usize;
68 }
69 }
70}
71
72impl Period for HullMovingAverage {
73 fn period(&self) -> usize {
74 self.period
75 }
76}
77
78impl Next<f64> for HullMovingAverage {
79 type Output = f64;
80
81 fn next(&mut self, input: f64) -> Self::Output {
82 let source = (2.0 * self.short_wma.next(input)) - self.regular_wma.next(input);
85 self.wrapping_wma.next(source)
86 }
87}
88
89impl<T: Close> Next<&T> for HullMovingAverage {
90 type Output = f64;
91
92 fn next(&mut self, input: &T) -> Self::Output {
93 self.next(input.close())
94 }
95}
96
97impl Reset for HullMovingAverage {
98 fn reset(&mut self) {
99 self.short_wma.reset();
100 self.regular_wma.reset();
101 self.wrapping_wma.reset();
102 }
103}
104
105impl Default for HullMovingAverage {
106 fn default() -> Self {
107 Self::new(9).unwrap()
108 }
109}
110
111impl fmt::Display for HullMovingAverage {
112 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113 write!(f, "HMA({})", self.period)
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use crate::test_helper::*;
121 use alloc::format;
122
123 test_indicator!(HullMovingAverage);
124
125 #[test]
126 fn test_new() {
127 assert!(HullMovingAverage::new(0).is_err());
128 assert!(HullMovingAverage::new(1).is_err());
129 assert!(HullMovingAverage::new(2).is_ok());
130 assert!(HullMovingAverage::new(9).is_ok());
131 }
132
133 #[test]
134 fn test_next() {
135 let mut hma = HullMovingAverage::new(3).unwrap();
136
137 assert_eq!(round(hma.next(12.0)), 12.0);
138 assert_eq!(round(hma.next(9.0)), 8.0);
139 assert_eq!(round(hma.next(7.0)), 5.5);
140 assert_eq!(round(hma.next(13.0)), 15.667);
141
142 let mut hma = HullMovingAverage::new(3).unwrap();
143 let bar1 = Bar::new().close(8);
144 let bar2 = Bar::new().close(5);
145 assert_eq!(hma.next(&bar1), 8.0);
146 assert_eq!(hma.next(&bar2), 4.0);
147 }
148
149 #[test]
150 fn test_reset() {
151 let mut hma = HullMovingAverage::new(5).unwrap();
152
153 assert_eq!(hma.next(4.0), 4.0);
154 hma.next(10.0);
155 hma.next(15.0);
156 hma.next(20.0);
157 assert_ne!(hma.next(4.0), 4.0);
158
159 hma.reset();
160 assert_eq!(hma.next(4.0), 4.0);
161 }
162
163 #[test]
164 fn test_default() {
165 HullMovingAverage::default();
166 }
167
168 #[test]
169 fn test_display() {
170 let hma = HullMovingAverage::new(7).unwrap();
171 assert_eq!(format!("{}", hma), "HMA(7)");
172 }
173}