velora_ta/patterns/
double.rs

1//! Two-candle patterns
2
3use crate::{
4    patterns::detector::{PatternDetector, PatternSignal},
5    types::OhlcBar,
6};
7
8#[derive(Debug, Clone)]
9pub struct BullishEngulfing;
10
11impl BullishEngulfing {
12    pub fn new() -> Self {
13        Self
14    }
15}
16
17impl Default for BullishEngulfing {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl PatternDetector for BullishEngulfing {
24    fn name(&self) -> &str {
25        "Bullish Engulfing"
26    }
27
28    fn detect(&self, candles: &[OhlcBar]) -> Option<PatternSignal> {
29        if candles.len() < 2 {
30            return None;
31        }
32
33        let prev = &candles[candles.len() - 2];
34        let curr = &candles[candles.len() - 1];
35
36        // Previous candle is bearish
37        if prev.close >= prev.open {
38            return None;
39        }
40
41        // Current candle is bullish and engulfs previous
42        if curr.close > curr.open && curr.open < prev.close && curr.close > prev.open {
43            Some(PatternSignal::Bullish)
44        } else {
45            None
46        }
47    }
48}
49
50#[derive(Debug, Clone)]
51pub struct BearishEngulfing;
52
53impl BearishEngulfing {
54    pub fn new() -> Self {
55        Self
56    }
57}
58
59impl Default for BearishEngulfing {
60    fn default() -> Self {
61        Self::new()
62    }
63}
64
65impl PatternDetector for BearishEngulfing {
66    fn name(&self) -> &str {
67        "Bearish Engulfing"
68    }
69
70    fn detect(&self, candles: &[OhlcBar]) -> Option<PatternSignal> {
71        if candles.len() < 2 {
72            return None;
73        }
74
75        let prev = &candles[candles.len() - 2];
76        let curr = &candles[candles.len() - 1];
77
78        // Previous candle is bullish
79        if prev.close <= prev.open {
80            return None;
81        }
82
83        // Current candle is bearish and engulfs previous
84        if curr.close < curr.open && curr.open > prev.close && curr.close < prev.open {
85            Some(PatternSignal::Bearish)
86        } else {
87            None
88        }
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn test_bullish_engulfing() {
98        let pattern = BullishEngulfing::new();
99        assert_eq!(pattern.name(), "Bullish Engulfing");
100    }
101}