Discover more from All About Trading!
Chart Pattern Recognition in Python
Creating a Trading Pattern Recognition Strategy Using K’s Candlesticks
Candlestick patterns deserve to be studied thoroughly and even though a strategy relying solely on them will be unstable and unprofitable, they can be a valuable addition into a full trading system that uses other techniques. In this article, we will see a full presentation and code of the Doji pattern on an alternative charting system.
Start your Free Trial now at O’Reilly and start reading the Early Release of my upcoming book “Mastering Financial Pattern Recognition” which covers everything you need to know about candlestick patterns and how to code them in Python! (Bonus points: You will discover new never-seen-before patterns). You will also find technical indicators, risk management, and even behavioral finance covered in the book!
Standard Charting Systems
This section will deal with the import of OHLC historical data and charting chandlesticks. One of the most famous trading platforms in the retail community is the MetaTrader5 software. It is a powerful tool that comes with its own programming language and its huge online community support. It also offers the possibility to export its historical short-term and long-term FX data.
The first thing we need to do is to simply download the platform from the official website. Then, after creating the demo account, we are ready to import the library in Python that allows to import the OHLC data from MetaTrader5.
A library is a group of structured functions that can be imported into our Python interpreter from where we can call and use the ones we want.
The easiest way to install the MetaTrader5 library is to go to the Python prompt on our computer and type:
pip install MetaTrader5
This should install the library in our local Python. Now, we want to import it to the Python interpreter (such as Pycharm or SPYDER) so that we can use it. Let us actually import all the libraries we will be using for this:
import datetime # Date acquiring import pytz # Time zone management import pandas as pd # Mostly for Data frame manipulation import MetaTrader5 as mt5 # Importing OHLC data import matplotlib.pyplot as plt # Plotting charts import numpy as np # Mostly for array manipulation
Anything that comes after “as” is a shortcut. The plt shortcut is there so that each time we want to call a function from that library we do not have to type the full matplotlib.pyplot statement. The official documentation for the Metatrader5 libary can be found here.
The first thing we can do is to select which time frame we want to import. Let us suppose that there are only two time frames, the 30-minute and the hourly bars. We can therefore create variables that hold the statement to tell the MetaTrader5 library which time frame we want.
# Choosing the 30-minute time frame frame_M30 = mt5.TIMEFRAME_M30
# Choosing the hourly time frame frame_H1 = mt5.TIMEFRAME_H1
Then, by staying in the spirit of importing variables, we can define the variable that states what date is it now. This helps the algorithm know the stopping date of the import. We can do this by the simple line of code below.
# Defining the variable now to give out the current date now = datetime.datetime.now()
Note that these code snippets are better used chronologically, hence, I encourage you to copy them in order and then execute them one by one so that you understand the evolution of what you are doing. The below is a function that holds which assets we want. Generally, I use 10 or more but for simplicity, let us consider that there are only two currency pairs: EURUSD and USDCHF.
def asset_list(asset_set): if asset_set == 'FX':
assets = ['EURUSD', 'USDCHF'] return assets
Now, with the key function that gets us the OHLC data. The below establishes a connection to MetaTrader5, applies the current date, and extracts the needed data. Notice the arguments year, month, and day. These will be filled by us to select from when do we want the data to start. Note, I have inputed Europe/Paris as my time zone, you should use your time zone to get more accurate data.
def get_quotes(time_frame, year = 2005, month = 1, day = 1, asset = "EURUSD"): # Establish connection to MetaTrader 5 if not mt5.initialize(): print("initialize() failed, error code =", mt5.last_error()) quit() timezone = pytz.timezone("Europe/Paris") utc_from = datetime.datetime(year, month, day, tzinfo = timezone) utc_to = datetime.datetime(now.year, now.month, now.day + 1, tzinfo = timezone) rates = mt5.copy_rates_range(asset, time_frame, utc_from, utc_to) rates_frame = pd.DataFrame(rates)
And finally, the last function we will use is the one that uses the below get_quotes function and then cleans the results so that we have a nice array. We have selected data since January 2019 as shown below.
def mass_import(asset, horizon): if horizon == 'M30': data = get_quotes(frame_M30, 2019, 1, 1, asset = assets[asset]) data = data.iloc[:, 1:5].values data = data.round(decimals = 5)
Finally, we are done building the blocks necessary to import the data. To import EURUSD OHLC historical data, we simply use the below code line:
# Choosing the horizon horizon = 'M30'
# Creating an array called EURUSD having M30 data since 2019 EURUSD = mass_import(0, horizon)
And voila, now we have the EURUSD OHLC data from 2019. Now let us see how to transform the data into candles.
Candlestick charts are among the most famous ways to analyze the time series visually. They contain more information than a simple line chart and have more visual interpretability than bar charts. Many libraries in Python offer charting functions but being someone who suffers from malfunctioning import of libraries and functions alongside their fogginess, I have created my own simple function that charts candlesticks manually with no exogenous help needed.
OHLC data is an abbreviation for Open, High, Low, and Close price. They are the four main ingredients for a timestamp. It is always better to have these four values together so that our analysis reflects more the reality. Here is a table that summarizes the OHLC data of hypothetical security:
Our job now is to plot the data so that we can visually interpret what kind of trend is the price following. We will start with the basic line plot before we move on to candlestick plotting.
Note that you can download the data manually or using Python. In case you have an excel file that has OHLC only data starting from the first row and column, you can import it using the below code snippet:
import numpy as np import pandas as pd
# Importing the OHLC Historical Data in Excel format my_ohlc_data = pd.read_excel('my_ohlc_data.xlsx')
# Converting to Array my_ohlc_data = np.array(my_ohlc_data)
Plotting basic line plots is extremely easy in Python and requires only one line of code. We have to make sure that we have imported a library called matplotlib and then we will call a function that plots the data for us.
# Importing the necessary charting library import matplotlib.pyplot as plt
# The syntax to plot a line chart plt.plot(my_data[-100:, 3], color = 'black', label = 'EURUSD')
# The syntax to add the label created above plt.legend()
# The syntax to add a grid plt.grid()
Now that we have seen how to create normal line charts, it is time to take it to the next level with candlestick charts. The way to do this with no complications is to think about vertical lines. Here is the intuition (followed by an application of the function below):
Select a lookback period. This is the number of values you want to appear on the chart.
Plot vertical lines for each row representing the highs and lows. For example, on OHLC data, we will use a matplotlib function called vlines which plots a vertical line on the chart using a minimum (low) value and a maximum (high value).
Make a color condition which states that if the closing price is greater than the opening price, then execute the selected block of code (which naturally contains the color green). Do this with the color red (bearish candle) and the color black (Doji candle).
Plot vertical lines using the conditions with the min and max values representing closing prices and opening prices. Make sure to make the line’s width extra big so that the body of the candle appears sufficiently enough that the chart is deemed a candlestick chart.
def ohlc_plot(Data, window, name): Chosen = Data[-window:, ] for i in range(len(Chosen)): plt.vlines(x = i, ymin = Chosen[i, 2], ymax = Chosen[i, 1], color = 'black', linewidth = 1) if Chosen[i, 3] > Chosen[i, 0]: color_chosen = 'green' plt.vlines(x = i, ymin = Chosen[i, 0], ymax = Chosen[i, 3], color = color_chosen, linewidth = 4)
if Chosen[i, 3] < Chosen[i, 0]: color_chosen = 'red' plt.vlines(x = i, ymin = Chosen[i, 3], ymax = Chosen[i, 0], color = color_chosen, linewidth = 4) if Chosen[i, 3] == Chosen[i, 0]: color_chosen = 'black' plt.vlines(x = i, ymin = Chosen[i, 3], ymax = Chosen[i, 0], color = color_chosen, linewidth = 4) plt.grid() plt.title(name)
# Using the function ohlc_plot(my_ohlc_data, 50, '')
Knowledge must be accessible to everyone. This is why, from now on, a purchase of either one of my new books “Contrarian Trading Strategies in Python” or “Trend Following Strategies in Python” comes with free PDF copies of my first three books (Therefore, purchasing one of the new books gets you 4 books in total). The two new books listed above feature a lot of advanced indicators and strategies with a GitHub page. You can use the below link to purchase one of the two books (Please specify which one and make sure to include your e-mail in the note).
K’s Candlestick Chart
A simple transformation of candlestick charts that I have found to be useful lies with moving averages. To create the K’s Candlestick chart, we need to transform the prices using the simple moving average formula. The steps are relatively easy:
Calculate a 3-period Simple Moving Average of the opening price.
Calculate a 3-period Simple Moving Average of the high price.
Calculate a 3-period Simple Moving Average of the low price.
Calculate a 3-period Simple Moving Average of the close price.
Then, we will treat the new four columns as the candlestick data while being careful from using them in trading as they are not real prices, but simple moving averages. We are interested in visually interpreting them and determining candlestick patterns which work better using this technique. The charts below show the difference between the normal candlestick chart and the K’s candlestick chart.
The above charts show the EURUSD charted differently. We can notice how smoother the K’s candlestick chart is compared to the noisy regular chart. By noisy, the meaning here is on the interpretability of the trend. When successive red or green candles are observed, the trend is easier to be determined.
We can tweak the lookback period of the moving average, but I recommend 2 or 3 lookback periods.
It becomes clear that we are interested in two things when analyzing the market using K’s Candlesticks:
Interpretability of the trend: Similar to the Heikin-Ashi, the K’s Candlestick chart smoothes out the data in order to remove the short-term noise and to deliver a clearer picture of the current trend.
Pattern Recognition: Doji and exhaustion patterns are more prevalent in the K’s candlesticks and therefore add a confirmation factor. They also work better than in regular charts according to my experience.
The below is the syntax to create the K’s candlestick charts using moving averages.
def ma(Data, lookback, close, where):
Data = adder(Data, 1)
for i in range(len(Data)):
try: Data[i, where] = (Data[i - lookback + 1:i + 1, close].mean()) except IndexError: pass
# Cleaning Data = jump(Data, lookback) return Datadef k_candlesticks(Data, opening, high, low, close, lookback, where): # Adding the necessary columns Data = adder(Data, 4) # Averaging the Open Data = ma(Data, lookback, opening, where) # Averaging the High Data = ma(Data, lookback, high, where + 1) # Averaging the Low Data = ma(Data, lookback, low, where + 2) # Averaging the Close Data = ma(Data, lookback, close, where + 3) return Data
The K’s Doji
The Bullish Doji pattern is composed of a candle that has its closing price equal to its opening price. It usually occurs after downward trending price action and is considered a bullish reversal or a correction pattern.
The Bullish Doji pattern is based on the psychology that the balance of power has been equalized after trending in one direction. Theoretically, we are supposed to buy at the close of the second and last candle to validate the pattern. For a bullish Doji pattern to be valid, we need a bullish candle after it.
The Bearish Doji pattern is composed of a candle that has its closing price equal to its opening price. It usually occurs after upward trending price action and is considered a bearish reversal or a correction pattern.
The Bearish Doji pattern is based on the psychology that the balance of power has been equalized after trending in one direction. Theoretically, we are supposed to sell at the close of the second and last candle to validate the pattern. For a bearish Doji pattern to be valid, we need a bearish candle after it.
Creating a Scanning Algorithm
Our aim is to create an algorithm that detects this pattern and places theoretical buy and sell orders. But first, we need to code the intuition of the pattern. Let us review what we will need for the bullish Doji pattern:
The K’s closing price equals the K’s opening price.
The current close is below the last close and the one prior to it.
The previous candle is not a Doji.
Similarly, for the bearish Doji pattern, we need the following conditions:
The K’s closing price equals the K’s opening price.
The current close is above the last close and the one prior to it.
The previous candle is not a Doji.
def signal(Data, opening, close, buy, sell): for i in range(len(Data)): # Bullish Doji if Data[i, close] == Data[i, opening] and Data[i, close] < Data[i - 1, close] and Data[i, close] < Data[i - 2, close] and \ Data[i - 1, buy] == 0: Data[i, buy] = 1 # Bearish Doji if Data[i, close] == Data[i, opening] and Data[i, close] > Data[i - 1, close] and Data[i, close] > Data[i - 2, close] and \ Data[i - 1, sell] == 0: Data[i, sell] = -1 return Data
The above function takes an OHLC data array with multiple empty columns to spare and populates columns 6 (Buy) and 7 (Sell) with the conditions that we discussed earlier. We want to input 1’s in the column we call “buy” and -1 in the column we call “sell”.
This later allows you to create a function that calculates the profit and loss by looping around these two columns and taking differences in the market price to find the profit and loss of a close-to-close strategy. Then you can use a risk management function that uses stops and profit orders.
The red arrows point to a sell short signal while the green arrows point to a buy signal based on the closing prices. The next step is evaluating the signal quality.
The above charts show the signals found using the K’s Candlesticks superimposed on the regular charts on the right while respecting the same time period.
Make sure to focus on the concepts and not the code. The most important thing is to comprehend the techniques and strategies. The code is a matter of execution but the idea is something you cook in your head for a while.
If you want to see how to create all sorts of algorithms yourself, feel free to check out Lumiwealth. From algorithmic trading to blockchain and machine learning, they have hands-on detailed courses that I highly recommend.
Learn Algorithmic Trading with Python Lumiwealth
Learn how to create your own trading algorithms for stocks, options, crypto and more from the experts at Lumiwealth. Click to learn more
To sum up, what I am trying to do is to simply contribute to the world of objective technical analysis which is promoting more transparent techniques and strategies that need to be back-tested before being implemented. This way, technical analysis will get rid of the bad reputation of being subjective and scientifically unfounded.
I recommend you always follow the the below steps whenever you come across a trading technique or strategy:
Have a critical mindset and get rid of any emotions.
Back-test it using real life simulation and conditions.
If you find potential, try optimizing it and running a forward test.
Always include transaction costs and any slippage simulation in your tests.
Always include risk management and position sizing in your tests.
Finally, even after making sure of the above, stay careful and monitor the strategy because market dynamics may shift and make the strategy unprofitable.