Skip to main content

quantwave_core/indicators/patterns/
cdlspinningtop.rs

1//! Native O(1) streaming CDLSPINNINGTOP (matches `talib_rs::pattern::cdl_spinningtop`).
2//! Source: TA-Lib
3
4use crate::indicators::patterns::candle_settings::{
5    BODY_SHORT, CandleWindow, RollingCandleAvg, candle_color, lower_shadow, real_body, upper_shadow,
6};
7use crate::traits::Next;
8
9const LOOKBACK: usize = 10;
10
11#[derive(Debug, Clone)]
12#[allow(non_camel_case_types)]
13pub struct CDLSPINNINGTOP {
14    bars_seen: usize,
15    window: CandleWindow,
16    body_short: RollingCandleAvg,
17}
18
19impl CDLSPINNINGTOP {
20    pub fn new() -> Self {
21        Self {
22            bars_seen: 0,
23            window: CandleWindow::new(1),
24            body_short: RollingCandleAvg::new(BODY_SHORT),
25        }
26    }
27}
28
29impl Default for CDLSPINNINGTOP {
30    fn default() -> Self {
31        Self::new()
32    }
33}
34
35impl Next<(f64, f64, f64, f64)> for CDLSPINNINGTOP {
36    type Output = f64;
37
38    fn next(&mut self, (open, high, low, close): (f64, f64, f64, f64)) -> Self::Output {
39        self.bars_seen += 1;
40        self.window.push(open, high, low, close);
41        self.body_short.push(open, high, low, close);
42
43        if self.bars_seen <= LOOKBACK {
44            return 0.0;
45        }
46
47        let curr = self.window.bar(0);
48
49        let out = (real_body(curr.open, curr.close) < self.body_short.val(0)
50            && upper_shadow(curr.open, curr.high, curr.close) > real_body(curr.open, curr.close)
51            && lower_shadow(curr.open, curr.low, curr.close) > real_body(curr.open, curr.close))
52            as i32
53            * candle_color(curr.open, curr.close)
54            * 100;
55
56        out as f64
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    crate::test_pattern_parity!(
64        test_cdlspinningtop_parity,
65        CDLSPINNINGTOP,
66        talib_rs::pattern::cdl_spinningtop
67    );
68}