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