This post is part of the T4p Series.
In the previous post, I briefly introduced candlesticks and how to detect a bearish and bullish candle in Python.
In this post, we are going deeper, discussing candlestick patterns, what they are all about, and how to detect and represent them in Python
Introduction
As you learned in the previous post, there are mainly two types of candles: Bearish and Bullish. The size of the wick and the body determines what kind of bearish and bullish candle it is. Candlestick patterns visually help traders learn about the price movement to interpret the market sentiments.
History
Candlestick patterns were developed centuries ago by Japanese rice traders looking to predict price shifts in the market. Today, traders worldwide use these same patterns to spot potential trends and reversals, blending ancient wisdom with modern trading techniques.
Types of Candlestick Patterns
In a broader sense, we can divide patterns into two categories:
- Single Candlestick Patterns: Doji, Hammer, and Shooting Star.
- Multiple Candlestick Patterns: Engulfing, Morning Star, and Evening Star.
Some of the patterns represent either a bearish or bullish market while other patterns emerge in both.
There are many candlestick patterns, such as Doji, Engulfing, Hammer, Morning Star, Evening Star, etc. I can’t cover all those in a single post, so I will try to cover them in separate posts. For now, I am discussing the Doji and Engulfing patterns here.
Doji Pattern
It is a pattern that occurs when the opening and closing prices are virtually equal, creating a cross or a plus sign shape. The name Doji comes from Japanese and means blunder or mistake, referring to the rarity of having the same opening and closing prices.
Usage
Doji is used as a reversal pattern.
After an Uptrend:
- When you spot a Doji after a strong upward move
- Wait for confirmation (next candle closing lower)
- Consider taking profit on long positions or entering short positions
- Place stop loss above the Doji’s high
After a Downtrend:
- When you spot a Doji after a strong downward move
- Wait for confirmation (next candle closing higher)
- Consider entering long positions or covering shorts
- Place stop loss below the Doji’s low
Types of Doji
- Standard Doji:
- Open and Close should be the same/very close
- Equal shadows above and below
- Dragonfly Doji:
- Open and Close at/near the high
- Long lower shadow
- Little to no upper shadow
- Gravestone Doji:
- Open and Close at/near the low
- Long upper shadow
- Little to no lower shadow
Visual Representation
Now let’s write some code in Python to learn how you can represent Doji patterns
import mplfinance as mpf
import pandas as pd
def plot_candlestick_from_dict(data,title="CandleStick Chart"):
df = pd.DataFrame(data)
df['Date'] = pd.to_datetime(df['Date'])
df.set_index('Date', inplace=True)
mpf.plot(df, type='candle', style='charles', title=title, ylabel='Price')
Standard Doji
ohlc_data = [
{"Date":"2024-09-01", "Open":100, "High":105, "Low":95, "Close":100},
]
plot_candlestick_from_dict(ohlc_data,"Standard Doji")
Dragonfly Doji
ohlc_data = [
{"Date":"2024-09-01", "Open":100, "High":100.5, "Low":95, "Close":100},
]
plot_candlestick_from_dict(ohlc_data,"Dragonfly Doji")
Gravestone Doji
ohlc_data = [
{"Date":"2024-09-01", "Open":100, "High":105, "Low":99.5, "Close":100},
]
plot_candlestick_from_dict(ohlc_data,"Gravestone Doji")
I have embedded the entire notebook. Visual representation is not enough. Now let’s write some code to detect Doji patterns in the trading OHLC data.
def is_doji(open_price, high, low, close, doji_size=0.1): """ Check if a candlestick is a Doji pattern Parameters: open_price: Opening price high: High price low: Low price close: Closing price doji_size: maximum body size ratio threshold (default 0.1 or 10% of range) """ body_size = abs(close - open_price) candle_range = high - low # Avoid division by zero if candle_range == 0: return False # Calculate body size as a ratio of the candle range body_ratio = body_size / candle_range return body_ratio <= doji_size
I have written functions for all Doji types but here I am explaining the core concept that will be helpful to figure out different Doji types.
Here are a few concepts:
- Body Size: It should be very small for Doji. We are computing it by taking the difference between closing and opening prices.
- Candle Range: Total range of price movement.
- Body Ratio: Compares the body to the total range.
- Doji Size: Threshold (default 0.1 or 10%).
The visual representation would be something like the below:
Normal Candle: Doji Candle: High 105 ─┬─ High 105 ─┬─ │ │ │ │ Open 100 ─┤ Open 100 ─┼─ Close 100.2 │ │ │ │ Close 95 ─┴─ Low 95 ─┴─ Body Ratio: 30% Body Ratio: 2% (Not a Doji) (Is a Doji!)
You can see the entire code in the Jupyter Notebook.
Engulfing Pattern
It is formed by two candles where the body of the first candle is engulfed by the body of the second candle. Trading volume often increases on the engulfing candle. There are two types of engulfing patterns: Bearish and Bullish.
- Bullish Engulfing: A two-candle pattern where a smaller bearish candle is completely “engulfed” by a larger bullish candle, suggesting a potential upward reversal.
- Bearish Engulfing: The opposite, where a smaller bullish candle is engulfed by a larger bearish candle, hinting at a possible downward trend.
Visual Representation
Below is the exported Jupyter Notebook for the visual representation of the engulfing pattern in Python:
import mplfinance as mpf
import pandas as pd
def plot_engulfing_pattern(data, title):
# Convert to DataFrame
df = pd.DataFrame(data)
df['Date'] = pd.to_datetime(df['Date'])
df.set_index('Date', inplace=True)
# Create the plot
mpf.plot(df,
type='candle',
title=title,
volume=False, # Set to True if you have volume data
style='charles',
figsize=(10,5))
bullish_engulfing_data = [
# Downtrend before pattern
{"Date":"2024-09-01", "Open":110, "High":112, "Low":108, "Close":109},
{"Date":"2024-09-02", "Open":109, "High":110, "Low":106, "Close":107},
{"Date":"2024-09-03", "Open":107, "High":108, "Low":104, "Close":105},
# Engulfing pattern
{"Date":"2024-09-04", "Open":105, "High":105.5, "Low":103, "Close":104}, # Small bearish candle
{"Date":"2024-09-05", "Open":103, "High":108, "Low":102, "Close":107}, # Bullish engulfing
# Uptrend after pattern
{"Date":"2024-09-06", "Open":107, "High":110, "Low":106, "Close":109}
]
# Sample data showing Bearish Engulfing Pattern
bearish_engulfing_data = [
# Uptrend before pattern
{"Date":"2024-09-01", "Open":100, "High":102, "Low":99, "Close":101},
{"Date":"2024-09-02", "Open":101, "High":104, "Low":100, "Close":103},
{"Date":"2024-09-03", "Open":103, "High":106, "Low":102, "Close":105},
# Engulfing pattern
{"Date":"2024-09-04", "Open":105, "High":108, "Low":104, "Close":107}, # Small bullish candle
{"Date":"2024-09-05", "Open":108, "High":109, "Low":103, "Close":104}, # Bearish engulfing
# Downtrend after pattern
{"Date":"2024-09-06", "Open":104, "High":105, "Low":101, "Close":102}
]
plot_engulfing_pattern(bullish_engulfing_data, 'Bullish Engulfing Pattern')
plot_engulfing_pattern(bearish_engulfing_data, 'Bearish Engulfing Pattern')
I added three functions to detect both bearish and bullish candles:
def is_bearish_engulfing(current_candle, previous_candle): """ Detect bearish engulfing pattern. Args: current_candle (dict): Current day's OHLC data previous_candle (dict): Previous day's OHLC data """ # Check if previous candle is bullish prev_bullish = previous_candle["Close"] > previous_candle["Open"] # Check if current candle is bearish curr_bearish = current_candle["Close"] < current_candle["Open"] # Check engulfing conditions engulfs_body = (current_candle["Open"] > previous_candle["Close"] and current_candle["Close"] < previous_candle["Open"]) return prev_bullish and curr_bearish and engulfs_body def is_bullish_engulfing(current_candle, previous_candle): """ Detect bullish engulfing pattern. Args: current_candle (dict): Current day's OHLC data previous_candle (dict): Previous day's OHLC data """ # Check if previous candle is bearish prev_bearish = previous_candle["Close"] < previous_candle["Open"] # Check if current candle is bullish curr_bullish = current_candle["Close"] > current_candle["Open"] # Check engulfing conditions engulfs_body = (current_candle["Open"] < previous_candle["Close"] and current_candle["Close"] > previous_candle["Open"]) return prev_bearish and curr_bullish and engulfs_body def find_engulfing_patterns(candles): """ Find all engulfing patterns in a list of candles. Args: candles (list): List of dictionaries containing OHLC data """ patterns = [] for i in range(1, len(candles)): current = candles[i] previous = candles[i-1] if is_bearish_engulfing(current, previous): patterns.append({ "date": current["Date"], "pattern": "Bearish Engulfing", "previous_candle": previous, "engulfing_candle": current }) if is_bullish_engulfing(current, previous): patterns.append({ "date": current["Date"], "pattern": "Bullish Engulfing", "previous_candle": previous, "engulfing_candle": current }) return patterns
When you run these functions you’ll see something like the below in the notebook:
Conclusion
There are many other patterns that I cannot cover in this post. It has already become quite lengthy. In the coming posts, I will be discussing each pattern in a separate post. Like always, the code is available on GitHub.
Have a winning trading strategy and want it to trade for you automatically, without lifting a finger? I specialize in turning your strategies into fully automated trading bots—whether for stocks, crypto, or forex. Let your strategy work around the clock and maximize your potential. Ready to bring it to life? Email me at kadnan @ gmail.com