wickra_core/indicators/
high_wave.rs1use crate::ohlcv::Candle;
4use crate::traits::Indicator;
5
6#[derive(Debug, Clone, Default)]
40pub struct HighWave {
41 has_emitted: bool,
42}
43
44impl HighWave {
45 pub const fn new() -> Self {
47 Self { has_emitted: false }
48 }
49}
50
51impl Indicator for HighWave {
52 type Input = Candle;
53 type Output = f64;
54
55 fn update(&mut self, candle: Candle) -> Option<f64> {
56 self.has_emitted = true;
57 let range = candle.high - candle.low;
58 if range <= 0.0 {
59 return Some(0.0);
60 }
61 let upper = candle.high - candle.open.max(candle.close);
62 let lower = candle.open.min(candle.close) - candle.low;
63 if upper >= 0.4 * range && lower >= 0.4 * range {
64 return Some(1.0);
65 }
66 Some(0.0)
67 }
68
69 fn reset(&mut self) {
70 self.has_emitted = false;
71 }
72
73 fn warmup_period(&self) -> usize {
74 1
75 }
76
77 fn is_ready(&self) -> bool {
78 self.has_emitted
79 }
80
81 fn name(&self) -> &'static str {
82 "HighWave"
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use crate::traits::BatchExt;
90
91 fn c(open: f64, high: f64, low: f64, close: f64, ts: i64) -> Candle {
92 Candle::new(open, high, low, close, 1.0, ts).unwrap()
93 }
94
95 #[test]
96 fn accessors_and_metadata() {
97 let t = HighWave::new();
98 assert_eq!(t.name(), "HighWave");
99 assert_eq!(t.warmup_period(), 1);
100 assert!(!t.is_ready());
101 }
102
103 #[test]
104 fn high_wave_is_plus_one() {
105 let mut t = HighWave::new();
106 assert_eq!(t.update(c(10.0, 12.0, 8.0, 10.3, 0)), Some(1.0));
107 }
108
109 #[test]
110 fn short_upper_shadow_yields_zero() {
111 let mut t = HighWave::new();
112 assert_eq!(t.update(c(11.5, 12.0, 8.0, 11.7, 0)), Some(0.0));
114 }
115
116 #[test]
117 fn short_lower_shadow_yields_zero() {
118 let mut t = HighWave::new();
119 assert_eq!(t.update(c(8.3, 12.0, 8.0, 8.5, 0)), Some(0.0));
121 }
122
123 #[test]
124 fn big_body_yields_zero() {
125 let mut t = HighWave::new();
126 assert_eq!(t.update(c(8.5, 12.0, 8.0, 11.5, 0)), Some(0.0));
128 }
129
130 #[test]
131 fn zero_range_yields_zero() {
132 let mut t = HighWave::new();
133 assert_eq!(t.update(c(10.0, 10.0, 10.0, 10.0, 0)), Some(0.0));
134 }
135
136 #[test]
137 fn batch_equals_streaming() {
138 let candles: Vec<Candle> = (0..40)
139 .map(|i| {
140 let base = 100.0 + i as f64;
141 c(base, base + 3.0, base - 3.0, base + 0.2, i)
142 })
143 .collect();
144 let mut a = HighWave::new();
145 let mut b = HighWave::new();
146 assert_eq!(
147 a.batch(&candles),
148 candles.iter().map(|x| b.update(*x)).collect::<Vec<_>>()
149 );
150 }
151
152 #[test]
153 fn reset_clears_state() {
154 let mut t = HighWave::new();
155 t.update(c(10.0, 12.0, 8.0, 10.3, 0));
156 assert!(t.is_ready());
157 t.reset();
158 assert!(!t.is_ready());
159 }
160}