candlestick_rs/
candle_stream.rs

1use crate::{utils::midpoint, CandleStick};
2
3const SERIES_SIZE: usize = 5;
4
5/// The `CandleStream` provides detection capabilities for powerful multi-candle patterns
6///
7/// - **Reversal Patterns**: Engulfing, Harami, Morning/Evening Stars, Doji Stars
8/// - **Continuation Patterns**: Three White Soldiers, Three Black Crows
9/// - **Top/Bottom Formations**: Dark Cloud Cover and other significant reversal signals
10///
11/// These formations often provide stronger trading signals than single-candle patterns,
12/// offering insights into potential trend reversals, continuations, or exhaustion points.
13/// Each pattern detection method includes detailed documentation about market context
14/// and trading significance.
15///
16/// # Examples
17///
18/// ```
19/// use candlestick_rs::{CandleStick, CandleStream};
20///
21/// // Create a new stream and add candles
22/// let candle1 = (100.0, 105.0, 99.0, 104.0, 0.0);
23/// let candle2 = (104.5, 110.0, 104.0, 109.0, 0.0);
24///
25/// let mut stream = CandleStream::new();
26/// stream.push(&candle1).push(&candle2);
27///
28/// // Check for patterns
29/// if stream.is_bullish_engulfing() {
30///     println!("Bullish engulfing pattern detected!");
31/// }
32/// ```
33
34#[derive(Debug)]
35pub struct CandleStream<'s, T> {
36    series: [Option<&'s T>; SERIES_SIZE],
37    idx: usize,
38}
39
40impl<'s, T> CandleStream<'s, T> {
41    /// Returns a new candle series
42    pub fn new() -> Self {
43        Self::default()
44    }
45
46    // Returns the candle at the given index
47    fn at(&self, idx: usize) -> Option<&T> {
48        match idx < SERIES_SIZE {
49            true => self.series[idx],
50            false => None,
51        }
52    }
53
54    // Fetches reference to the current candle
55    fn get(&self) -> Option<&T> {
56        self.at(self.idx.checked_sub(1)?)
57    }
58
59    // Returns the previous candle
60    fn prev(&self, n: usize) -> Option<&T> {
61        self.at(self.idx.checked_sub(n + 1)?)
62    }
63
64    /// Pushes a candle to the series
65    pub fn push(&mut self, candle: &'s T) -> &mut Self {
66        self.series[self.idx % SERIES_SIZE] = Some(candle);
67        self.idx = (self.idx + 1) % SERIES_SIZE;
68        self
69    }
70}
71
72impl<T: CandleStick> CandleStream<'_, T> {
73    /// Identifies a Bullish Doji Star pattern, a potential reversal signal in downtrends.
74    ///
75    /// This two-candle pattern occurs when a bearish candle is followed by a Doji that gaps below
76    /// the prior candle's low. The Doji represents market indecision after a dominant downtrend.
77    ///
78    /// **Trading Significance**:
79    /// - Signals potential exhaustion of selling pressure
80    /// - Often precedes bullish price movements when confirmed
81    /// - Traders typically wait for a third bullish candle before entering long positions
82    /// - Most effective when appearing at support levels or after extended downtrends
83    ///
84    /// # Example
85    /// ```
86    /// use candlestick_rs::CandleStream;
87    /// let prev = (52.0, 52.5, 48.0, 48.5, 0.0);      
88    /// let curr = (47.0, 47.5, 46.8, 47.0, 0.0);
89    /// let mut series = CandleStream::new();
90    /// assert!(series.push(&prev).push(&curr).is_bullish_doji_star());
91    /// ```
92    pub fn is_bullish_doji_star(&self) -> bool {
93        self.get()
94            .zip(self.prev(1))
95            .is_some_and(|(c, p)| p.is_bearish() && c.is_doji() && c.high() < p.low())
96    }
97
98    /// Identifies a Bearish Doji Star pattern, a potential reversal signal in uptrends.
99    ///
100    /// This two-candle pattern occurs when a bullish candle is followed by a Doji that gaps above
101    /// the prior candle's high. The Doji represents market indecision after a dominant uptrend.
102    ///
103    /// **Trading Significance**:
104    /// - Signals potential exhaustion of buying pressure
105    /// - Often precedes bearish price movements when confirmed
106    /// - Traders typically wait for a third bearish candle before entering short positions
107    /// - Most effective when appearing at resistance levels or after extended uptrends
108    ///
109    /// # Example
110    /// ```
111    /// use candlestick_rs::CandleStream;
112    /// let prev = (48.0, 52.5, 47.8, 52.0, 0.0);
113    /// let curr = (52.6, 53.2, 52.6, 52.6, 0.0);
114    /// let mut series = CandleStream::new();
115    /// assert!(series.push(&prev).push(&curr).is_bearish_doji_star());
116    /// ```
117    pub fn is_bearish_doji_star(&self) -> bool {
118        self.get()
119            .zip(self.prev(1))
120            .is_some_and(|(c, p)| p.is_bullish() && c.is_doji() && c.low() > p.high())
121    }
122
123    ///
124    /// Identifies a Bullish Engulfing pattern, a strong reversal signal at the end of downtrends.
125    ///
126    /// This two-candle pattern occurs when a bearish candle is completely engulfed by a larger bullish candle
127    /// (open lower than prior close, close higher than prior open). It shows buyers overwhelmingly defeating sellers.
128    ///
129    /// **Trading Significance**:
130    /// - Indicates strong shift from selling to buying pressure
131    /// - More reliable than single-candle patterns due to the decisive price action
132    /// - Often used as an immediate entry signal, especially when volume increases
133    /// - Higher reliability when occurring at support zones or after extended downtrends
134    ///
135    /// # Example
136    /// ```
137    /// use candlestick_rs::CandleStream;
138    /// let prev = (101.0, 102.0, 99.5, 100.5, 0.0); // bearish: open > close
139    /// let curr = (99.0, 103.0, 98.5, 102.5, 0.0);  // bullish: open < close, engulfs prev body
140    /// let mut series = CandleStream::new();
141    /// assert!(series.push(&prev).push(&curr).is_bullish_engulfing());
142    /// ```
143    pub fn is_bullish_engulfing(&self) -> bool {
144        self.get().zip(self.prev(1)).is_some_and(|(c, p)| {
145            p.is_bearish() && c.is_bullish() && c.open() < p.close() && c.close() > p.open()
146        })
147    }
148
149    /// Identifies a Bearish Engulfing pattern, a strong reversal signal at the end of uptrends.
150    ///
151    /// This two-candle pattern occurs when a bullish candle is completely engulfed by a larger bearish candle
152    /// (open higher than prior close, close lower than prior open). It shows sellers overwhelmingly defeating buyers.
153    ///
154    /// **Trading Significance**:
155    /// - Indicates strong shift from buying to selling pressure
156    /// - More reliable than single-candle patterns due to the decisive price action
157    /// - Often used as an immediate exit signal for longs or entry for shorts
158    /// - Higher reliability when occurring at resistance zones or after extended uptrends
159    ///
160    /// # Example
161    /// ```
162    /// use candlestick_rs::CandleStream;
163    /// let prev = (99.0, 100.5, 98.5, 100.0, 0.0);  // bullish: open < close
164    /// let curr = (101.5, 102.0, 97.0, 98.5, 0.0);  // bearish: open > close, engulfs prev body
165    /// let mut series = CandleStream::new();
166    /// assert!(series.push(&prev).push(&curr).is_bearish_engulfing());
167    /// ```
168    pub fn is_bearish_engulfing(&self) -> bool {
169        self.get().zip(self.prev(1)).is_some_and(|(c, p)| {
170            p.is_bullish() && c.is_bearish() && c.open() > p.close() && c.close() < p.open()
171        })
172    }
173
174    /// Identifies a Bullish Harami pattern, indicating potential reversal or continuation in downtrends.
175    ///
176    /// This two-candle pattern occurs when a small bullish candle is contained within the trading range of a
177    /// preceding larger bearish candle. The Japanese word "harami" means pregnant, describing the visual appearance.
178    ///
179    /// **Trading Significance**:
180    /// - Signals indecision after a bearish move and possible loss of downward momentum
181    /// - Less powerful than engulfing patterns but still a notable reversal signal
182    /// - Traders typically wait for additional confirmation before entering long positions
183    /// - Part of contingent trading strategies where position size increases after confirmation
184    ///
185    /// # Example
186    /// ```
187    /// use candlestick_rs::CandleStream;
188    /// let prev = (129.0, 130.0, 124.0, 125.0, 0.0);
189    /// let curr = (125.2, 127.0, 124.8, 126.5, 0.0);
190    /// let mut series = CandleStream::new();
191    /// assert!(series.push(&prev).push(&curr).is_bullish_harami());
192    /// ```
193    pub fn is_bullish_harami(&self) -> bool {
194        self.get().zip(self.prev(1)).is_some_and(|(c, p)| {
195            p.is_bearish() && c.is_bullish() && c.open() > p.close() && c.close() < p.open()
196        })
197    }
198
199    /// Identifies a Bearish Harami pattern, indicating potential reversal or continuation in uptrends.
200    ///
201    /// This two-candle pattern occurs when a small bearish candle is contained within the trading range of a
202    /// preceding larger bullish candle. The Japanese word "harami" means pregnant, describing the visual appearance.
203    ///
204    /// **Trading Significance**:
205    /// - Signals indecision after a bullish move and possible loss of upward momentum
206    /// - Less powerful than engulfing patterns but still a notable reversal warning
207    /// - Often used to protect profits on long positions or tighten stop losses
208    /// - Sometimes precedes a period of consolidation rather than immediate reversal
209    ///
210    /// # Example
211    /// ```
212    /// use candlestick_rs::CandleStream;
213    /// let prev = (124.0, 129.0, 122.0, 127.0, 0.0);
214    /// let curr = (126.9, 129.7, 125.0, 124.8, 0.0);
215    /// let mut series = CandleStream::new();
216    /// assert!(series.push(&prev).push(&curr).is_bearish_harami());
217    /// ```
218    pub fn is_bearish_harami(&self) -> bool {
219        self.get().zip(self.prev(1)).is_some_and(|(c, p)| {
220            p.is_bullish() && c.is_bearish() && c.open() < p.close() && c.close() > p.open()
221        })
222    }
223
224    /// Identifies a Dark Cloud Cover pattern, a bearish reversal signal in uptrends.
225    ///
226    /// This two-candle pattern occurs when a bearish candle opens above the prior bullish candle's close
227    /// but closes below the midpoint of the prior candle's body. It shows rejection of higher prices.
228    ///
229    /// **Trading Significance**:
230    /// - Signals strong selling pressure after an uptrend
231    /// - More significant when the bearish candle closes deep into the prior bullish candle
232    /// - Often used by traders to exit long positions or initiate short positions
233    /// - Particularly effective when appearing at historical resistance levels
234    ///
235    /// # Example
236    /// ```
237    /// use candlestick_rs::CandleStream;
238    /// let prev = (100.0, 105.0, 99.5, 104.5, 0.0);
239    /// let curr = (105.5, 106.0, 102.0, 101.5, 0.0);
240    /// let mut series = CandleStream::new();
241    /// assert!(series.push(&prev).push(&curr).is_dark_cloud_cover());
242    /// ```
243    pub fn is_dark_cloud_cover(&self) -> bool {
244        self.get().zip(self.prev(1)).is_some_and(|(c, p)| {
245            c.is_bearish()
246                && p.is_bullish()
247                && c.open() > p.close()
248                && c.close() < midpoint(p.open(), p.close())
249        })
250    }
251
252    /// Identifies an Evening Star pattern, a bearish reversal formation at market tops.
253    ///
254    /// This three-candle pattern consists of:
255    /// 1. A strong bullish candle extending the uptrend
256    /// 2. A small-bodied candle showing indecision (star), often with a gap
257    /// 3. A bearish candle closing well into the first candle's body
258    ///
259    /// **Trading Significance**:
260    /// - Represents a complete shift from bullish to bearish sentiment
261    /// - Considered one of the most reliable bearish reversal patterns
262    /// - Traders often exit longs or enter shorts when the third candle confirms
263    /// - Effectiveness increases with the size of the third bearish candle
264    ///
265    /// # Example
266    /// ```
267    /// use candlestick_rs::CandleStream;
268    /// let prev2 = (100.0, 106.0, 99.5, 105.5, 0.0);
269    /// let prev1 = (106.2, 107.0, 105.8, 106.5, 0.0);
270    /// let curr = (105.5, 106.0, 102.0, 101.5, 0.0);
271    /// let mut series = CandleStream::new();
272    /// assert!(series.push(&prev2).push(&prev1).push(&curr).is_evening_star());
273    /// ```
274    pub fn is_evening_star(&self) -> bool {
275        self.get()
276            .zip(self.prev(1))
277            .zip(self.prev(2))
278            .is_some_and(|((c, p1), p2)| {
279                p2.is_bullish()
280                    && (p1.is_doji() || p1.open() < p1.close())
281                    && c.is_bearish()
282                    && c.close() < midpoint(p2.open(), p2.close())
283            })
284    }
285
286    /// Identifies an Evening Star Doji variant, a strong bearish reversal pattern at market tops.
287    ///
288    /// This three-candle pattern is similar to the Evening Star, but the middle candle is specifically
289    /// a Doji (open ≈ close), emphasizing the perfect equilibrium between buyers and sellers before
290    /// bears take control.
291    ///
292    /// **Trading Significance**:
293    /// - Considered stronger than the standard Evening Star due to the Doji's stronger indecision signal
294    /// - Often precedes significant price declines when confirmed by the third candle
295    /// - Used by traders as a high-probability signal to exit long positions
296    /// - Particularly powerful when occurring after an extended uptrend with high momentum
297    ///
298    /// # Example
299    /// ```
300    /// use candlestick_rs::CandleStream;
301    /// let prev2 =  (100.0, 106.0, 99.5, 105.5, 0.0);
302    /// let prev1 =  (106.1, 107.0, 105.8, 106.1, 0.0);
303    /// let curr = (105.0, 105.2, 99.8, 101.0, 0.0);
304    /// let mut series = CandleStream::new();
305    /// assert!(series.push(&prev2).push(&prev1).push(&curr).is_evening_star_doji());
306    /// ```
307    pub fn is_evening_star_doji(&self) -> bool {
308        self.get()
309            .zip(self.prev(1))
310            .zip(self.prev(2))
311            .is_some_and(|((c, p1), p2)| {
312                p2.is_bullish()
313                    && p1.is_doji() & c.is_bearish()
314                    && c.close() < midpoint(p2.open(), p2.close())
315            })
316    }
317
318    /// Identifies a Morning Star pattern, a bullish reversal formation at market bottoms.
319    ///
320    /// This three-candle pattern consists of:
321    /// 1. A strong bearish candle extending the downtrend
322    /// 2. A small-bodied candle showing indecision (star), often with a gap
323    /// 3. A bullish candle closing well into the first candle's body
324    ///
325    /// **Trading Significance**:
326    /// - Represents a complete shift from bearish to bullish sentiment
327    /// - Considered one of the most reliable bullish reversal patterns
328    /// - Traders often enter long positions when the third candle confirms
329    /// - Effectiveness increases with the size of the third bullish candle and supporting volume
330    ///
331    /// # Example
332    /// ```
333    /// use candlestick_rs::CandleStream;
334    /// let prev2 = (52.0, 52.5, 48.0, 48.5, 0.0);
335    /// let prev1 = (48.2, 48.9, 47.5, 48.3, 0.0);
336    /// let curr = (48.7, 51.5, 48.5, 51.2, 0.0);   
337    /// let mut series = CandleStream::new();
338    /// assert!(series.push(&prev2).push(&prev1).push(&curr).is_morning_star());
339    /// ```
340    pub fn is_morning_star(&self) -> bool {
341        self.get()
342            .zip(self.prev(1))
343            .zip(self.prev(2))
344            .is_some_and(|((c, p1), p2)| {
345                p2.is_bearish()
346                    && (p1.is_doji() || p1.open() < p1.close())
347                    && c.is_bullish()
348                    && c.close() > midpoint(p2.open(), p2.close())
349            })
350    }
351
352    /// Identifies a Morning Star Doji variant, a strong bullish reversal pattern at market bottoms.
353    ///
354    /// This three-candle pattern is similar to the Morning Star, but the middle candle is specifically
355    /// a Doji (open ≈ close), emphasizing the perfect equilibrium between buyers and sellers before
356    /// bulls take control.
357    ///
358    /// **Trading Significance**:
359    /// - Considered stronger than the standard Morning Star due to the Doji's stronger indecision signal
360    /// - Often precedes significant price rallies when confirmed by the third candle
361    /// - Used by traders as a high-probability entry point for long positions
362    /// - Particularly powerful when occurring at support levels with increasing volume
363    ///
364    /// # Example
365    /// ```
366    /// use candlestick_rs::CandleStream;
367    /// let prev2 = (52.0, 52.5, 48.0, 48.5, 0.0);
368    /// let prev1 = (48.3, 48.9, 47.5, 48.4, 0.0);
369    /// let curr =  (48.7, 51.5, 48.5, 51.2, 0.0);
370    /// let mut series = CandleStream::new();
371    /// assert!(series.push(&prev2).push(&prev1).push(&curr).is_morning_star_doji());
372    /// ```
373    pub fn is_morning_star_doji(&self) -> bool {
374        self.get()
375            .zip(self.prev(1))
376            .zip(self.prev(2))
377            .is_some_and(|((c, p1), p2)| {
378                p2.is_bearish()
379                    && p1.is_doji()
380                    && c.is_bullish()
381                    && c.close() > midpoint(p2.open(), p2.close())
382            })
383    }
384
385    /// Identifies Three White Soldiers, a powerful bullish reversal or continuation pattern.
386    ///
387    /// This three-candle pattern consists of consecutive bullish candles, each opening within the previous
388    /// candle's body and closing higher, creating a stair-step appearance. Each candle shows progressively
389    /// stronger buying pressure overtaking sellers.
390    ///
391    /// **Trading Significance**:
392    /// - Indicates sustained buying pressure and strong bullish momentum
393    /// - Shows buyers controlling the market over multiple time periods
394    /// - Traders use it to confirm bullish trend reversals or continuations
395    /// - Most reliable when candles have minimal upper shadows (little selling pressure at highs)
396    ///
397    /// # Example
398    /// ```
399    /// use candlestick_rs::CandleStream;
400    /// let prev2 = (48.0, 50.5, 47.8, 50.2, 0.0);
401    /// let prev1 = (50.3, 52.7, 50.1, 52.4, 0.0);
402    /// let curr =  (52.5, 54.8, 52.3, 54.5, 0.0);
403    /// let mut series = CandleStream::new();
404    /// assert!(series.push(&prev2).push(&prev1).push(&curr).is_three_white_soldiers());
405    /// ```
406    pub fn is_three_white_soldiers(&self) -> bool {
407        self.get()
408            .zip(self.prev(1))
409            .zip(self.prev(2))
410            .is_some_and(|((c, p1), p2)| {
411                p2.is_bullish()
412                    && p1.is_bullish()
413                    && p1.open() > p2.close()
414                    && p1.close() > p2.close()
415                    && c.is_bullish()
416                    && c.open() > p1.close()
417                    && c.close() > p1.close()
418            })
419    }
420
421    /// Identifies Three Black Crows, a powerful bearish reversal or continuation pattern.
422    ///
423    /// This three-candle pattern consists of consecutive bearish candles, each opening within the previous
424    /// candle's body and closing lower, creating a downward stair-step appearance. Each candle shows progressively
425    /// stronger selling pressure overtaking buyers.
426    ///
427    /// **Trading Significance**:
428    /// - Indicates sustained selling pressure and strong bearish momentum
429    /// - Shows sellers controlling the market over multiple time periods
430    /// - Traders use it to confirm bearish trend reversals or continuations
431    /// - Most reliable when candles have minimal lower shadows (little buying pressure at lows)
432    ///
433    /// # Example
434    /// ```
435    /// use candlestick_rs::CandleStream;
436    /// let prev2 = (54.0, 54.5, 51.8, 52.2, 0.0);
437    /// let prev1 = (52.0, 52.3, 49.7, 50.4, 0.0);
438    /// let curr =  (50.2, 50.5, 47.9, 48.3, 0.0);
439    /// let mut series = CandleStream::new();
440    /// assert!(series.push(&prev2).push(&prev1).push(&curr).is_three_black_crows());
441    /// ```
442    pub fn is_three_black_crows(&self) -> bool {
443        self.get()
444            .zip(self.prev(1))
445            .zip(self.prev(2))
446            .is_some_and(|((c, p1), p2)| {
447                p2.is_bearish()
448                    && p1.is_bearish()
449                    && p1.open() < p2.close()
450                    && p1.close() < p2.close()
451                    && c.is_bearish()
452                    && c.open() < p1.close()
453                    && c.close() < p1.close()
454            })
455    }
456
457    /// Identifies the Three Inside Up pattern, a bullish reversal.
458    ///
459    /// This three-candle pattern typically appears in a downtrend and signals a potential
460    /// shift from bearish to bullish momentum. This pattern can be seen as a confirmation or
461    /// continuation of a bullish harami.
462    ///
463    /// **Trading Significance**:
464    /// - Suggests that prior selling pressure is weakening
465    /// - Indicates buyers are starting to regain control
466    /// - Often used as an early sign of a bullish reversal after a down move
467    /// - Considerably stronger when combined with support levels, volume confirmation,
468    ///   or higher-timeframe confluence
469    ///
470    /// # Example
471    /// ```
472    /// use candlestick_rs::CandleStream;
473    ///
474    /// let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
475    /// let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
476    /// let curr  = (52.9, 55.0, 52.7, 54.5, 0.0);
477    /// let mut series = CandleStream::new();
478    /// assert!(series.push(&prev2).push(&prev1).push(&curr).is_three_inside_up());
479    /// ```
480    pub fn is_three_inside_up(&self) -> bool {
481        self.get()
482            .zip(self.prev(1))
483            .zip(self.prev(2))
484            .is_some_and(|((c, p1), p2)| {
485                p2.is_bearish()
486                    && p1.is_bullish()
487                    && p1.open() > p2.close()
488                    && p1.close() < p2.open()
489                    && c.is_bullish()
490                    && c.close() > p1.close()
491                    && !c.is_doji()
492            })
493    }
494
495    /// Identifies the Three Inside Down pattern, a bearish reversal.
496    ///
497    /// This three-candle pattern typically appears in an uptrend and signals a potential
498    /// shift from bullish to bearish momentum. This pattern can be seen as a confirmation or
499    /// continuation of a bearish harami.
500    ///
501    /// **Trading Significance**:
502    /// - Suggests that prior buying pressure is weakening
503    /// - Indicates sellers are starting to regain control
504    /// - Often used as an early sign of a bearish reversal after an up move
505    /// - Considerably stronger when combined with resistance levels, volume confirmation,
506    ///   or higher-timeframe confluence
507    ///
508    /// # Example
509    /// ```
510    /// use candlestick_rs::CandleStream;
511    /// let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
512    /// let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
513    /// let curr  = (48.8, 49.0, 47.5, 47.9, 0.0);
514    /// let mut series = CandleStream::new();
515    /// assert!(series.push(&prev2).push(&prev1).push(&curr).is_three_inside_down());
516    /// ```
517    pub fn is_three_inside_down(&self) -> bool {
518        self.get()
519            .zip(self.prev(1))
520            .zip(self.prev(2))
521            .is_some_and(|((c, p1), p2)| {
522                p2.is_bullish()
523                    && p1.is_bearish()
524                    && p1.open() < p2.close()
525                    && p1.close() > p2.open()
526                    && c.is_bearish()
527                    && c.close() < p1.close()
528                    && !c.is_doji()
529            })
530    }
531}
532
533impl<T> Default for CandleStream<'_, T> {
534    fn default() -> Self {
535        Self {
536            series: [const { None }; SERIES_SIZE],
537            idx: 0,
538        }
539    }
540}
541
542#[cfg(test)]
543mod tests {
544    use super::*;
545
546    #[test]
547    fn test_is_three_inside_up() {
548        let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
549        let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
550        let curr = (52.9, 55.0, 52.7, 54.5, 0.0);
551
552        let mut series = CandleStream::new();
553
554        assert!(series
555            .push(&prev2)
556            .push(&prev1)
557            .push(&curr)
558            .is_three_inside_up());
559    }
560
561    #[test]
562    fn test_is_three_inside_up_if_curr_engulfs_prev1() {
563        let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
564        let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
565        let curr_engulf_prev1 = (52.0, 55.0, 51.9, 53.5, 0.0);
566
567        let mut series = CandleStream::new();
568
569        assert!(series
570            .push(&prev2)
571            .push(&prev1)
572            .push(&curr_engulf_prev1)
573            .is_three_inside_up());
574    }
575
576    #[test]
577    fn test_is_not_three_inside_up_if_curr_is_doji() {
578        let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
579        let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
580        let doji = (53.4, 55.0, 52.7, 53.5, 0.0);
581
582        let mut series = CandleStream::new();
583
584        assert!(!series
585            .push(&prev2)
586            .push(&prev1)
587            .push(&doji)
588            .is_three_inside_up());
589    }
590
591    #[test]
592    fn test_is_not_three_inside_up_if_prev2_not_bearish() {
593        let not_bearish_prev2 = (52.0, 54.5, 51.8, 54.0, 0.0);
594        let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
595        let curr = (52.9, 55.0, 52.7, 54.5, 0.0); // valid curr
596
597        let mut series = CandleStream::new();
598
599        assert!(!series
600            .push(&not_bearish_prev2)
601            .push(&prev1)
602            .push(&curr)
603            .is_three_inside_up());
604    }
605
606    #[test]
607    fn test_is_not_three_inside_up_if_prev2_is_doji() {
608        let doji_prev2 = (53.0, 54.5, 51.8, 53.0, 0.0);
609        let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
610        let curr = (52.9, 55.0, 52.7, 54.5, 0.0);
611
612        let mut series = CandleStream::new();
613
614        assert!(!series
615            .push(&doji_prev2)
616            .push(&prev1)
617            .push(&curr)
618            .is_three_inside_up());
619    }
620
621    #[test]
622    fn test_is_not_three_inside_up_if_prev1_not_bullish() {
623        let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
624        let not_bullish_prev1 = (52.8, 53.0, 52.0, 52.2, 0.0); // open > close
625        let curr = (52.9, 55.0, 52.7, 54.5, 0.0);
626
627        let mut series = CandleStream::new();
628
629        assert!(!series
630            .push(&prev2)
631            .push(&not_bullish_prev1)
632            .push(&curr)
633            .is_three_inside_up());
634    }
635
636    #[test]
637    fn test_is_not_three_inside_up_if_prev1_opens_below_prev2_close() {
638        let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
639        let prev1_open_below_prev2 = (51.9, 53.0, 51.8, 52.5, 0.0);
640        let curr = (52.9, 55.0, 52.7, 54.5, 0.0);
641
642        let mut series = CandleStream::new();
643
644        assert!(!series
645            .push(&prev2)
646            .push(&prev1_open_below_prev2)
647            .push(&curr)
648            .is_three_inside_up());
649    }
650
651    #[test]
652    fn test_is_not_three_inside_up_if_prev1_closes_above_prev2_open() {
653        let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
654        let prev1_close_above_prev2 = (52.2, 55.0, 52.0, 54.5, 0.0);
655        let curr = (54.6, 56.0, 52.7, 55.0, 0.0);
656
657        let mut series = CandleStream::new();
658
659        assert!(!series
660            .push(&prev2)
661            .push(&prev1_close_above_prev2)
662            .push(&curr)
663            .is_three_inside_up());
664    }
665
666    #[test]
667    fn test_is_not_three_inside_up_if_prev1_engulfs_prev2() {
668        let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
669        let prev1_engulf_prev2 = (51.5, 55.0, 51.0, 54.5, 0.0);
670        let curr = (54.6, 56.0, 53.5, 55.5, 0.0);
671
672        let mut series = CandleStream::new();
673
674        assert!(!series
675            .push(&prev2)
676            .push(&prev1_engulf_prev2)
677            .push(&curr)
678            .is_three_inside_up());
679    }
680
681    #[test]
682    fn test_is_not_three_inside_up_if_prev1_is_doji() {
683        let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
684        let doji_prev1 = (52.8, 53.0, 52.0, 52.8, 0.0);
685        let curr = (52.9, 55.0, 52.7, 54.5, 0.0);
686
687        let mut series = CandleStream::new();
688
689        assert!(!series
690            .push(&prev2)
691            .push(&doji_prev1)
692            .push(&curr)
693            .is_three_inside_up());
694    }
695
696    #[test]
697    fn test_is_not_three_inside_up_if_curr_is_inside_prev1() {
698        let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
699        let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
700        let curr_inside_prev1 = (52.3, 53.1, 52.1, 52.7, 0.0);
701
702        let mut series = CandleStream::new();
703
704        assert!(!series
705            .push(&prev2)
706            .push(&prev1)
707            .push(&curr_inside_prev1)
708            .is_three_inside_up());
709    }
710
711    #[test]
712    fn test_is_not_three_inside_up_if_curr_not_bullish() {
713        let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
714        let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
715        let not_bullish_curr = (55.0, 55.5, 52.7, 53.0, 0.0);
716
717        let mut series = CandleStream::new();
718
719        assert!(!series
720            .push(&prev2)
721            .push(&prev1)
722            .push(&not_bullish_curr)
723            .is_three_inside_up());
724    }
725
726    #[test]
727    fn test_is_not_three_inside_up_with_insufficient_candles() {
728        let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
729        let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
730
731        let mut series = CandleStream::new();
732
733        assert!(!series.push(&prev2).is_three_inside_up());
734        assert!(!series.push(&prev1).is_three_inside_up());
735    }
736
737    #[test]
738    fn test_is_three_inside_down() {
739        let prev2: (f64, f64, f64, f64, f64) = (48.0, 50.5, 47.8, 50.0, 0.0);
740        let prev1: (f64, f64, f64, f64, f64) = (49.5, 49.8, 48.5, 49.0, 0.0);
741        let curr: (f64, f64, f64, f64, f64) = (48.8, 49.0, 47.5, 47.9, 0.0);
742
743        let mut series: CandleStream<'_, (f64, f64, f64, f64, f64)> = CandleStream::new();
744
745        assert!(series
746            .push(&prev2)
747            .push(&prev1)
748            .push(&curr)
749            .is_three_inside_down());
750    }
751
752    #[test]
753    fn test_is_three_inside_down_if_curr_engulfs_prev1() {
754        let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
755        let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
756        let curr_engulf_prev1 = (49.8, 50.0, 47.5, 48.8, 0.0); // open > prev1.open, close < prev1.close
757
758        let mut series = CandleStream::new();
759
760        assert!(series
761            .push(&prev2)
762            .push(&prev1)
763            .push(&curr_engulf_prev1)
764            .is_three_inside_down());
765    }
766
767    #[test]
768    fn test_is_not_three_inside_down_if_curr_is_doji() {
769        let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
770        let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
771        let doji = (48.5, 50.0, 47.5, 48.5, 0.0); // open == close
772
773        let mut series = CandleStream::new();
774
775        assert!(!series
776            .push(&prev2)
777            .push(&prev1)
778            .push(&doji)
779            .is_three_inside_down());
780    }
781
782    #[test]
783    fn test_is_not_three_inside_down_if_prev2_not_bullish() {
784        let not_bullish_prev2 = (50.0, 50.5, 47.8, 48.0, 0.0); // bearish instead of bullish
785        let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
786        let curr = (48.8, 49.0, 47.5, 47.9, 0.0); // valid curr
787
788        let mut series = CandleStream::new();
789
790        assert!(!series
791            .push(&not_bullish_prev2)
792            .push(&prev1)
793            .push(&curr)
794            .is_three_inside_down());
795    }
796
797    #[test]
798    fn test_is_not_three_inside_down_if_prev2_is_doji() {
799        let doji_prev2 = (49.0, 50.5, 47.8, 49.0, 0.0); // open == close
800        let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
801        let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
802
803        let mut series = CandleStream::new();
804
805        assert!(!series
806            .push(&doji_prev2)
807            .push(&prev1)
808            .push(&curr)
809            .is_three_inside_down());
810    }
811
812    #[test]
813    fn test_is_not_three_inside_down_if_prev1_not_bearish() {
814        let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
815        let not_bearish_prev1 = (48.5, 49.5, 48.0, 49.2, 0.0); // open < close
816        let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
817
818        let mut series = CandleStream::new();
819
820        assert!(!series
821            .push(&prev2)
822            .push(&not_bearish_prev1)
823            .push(&curr)
824            .is_three_inside_down());
825    }
826
827    #[test]
828    fn test_is_not_three_inside_down_if_prev1_opens_above_prev2_close() {
829        let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
830        let prev1_open_above_prev2 = (50.2, 50.5, 48.5, 49.5, 0.0);
831        let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
832
833        let mut series = CandleStream::new();
834
835        assert!(!series
836            .push(&prev2)
837            .push(&prev1_open_above_prev2)
838            .push(&curr)
839            .is_three_inside_down());
840    }
841
842    #[test]
843    fn test_is_not_three_inside_down_if_prev1_closes_below_prev2_open() {
844        let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
845        let prev1_close_below_prev2 = (49.5, 49.8, 47.5, 47.9, 0.0); // close < 48.0
846        let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
847
848        let mut series = CandleStream::new();
849
850        assert!(!series
851            .push(&prev2)
852            .push(&prev1_close_below_prev2)
853            .push(&curr)
854            .is_three_inside_down());
855    }
856
857    #[test]
858    fn test_is_not_three_inside_down_if_prev1_engulfs_prev2() {
859        let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0); // body [48.0, 50.0]
860        let prev1_engulf_prev2 = (50.5, 51.0, 47.0, 47.5, 0.0); // open > 50.0, close < 48.0
861        let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
862
863        let mut series = CandleStream::new();
864
865        assert!(!series
866            .push(&prev2)
867            .push(&prev1_engulf_prev2)
868            .push(&curr)
869            .is_three_inside_down());
870    }
871
872    #[test]
873    fn test_is_not_three_inside_down_if_prev1_is_doji() {
874        let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
875        let doji_prev1 = (49.0, 49.5, 48.5, 49.0, 0.0); // open == close
876        let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
877
878        let mut series = CandleStream::new();
879
880        assert!(!series
881            .push(&prev2)
882            .push(&doji_prev1)
883            .push(&curr)
884            .is_three_inside_down());
885    }
886
887    #[test]
888    fn test_is_not_three_inside_down_if_curr_is_inside_prev1() {
889        let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
890        let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0); // body [49.0, 49.5]
891        let curr_inside_prev1 = (49.4, 49.6, 48.8, 49.1, 0.0); // close 49.1 > 49.0
892
893        let mut series = CandleStream::new();
894
895        assert!(!series
896            .push(&prev2)
897            .push(&prev1)
898            .push(&curr_inside_prev1)
899            .is_three_inside_down());
900    }
901
902    #[test]
903    fn test_is_not_three_inside_down_if_curr_not_bearish() {
904        let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
905        let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
906        let not_bearish_curr = (47.8, 48.5, 47.5, 48.6, 0.0); // bullish
907
908        let mut series = CandleStream::new();
909
910        assert!(!series
911            .push(&prev2)
912            .push(&prev1)
913            .push(&not_bearish_curr)
914            .is_three_inside_down());
915    }
916
917    #[test]
918    fn test_is_not_three_inside_down_with_insufficient_candles() {
919        let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
920        let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
921
922        let mut series = CandleStream::new();
923
924        assert!(!series.push(&prev2).is_three_inside_down());
925        assert!(!series.push(&prev1).is_three_inside_down());
926    }
927}