5 min read

How to calculate an EMA Death Cross with Python 3, MetaTrader 5, and Pandas

Learn how to calculate a classic EMA Cross of Death event for your Python Trading Bot. The article uses Python, Pandas, and MetaTrader 5
Title image for ongoing writing on Automated Trading Bot with MetaTrader 5 and Python
Automated Trading Bot with MetaTrader 5 and Python

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).

How to Build a MetaTrader 5 Python Trading Bot Expert Advisor
Learn how to build your Expert Advisor in Python using MetaTrader 5 in this series. You’ll get a step by step guide with incredible explanations to help you every step of the way.

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 Death Cross

Introduction to the Cross of Death

The ‘Cross of Death’ (aka Death Cross) is a stock market chart pattern reflecting recent price weakness. In generic terms, it occurs when a shorter-term moving average drops below a longer-term moving average.

For EMA stock calculations, these time periods are frequently the 15-day moving average and 50-day moving average. I.e. a death cross occurs when the 15-day moving average drops below the 50-day moving average. Visually this might look like this:

Chart demonstrating what a simulated Cross of Death might look like for stocks. The values used are arbitrary and for demonstration purposes only. Part of the series 8 Market Signals in 8 Days for Your MetaTrader 5 Python Trading Bot.
Visual display of stock market Cross of Death. Part of the series 8 Market Signals in 8 Days for Your MetaTrader 5 Python Trading Bot

How to Calculate

A Death Cross occurs mathematically when the previous short-term moving average is above the previous long-term moving average and the current short-term moving average is below the current long-term moving average.

Simplified into pseudo-code:

  1. Determine the previous 15-day moving average prev_15
  2. Determine the previous 50-day moving average prev_50
  3. Determine the current 15-day moving average curr_15
  4. Determine the current 50-day moving average curr_50
  5. If prev_15 > prev_50 AND curr_15 < curr_50 : cross_of_death = True

This can be calculated using the Simple Moving Average (SMA) and Exponential Moving Average (EMA).

How to Code

Before demonstrating the code to calculate an EMA Death Cross, 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 a single 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 Death Cross Function

Let’s start with a generic EMA function that returns an intact Pandas Dataframe. Here’s the code:

# Define function to calculate an arbitrary EMA and return Dataframe
def calc_generic_ema_with_dataframe(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_close]*(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

The previous function returns a list of all EMA’s for the last 1000 candles (required for a weighted moving average). Therefore, to determine if a Cross of Death event has occurred, extract the close values for the last 2 candles and compare them.

Here’s the code:

import generic_ema
import pandas


# Function to calculate a generic Cross of Death
def generic_ema_death_cross(symbol, timeframe):
    # Retrieve the 15 candle EMA
    ema_15 = generic_ema.calc_generic_ema_with_dataframe(symbol=symbol, timeframe=timeframe, ema_size=15, num_rows=1000)
    # Retrieve the 50 candle EMA
    ema_50 = generic_ema.calc_generic_ema_with_dataframe(symbol=symbol, timeframe=timeframe, ema_size=50, num_rows=1000)
    # Extract the previous values. Number_of_rows - 2 for both EMA 15 and EMA 50
    prev_15 = ema_15.loc[998, 'close']
    prev_50 = ema_50.loc[998, 'close']
    # Extract the current values. Number_of_rows - 1 for EMA 15 and EMA 50
    curr_15 = ema_15.loc[999, 'close']
    curr_50 = ema_50.loc[999, 'close']
    # Compare
    if prev_15 > prev_50 and curr_15 < curr_50:
        return True
    return False

EMA Death Cross Function

Finally, the function to calculate a ‘classic’ death cross is as follows:

# Calculate a 'classic' cross of death event
def ema_death_cross(symbol):
    return generic_ema_death_cross(symbol=symbol, timeframe="D1")

Wrapping Up

Nice. Calculating an EMA Death Cross can really help your trading algorithm make smarter decisions. Let me know how you’re planning to use it 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.