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