velora_ta/patterns/
double.rs1use 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 if prev.close >= prev.open {
38 return None;
39 }
40
41 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 if prev.close <= prev.open {
80 return None;
81 }
82
83 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}