How to calculate a 50-Day EMA with Python 3, Pandas, and MetaTrader 5
Automated Trading Bot with MetaTrader5 and Python
There are a ton of signals to analyze when using quantitative analysis for stock/crypto/futures/FOREX trading!
About This Series
This series demonstrates the automated analysis of 8 different market signals.
Using Python 3, Python Pandas, and MetaTrader5, I’ll show you how to calculate 8 common signals.
All code for this tutorial can be found on my GitHub, and I’ve included working code samples throughout (use at your own risk, give me a shout-out if you do).
What You Need
Requirements and assumed knowledge as follows:
- Already connected to MetaTrader 5. This article shows you how, I’m using the variant from IC Markets.
- Windows 10 or above. For reasons known only to MetaTrader, the Python API only works on Windows 😊
- Python 3. This series was built with Python 3.10
The EMA
Introduction to the Exponential Moving Average
In chart pattern analysis of stocks/forex/crypto/futures, the Exponential Moving Average (EMA) plays an important role. It is technically a weighted moving average and is frequently used to see price trends over time. Investopedia states that “the EMA is designed to improve on the idea of an SMA (Simple Moving Average) by giving more weight to the most recent price data”. As a result, the EMA is believed to respond more quickly to price changes than the SMA.
Including an EMA calculation in your Python Trading Bot could be a great way to help it make algorithmic decisions about Buy and Sell movements more effectively.
Let’s do just that!
How to Calculate
The formula for an EMA is:
EMA = Todays_Price * multiplier + Yesterdays_EMA * (1 — multiplier)
Where the multiplier is: 2/(Number_of_Periods + 1)
The very first EMA value is the SMA.
While this sounds a bit abstract, I’ll demonstrate how to do it in code in the next section.
How to Code
Before demonstrating the code to calculate an EMA, I’ll outline a couple of considerations:
- Precision. Because the EMA is a weighted average, any imprecision in the price will continue to replicate, effectively forever. The only way to eliminate any imprecision would be to calculate the EMA using every available data point for a given timeframe.
- Compute Time. Depending on your system, the compute time required to calculate an EMA can be huge. For example, in my testing, I calculated the EMA across 50,000 1-minute candles, taking approximately 15 minutes. Clearly, that is unworkable. There are techniques such as multiprocessing to speed this up, however, I’ve chosen to limit the calculation for this series.
As a result, you’ll need to figure out what level of precision you’re comfortable with. I’ve chosen to use a candle limit of 1000 rows for this series, on my system this completes in < 1 second.
Generic EMA Function
Let’s start by building a generic EMA function. This function can calculate an EMA on an arbitrary timeframe for a specified number of candles. Here’s how it works:
- Retrieve the raw_data from MetaTrader 5
- Calculate the multiplier for the most recent data
- Calculate the initial SMA
- Iterate through the Dataframe, storing each result in a new column called
ema_<whatever_ema_you_choose>
- Return the final EMA value (this can be easily modified to return the entire Dataframe if desired)
Here’s the code:
import mt5_interface
import pandas
# Define function to calculate an arbitrary EMA
def calc_generic_ema(symbol, timeframe, ema_size, num_rows):
raw_data = mt5_interface.query_historic_data(symbol=symbol, timeframe=timeframe, number_of_candles=num_rows)
# Convert into Dataframe
dataframe = pandas.DataFrame(raw_data)
# Create column string
ema_name = "ema_" + str(ema_size)
# Create the multiplier
multiplier = 2/(ema_size + 1)
# Calculate the initial value (SMA)
# pandas.set_option('display.max_columns', None) # <- use this to show all columns
# pandas.set_option('display.max_rows', None) # <- use this to show all the rows
initial_mean = dataframe['close'].head(ema_size).mean()
# Iterate through Dataframe
for i in range(len(dataframe)):
if i == ema_size:
dataframe.loc[i, ema_name] = initial_mean
elif i > ema_size:
ema_value = dataframe.loc[i, 'close'] * multiplier + dataframe.loc[i-1, ema_name]*(1-multiplier)
dataframe.loc[i, ema_name] = ema_value
else:
dataframe.loc[i, ema_name] = 0.00
# print(dataframe) # <- use this to print the dataframe if you want to inspect
return dataframe.loc[num_rows-1, ema_name]
A couple of things to note if you’re inspecting the code:
- If you want to review the Dataframe,
pandas.set_option('display.max_columns', None)
will set Pandas to show you all the columns.pandas.set_option('display.max_rows', None)
will set Pandas to show you all the rows. I’ve left these commented out in the code (along with a print statement) - The first rows equal to
ema_size
will be0.00
. This is because it is not possible to calculate the initial SMA value until the number of rows equal to EMA size is reached.
50-Day EMA Golden Cross Function
With the generic function sorted, create a new function with the required default signals stored. Here’s mine:
import generic_ema
# Function to calculate 50-day EMA. Precision set to 1000 rows
def calc_ema_50(symbol):
return generic_ema.calc_generic_ema(symbol=symbol, timeframe="D1", ema_size=15, num_rows=1000)
Note. If you’re looking for how to connect to MetaTrader, the file mt5_interface.py
has the details.
Wrapping Up
50-day EMA…eat your heart out 😊. That’s it for this market signal! I’d love to hear how you’ve used it, drop me a note in the comments below!
Say Hi!
I love hearing from my readers, so feel free to reach out. It means a ton to me when you clap for my articles or drop a friendly comment — it helps me know that my content is helping.
❤