One of the most famous indicators out there is the Moving Average Convergence Divergence — MACD. It is a heavily followed oscillator that can be used in trend-following systems or in contrarian strategies.
I have released a new book after the success of my previous one “Trend Following Strategies in Python”. It features advanced contrarian indicators and strategies with a GitHub page dedicated to the continuously updated code. If you feel that this interests you, feel free to visit the below Amazon link (which contains a sample), or if you prefer to buy the PDF version, you could check the link at the end of the article.
Contrarian Trading Strategies in Python
Amazon.com: Contrarian Trading Strategies in Python: 9798434008075: Kaabar, Sofien: Bookswww.amazon.com
Creating the MACD From Scratch
The MACD is probably the second most known oscillator after the RSI. One that is heavily followed by traders. It stands for moving average convergence divergence and it is used mainly for divergences and flips. Many people also consider it a trend-following indicator but others use graphical analysis on it to find reversal points, making the MACD a versatile indicator.
Before we start building this oscillator, we must understand what moving averages are and how do we code them. Moving averages help us confirm and ride the trend. They are the most known technical indicator and this is because of their simplicity and their proven track record of adding value to the analyses. We can use them to find support and resistance levels, stops and targets, and to understand the underlying trend. This versatility makes them an indispensable tool in our trading arsenal.
As the name suggests, this is your plain simple mean that is used everywhere in statistics and basically any other part in our lives. It is simply the total values of the observations divided by the number of observations. Mathematically speaking, it can be written down as:
The code for the moving average can be written down as the following:
# The function to add a number of columns inside an array
def adder(Data, times):
for i in range(1, times + 1):
new_col = np.zeros((len(Data), 1), dtype = float)
Data = np.append(Data, new_col, axis = 1)
return Data
# The function to delete a number of columns starting from an index
def deleter(Data, index, times):
for i in range(1, times + 1):
Data = np.delete(Data, index, axis = 1)
return Data
# The function to delete a number of rows from the beginning
def jump(Data, jump):
Data = Data[jump:, ]
return Data
# Example of adding 3 empty columns to an array
my_ohlc_array = adder(my_ohlc_array, 3)
# Example of deleting the 2 columns after the column indexed at 3
my_ohlc_array = deleter(my_ohlc_array, 3, 2)
# Example of deleting the first 20 rows
my_ohlc_array = jump(my_ohlc_array, 20)
# Remember, OHLC is an abbreviation of Open, High, Low, and Close and it refers to the standard historical data file
The below states that the moving average function will be called on the array named my_data for a lookback period of 200, on the column indexed at 3 (closing prices in an OHLC array). The moving average values will then be put in the column indexed at 4 which is the one we have added using the adder function.
my_data = ma(my_data, 200, 3, 4)
An exponential moving average is a special type that gives more weight to the recent values. To spare you the boring details, here is the code.
def ema(data, alpha, lookback, what, where):
alpha = alpha / (lookback + 1.0)
beta = 1 - alpha
data = ma(data, lookback, what, where)
data[lookback + 1, where] = (data[lookback + 1, what] * alpha) + (data[lookback, where] * beta)
for i in range(lookback + 2, len(data)):
try:
data[i, where] = (data[i, what] * alpha) + (data[i - 1, where] * beta)
except IndexError:
pass
return data
How is the MACD calculated? It is the difference between the 26-period exponential moving average applied to the closing price and the 12-period exponential moving average also applied to the closing price. The value found after taking the difference is called the MACD line. The 9-period exponential moving average of that calculation is called the MACD signal.
def macd(Data, what, long_ema, short_ema, signal_ema, where):
Data = adder(Data, 1)
Data = ema(Data, 2, long_ema, what, where)
Data = ema(Data, 2, short_ema, what, where + 1)
Data[:, where + 2] = Data[:, where + 1] - Data[:, where]
Data = jump(Data, long_ema)
Data = ema(Data, 2, signal_ema, where + 2, where + 3)
Data = deleter(Data, where, 2)
Data = jump(Data, signal_ema)
return Data
Strategy #1: The Flip
The flip strategy is the most known strategy to be used on the MACD. It has the following conditions:
A long (Buy) signal is generated whenever the MACD line surpasses 0.
A short (Sell) signal is generated whenever the MACD line breaks 0.
This is equivalent to the color change on the MACD as values above zero are shown in green and values below zero are shown in red.
The above plot shows the signals whenever the MACD switches signs.
Main strength: Tends to capture strong trends and has generally rare whipsaws (the line switches sign too frequently).
Main weakness: As it is composed of moving averages, it suffers from lags and is useless in ranging markets.
def signal(Data, macd_column, buy_column, sell_column):
for i in range(len(Data)):
if Data[i, macd_column] > 0 and Data[i - 1, macd_column] < 0:
Data[i, buy_column] = 1
if Data[i, macd_column] < 0 and Data[i - 1, macd_column] > 0:
Data[i, sell_column] = -1
Strategy #2: The Cross
The Crossover strategy is the intersection between the MACD line and the MACD signal:
A long (Buy) signal is generated whenever the MACD line surpasses the signal line.
A short (Sell) signal is generated whenever the MACD line breaks the signal line.
This is equivalent to a simple moving average cross strategy.
The above plot shows the signals whenever the MACD changes position with the signal line.
Main strength: Can help avoid false signals by smoothing the crossovers.
Main weakness: Trends to suffer from great lag due to extra smoothing.
def signal(Data, macd_line, macd_signal, buy, sell):
for i in range(len(Data)):
if Data[i, macd_line] > Data[i, macd_signal] and Data[i - 1, macd_line] < Data[i - 1, macd_signal]:
Data[i, buy] = 1
if Data[i, macd_line] < Data[i, macd_signal] and Data[i - 1, macd_line] > Data[i - 1, macd_signal]:
Data[i, sell] = -1
Strategy #3: Divergence
The divergence strategy is a form of a weakening momentum:
A long (Buy) signal is generated whenever the MACD line is making higher lows while the market price is making lower lows.
A short (Sell) signal is generated whenever the MACD line is making lower highs while the market price is making higher highs.
This is equivalent to the divergence strategy on the RSI.
Main strength: Early signal to trend reversals.
Main weakness: Rare occurrence with the MACD and sometimes too complicated for a divergence to be valid.
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
Summary
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.