, ,

How to Identify Candlestick Patterns with Python

Posted by

identify candlestick patterns with Python
Python candlestick pattern recognition
Table of Contents

    Introduction

    Candlestick pattern formations commonly consist of 1 to 3 bar structures. You may wish to automatically identify candlestick patterns on your chart where they are occurring and be alerted to them. While some technical analysis software has this feature built in already, you might wonder how you can code candlestick pattern detection using Python for free.

    In this post we will look at replicating something along the lines of CQG’s Candlestick Formations feature available in their Integrated Client platform. These particular patterns were referenced from Steve Nison’s book ‘Japanese Candlestick Charting Techniques‘, Second Edition (Prentice Hall, 2001). Steve Nison is the foremost authority on candlestick patterns having introduced the ancient Japanese charting technique to the West back in 1989 in a two page article before the first edition of this book in 1991.

    Starting with Python

    We will be using Python to identify candlestick patterns in our data, if you’re unfamiliar with Python you can download VSCode for free from Microsoft and follow its step by step instructions for setting up Python on your computer. You then just need to create the complete single python file (it’s easy, you can copy and paste the sections, in order, from this page, then save it as .py file type) and then just click the play icon in the top right of VSCode to run it.

    You will need to install any Python modules you use, which you can do in the Terminal window at the base of your VSCode Python setup e.g type pip install yfinance to install the Yahoo Finance module for example and press enter. You can see which other ones you need to install in Step 1. Any error in the Terminal output when running the .py file will indicate if you’re missing any additional module installations compared to those we import.

    In the following I will break down what goes into this single .py file and why.

    Step 1: Import Necessary Libraries

    As mentioned you may need to ‘pip install’ any of these not already on your system from the Terminal window, e.g. ‘pip install mplfinance’. Then at the top of the python file you have created you first need to import the modules we intend to use as the first few lines:

    Python
    import pandas as pd
    import matplotlib.pyplot as plt
    import mplfinance as mpf
    import yfinance as yf
    import matplotlib.patches as patches
    import matplotlib.dates as mdates

    Step 2: Load Your Data

    In this example we will choose daily data for Apple stock for the full year of 2022 but feel free to adjust the dates for 2023 if you prefer. This is pulled down from yfinance (Yahoo finance) imported at the start of our file above, to create the data frame. So place the following code beneath what we have above in Step 1:

    Python
    # Define the ticker symbol
    tickerSymbol = 'AAPL'
    
    # Get data on this ticker
    tickerData = yf.Ticker(tickerSymbol)
    
    # Get the historical prices for this ticker
    df = tickerData.history(period='1d', start='2022-01-01', end='2022-12-31')
    
    # Ensure that data is sorted by date
    df = df.sort_values('Date')
    
    # Define the plot style
    style = mpf.make_mpf_style(base_mpf_style='classic')

    Step 3: Define the Candlestick Patterns

    Next we will get Python to identify candlestick patterns for the same 15 formations which CQG uses in their tool, to keep things simple. Below we cover what candlestick pattern analysis needs to occur, i.e. the rules for it, and then present the corresponding Python code to detect that particular candlestick pattern.

    You would add each of these code blocks into your .py file.

    Single Bar Candlestick Formations

    Hammer

    The conditions for the Hammer pattern in our code are:

    1. The close price is higher than the open price (indicating a bullish candle).
    2. The low price is lower than both the open and close prices (indicating a long lower shadow).
    3. The difference between the high price and the close price is less than 10% of the difference between the close and open prices (indicating a short upper shadow).

    The Hammer pattern is typically identified by a small real body (the difference between the open and close prices) at the top of the trading range, a long lower shadow (at least twice the length of the real body), and a short or non-existent upper shadow.

    The code checks if the real body is at the upper end of the trading range, the lower shadow is at least twice the length of the real body, and the upper shadow is short (less than 10% of the real body size) as follows:

    Python
    # Define Hammer (HR) pattern
    def hammer(df):
        real_body = abs(df['Close'] - df['Open'])  # Real body size
        lower_shadow = df['Open'] - df['Low']  # Lower shadow size
        upper_shadow = df['High'] - df['Close']  # Upper shadow size
    
        cond1 = df['Close'] > df['Open']  # Real body is at the upper end of the trading range
        cond2 = lower_shadow > 2 * real_body  # Long lower shadow (at least twice the length of the real body)
        cond3 = upper_shadow < 0.1 * real_body  # Short upper shadow
        return cond1 & cond2 & cond3
    

    Hanging Man

    The conditions for the Hanging Man pattern in our code are:

    1. The close price is lower than the open price (indicating a bearish candle).
    2. The low price is lower than both the open and close prices (indicating a long lower shadow).
    3. The difference between the high price and the close price is less than 10% of the difference between the close and open prices (indicating a short upper shadow).

    The Hanging Man pattern is typically identified by a small real body (the difference between the open and close prices) at the top of the trading range, a long lower shadow (at least twice the length of the real body), and a short or non-existent upper shadow.

    This code calculates the size of the real body, upper shadow, and lower shadow for each candle. It then checks if the real body is at the upper end of the trading range, the lower shadow is at least twice the length of the real body, and the upper shadow is short (less than 10% of the real body size).

    Python
    # Define Hanging Man (HM) pattern
    def hanging_man(df):
        real_body = abs(df['Close'] - df['Open'])  # Real body size
        upper_shadow = df['High'] - df['Open']  # Upper shadow size
        lower_shadow = df['Open'] - df['Low']  # Lower shadow size
    
        cond1 = df['Close'] < df['Open']  # Real body is at the upper end of the trading range
        cond2 = lower_shadow > 2 * real_body  # Long lower shadow (at least twice the length of the real body)
        cond3 = upper_shadow < 0.1 * real_body  # Short upper shadow
        return cond1 & cond2 & cond3

    Inverted Hammer

    The conditions for the Inverted Hammer pattern in our code are:

    1. The close price is higher than the open price (indicating a bullish candle).
    2. The high price is higher than both the open and close prices (indicating a long upper shadow).
    3. The difference between the close price and the low price is less than 10% of the difference between the open and close prices (indicating a short lower shadow).

    The Inverted Hammer pattern is typically identified by a small real body (the difference between the open and close prices) at the bottom of the trading range, a long upper shadow (at least twice the length of the real body), and a short or non-existent lower shadow.

    This code calculates the size of the real body, upper shadow, and lower shadow for each candle. It then checks if the real body is at the lower end of the trading range, the upper shadow is at least twice the length of the real body, and the lower shadow is short (less than 10% of the real body size).

    Python
    # Define Inverted Hammer (IH) pattern
    def inverted_hammer(df):
        real_body = abs(df['Close'] - df['Open'])  # Real body size
        upper_shadow = df['High'] - df['Close']  # Upper shadow size
        lower_shadow = df['Close'] - df['Low']  # Lower shadow size
    
        cond1 = df['Close'] > df['Open']  # Real body is at the lower end of the trading range
        cond2 = upper_shadow > 2 * real_body  # Long upper shadow (at least twice the length of the real body)
        cond3 = lower_shadow < 0.1 * real_body  # Short lower shadow
        return cond1 & cond2 & cond3
    

    Shooting Star

    The conditions for the Shooting Star pattern in our code are:

    1. The close price is lower than the open price (indicating a bearish candle).
    2. The high price is higher than both the open and close prices (indicating a long upper shadow).
    3. The difference between the close price and the low price is less than 10% of the difference between the open and close prices (indicating a short lower shadow).

    The Shooting Star pattern is typically identified by a small real body (the difference between the open and close prices) at the bottom of the trading range, a long upper shadow (at least twice the length of the real body), and a short or non-existent lower shadow.

    This code calculates the size of the real body, upper shadow, and lower shadow for each candle. It then checks if the real body is at the lower end of the trading range, the upper shadow is at least twice the length of the real body, and the lower shadow is short (less than 10% of the real body size).

    Python
    # Define Shooting Star (SS) pattern
    def shooting_star(df):
        real_body = abs(df['Close'] - df['Open'])  # Real body size
        upper_shadow = df['High'] - df['Close']  # Upper shadow size
        lower_shadow = df['Close'] - df['Low']  # Lower shadow size
    
        cond1 = df['Close'] < df['Open']  # Real body is at the lower end of the trading range
        cond2 = upper_shadow > 2 * real_body  # Long upper shadow (at least twice the length of the real body)
        cond3 = lower_shadow < 0.1 * real_body  # Short lower shadow
        return cond1 & cond2 & cond3

    2-Bar Candlestick Formations

    Engulfing Bearish

    The conditions for the Engulfing Bearish pattern in our code are:

    1. The close price of the previous candle is higher than its open price (indicating a bullish candle).
    2. The close price of the current candle is lower than its open price (indicating a bearish candle).
    3. The open price of the current candle is higher than the close price of the previous candle (indicating that the current candle opens above the previous close).
    4. The close price of the current candle is lower than the open price of the previous candle (indicating that the current candle closes below the previous open).

    This pattern is typically identified by a small bullish candle (the previous candle) completely covered or “engulfed” by a larger bearish candle (the current candle). The bearish candle’s open price is higher than the bullish candle’s close price, and the bearish candle’s close price is lower than the bullish candle’s open price.

    This code calculates the size of the real body for the previous and current candles. It then checks if the current candle’s real body is larger than the previous candle’s real body, in addition to the other conditions.

    Python
    # Define Engulfing Bearish (EGb) pattern
    def engulfing_bearish(df):
        real_body_prev = abs(df['Close'].shift(1) - df['Open'].shift(1))  # Real body size of the previous candle
        real_body_curr = abs(df['Close'] - df['Open'])  # Real body size of the current candle
    
        cond1 = df['Close'].shift(1) > df['Open'].shift(1)  # Previous candle is bullish
        cond2 = df['Close'] < df['Open']  # Current candle is bearish
        cond3 = df['Open'] > df['Close'].shift(1)  # Current candle opens above previous close
        cond4 = df['Close'] < df['Open'].shift(1)  # Current candle closes below previous open
        cond5 = real_body_curr > real_body_prev  # Current candle's real body is larger than the previous candle's real body
        return cond1 & cond2 & cond3 & cond4 & cond5

    Engulfing Bullish

    The conditions for the Engulfing Bullish pattern in our code are:

    1. The close price of the previous candle is lower than its open price (indicating a bearish candle).
    2. The close price of the current candle is higher than its open price (indicating a bullish candle).
    3. The open price of the current candle is lower than the close price of the previous candle (indicating that the current candle opens below the previous close).
    4. The close price of the current candle is higher than the open price of the previous candle (indicating that the current candle closes above the previous open).

    This pattern is typically identified by a small bearish candle (the previous candle) completely covered or “engulfed” by a larger bullish candle (the current candle). The bullish candle’s open price is lower than the bearish candle’s close price, and the bullish candle’s close price is higher than the bearish candle’s open price.

    This code calculates the size of the real body for the previous and current candles. It then checks if the current candle’s real body is larger than the previous candle’s real body, in addition to the other conditions.

    Python
    # Define Engulfing Bullish (EGB) pattern
    def engulfing_bullish(df):
        real_body_prev = abs(df['Close'].shift(1) - df['Open'].shift(1))  # Real body size of the previous candle
        real_body_curr = abs(df['Close'] - df['Open'])  # Real body size of the current candle
    
        cond1 = df['Close'].shift(1) < df['Open'].shift(1)  # Previous candle is bearish
        cond2 = df['Close'] > df['Open']  # Current candle is bullish
        cond3 = df['Open'] < df['Close'].shift(1)  # Current candle opens below previous close
        cond4 = df['Close'] > df['Open'].shift(1)  # Current candle closes above previous open
        cond5 = real_body_curr > real_body_prev  # Current candle's real body is larger than the previous candle's real body
        return cond1 & cond2 & cond3 & cond4 & cond5
    

    Dark Cloud Cover

    The conditions for the Dark Cloud Cover pattern in our code are:

    1. The close price of the previous candle is higher than its open price (indicating a bullish candle).
    2. The close price of the current candle is lower than its open price (indicating a bearish candle).
    3. The open price of the current candle is higher than the high price of the previous candle (indicating that the current candle opens above the previous high).
    4. The close price of the current candle is lower than the midpoint of the previous candle (indicating that the current candle closes below the midpoint of the previous candle).

    This pattern is typically identified by a bullish candle (the previous candle) followed by a bearish candle (the current candle) that opens above the high of the previous candle and closes below the midpoint of the previous candle.

    Python
    # Define Dark Cloud (DC) pattern
    def dark_cloud(df):
        cond1 = df['Close'].shift(1) > df['Open'].shift(1)  # Previous candle is bullish
        cond2 = df['Close'] < df['Open']  # Current candle is bearish
        cond3 = df['Open'] > df['Close'].shift(1)  # Current candle opens above previous close
        cond4 = df['Close'] < (df['Open'].shift(1) + df['Close'].shift(1)) / 2  # Current candle closes below midpoint of previous candle
        return cond1 & cond2 & cond3 & cond4

    Double Doji

    The conditions for the Double Doji pattern in our code are:

    1. The open price of the previous candle is equal to its close price (indicating a doji).
    2. The open price of the current candle is equal to its close price (indicating a doji).

    This pattern is typically identified by two consecutive doji candles, where the open and close prices are the same or very close to each other.

    In real-world scenarios, the open and close prices may not be exactly the same due to the volatility of the market. In this code, 0.001 * df['Close'] is 0.1% of the close price. You can adjust this value based on how much difference you want to allow.

    Python
    # Define Double Doji (DD) pattern
    def double_doji(df):
        cond1 = abs(df['Open'].shift(1) - df['Close'].shift(1)) < (0.001 * df['Close'].shift(1))  # Previous candle is a doji (open is close to close) within .1% of close
        cond2 = abs(df['Open'] - df['Close']) < (0.001 * df['Close'])  # Current candle is a doji (open is close to close) within 1% of close
        return cond1 & cond2

    Harami Bearish

    The conditions for the Harami Bearish pattern in our code are:

    1. The close price of the previous candle is greater than its open price (indicating a bullish candle).
    2. The close price of the current candle is less than its open price (indicating a bearish candle).
    3. The open price of the current candle is less than the close price of the previous candle.
    4. The close price of the current candle is greater than the open price of the previous candle.

    This pattern is typically identified by a large bullish candle followed by a smaller bearish candle where the open and close prices of the bearish candle are within the open and close prices of the previous bullish candle.

    In this code, 0.001 * df['Close'].shift(1) and 0.001 * df['Open'].shift(1) are 0.1% of the close and open prices of the previous candle. You can adjust these values based on how much difference you want to allow.

    Python
    # Define Harami Bearish (HIb) pattern
    def harami_bearish(df):
        cond1 = df['Close'].shift(1) > df['Open'].shift(1)  # Previous candle is bullish
        cond2 = df['Close'] < df['Open']  # Current candle is bearish
        cond3 = df['Open'] <= df['Close'].shift(1) + (0.001 * df['Close'].shift(1))  # Current candle opens below or close to previous close within .1% of close
        cond4 = df['Close'] >= df['Open'].shift(1) - (0.001 * df['Open'].shift(1))  # Current candle closes above or close to previous open within .1% of open
        return cond1 & cond2 & cond3 & cond4

    Harami Bullish

    The conditions for the Harami Bullish pattern in our code are:

    1. The close price of the previous candle is less than its open price (indicating a bearish candle).
    2. The close price of the current candle is greater than its open price (indicating a bullish candle).
    3. The open price of the current candle is greater than the close price of the previous candle.
    4. The close price of the current candle is less than the open price of the previous candle.

    This pattern is typically identified by a large bearish candle followed by a smaller bullish candle where the open and close prices of the bullish candle are within the open and close prices of the previous bearish candle.

    Similar to above, in this code, 0.001 * df['Close'].shift(1) and 0.001 * df['Open'].shift(1) are 0.1% of the close and open prices of the previous candle. You can adjust these values based on how much difference you want to allow.

    Python
    # Define Harami Bullish (HIB) pattern
    def harami_bullish(df):
        cond1 = df['Close'].shift(1) < df['Open'].shift(1)  # Previous candle is bearish
        cond2 = df['Close'] > df['Open']  # Current candle is bullish
        cond3 = df['Open'] <= df['Close'].shift(1) + (0.001 * df['Close'].shift(1))  # Current candle opens above or close to previous close within .1% of close
        cond4 = df['Close'] >= df['Open'].shift(1) - (0.001 * df['Open'].shift(1))  # Current candle closes below or close to previous open within .1% of open
        return cond1 & cond2 & cond3 & cond4

    Piercing Line

    The conditions for the Piercing Line pattern in our code are:

    1. The close price of the previous candle is less than its open price (indicating a bearish candle).
    2. The close price of the current candle is greater than its open price (indicating a bullish candle).
    3. The open price of the current candle is less than the low price of the previous candle.
    4. The close price of the current candle is greater than the midpoint of the open and close prices of the previous candle.

    This pattern is typically identified by a bearish candle followed by a bullish candle where the bullish candle opens lower than the low of the previous day and closes more than halfway into the previous bearish candle’s body.

    In this code, 0.001 * df['Low'].shift(1) is 0.1% of the low price of the previous candle. You can adjust this value based on how much difference you want to allow.

    Python
    # Define Piercing Line (PL) pattern
    def piercing_line(df):
        cond1 = df['Close'].shift(1) < df['Open'].shift(1)  # Previous candle is bearish
        cond2 = df['Close'] > df['Open']  # Current candle is bullish
        cond3 = df['Open'] <= df['Low'].shift(1) + (0.001 * df['Low'].shift(1))  # Current candle opens below or close to previous low within .1%
        cond4 = df['Close'] >= (df['Open'].shift(1) + df['Close'].shift(1)) / 2  # Current candle closes above or close to midpoint of previous candle
        return cond1 & cond2 & cond3 & cond4
    

    3-Bar Candlestick Formations

    Morning Star

    The conditions for the Morning Star pattern in our code are:

    1. The close price of the first candle (two periods ago) is less than its open price (indicating a bearish candle).
    2. The close price of the second candle (one period ago) is greater than its open price (indicating a bullish candle).
    3. The close price of the current candle is greater than its open price (indicating a bullish candle).
    4. The close price of the second candle is less than the low price of the first candle.
    5. The close price of the current candle is greater than the midpoint of the open and close prices of the first candle.

    This pattern is typically identified by a bearish candle followed by a small bullish or bearish candle that gaps below the close of the previous day, followed by a bullish candle that closes well into the first session’s bearish candle body.

    In this code, 0.001 * df['Low'].shift(2) is 0.1% of the low price of the first candle. You can adjust this value based on how much difference you want to allow.

    Python
    def morning_star(df):
        cond1 = df['Close'].shift(2) < df['Open'].shift(2)  # First candle is bearish
        cond2 = df['Close'].shift(1) > df['Open'].shift(1)  # Second candle is bullish
        cond3 = df['Close'] > df['Open']  # Third candle is bullish
        cond4 = df['Close'].shift(1) <= df['Low'].shift(2) + (0.001 * df['Low'].shift(2))  # Second candle closes below or close to first candle's low within .1%
        cond5 = df['Close'] >= (df['Open'].shift(2) + df['Close'].shift(2)) / 2  # Third candle closes above or close to midpoint of first candle
        return cond1 & cond2 & cond3 & cond4 & cond5
    

    Morning Doji Star

    The conditions for the Morning Doji Star pattern in our code are:

    1. The close price of the first candle (two periods ago) is less than its open price (indicating a bearish candle).
    2. The open price of the second candle (one period ago) equals its close price (indicating a doji).
    3. The close price of the current candle is greater than its open price (indicating a bullish candle).
    4. The open price of the second candle is less than the low price of the first candle.
    5. The close price of the current candle is greater than the midpoint of the open and close prices of the first candle.

    This pattern is typically identified by a bearish candle followed by a doji that gaps below the close of the previous day, followed by a bullish candle that closes well into the first session’s bearish candle body.

    In this code, 0.001 * df['Close'].shift(1) and 0.001 * df['Low'].shift(2) are 0.1% of the close price of the second candle and the low price of the first candle, respectively. You can adjust these values based on how much difference you want to allow.

    Python
    # Define Morning Doji Star (MDS) pattern
    def morning_doji_star(df):
        cond1 = df['Close'].shift(2) < df['Open'].shift(2)  # First candle is bearish
        cond2 = abs(df['Open'].shift(1) - df['Close'].shift(1)) <= (0.001 * df['Close'].shift(1))  # Second candle is a doji (open equals close or close to it) within .1%
        cond3 = df['Close'] > df['Open']  # Third candle is bullish
        cond4 = df['Open'].shift(1) <= df['Low'].shift(2) + (0.001 * df['Low'].shift(2))  # Second candle opens below or close to first candle's low within .1%
        cond5 = df['Close'] >= (df['Open'].shift(2) + df['Close'].shift(2)) / 2  # Third candle closes above or close to midpoint of first candle
        return cond1 & cond2 & cond3 & cond4 & cond5

    Evening Star

    The conditions for the Evening Star pattern in our code are:

    1. The close price of the first candle (two periods ago) is greater than its open price (indicating a bullish candle).
    2. The close price of the second candle (one period ago) is less than its open price (indicating a bearish candle).
    3. The close price of the current candle is less than its open price (indicating a bearish candle).
    4. The close price of the second candle is greater than the high price of the first candle.
    5. The close price of the current candle is less than the midpoint of the open and close prices of the first candle.

    This pattern is typically identified by a bullish candle followed by a bearish candle that gaps above the close of the previous day, followed by a bearish candle that closes well into the first session’s bullish candle body.

    In this code, 0.001 * df['High'].shift(2) is 0.1% of the high price of the first candle. You can adjust this value based on how much difference you want to allow.

    Python
    # Define Evening Star (ES) pattern
    def evening_star(df):
        cond1 = df['Close'].shift(2) > df['Open'].shift(2)  # First candle is bullish
        cond2 = df['Close'].shift(1) < df['Open'].shift(1)  # Second candle is bearish
        cond3 = df['Close'] < df['Open']  # Third candle is bearish
        cond4 = df['Close'].shift(1) >= df['High'].shift(2) - (0.001 * df['High'].shift(2))  # Second candle closes above or close to first candle's high within .1%
        cond5 = df['Close'] <= (df['Open'].shift(2) + df['Close'].shift(2)) / 2  # Third candle closes below or close to midpoint of first candle
        return cond1 & cond2 & cond3 & cond4 & cond5

    Evening Doji Star

    The conditions for the Evening Doji Star pattern in our code are:

    1. The close price of the first candle (two periods ago) is greater than its open price (indicating a bullish candle).
    2. The open price of the second candle (one period ago) equals its close price (indicating a doji).
    3. The close price of the current candle is less than its open price (indicating a bearish candle).
    4. The open price of the second candle is greater than the high price of the first candle.
    5. The close price of the current candle is less than the midpoint of the open and close prices of the first candle.

    This pattern is generally identified by a bullish candle followed by a doji that gaps above the close of the previous day, followed by a bearish candle that closes well into the first session’s bullish candle body.

    In this code, 0.001 * df['Close'].shift(1) and 0.001 * df['High'].shift(2) are 0.1% of the close price of the second candle and the high price of the first candle, respectively. Again, adjust these values based on how much difference you want to allow.

    Python
    # Define Evening Doji Star (EDS) pattern
    def evening_doji_star(df):
        cond1 = df['Close'].shift(2) > df['Open'].shift(2)  # First candle is bullish
        cond2 = abs(df['Open'].shift(1) - df['Close'].shift(1)) <= (0.001 * df['Close'].shift(1))  # Second candle is a doji (open equals close) within .1%
        cond3 = df['Close'] < df['Open']  # Third candle is bearish
        cond4 = df['Open'].shift(1) >= df['High'].shift(2) - (0.001 * df['High'].shift(2))  # Second candle opens above or close to first candle's high within .1%
        cond5 = df['Close'] <= (df['Open'].shift(2) + df['Close'].shift(2)) / 2  # Third candle closes below or close to midpoint of first candle
        return cond1 & cond2 & cond3 & cond4 & cond5

    Step 4: Apply the Patterns to the Data

    Next we add the pattern columns into the data frame as follows:

    Python
    # Add the pattern columns to the DataFrame
    df['Hammer'] = hammer(df)
    df['Hanging Man'] = hanging_man(df)
    df['Inverted Hammer'] = inverted_hammer(df)
    df['Shooting Star'] = shooting_star(df)
    df['Engulfing Bearish'] = engulfing_bearish(df)
    df['Engulfing Bullish'] = engulfing_bullish(df)
    df['Dark Cloud'] = dark_cloud(df)
    df['Double Doji'] = double_doji(df)
    df['Harami Bearish'] = harami_bearish(df)
    df['Harami Bullish'] = harami_bullish(df)
    df['Piercing Line'] = piercing_line(df)
    df['Morning Star'] = morning_star(df)
    df['Morning Doji Star'] = morning_doji_star(df)
    df['Evening Star'] = evening_star(df)
    df['Evening Doji Star'] = evening_doji_star(df)

    Step 5: Arrange Colouring and Labelling

    In this part of the file we make abbreviations for each candlestick pattern so that we can label them concisely on the chart to save space and also choose some colours for the rectangles that will be highlighting where the candlesticks generating these patterns are occurring.

    Python
    # Define abbreviations for each pattern
    abbreviations = {
        'HR': 'Hammer',
        'HM': 'Hanging Man',
        'IH': 'Inverted Hammer',
        'SS': 'Shooting Star',
        'EGb': 'Engulfing Bearish',
        'EGB': 'Engulfing Bullish',
        'DC': 'Dark Cloud',
        'DD': 'Double Doji',
        'HIb': 'Harami Bearish',
        'HIB': 'Harami Bullish',
        'PL': 'Piercing Line',
        'MS': 'Morning Star',
        'MDS': 'Morning Doji Star',
        'ES': 'Evening Star',
        'EDS': 'Evening Doji Star'
    }
    
    # Define colors for each pattern
    colors = {
        'HR': 'red',
        'HM': 'blue',
        'IH': 'green',
        'SS': 'purple',
        'EGb': 'orange',
        'EGB': 'pink',
        'DC': 'brown',
        'DD': 'gray',
        'HIb': 'cyan',
        'HIB': 'magenta',
        'PL': 'yellow',
        'MS': 'lime',
        'MDS': 'indigo',
        'ES': 'gold',
        'EDS': 'silver'
    }
    

    Step 6: Identify Candlestick Patterns Visually

    To visualise the candlestick pattern detection in Python, we create the code to place highlighted rectangles and labels over the identified formations on a chart, in a similar way to how CQG do it. So for the single bar patterns we only highlight one candle but for the 2 and 3 bar candlestick patterns we highlight the full pattern so that the rectangle covers 2 or 3 candles respectively. I have set the alpha to 0.3 so that we can still see the candle formation behind the highlighting rectangles.

    Sometimes I’ve found candlesticks are detected as being part of more than one pattern, so a quick way to stop the labels overlapping each other and making them all illegible, is to place them at a percentage distance of the candle length apart. I’ve set this at 30% (0.3) in the code below, there are likely more elegant ways to do it but it works for now.

    Python
    # Create a new figure and axes
    fig, axes = mpf.plot(df, type='candle', style=style, title='Candlestick Chart - AAPL', ylabel='Price', returnfig=True)
    
    # Get the main Axes object
    ax = axes[0]
    
    # Create a dictionary to store labels for each candle
    labels = {}
    
    # Iterate over the DataFrame rows
    for i in range(len(df)):
        # Check each pattern
        for abbr, pattern in abbreviations.items():
            # If the pattern is found in the current row
            if df[pattern].iloc[i]:
                # Calculate the rectangle parameters
                if pattern in ['Morning Star', 'Morning Doji Star', 'Evening Star', 'Evening Doji Star'] and i > 1:
                    x = df.index.get_loc(df.index[i-2])
                    width = 3
                elif pattern not in ['Hammer', 'Hanging Man', 'Inverted Hammer', 'Shooting Star'] and i > 0:
                    x = df.index.get_loc(df.index[i-1])
                    width = 2
                else:
                    x = df.index.get_loc(df.index[i])
                    width = 1
    
                y1 = df[['Low']].iloc[i-width+1:i+1].min().values[0]
                y2 = df[['High']].iloc[i-width+1:i+1].max().values[0]
    
                # Create the rectangle
                ax.fill_between([x-0.5, x+width-0.5], y1, y2, color=colors[abbr], alpha=0.3)
    
                # Add the abbreviation label to the labels dictionary
                if x in labels:
                    labels[x].append(abbr)
                else:
                    labels[x] = [abbr]
    
    # Add the labels to the plot
    for x, label_list in labels.items():
        for j, label in enumerate(label_list):
            ax.text(x, df['Low'][x] - (j * 0.3 * (df['High'][x] - df['Low'][x])), label, color='black', fontsize=9, ha='center', va='top')
    
    # Display the plot
    plt.show()

    Step 7: Run the file

    If all goes to plan your python code will now identify candlestick patterns in your data by producing a chart similar, if not identical to mine below:

    python for candlestick patterns

    If you zoom in you can better see our various patterns coming through, like the Bearish Harami 2-bar pattern (turquoise rectangle) and 5 the Evening Star 3-bar pattern (yellow rectangle) which I’ve focussed in on in the image below:

    candlestick formations with python

    Or in this next image you can see a bearish engulfing candle pattern is detected but at the same time the Python code has detected a dark cloud cover candle pattern. It is scenarios like this that need the label spacing applied to be able to read them.

    Summary

    So I hope you found this step by step guide to identifying candlestick patterns with Python helpful and a good starting point to understand how such pattern recognition tools can be coded in Python. You can use this as a building block for further development and utilise it as a step to approach back-testing ideas. For example, did the next 10 days after a bearish candle pattern was identified with the Python code create a profitable trade scenario for this security or not? You could try adding more patterns or work on identifying candlestick patterns in real time with Python over much smaller timeframes. Do these candlestick patterns you identify enforce your other indicators’ buy and sell signals too or conflict with them? I would be very interested to know how you get on.

    Pattern NamePattern TypeNumber of BarsPattern DescriptionMarket Implication
    HammerBullishSingleSmall real body at the top, long lower shadow, short/no upper shadow.Potential trend reversal, buying opportunity.
    Hanging ManBearishSingleSimilar to Hammer but occurs at the end of an uptrend.Possible top formation, selling opportunity.
    Inverted HammerBullishSingleSmall real body at the bottom, long upper shadow, short/no lower shadow.Indicative of a potential trend reversal.
    Shooting StarBearishSingleInverse of Inverted Hammer, appears in an uptrend.Signals potential downward reversal.
    Engulfing BearishBearish2-BarA small bullish candle followed by a large bearish candle that engulfs the former.Indicates a bearish reversal.
    Engulfing BullishBullish2-BarA small bearish candle followed by a large bullish candle that engulfs the former.Suggests a bullish reversal.
    Dark Cloud CoverBearish2-BarA bullish candle followed by a bearish candle that opens above the first’s high and closes below its midpoint.Bearish reversal pattern in an uptrend.
    Double DojiNeutral or Bearish2-BarTwo consecutive doji candles, where the open and close prices are almost the same.Indicates indecision, potential for a reversal.
    Harami BearishBearish2-BarLarge bullish candle followed by a smaller bearish candle within the previous body.Signals a potential bearish reversal.
    Harami BullishBullish2-BarLarge bearish candle followed by a smaller bullish candle within the previous body.Suggests a potential bullish reversal.
    Piercing LineBullish2-BarA bearish candle followed by a bullish candle that opens below the previous low and closes more than halfway into its body.Indicates a bullish reversal in a downtrend.
    Morning StarBullish3-BarA bearish candle followed by a short body candle and then a bullish candle.Suggests a bullish reversal after a downtrend.
    Morning Doji StarBullish3-BarSimilar to Morning Star but with a Doji as the second candle.Strong indicator of a bullish reversal.
    Evening StarBearish3-BarA bullish candle followed by a short body candle and then a bearish candle.Indicates a bearish reversal after an uptrend.
    Evening Doji StarBearish3-BarSimilar to Evening Star but with a Doji as the second candle.Strong indicator of a bearish reversal.
    A summary of the candlestick patterns covered in the article, along with their implications in the market, check the article for more definitive identification.

    Further Reading

    Nison, Steve. Japanese Candlestick Charting Techniques, Second Edition. Prentice Hall, 2001, New York.

    Leave a Reply

    Your email address will not be published. Required fields are marked *