wickra_core/indicators/
macd_fix.rs1use crate::error::Result;
4use crate::indicators::macd::{MacdIndicator, MacdOutput};
5use crate::traits::Indicator;
6
7#[derive(Debug, Clone)]
28pub struct MacdFix {
29 inner: MacdIndicator,
30}
31
32impl MacdFix {
33 pub fn new(signal: usize) -> Result<Self> {
38 Ok(Self {
39 inner: MacdIndicator::new(12, 26, signal)?,
40 })
41 }
42
43 pub fn signal_period(&self) -> usize {
45 self.inner.periods().2
46 }
47}
48
49impl Indicator for MacdFix {
50 type Input = f64;
51 type Output = MacdOutput;
52
53 fn update(&mut self, value: f64) -> Option<MacdOutput> {
54 self.inner.update(value)
55 }
56
57 fn reset(&mut self) {
58 self.inner.reset();
59 }
60
61 fn warmup_period(&self) -> usize {
62 self.inner.warmup_period()
63 }
64
65 fn is_ready(&self) -> bool {
66 self.inner.is_ready()
67 }
68
69 fn name(&self) -> &'static str {
70 "MACDFIX"
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use crate::traits::BatchExt;
78
79 #[test]
80 fn rejects_zero_signal() {
81 assert!(MacdFix::new(0).is_err());
82 }
83
84 #[test]
85 fn accessors_report_config() {
86 let m = MacdFix::new(9).unwrap();
87 assert_eq!(m.signal_period(), 9);
88 assert_eq!(m.name(), "MACDFIX");
89 assert!(!m.is_ready());
90 assert_eq!(
91 m.warmup_period(),
92 MacdIndicator::new(12, 26, 9).unwrap().warmup_period()
93 );
94 }
95
96 #[test]
97 fn matches_macd_with_fixed_periods() {
98 let prices: Vec<f64> = (0..80)
99 .map(|i| 100.0 + (f64::from(i) * 0.3).sin() * 5.0)
100 .collect();
101 let fix: Vec<Option<MacdOutput>> = MacdFix::new(9).unwrap().batch(&prices);
102 let classic: Vec<Option<MacdOutput>> =
103 MacdIndicator::new(12, 26, 9).unwrap().batch(&prices);
104 assert_eq!(fix, classic);
105 assert!(fix.iter().any(Option::is_some));
106 }
107
108 #[test]
109 fn reset_clears_state() {
110 let prices: Vec<f64> = (0..80).map(|i| 100.0 + f64::from(i)).collect();
111 let mut m = MacdFix::new(9).unwrap();
112 let _ = m.batch(&prices);
113 assert!(m.is_ready());
114 m.reset();
115 assert!(!m.is_ready());
116 }
117}