A Comprehensive Guide to Implementing a Moving Average Distance Strategy
Written on
Chapter 1: Introduction to Moving Averages
Moving averages are among the most straightforward indicators in trading. Despite their limitations, they have consistently proven effective over time. This guide walks you through a contrarian distance strategy from start to finish using Python.
I have recently published a new book following the success of my previous work. This book delves into advanced trend-following indicators and strategies, complete with a GitHub repository for ongoing code updates. It features an optimized color palette for printing. If this piques your interest, you can visit the Amazon link below, or reach out to me on LinkedIn for a PDF version.
Trend Following Strategies in Python: How to Use Indicators to Follow the Trend
Amazon.com: Trend Following Strategies in Python: How to Use Indicators to Follow the Trend.: 9798756939620: Kaabar…
www.amazon.com
Chapter 2: Retrieving Historical OHLC Data
One of the most popular trading platforms among retail traders is MetaTrader5. This robust software includes its programming language and boasts substantial community support, allowing users to export both short-term and long-term FX data.
To begin, download MetaTrader5 from its official site. After setting up a demo account, we can import the necessary library in Python to retrieve OHLC data.
A library is a collection of structured functions that can be imported into our Python environment. To install the MetaTrader5 library, simply open your Python prompt and type:
pip install MetaTrader5
This command will add the library to your local Python setup. Next, we'll import it along with other essential libraries:
import datetime # For date handling
import pytz # For time zone management
import pandas as pd # For DataFrame manipulation
import MetaTrader5 as mt5 # To import OHLC data
import matplotlib.pyplot as plt # For plotting charts
import numpy as np # For array manipulation
Here, each alias (like plt for matplotlib.pyplot) simplifies our code by allowing us to reference libraries with shorter names. You can find the official documentation for the MetaTrader5 library here.
We can choose which timeframe to import data from; for instance, we can work with 30-minute and hourly bars:
# Selecting the 30-minute timeframe
frame_M30 = mt5.TIMEFRAME_M30
# Selecting the hourly timeframe
frame_H1 = mt5.TIMEFRAME_H1
Next, we define a variable to capture the current date, which will help our algorithm determine when to stop importing data:
# Defining the current date
now = datetime.datetime.now()
It’s advisable to execute these code snippets sequentially to fully grasp each step. Below is a function to specify which assets we want to analyze. For simplicity, we'll only consider two currency pairs: EURUSD and USDCHF.
def asset_list(asset_set):
if asset_set == 'FX':
assets = ['EURUSD', 'USDCHF']return assets
Now, let’s establish a connection to MetaTrader5, set the current date, and extract the desired data. The parameters for year, month, and day will be determined by us to specify the start date for data retrieval. Note that I’ve set the timezone to Europe/Paris; you should adjust it to your local timezone for more accurate results.
def get_quotes(time_frame, year=2005, month=1, day=1, asset="EURUSD"):
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
Lastly, we will clean the results to create a tidy array. The following function retrieves historical 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
To import the EURUSD OHLC historical data, we use the following code:
# Choosing the horizon
horizon = 'M30'
# Creating an array for EURUSD with M30 data since 2019
EURUSD = mass_import(0, horizon)
Now we have the EURUSD OHLC data from 2019.
Chapter 3: Understanding Moving Averages
Moving averages help confirm trends and maintain our position within them. Their simplicity and effectiveness in technical analysis have made them a staple for traders. We can leverage them to identify support and resistance levels, stops and targets, and to discern the overall market trend.
As the term suggests, a moving average is the arithmetic mean of a set of values, commonly used in statistics and various aspects of daily life. Mathematically, it can be expressed as:
Observations show that moving averages can offer significant dynamic support and resistance levels, which are useful for placing orders if the market retraces.
The following code outlines how to create a moving average function:
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
The function below calculates the moving average on the array named my_data for a lookback period of 200, placing the results in an additional column:
my_data = ma(my_data, 200, 3, 4)
If you're interested in expanding your knowledge of technical indicators and strategies, consider checking out my book:
The Book of Trading Strategies
Amazon.com: The Book of Trading Strategies: 9798532885707: Kaabar, Sofien: Books
www.amazon.com
Chapter 4: Developing the Distance Strategy
The price's distance from its moving average can signal potential corrections, making this a mean-reversion strategy based on the relative distances between two variables. A bullish signal is triggered when the Distance Indicator reaches 0, anticipating a move back toward the moving average, while a bearish signal occurs at 100.
To implement this, follow the syntax below:
lookback = 60
my_data = adder(my_data, 10)
def stochastic(Data, lookback, close, where, genre='High-Low'):
Data = adder(Data, 1)
if genre == 'High-Low':
for i in range(len(Data)):
try:
Data[i, where] = (Data[i, close] - min(Data[i - lookback + 1:i + 1, 2])) / (max(Data[i - lookback + 1:i + 1, 1]) - min(Data[i - lookback + 1:i + 1, 2]))except ValueError:
passData[:, where] = Data[:, where] * 100
Data = jump(Data, lookback)
return Data
The following function calculates the distance indicator:
def distance_indicator(Data, lookback, close, where):
Data = adder(Data, 2)
Data = ma(Data, lookback, close, where)
Data[:, where + 1] = Data[:, close] - Data[:, where]
Data = stochastic(Data, lookback, where + 1, where + 2, genre='Normalization')
Data = deleter(Data, where, 2)
return Data
The signal function below indicates that a buy order can be initiated when the indicator hits 0, while a sell order is suggested when it reaches 100:
def signal(Data, indicator, buy, sell):
Data = adder(Data, 2)
for i in range(len(Data)):
if Data[i, indicator] == 0.0000 and all(Data[i - j, buy] == 0 for j in range(1, 5)):
Data[i, buy] = 1elif Data[i, indicator] == 100.0000 and all(Data[i - j, sell] == 0 for j in range(1, 5)):
Data[i, sell] = -1return Data
For more articles on trading strategies, consider subscribing to my daily newsletter, which includes my Medium articles, additional trading strategies, coding lessons, and a free PDF copy of my first book. You can expect 5-7 articles weekly with a paid subscription and 1-2 articles weekly with the free plan.
Chapter 5: Final Thoughts
Remember always to conduct back-tests. It's crucial to maintain skepticism about others' strategies. What works for me might not suit you. I advocate for self-directed learning; grasp the concepts, intuition, and conditions of the strategy, and then develop your own approach to back-test and refine it before going live.
I also recently launched an NFT collection aimed at supporting various humanitarian and medical causes. The Society of Light is a limited collection where a portion of each sale is donated to charity. Here are some benefits of purchasing these NFTs:
- High-potential gain: I'm focusing remaining sales proceeds on marketing to maximize secondary market value, with a portion of royalties donated to the charity.
- Art collection and portfolio diversification: Owning avatars representing good deeds is rewarding. Investing doesn't have to be purely profit-driven; it can also benefit others.
- Flexible donations: Allocate funds to various charities as you see fit.
- A free copy of my book in PDF: Buyers of any NFT will receive a complimentary copy of my latest book.
Consider supporting this cause by purchasing an NFT.