candlestick_rs/candle_stick.rs
1/// The `CandleStick` trait provides analytical capabilities to detect key single-candle
2/// formations that signal potential market reversals, continuations, or indecision.
3///
4/// These patterns form the foundation of candlestick chart analysis, a technique
5/// developed in 18th century Japan and widely used in modern technical trading.
6///
7/// Each pattern detection method (`is_*`) returns whether the specific formation criteria
8/// are met, along with detailed documentation on trading significance and application.
9///
10/// Implementers need to provide only the four basic OHLC methods (open, high, low, close),
11/// and all pattern recognition capabilities become automatically available.
12///
13/// For multi-candle pattern identification across a series of candles, use: [`crate::CandleStream`]
14pub trait CandleStick {
15 /// Hammer body to range ratio for both hammer and inverse hammer pattern.
16 /// Can be overridden for custom ratio.
17 ///
18 /// Default: __30%__
19 fn hammer_body_ratio(&self) -> f64 {
20 0.3
21 }
22
23 /// Hammer upper shadow or wick to range ratio. Also, inversely used as tail to range ratio for inverse hammer pattern.
24 /// Can be overridden for custom ratio
25 ///
26 /// Default: __20%__
27 fn hammer_wick_ratio(&self) -> f64 {
28 0.2
29 }
30
31 /// Hammer lower shadow or tail to range ratio. Also, inversely used as wick to range ratio for inverse hammer pattern.
32 /// Can be overridden for custom ratio
33 ///
34 /// Default: __60%__
35 fn hammer_tail_ratio(&self) -> f64 {
36 0.6
37 }
38
39 /// Spinning top body ratio. Can be overridden for custom ratio
40 ///
41 /// Default: __20%__
42 fn spinning_top_body_ratio(&self) -> f64 {
43 0.2
44 }
45
46 /// Spinning top shadow ratio. Can be overridden for custom ratio
47 ///
48 /// Default: __30%__
49 fn spinning_top_shadow_ratio(&self) -> f64 {
50 0.3
51 }
52
53 /// Doji body to range ratio. Can be overridden for custom ratio.
54 ///
55 /// Default: __10%__
56 fn doji_body_ratio(&self) -> f64 {
57 0.1
58 }
59
60 /// Doji long leg to range ratio. Can be overridden for custom ratio.
61 ///
62 /// Default: __30%__
63 fn doji_long_leg_ratio(&self) -> f64 {
64 0.3
65 }
66
67 /// Doji tail to range ratio. Can be overridden for custom ratio.
68 ///
69 /// Default: __30%__
70 fn doji_tail_ratio(&self) -> f64 {
71 0.3
72 }
73
74 /// Doji wick to range ratio. Can be overridden for custom ratio.
75 ///
76 /// Default: __30%__
77 fn doji_wick_ratio(&self) -> f64 {
78 0.3
79 }
80
81 /// Doji minimum ratio. Can be overridden for custom ratio.
82 ///
83 /// Default: __5%__
84 fn doji_min_ratio(&self) -> f64 {
85 0.05
86 }
87
88 /// Marubozu minimum ratio. Can be overridden for custom ratio.
89 ///
90 /// Default: __20%__
91 fn marubozu_ratio(&self) -> f64 {
92 0.2
93 }
94
95 /// Returns the open price
96 fn open(&self) -> f64;
97
98 /// Returns the high price
99 fn high(&self) -> f64;
100
101 /// Returns the low price
102 fn low(&self) -> f64;
103
104 /// Returns the close price
105 fn close(&self) -> f64;
106
107 /// Returns the volume
108 fn volume(&self) -> f64;
109
110 /// Helper function to return the OHLC tuple
111 #[doc(hidden)]
112 fn ohlc(&self) -> (f64, f64, f64, f64) {
113 (self.open(), self.high(), self.low(), self.close())
114 }
115
116 /// Helper function to return the candle length with small epsilon
117 #[doc(hidden)]
118 fn range(&self) -> f64 {
119 (self.high() - self.low()).max(0.001)
120 }
121
122 /// Helper function to return the candle wick length of the candle
123 #[doc(hidden)]
124 fn wick(&self) -> f64 {
125 self.high() - self.open().max(self.close())
126 }
127
128 /// Helper function to return the candle body as the absolute difference between the open and close prices with small epsilon
129 #[doc(hidden)]
130 fn body(&self) -> f64 {
131 ((self.open() - self.close()).abs()).max(0.0001)
132 }
133
134 /// Helper function to return the candle tail length
135 #[doc(hidden)]
136 fn tail(&self) -> f64 {
137 self.open().min(self.close()) - self.low()
138 }
139
140 /// Helper function to return the candle's wick to range ratio
141 #[doc(hidden)]
142 fn wick_range_ratio(&self) -> f64 {
143 self.wick() / self.range()
144 }
145
146 /// Helper function to return the candle's wick to body ratio
147 #[doc(hidden)]
148 fn wick_body_ratio(&self) -> f64 {
149 self.wick() / self.body()
150 }
151
152 /// Helper function to return the candle's body to range ratio
153 #[doc(hidden)]
154 fn body_range_ratio(&self) -> f64 {
155 self.body() / self.range()
156 }
157
158 /// Helper function to return the candle's tail to range ratio
159 #[doc(hidden)]
160 fn tail_range_ratio(&self) -> f64 {
161 self.tail() / self.range()
162 }
163
164 /// Helper function to return the candle's tail to body ratio
165 #[doc(hidden)]
166 fn tail_body_ratio(&self) -> f64 {
167 self.tail() / self.body()
168 }
169
170 /// Identifies a Bullish Candlestick, a foundational pattern in price action analysis.
171 ///
172 /// This basic pattern forms when the closing price is higher than the opening price,
173 /// creating a filled (often green/white) candle body. The length of the body indicates
174 /// the strength of buying pressure during the period.
175 ///
176 /// **Trading Significance**:
177 /// - Signals buying pressure and bullish sentiment in the market
178 /// - Longer bullish bodies indicate stronger buying conviction
179 /// - Series of bullish candles confirm uptrends, especially with higher highs and lows
180 /// - Often used as confirmation for other technical signals in trend-following strategies
181 ///
182 /// # Example
183 /// ```
184 /// use candlestick_rs::CandleStick;
185 /// let candle = (100.0, 110.0, 99.0, 109.0, 0.0);
186 /// assert!(candle.is_bullish());
187 /// ```
188 fn is_bullish(&self) -> bool {
189 self.open() < self.close()
190 }
191
192 /// Identifies a Bearish Candlestick, a foundational pattern in price action analysis.
193 ///
194 /// This basic pattern forms when the closing price is lower than the opening price,
195 /// creating a filled (often red/black) candle body. The length of the body indicates
196 /// the strength of selling pressure during the period.
197 ///
198 /// **Trading Significance**:
199 /// - Signals selling pressure and bearish sentiment in the market
200 /// - Longer bearish bodies indicate stronger selling conviction
201 /// - Series of bearish candles confirm downtrends, especially with lower highs and lows
202 /// - Often used as confirmation for other technical signals in trend-following strategies
203 ///
204 /// # Example
205 /// ```
206 /// use candlestick_rs::CandleStick;
207 /// let candle = (110.0, 111.0, 99.0, 100.0, 0.0);
208 /// assert!(candle.is_bearish());
209 /// ```
210 fn is_bearish(&self) -> bool {
211 self.open() > self.close()
212 }
213
214 /// Identifies a Marubozu pattern, one of the strongest single-candle signals.
215 ///
216 /// This pattern forms when a candle has virtually no upper or lower shadows (wicks),
217 /// with the body extending across nearly the entire range. The term "marubozu" means
218 /// "bald head" or "shaved head" in Japanese, referring to the absence of shadows.
219 ///
220 /// **Trading Significance**:
221 /// - Represents complete dominance of either buyers (bullish marubozu) or sellers (bearish marubozu)
222 /// - Signals exceptional conviction in the market direction
223 /// - Often precedes continuation in the same direction, especially early in trends
224 /// - When appearing against the prevailing trend, can signal potential exhaustion and reversal
225 ///
226 /// # Example
227 /// ```
228 /// use candlestick_rs::CandleStick;
229 /// let candle = (100.0, 110.0, 99.0, 109.0, 0.0);
230 /// assert!(candle.is_marubozu());
231 /// ```
232 fn is_marubozu(&self) -> bool {
233 self.wick_body_ratio() < self.marubozu_ratio()
234 && self.tail_body_ratio() < self.marubozu_ratio()
235 }
236
237 /// Identifies a Bullish Marubozu, a powerful signal of buyer dominance.
238 ///
239 /// This pattern forms when a bullish candle (close > open) has virtually no shadows,
240 /// with the open at or near the low and the close at or near the high. It shows
241 /// buyers controlled the price action throughout the entire period with no significant
242 /// selling pressure.
243 ///
244 /// **Trading Significance**:
245 /// - Indicates exceptional buying pressure and momentum
246 /// - Often signals the beginning or acceleration of an uptrend
247 /// - Traders frequently use it as a strong entry signal for long positions
248 /// - When appearing after a consolidation or pullback, suggests the resumption of a bullish trend
249 ///
250 /// # Example
251 /// ```
252 /// use candlestick_rs::CandleStick;
253 /// let candle = (100.0, 110.0, 99.0, 109.0, 0.0);
254 /// assert!(candle.is_bullish_marubozu());
255 /// ```
256 fn is_bullish_marubozu(&self) -> bool {
257 self.is_bullish() && self.is_marubozu()
258 }
259
260 /// Identifies a Bearish Marubozu, a powerful signal of seller dominance.
261 ///
262 /// This pattern forms when a bearish candle (close < open) has virtually no shadows,
263 /// with the open at or near the high and the close at or near the low. It shows
264 /// sellers controlled the price action throughout the entire period with no significant
265 /// buying pressure.
266 ///
267 /// **Trading Significance**:
268 /// - Indicates exceptional selling pressure and downward momentum
269 /// - Often signals the beginning or acceleration of a downtrend
270 /// - Traders frequently use it as a strong exit signal for long positions or entry for shorts
271 /// - When appearing after an uptrend, can signal a potential trend reversal
272 ///
273 /// # Example
274 /// ```
275 /// use candlestick_rs::CandleStick;
276 /// let candle = (110.0, 111.0, 99.0, 100.0, 0.0);
277 /// assert!(candle.is_bearish_marubozu());
278 /// ```
279 fn is_bearish_marubozu(&self) -> bool {
280 self.is_bearish() && self.is_marubozu()
281 }
282
283 /// Identifies a Hammer pattern, a significant bullish reversal signal.
284 ///
285 /// This single-candle pattern is characterized by a small body at the upper portion of the
286 /// trading range and a long lower shadow (tail) at least twice the length of the body.
287 /// It resembles a hammer with a handle at the top.
288 ///
289 /// **Trading Significance**:
290 /// - Signals potential bullish reversal when appearing at the bottom of a downtrend
291 /// - Indicates rejection of lower prices as buyers stepped in after initial selling
292 /// - More reliable when followed by confirmation (a bullish candle or increased volume)
293 /// - Body color is less important than the overall shape, though bullish hammers (close > open) are slightly more significant
294 ///
295 /// # Example
296 /// ```
297 /// use candlestick_rs::CandleStick;
298 /// let candle = (100.0, 101.0, 95.0, 100.8, 0.0);
299 /// assert!(candle.is_hammer());
300 /// ```
301 fn is_hammer(&self) -> bool {
302 self.body_range_ratio() < self.hammer_body_ratio()
303 && self.wick_range_ratio() < self.hammer_wick_ratio()
304 && self.tail_range_ratio() > self.hammer_tail_ratio()
305 }
306
307 /// Identifies an Inverted Hammer pattern, a potential bullish reversal signal.
308 ///
309 /// This single-candle pattern features a small body at the lower portion of the
310 /// trading range and a long upper shadow (wick) at least twice the length of the body.
311 /// It resembles an upside-down hammer.
312 ///
313 /// **Trading Significance**:
314 /// - Signals potential bullish reversal when appearing at the bottom of a downtrend
315 /// - Indicates attempted upside movement, though sellers pushed prices back down
316 /// - Generally requires stronger confirmation than a standard hammer
317 /// - Often precedes the end of a downtrend, especially when followed by bullish price action
318 ///
319 /// # Example
320 /// ```
321 /// use candlestick_rs::CandleStick;
322 /// let candle = (100.0, 104.0, 99.8, 100.5, 0.0);
323 /// assert!(candle.is_inverted_hammer());
324 /// ```
325 fn is_inverted_hammer(&self) -> bool {
326 self.body_range_ratio() < self.hammer_body_ratio()
327 && self.wick_range_ratio() > self.hammer_tail_ratio()
328 && self.tail_range_ratio() < self.hammer_wick_ratio()
329 }
330
331 /// Identifies a Hanging Man pattern, an important bearish reversal signal.
332 ///
333 /// This pattern has the same shape as a hammer (small body at the top with a long lower shadow),
334 /// but appears during an uptrend. The long lower shadow indicates selling pressure that emerged
335 /// but was overcome by buyers—a warning sign after an advance.
336 ///
337 /// **Trading Significance**:
338 /// - Signals potential bearish reversal when appearing after an uptrend
339 /// - Suggests market vulnerability as sellers tested lower prices
340 /// - Most effective at resistance levels or after extended price advances
341 /// - Traders typically wait for next-day confirmation (a bearish candle or gap down)
342 ///
343 /// # Example
344 /// ```
345 /// use candlestick_rs::CandleStick;
346 /// let candle = (592.0, 593.75, 587.0, 593.0, 0.0);
347 /// assert!(candle.is_hanging_man());
348 /// ```
349 fn is_hanging_man(&self) -> bool {
350 self.is_hammer()
351 }
352
353 /// Identifies a Shooting Star pattern, a significant bearish reversal signal.
354 ///
355 /// This pattern has the same shape as an inverted hammer (small body at the bottom with a long upper shadow),
356 /// but appears during an uptrend. The long upper shadow indicates rejection of higher prices,
357 /// as buyers attempted to push prices up but ultimately failed.
358 ///
359 /// **Trading Significance**:
360 /// - Signals potential bearish reversal when appearing after an uptrend
361 /// - Indicates strong rejection of higher prices and exhaustion of buying pressure
362 /// - More significant when the upper shadow is at least twice the length of the body
363 /// - Often used by traders to exit long positions or initiate shorts, especially when confirmed
364 ///
365 /// # Example
366 /// ```
367 /// use candlestick_rs::CandleStick;
368 /// let candle = (100.0, 106.0, 99.7, 100.8, 0.0);
369 /// assert!(candle.is_shooting_star());
370 /// ```
371 fn is_shooting_star(&self) -> bool {
372 self.is_inverted_hammer()
373 }
374
375 /// Identifies a Spinning Top pattern, a signal of market indecision and equilibrium.
376 ///
377 /// This single-candle pattern features a small body centered within the trading range
378 /// with relatively long upper and lower shadows of similar length. The small body shows
379 /// little net movement from open to close despite the larger trading range.
380 ///
381 /// **Trading Significance**:
382 /// - Indicates indecision and balance between buyers and sellers
383 /// - Often appears during consolidation phases or at potential reversal points
384 /// - Suggests weakening of the current trend when appearing after a strong directional move
385 /// - By itself provides limited directional bias; traders use it as an alert for potential change
386 ///
387 /// # Example
388 /// ```
389 /// use candlestick_rs::CandleStick;
390 /// let candle = (100.0, 105.0, 95.0, 100.5, 0.0);
391 /// assert!(candle.is_spinning_top());
392 /// ```
393 fn is_spinning_top(&self) -> bool {
394 self.body_range_ratio() < self.spinning_top_body_ratio()
395 && self.wick_range_ratio() > self.spinning_top_shadow_ratio()
396 && self.tail_range_ratio() > self.spinning_top_shadow_ratio()
397 }
398
399 /// Identifies a Doji pattern, a powerful signal of market equilibrium and indecision.
400 ///
401 /// This pattern forms when the opening and closing prices are virtually the same,
402 /// creating an extremely small or non-existent body with shadows extending above and below.
403 /// The Japanese word "doji" means "mistake" or "blunder," referring to the rare equality of open and close.
404 ///
405 /// **Trading Significance**:
406 /// - Represents perfect equilibrium between buyers and sellers
407 /// - Signals potential trend reversal, especially after extended price moves
408 /// - Indicates exhaustion of the prevailing trend and possible consolidation
409 /// - Traders use it as an alert to reduce position size or tighten stops
410 ///
411 /// # Example
412 /// ```
413 /// use candlestick_rs::CandleStick;
414 /// let candle = (100.0, 105.0, 95.0, 100.0, 0.0);
415 /// assert!(candle.is_doji());
416 /// ```
417 fn is_doji(&self) -> bool {
418 self.body_range_ratio() < self.doji_body_ratio()
419 }
420
421 /// Identifies a Long-Legged Doji, a volatility-based signal of strong market indecision.
422 ///
423 /// This distinctive doji variant has unusually long upper and lower shadows compared to its minimal body.
424 /// It shows significant price movement in both directions during the period, but ultimately
425 /// closing near the opening price.
426 ///
427 /// **Trading Significance**:
428 /// - Indicates extreme volatility and fierce battle between buyers and sellers
429 /// - Frequently signals an impending trend change when appearing in extended trends
430 /// - Represents exhaustion of the prevailing trend as neither side maintains control
431 /// - Particularly significant when occurring at support/resistance levels or after strong directional moves
432 ///
433 /// # Example
434 /// ```
435 /// use candlestick_rs::CandleStick;
436 /// let candle = (100.0, 110.0, 90.0, 100.2, 0.0);
437 /// assert!(candle.is_long_legged_doji());
438 /// ```
439 fn is_long_legged_doji(&self) -> bool {
440 self.is_doji()
441 && self.tail_range_ratio() > self.doji_long_leg_ratio()
442 && self.wick_range_ratio() > self.doji_long_leg_ratio()
443 }
444
445 /// Identifies a Dragonfly Doji, a specialized pattern suggesting potential bullish reversal.
446 ///
447 /// This doji variant has its open and close at or near the high of the period,
448 /// with virtually no upper shadow but a long lower shadow, creating a "T" shape.
449 /// It shows sellers pushing prices down during the period, but buyers regained control by the close.
450 ///
451 /// **Trading Significance**:
452 /// - Strong bullish reversal signal when appearing at the bottom of downtrends
453 /// - Indicates rejection of lower prices and return to the opening level
454 /// - More reliable when formed at key support levels or round numbers
455 /// - When appearing at market tops, can act as a warning signal of waning momentum
456 ///
457 /// # Example
458 /// ```
459 /// use candlestick_rs::CandleStick;
460 /// let candle = (100.0, 100.5, 90.0, 100.1, 0.0);
461 /// assert!(candle.is_dragonfly_doji());
462 /// ```
463 fn is_dragonfly_doji(&self) -> bool {
464 self.is_doji()
465 && self.tail_range_ratio() > self.doji_tail_ratio()
466 && self.wick_range_ratio() < self.doji_min_ratio()
467 }
468
469 /// Identifies a Gravestone Doji, a specialized pattern suggesting potential bearish reversal.
470 ///
471 /// This doji variant has its open and close at or near the low of the period,
472 /// with virtually no lower shadow but a long upper shadow, creating an inverted "T" shape.
473 /// It shows buyers pushing prices up during the period, but sellers regained control by the close.
474 ///
475 /// **Trading Significance**:
476 /// - Strong bearish reversal signal when appearing at the top of uptrends
477 /// - Indicates rejection of higher prices and return to the opening level
478 /// - Particularly ominous when formed at key resistance levels or after extended rallies
479 /// - Named for its resemblance to a gravestone, suggesting the "death" of the current uptrend
480 ///
481 /// # Example
482 /// ```
483 /// use candlestick_rs::CandleStick;
484 /// let candle = (100.0, 110.0, 99.5, 100.1, 0.0);
485 /// assert!(candle.is_gravestone_doji());
486 /// ```
487 fn is_gravestone_doji(&self) -> bool {
488 self.is_doji()
489 && self.wick_range_ratio() > self.doji_wick_ratio()
490 && self.tail_range_ratio() < self.doji_min_ratio()
491 }
492
493 /// Summarizes the price action for the candle
494 fn typical_price(&self) -> f64 {
495 (self.high() + self.low() + self.close()) / 3.0
496 }
497
498 /// Flow of money into or out
499 fn raw_money_flow(&self) -> f64 {
500 self.typical_price() * self.volume()
501 }
502}
503
504impl CandleStick for (f64, f64, f64, f64, f64) {
505 fn open(&self) -> f64 {
506 self.0
507 }
508
509 /// Returns the high price
510 fn high(&self) -> f64 {
511 self.1
512 }
513
514 /// Returns the low price
515 fn low(&self) -> f64 {
516 self.2
517 }
518
519 /// Returns the close price
520 fn close(&self) -> f64 {
521 self.3
522 }
523
524 /// Returns the volume
525 fn volume(&self) -> f64 {
526 self.4
527 }
528}
529
530impl CandleStick for &(f64, f64, f64, f64, f64) {
531 fn open(&self) -> f64 {
532 self.0
533 }
534
535 /// Returns the high price
536 fn high(&self) -> f64 {
537 self.1
538 }
539
540 /// Returns the low price
541 fn low(&self) -> f64 {
542 self.2
543 }
544
545 /// Returns the close price
546 fn close(&self) -> f64 {
547 self.3
548 }
549
550 /// Returns the volume
551 fn volume(&self) -> f64 {
552 self.4
553 }
554}