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<T> {
36 series: [Option<T>; SERIES_SIZE],
37 idx: usize,
38}
39
40impl<T> CandleStream<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].as_ref(),
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: 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 /// Identifies a downtrend from the last 3 candles.
542 /// A downtrend is defined as a series of strictly lower highs and lower lows.
543 pub fn is_downtrend(&self) -> bool {
544 match (self.get(), self.prev(1), self.prev(2)) {
545 (Some(c0), Some(c1), Some(c2)) => {
546 (c0.high() < c1.high() && c1.high() < c2.high())
547 && (c0.low() < c1.low() && c1.low() < c2.low())
548 }
549 _ => false,
550 }
551 }
552
553 /// Identifies an uptrend from the last 3 candles.
554 /// An uptrend is defined as a series of strictly higher highs and higher lows.
555 pub fn is_uptrend(&self) -> bool {
556 match (self.get(), self.prev(1), self.prev(2)) {
557 (Some(c0), Some(c1), Some(c2)) => {
558 (c0.high() > c1.high() && c1.high() > c2.high())
559 && (c0.low() > c1.low() && c1.low() > c2.low())
560 }
561 _ => false,
562 }
563 }
564}
565
566impl<T> Default for CandleStream<T> {
567 fn default() -> Self {
568 Self {
569 series: [const { None }; SERIES_SIZE],
570 idx: 0,
571 }
572 }
573}
574
575#[cfg(test)]
576mod tests {
577 use super::*;
578
579 #[test]
580 fn test_nth_index() {
581 let candle1 = (100.0, 105.0, 99.0, 104.0, 0.0);
582 let candle2 = (104.5, 110.0, 104.0, 109.0, 0.0);
583 let candle3 = (109.5, 112.0, 108.0, 111.0, 0.0);
584 let candle4 = (111.5, 115.0, 110.0, 114.0, 0.0);
585 let candle5 = (114.5, 118.0, 113.0, 117.0, 0.0);
586 let candle6 = (117.5, 120.0, 116.0, 119.0, 0.0);
587
588 let mut stream = CandleStream::new();
589
590 assert_eq!(stream.nth_index(6), None);
591
592 stream.push(&candle1).push(&candle2);
593
594 assert_eq!(stream.nth_index(0), Some(2));
595 assert_eq!(stream.nth_index(1), Some(1));
596 assert_eq!(stream.nth_index(2), Some(0));
597
598 stream.push(&candle3).push(&candle4).push(&candle5);
599
600 assert_eq!(stream.nth_index(0), Some(0));
601 assert_eq!(stream.nth_index(1), Some(4));
602 assert_eq!(stream.nth_index(2), Some(3));
603 assert_eq!(stream.nth_index(3), Some(2));
604 assert_eq!(stream.nth_index(4), Some(1));
605 assert_eq!(stream.nth_index(5), Some(0));
606
607 stream.push(&candle6);
608
609 assert_eq!(stream.nth_index(0), Some(1));
610 assert_eq!(stream.nth_index(1), Some(0));
611 assert_eq!(stream.nth_index(2), Some(4));
612 assert_eq!(stream.nth_index(3), Some(3));
613 assert_eq!(stream.nth_index(4), Some(2));
614 assert_eq!(stream.nth_index(5), Some(1));
615 }
616
617 #[test]
618 fn test_at() {
619 let candle1 = (100.0, 105.0, 99.0, 104.0, 0.0);
620 let candle2 = (104.5, 110.0, 104.0, 109.0, 0.0);
621 let candle3 = (109.5, 112.0, 108.0, 111.0, 0.0);
622 let candle4 = (111.5, 115.0, 110.0, 114.0, 0.0);
623 let candle5 = (114.5, 118.0, 113.0, 117.0, 0.0);
624 let candle6 = (117.5, 120.0, 116.0, 119.0, 0.0);
625
626 let mut stream = CandleStream::new();
627 stream.push(candle1).push(candle2);
628
629 assert_eq!(stream.at(0), Some(&candle1));
630 assert_eq!(stream.at(1), Some(&candle2));
631 assert_eq!(stream.at(2), None);
632
633 stream.push(candle3).push(candle4).push(candle5);
634
635 assert_eq!(stream.at(0), Some(&candle1));
636 assert_eq!(stream.at(1), Some(&candle2));
637 assert_eq!(stream.at(2), Some(&candle3));
638 assert_eq!(stream.at(3), Some(&candle4));
639 assert_eq!(stream.at(4), Some(&candle5));
640
641 stream.push(candle6);
642
643 assert_eq!(stream.at(0), Some(&candle6));
644 assert_eq!(stream.at(1), Some(&candle2));
645 assert_eq!(stream.at(2), Some(&candle3));
646 assert_eq!(stream.at(3), Some(&candle4));
647 assert_eq!(stream.at(4), Some(&candle5));
648 }
649
650 #[test]
651 fn test_get() {
652 let candle1 = (100.0, 105.0, 99.0, 104.0, 0.0);
653 let candle2 = (104.5, 110.0, 104.0, 109.0, 0.0);
654 let candle3 = (109.5, 112.0, 108.0, 111.0, 0.0);
655
656 let mut stream = CandleStream::new();
657 assert_eq!(stream.get(), None);
658
659 stream.push(candle1);
660 assert_eq!(stream.get(), Some(&candle1));
661
662 stream.push(candle2);
663 assert_eq!(stream.get(), Some(&candle2));
664
665 stream.push(candle3).push(candle1).push(candle2);
666 assert_eq!(stream.get(), Some(&candle2));
667
668 stream.push(candle3);
669 assert_eq!(stream.get(), Some(&candle3));
670 }
671
672 #[test]
673 fn test_prev() {
674 let candle1 = (100.0, 105.0, 99.0, 104.0, 0.0);
675 let candle2 = (104.5, 110.0, 104.0, 109.0, 0.0);
676 let candle3 = (109.5, 112.0, 108.0, 111.0, 0.0);
677
678 let mut stream = CandleStream::new();
679 assert_eq!(stream.prev(1), None);
680
681 stream.push(candle1);
682 assert_eq!(stream.prev(1), None);
683
684 stream.push(candle2);
685 assert_eq!(stream.prev(1), Some(&candle1));
686
687 stream.push(candle3);
688 assert_eq!(stream.prev(1), Some(&candle2));
689 assert_eq!(stream.prev(2), Some(&candle1));
690 }
691
692 #[test]
693 fn test_is_three_inside_up() {
694 let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
695 let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
696 let curr = (52.9, 55.0, 52.7, 54.5, 0.0);
697
698 let mut series = CandleStream::new();
699
700 assert!(series
701 .push(&prev2)
702 .push(&prev1)
703 .push(&curr)
704 .is_three_inside_up());
705 }
706
707 #[test]
708 fn test_is_three_inside_up_if_curr_engulfs_prev1() {
709 let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
710 let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
711 let curr_engulf_prev1 = (52.0, 55.0, 51.9, 53.5, 0.0);
712
713 let mut series = CandleStream::new();
714
715 assert!(series
716 .push(&prev2)
717 .push(&prev1)
718 .push(&curr_engulf_prev1)
719 .is_three_inside_up());
720 }
721
722 #[test]
723 fn test_is_not_three_inside_up_if_curr_is_doji() {
724 let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
725 let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
726 let doji = (53.4, 55.0, 52.7, 53.5, 0.0);
727
728 let mut series = CandleStream::new();
729
730 assert!(!series
731 .push(&prev2)
732 .push(&prev1)
733 .push(&doji)
734 .is_three_inside_up());
735 }
736
737 #[test]
738 fn test_is_not_three_inside_up_if_prev2_not_bearish() {
739 let not_bearish_prev2 = (52.0, 54.5, 51.8, 54.0, 0.0);
740 let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
741 let curr = (52.9, 55.0, 52.7, 54.5, 0.0); // valid curr
742
743 let mut series = CandleStream::new();
744
745 assert!(!series
746 .push(¬_bearish_prev2)
747 .push(&prev1)
748 .push(&curr)
749 .is_three_inside_up());
750 }
751
752 #[test]
753 fn test_is_not_three_inside_up_if_prev2_is_doji() {
754 let doji_prev2 = (53.0, 54.5, 51.8, 53.0, 0.0);
755 let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
756 let curr = (52.9, 55.0, 52.7, 54.5, 0.0);
757
758 let mut series = CandleStream::new();
759
760 assert!(!series
761 .push(&doji_prev2)
762 .push(&prev1)
763 .push(&curr)
764 .is_three_inside_up());
765 }
766
767 #[test]
768 fn test_is_not_three_inside_up_if_prev1_not_bullish() {
769 let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
770 let not_bullish_prev1 = (52.8, 53.0, 52.0, 52.2, 0.0); // open > close
771 let curr = (52.9, 55.0, 52.7, 54.5, 0.0);
772
773 let mut series = CandleStream::new();
774
775 assert!(!series
776 .push(&prev2)
777 .push(¬_bullish_prev1)
778 .push(&curr)
779 .is_three_inside_up());
780 }
781
782 #[test]
783 fn test_is_not_three_inside_up_if_prev1_opens_below_prev2_close() {
784 let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
785 let prev1_open_below_prev2 = (51.9, 53.0, 51.8, 52.5, 0.0);
786 let curr = (52.9, 55.0, 52.7, 54.5, 0.0);
787
788 let mut series = CandleStream::new();
789
790 assert!(!series
791 .push(&prev2)
792 .push(&prev1_open_below_prev2)
793 .push(&curr)
794 .is_three_inside_up());
795 }
796
797 #[test]
798 fn test_is_not_three_inside_up_if_prev1_closes_above_prev2_open() {
799 let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
800 let prev1_close_above_prev2 = (52.2, 55.0, 52.0, 54.5, 0.0);
801 let curr = (54.6, 56.0, 52.7, 55.0, 0.0);
802
803 let mut series = CandleStream::new();
804
805 assert!(!series
806 .push(&prev2)
807 .push(&prev1_close_above_prev2)
808 .push(&curr)
809 .is_three_inside_up());
810 }
811
812 #[test]
813 fn test_is_not_three_inside_up_if_prev1_engulfs_prev2() {
814 let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
815 let prev1_engulf_prev2 = (51.5, 55.0, 51.0, 54.5, 0.0);
816 let curr = (54.6, 56.0, 53.5, 55.5, 0.0);
817
818 let mut series = CandleStream::new();
819
820 assert!(!series
821 .push(&prev2)
822 .push(&prev1_engulf_prev2)
823 .push(&curr)
824 .is_three_inside_up());
825 }
826
827 #[test]
828 fn test_is_not_three_inside_up_if_prev1_is_doji() {
829 let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
830 let doji_prev1 = (52.8, 53.0, 52.0, 52.8, 0.0);
831 let curr = (52.9, 55.0, 52.7, 54.5, 0.0);
832
833 let mut series = CandleStream::new();
834
835 assert!(!series
836 .push(&prev2)
837 .push(&doji_prev1)
838 .push(&curr)
839 .is_three_inside_up());
840 }
841
842 #[test]
843 fn test_is_not_three_inside_up_if_curr_is_inside_prev1() {
844 let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
845 let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
846 let curr_inside_prev1 = (52.3, 53.1, 52.1, 52.7, 0.0);
847
848 let mut series = CandleStream::new();
849
850 assert!(!series
851 .push(&prev2)
852 .push(&prev1)
853 .push(&curr_inside_prev1)
854 .is_three_inside_up());
855 }
856
857 #[test]
858 fn test_is_not_three_inside_up_if_curr_not_bullish() {
859 let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
860 let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
861 let not_bullish_curr = (55.0, 55.5, 52.7, 53.0, 0.0);
862
863 let mut series = CandleStream::new();
864
865 assert!(!series
866 .push(&prev2)
867 .push(&prev1)
868 .push(¬_bullish_curr)
869 .is_three_inside_up());
870 }
871
872 #[test]
873 fn test_is_not_three_inside_up_with_insufficient_candles() {
874 let prev2 = (54.0, 54.5, 51.8, 52.0, 0.0);
875 let prev1 = (52.2, 53.0, 52.0, 52.8, 0.0);
876
877 let mut series = CandleStream::new();
878
879 assert!(!series.push(&prev2).is_three_inside_up());
880 assert!(!series.push(&prev1).is_three_inside_up());
881 }
882
883 #[test]
884 fn test_is_three_inside_down() {
885 let prev2: (f64, f64, f64, f64, f64) = (48.0, 50.5, 47.8, 50.0, 0.0);
886 let prev1: (f64, f64, f64, f64, f64) = (49.5, 49.8, 48.5, 49.0, 0.0);
887 let curr: (f64, f64, f64, f64, f64) = (48.8, 49.0, 47.5, 47.9, 0.0);
888
889 let mut series: CandleStream<(f64, f64, f64, f64, f64)> = CandleStream::new();
890
891 assert!(series
892 .push(prev2)
893 .push(prev1)
894 .push(curr)
895 .is_three_inside_down());
896 }
897
898 #[test]
899 fn test_is_three_inside_down_if_curr_engulfs_prev1() {
900 let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
901 let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
902 let curr_engulf_prev1 = (49.8, 50.0, 47.5, 48.8, 0.0); // open > prev1.open, close < prev1.close
903
904 let mut series = CandleStream::new();
905
906 assert!(series
907 .push(&prev2)
908 .push(&prev1)
909 .push(&curr_engulf_prev1)
910 .is_three_inside_down());
911 }
912
913 #[test]
914 fn test_is_not_three_inside_down_if_curr_is_doji() {
915 let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
916 let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
917 let doji = (48.5, 50.0, 47.5, 48.5, 0.0); // open == close
918
919 let mut series = CandleStream::new();
920
921 assert!(!series
922 .push(&prev2)
923 .push(&prev1)
924 .push(&doji)
925 .is_three_inside_down());
926 }
927
928 #[test]
929 fn test_is_not_three_inside_down_if_prev2_not_bullish() {
930 let not_bullish_prev2 = (50.0, 50.5, 47.8, 48.0, 0.0); // bearish instead of bullish
931 let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
932 let curr = (48.8, 49.0, 47.5, 47.9, 0.0); // valid curr
933
934 let mut series = CandleStream::new();
935
936 assert!(!series
937 .push(¬_bullish_prev2)
938 .push(&prev1)
939 .push(&curr)
940 .is_three_inside_down());
941 }
942
943 #[test]
944 fn test_is_not_three_inside_down_if_prev2_is_doji() {
945 let doji_prev2 = (49.0, 50.5, 47.8, 49.0, 0.0); // open == close
946 let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
947 let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
948
949 let mut series = CandleStream::new();
950
951 assert!(!series
952 .push(&doji_prev2)
953 .push(&prev1)
954 .push(&curr)
955 .is_three_inside_down());
956 }
957
958 #[test]
959 fn test_is_not_three_inside_down_if_prev1_not_bearish() {
960 let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
961 let not_bearish_prev1 = (48.5, 49.5, 48.0, 49.2, 0.0); // open < close
962 let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
963
964 let mut series = CandleStream::new();
965
966 assert!(!series
967 .push(&prev2)
968 .push(¬_bearish_prev1)
969 .push(&curr)
970 .is_three_inside_down());
971 }
972
973 #[test]
974 fn test_is_not_three_inside_down_if_prev1_opens_above_prev2_close() {
975 let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
976 let prev1_open_above_prev2 = (50.2, 50.5, 48.5, 49.5, 0.0);
977 let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
978
979 let mut series = CandleStream::new();
980
981 assert!(!series
982 .push(&prev2)
983 .push(&prev1_open_above_prev2)
984 .push(&curr)
985 .is_three_inside_down());
986 }
987
988 #[test]
989 fn test_is_not_three_inside_down_if_prev1_closes_below_prev2_open() {
990 let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
991 let prev1_close_below_prev2 = (49.5, 49.8, 47.5, 47.9, 0.0); // close < 48.0
992 let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
993
994 let mut series = CandleStream::new();
995
996 assert!(!series
997 .push(&prev2)
998 .push(&prev1_close_below_prev2)
999 .push(&curr)
1000 .is_three_inside_down());
1001 }
1002
1003 #[test]
1004 fn test_is_not_three_inside_down_if_prev1_engulfs_prev2() {
1005 let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0); // body [48.0, 50.0]
1006 let prev1_engulf_prev2 = (50.5, 51.0, 47.0, 47.5, 0.0); // open > 50.0, close < 48.0
1007 let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
1008
1009 let mut series = CandleStream::new();
1010
1011 assert!(!series
1012 .push(&prev2)
1013 .push(&prev1_engulf_prev2)
1014 .push(&curr)
1015 .is_three_inside_down());
1016 }
1017
1018 #[test]
1019 fn test_is_not_three_inside_down_if_prev1_is_doji() {
1020 let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
1021 let doji_prev1 = (49.0, 49.5, 48.5, 49.0, 0.0); // open == close
1022 let curr = (48.8, 49.0, 47.5, 47.9, 0.0);
1023
1024 let mut series = CandleStream::new();
1025
1026 assert!(!series
1027 .push(&prev2)
1028 .push(&doji_prev1)
1029 .push(&curr)
1030 .is_three_inside_down());
1031 }
1032
1033 #[test]
1034 fn test_is_not_three_inside_down_if_curr_is_inside_prev1() {
1035 let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
1036 let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0); // body [49.0, 49.5]
1037 let curr_inside_prev1 = (49.4, 49.6, 48.8, 49.1, 0.0); // close 49.1 > 49.0
1038
1039 let mut series = CandleStream::new();
1040
1041 assert!(!series
1042 .push(&prev2)
1043 .push(&prev1)
1044 .push(&curr_inside_prev1)
1045 .is_three_inside_down());
1046 }
1047
1048 #[test]
1049 fn test_is_not_three_inside_down_if_curr_not_bearish() {
1050 let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
1051 let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
1052 let not_bearish_curr = (47.8, 48.5, 47.5, 48.6, 0.0); // bullish
1053
1054 let mut series = CandleStream::new();
1055
1056 assert!(!series
1057 .push(&prev2)
1058 .push(&prev1)
1059 .push(¬_bearish_curr)
1060 .is_three_inside_down());
1061 }
1062
1063 #[test]
1064 fn test_is_not_three_inside_down_with_insufficient_candles() {
1065 let prev2 = (48.0, 50.5, 47.8, 50.0, 0.0);
1066 let prev1 = (49.5, 49.8, 48.5, 49.0, 0.0);
1067
1068 let mut series = CandleStream::new();
1069
1070 assert!(!series.push(&prev2).is_three_inside_down());
1071 assert!(!series.push(&prev1).is_three_inside_down());
1072 }
1073
1074 #[test]
1075 fn test_is_downtrend() {
1076 let prev2 = (109.5, 112.0, 108.0, 111.0, 0.0);
1077 let prev1 = (104.5, 110.0, 104.0, 109.0, 0.0);
1078 let curr = (100.0, 105.0, 99.0, 104.0, 0.0);
1079
1080 let mut series = CandleStream::new();
1081
1082 assert!(series.push(&prev2).push(&prev1).push(&curr).is_downtrend());
1083 }
1084
1085 #[test]
1086 fn test_is_not_downtrend() {
1087 let prev2 = (109.5, 112.0, 108.0, 111.0, 0.0);
1088 let prev1 = (100.0, 105.0, 99.0, 104.0, 0.0);
1089 let curr = (104.5, 110.0, 104.0, 109.0, 0.0);
1090
1091 let mut series = CandleStream::new();
1092
1093 assert!(!series.push(&prev2).push(&prev1).push(&curr).is_downtrend());
1094 }
1095
1096 #[test]
1097 fn test_is_uptrend() {
1098 let prev2 = (100.0, 105.0, 99.0, 104.0, 0.0);
1099 let prev1 = (104.5, 110.0, 104.0, 109.0, 0.0);
1100 let curr = (109.5, 112.0, 108.0, 111.0, 0.0);
1101
1102 let mut series = CandleStream::new();
1103
1104 assert!(series.push(&prev2).push(&prev1).push(&curr).is_uptrend());
1105 }
1106
1107 #[test]
1108 fn test_is_not_uptrend() {
1109 let prev2 = (100.0, 105.0, 99.0, 104.0, 0.0);
1110 let prev1 = (109.5, 112.0, 108.0, 111.0, 0.0);
1111 let curr = (104.5, 110.0, 104.0, 109.0, 0.0);
1112
1113 let mut series = CandleStream::new();
1114
1115 assert!(!series.push(&prev2).push(&prev1).push(&curr).is_uptrend());
1116 }
1117}