A Comprehensive Overview of Moving Average Crossover Strategies
Written on
Chapter 1: Introduction to Moving Average Crossover Strategies
Moving average crossovers represent a foundational trading method, renowned for their simplicity despite certain limitations. This guide offers a complete walkthrough of implementing a moving average crossover strategy in Python.
I am excited to announce the release of my latest book, building on the success of my previous work. This new edition delves into advanced trend-following indicators and strategies, complete with a GitHub repository for ongoing code updates. It also features optimized colors for print. For those interested, feel free to check out the Amazon link below, or reach out to me on LinkedIn for a PDF version.
Fetching Historical OHLC Data
MetaTrader5 is a widely used trading platform within the retail trading community, offering robust features and extensive online support. It allows users to export both short-term and long-term historical foreign exchange data.
To get started, download the platform from the official website. After setting up a demo account, we can proceed to import the necessary library in Python to retrieve OHLC data from MetaTrader5.
A library is essentially a collection of structured functions that can be imported into our Python environment. To install the MetaTrader5 library, simply open your Python prompt and execute:
pip install MetaTrader5
This command installs the library locally. Next, we will import it along with other libraries we need for our analysis:
import datetime # For date management
import pytz # For time zone handling
import pandas as pd # For DataFrame manipulation
import MetaTrader5 as mt5 # For importing OHLC data
import matplotlib.pyplot as plt # For chart plotting
import numpy as np # For array operations
Using shorthand for libraries makes coding more efficient; for instance, using plt instead of matplotlib.pyplot.
For more information, refer to the official MetaTrader5 documentation.
We can begin by selecting the time frames for our data. Suppose we have two options: 30-minute and hourly bars. We can define variables to specify which time frame we want:
# Selecting the 30-minute time frame
frame_M30 = mt5.TIMEFRAME_M30
# Selecting the hourly time frame
frame_H1 = mt5.TIMEFRAME_H1
Next, we can create a variable that captures the current date to define when to stop our data import:
# Current date variable
now = datetime.datetime.now()
It's advisable to execute the following code snippets in sequence for a better understanding of the process. We will define a function to specify which assets we want to analyze. For simplicity, we'll focus on two currency pairs: EURUSD and USDCHF.
def asset_list(asset_set):
if asset_set == 'FX':
assets = ['EURUSD', 'USDCHF']return assets
Next, we will create a function to obtain OHLC data, establishing a connection to MetaTrader5 and applying the current date for data extraction.
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)
return rates_frame
Finally, we can use the get_quotes function to clean and format our data, focusing on the EURUSD pair. This will give us the historical OHLC data since January 2019.
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)
return data
# Fetching EURUSD OHLC historical data
horizon = 'M30'
EURUSD = mass_import(0, horizon)
And just like that, we have successfully acquired the EURUSD OHLC data since 2019.
Chapter 2: Understanding Moving Averages
Moving averages are essential for confirming and riding trends in trading. Their simplicity and effectiveness in analysis have made them one of the most recognized technical indicators. They serve to identify support and resistance levels, as well as stops and targets, making them an invaluable tool for traders.
The basic concept of a moving average involves calculating the mean of a set of values over a specified period. It can be mathematically represented as:
Moving Average = frac{text{Sum of Values}}{text{Number of Observations}}
Moving averages can provide dynamic support and resistance levels, guiding traders in their order placements. The following code illustrates how to implement a moving average in Python:
def adder(Data, times):
for i in range(1, times + 1):
new = np.zeros((len(Data), 1), dtype=float)
Data = np.append(Data, new, axis=1)
return Data
def deleter(Data, index, times):
for i in range(1, times + 1):
Data = np.delete(Data, index, axis=1)return Data
def jump(Data, jump):
Data = Data[jump:,]
return Data
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:
passreturn Data
We can apply the moving average function to our data as follows:
my_data = ma(my_data, 200, 3, 4)
In this case, we are calculating a 200-period moving average on the closing prices.
Chapter 3: Crafting the Crossover Strategy
A moving average crossover occurs when two moving averages of different periods intersect. This can signal a trend change, prompting traders to act. A bullish signal is indicated when a short-term moving average crosses above a long-term moving average, known as a Golden Cross. Conversely, a bearish signal is indicated when the short-term moving average crosses below the long-term moving average, referred to as a Death Cross.
To implement this, we can use the following syntax:
short_ma = 60
long_ma = 200
my_data = adder(my_data, 10)
my_data = ma(my_data, short_ma, 3, 4)
my_data = ma(my_data, long_ma, 3, 5)
The following function generates buy and sell signals based on the crossover conditions:
def signal(Data, short_ma_col, long_ma_col, buy, sell):
for i in range(len(Data)):
# Bullish Crossover
if Data[i, short_ma_col] > Data[i, long_ma_col] and Data[i - 1, short_ma_col] < Data[i - 1, long_ma_col]:
Data[i, buy] = 1# Bearish Crossover
elif Data[i, short_ma_col] < Data[i, long_ma_col] and Data[i - 1, short_ma_col] > Data[i - 1, long_ma_col]:
Data[i, sell] = -1return Data
my_data = signal(my_data, 4, 5, 6, 7)
Chapter 4: Evaluating Your Strategy
Once we have established our signals, we can simulate historical trading conditions to assess our strategy's performance. This involves calculating returns and analyzing key performance metrics, such as Signal Quality, which measures the market response after a specified period.
The Signal Quality metric can be defined as follows:
period = 20
def signal_quality(Data, closing, buy, sell, period, where):
Data = adder(Data, 1)
for i in range(len(Data)):
try:
if Data[i, buy] == 1:
Data[i + period, where] = Data[i + period, closing] - Data[i, closing]if Data[i, sell] == -1:
Data[i + period, where] = Data[i, closing] - Data[i + period, closing]except IndexError:
passreturn Data
# Applying the Signal Quality Function
my_data = signal_quality(my_data, 3, 6, 7, period, 8)
positives = my_data[my_data[:, 8] > 0]
negatives = my_data[my_data[:, 8] < 0]
# Calculating Signal Quality
signal_quality = len(positives) / (len(negatives) + len(positives))
print('Signal Quality = ', round(signal_quality * 100, 2), '%')
A Signal Quality of 56.90% indicates a potential for profitable trades.
For further insights into trading strategies, consider subscribing to my daily newsletter. It offers a wealth of articles on trading strategies, coding lessons, and more.
Always remember to conduct thorough back-tests and trust your own analysis. My indicators and methods may not suit everyone; it's crucial to develop your own understanding and modify strategies to fit your trading style.
Chapter 5: Final Thoughts
I have recently launched an NFT collection supporting various humanitarian and medical initiatives. The Society of Light aims to contribute to charitable causes with each sale. Here are some key benefits of participating in this project:
- Potential for Gain: The remaining sales will focus on marketing to enhance the NFT's value in the secondary market.
- Art and Investment: Owning avatars that symbolize charitable actions can be fulfilling, combining profit with purpose.
- Flexible Donations: Allocate funds to causes you care about.
- Free Book: Buyers will receive a complimentary PDF copy of my latest book.
Click here to explore my NFT collection and support meaningful causes.