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(¬_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(¬_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(¬_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(¬_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(¬_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(¬_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}