Algorithmic Trading with MACD in Python

A step-by-step guide to implementing a powerful strategy



Introduction


In the previous article of this algorithmic trading series, we saw how Bollinger bands can be used to make successful trades. In this article, we are going to discover yet another powerful technical indicator that is considered to be one of the most popular among traders. It’s none other than Moving Average Convergence/Divergence (MACD). We will first understand what this trading indicator is all about then, we will be implementing and backtesting a trading strategy based on this indicator in python to see how well it’s working in the real world. Let’s dive into the article!


MACD


Before moving on to MACD, it is essential to know what Exponential Moving Average (EMA) means. EMA is a type of Moving Average (MA) that automatically allocates greater weighting (nothing but importance) to the most recent data point and lesser weighting to data points in the distant past. For example, a question paper would consist of 10% of one mark questions, 40% of three mark questions, and 50% of long answer questions. From this example, you can observe that we are assigning unique weights to each section of the question paper based on the importance level (probably long answer questions are given more importance than the one mark questions).


Now, MACD is a trend-following leading indicator that is calculated by subtracting two Exponential Moving Averages (one with longer and the other shorter periods). There are three notable components in a MACD indicator.

  • MACD Line: This line is the difference between two given Exponential Moving Averages. To calculate the MACD line, one EMA with a longer period known as slow length and another EMA with a shorter period known as fast length is calculated. The most popular length of the fast and slow is 12, 26 respectively. The final MACD line values can be arrived at by subtracting the slow length EMA from the fast length EMA. The formula to calculate the MACD line can be represented as follows:



MACD LINE = FAST LENGTH EMA - SLOW LENGTH EMA


  • Signal Line: This line is the Exponential Moving Average of the MACD line itself for a given period of time. The most popular period to calculate the Signal line is 9. As we are averaging out the MACD line itself, the Signal line will be smoother than the MACD line.

  • Histogram: As the name suggests, it is a histogram purposely plotted to reveal the difference between the MACD line and the Signal line. It is a great component to be used to identify trends. The formula to calculate the Histogram can be represented as follows:


HISTOGRAM = MACD LINE - SIGNAL LINE


Now that we have an understanding of what MACD exactly is. Let’s gain some intuitions on the trading strategy we are going to build.


About the trading strategy: In this article, we are going to build a simple crossover strategy that will reveal a buy signal whenever the MACD line crosses above the Signal line. Likewise, the strategy will reveal a sell signal whenever the Signal line crosses above the MACD line. Our MACD crossover trading strategy can be represented as follows:



IF MACD LINE > SIGNAL LINE => BUY THE STOCK
IF SIGNAL LINE > MACD LINE => SELL THE STOCK


Before moving on, a note on disclaimer: This article’s sole purpose is to educate people and must be considered as an information piece but not as investment advice or so.


Implementation in Python


After coming across the process of learning what MACD is and gaining some understandings of our trading strategy, we are now set to code our strategy in python and see some interesting results. The coding part is classified into various steps as follows:



1. Importing Packages
2. Extracting Data from Alpha Vantage
3. MACD Calculation
4. MACD Plot
5. Creating the Trading Strategy
6. Plotting the Trading Lists
7. Creating our Position
8. Backtesting
9. SPY ETF Comparison


We will be following the order mentioned in the above list and buckle up your seat belts to follow every upcoming coding part.


Step-1: Importing Packages


Importing the required packages into the python environment is a non-skippable step. The primary packages are going to be Pandas to work with data, NumPy to work with arrays and for complex functions, Matplotlib for plotting purposes, and Requests to make API calls. The secondary packages are going to be Math for mathematical functions and Termcolor for font customization (optional).


Python Implementation:



import requests
import pandas as pd
import numpy as np
from math import floor
from termcolor import colored as cl
import matplotlib.pyplot as plt

plt.rcParams['figure.figsize'] = (20, 10)
plt.style.use('fivethirtyeight')


Now that we have imported all the essential packages into our python environment. Let’s proceed with pulling the historical data of Google with Alpha Vantage’s powerful stock API.


Step-2: Extracting Data from Alpha Vantage


In this step, we are going to pull the historical data of Google using an API endpoint provided by Alpha Vantage. Before that, a note on Alpha Vantage: Alpha Vantage provides free stock APIs through which users can access a wide range of data like real-time updates, and historical data on equities, currencies, and cryptocurrencies. Make sure that you have an account on Alpha Vantage, only then, you will be able to access your secret API key (a crucial element for pulling data using an API).


Python Implementation:



def get_historical_data(symbol, start_date = None):
    api_key = open(r'api_key.txt')
    api_url = f'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol={symbol}&apikey={api_key}&outputsize=full'
    raw_df = requests.get(api_url).json()
    df = pd.DataFrame(raw_df[f'Time Series (Daily)']).T
    df = df.rename(columns = {'1. open': 'open', '2. high': 'high', '3. low': 'low', '4. close': 'close', '5. adjusted close': 'adj close', '6. volume': 'volume'})
    for i in df.columns:
        df[i] = df[i].astype(float)
    df.index = pd.to_datetime(df.index)
    df = df.iloc[::-1].drop(['7. dividend amount', '8. split coefficient'], axis = 1)
    if start_date:
        df = df[df.index >= start_date]
    return df

googl = get_historical_data('GOOGL', '2020-01-01')
googl


Output:



Code Explanation: The first thing we did is to define a function named ‘get_historical_data’ that takes the stock’s symbol (‘symbol’) as a required parameter and the starting date of the historical data (‘start_date’) as an optional parameter. Inside the function, we are defining the API key and the URL and stored them into their respective variable. Next, we are extracting the historical data in JSON format using the ‘get’ function and stored it into the ‘raw_df’ variable. After doing some processes to clean and format the raw JSON data, we are returning it in the form of a clean Pandas dataframe. Finally, we are calling the created function to pull the historic data of Google from the starting of 2020 and stored it into the ‘googl’ variable.


Step-3: MACD Calculation


In this step, we are going to calculate all the components of the MACD indicator from the extracted historical data of Google.


Python Implementation:



def get_macd(price, slow, fast, smooth):
    exp1 = price.ewm(span = fast, adjust = False).mean()
    exp2 = price.ewm(span = slow, adjust = False).mean()
    macd = pd.DataFrame(exp1 - exp2).rename(columns = {'close':'macd'})
    signal = pd.DataFrame(macd.ewm(span = smooth, adjust = False).mean()).rename(columns = {'macd':'signal'})
    hist = pd.DataFrame(macd['macd'] - signal['signal']).rename(columns = {0:'hist'})
    frames =  [macd, signal, hist]
    df = pd.concat(frames, join = 'inner', axis = 1)
    return df

googl_macd = get_macd(googl['close'], 26, 12, 9)
googl_macd.tail()


Output:



Code Explanation: Firstly, we are defining a function named ‘get_macd’ that takes the stock’s price (‘prices’), length of the slow EMA (‘slow’), length of the fast EMA (‘fast’), and the period of the Signal line (‘smooth’).


Inside the function, we are first calculating the fast and slow length EMAs using the ‘ewm’ function provided by Pandas and stored them into the ‘ema1’ and ‘ema2’ variables respectively. Next, we calculated the values of the MACD line by subtracting the slow length EMA from the fast length EMA and stored it into the ‘macd’ variable in the form of a Pandas dataframe. Followed by that, we defined a variable named ‘signal’ to store the values of the Signal line calculated by taking the EMA of the MACD line’s values (‘macd’) for a specified number of periods. Then, we calculated the Histogram values by subtracting the MACD line’s values (‘macd’) from the Signal line’s values (‘signal’) and stored them into the ‘hist’ variable.


Finally, we combined all the calculated values into one dataframe using the ‘concat’ function by the Pandas package and returned the final dataframe. Using the created function, we stored all the MACD components that are calculated from the stock price of Google and stored it into the ‘googl_macd’ variable. From the output, you could see that our dataframe has all the components we discussed before.


Step-4: MACD Plot


In this step, we are going to plot the calculated MACD components to make more sense out of them. Before moving on, it is necessary to know that leading indicators are plotted below the stock prices separately. MACD being a leading indicator needs to be plotted the same way.


Python Implementation:



def plot_macd(prices, macd, signal, hist):
    ax1 = plt.subplot2grid((8,1), (0,0), rowspan = 5, colspan = 1)
    ax2 = plt.subplot2grid((8,1), (5,0), rowspan = 3, colspan = 1)

    ax1.plot(prices)
    ax2.plot(macd, color = 'grey', linewidth = 1.5, label = 'MACD')
    ax2.plot(signal, color = 'skyblue', linewidth = 1.5, label = 'SIGNAL')

    for i in range(len(prices)):
        if str(hist[i])[0] == '-':
            ax2.bar(prices.index[i], hist[i], color = '#ef5350')
        else:
            ax2.bar(prices.index[i], hist[i], color = '#26a69a')

    plt.legend(loc = 'lower right')

plot_macd(googl['close'], googl_macd['macd'], googl_macd['signal'], googl_macd['hist'])


Output:



We are not going to dive deep into the code used to produce the above MACD plot instead, we are going to discuss the plot. There are two panels in this plot: the top panel is the plot of Google’s close prices, and the bottom panel is a series of plots of the calculated MACD components. Let’s break and see each and every component.


The first and most visible component in the bottom panel is obviously the plot of the calculated Histogram values. You can notice that the plot turns red whenever the market shows a negative trend and turns green whenever the market reveals a positive trend. This feature of the Histogram plot becomes very handy when it comes to identifying the trend of the market. The Histogram plot spreads larger whenever the difference between the MACD line and the Signal line is huge and it is noticeable that the Histogram plot shrinks at times representing the difference between the two of the other components is comparatively smaller.


The next two components are the MACD line and the Signal line. The MACD line is the grey-colored line plot that shows the difference between the slow length EMA and the fast length EMA of Google’s stock prices. Similarly, the blue-colored line plot is the Signal line that represents the EMA of the MACD line itself. Like we discussed before, the Signal line seems to be more of a smooth-cut version of the MACD line because it is calculated by averaging out the values of the MACD line itself. That’s it about the chart which is shown above as output. Let’s proceed to the next step.


Step-5: Creating the Trading Strategy


In this step, we are going to implement the discussed MACD trading strategy in python.


Python Implementation:



def implement_macd_strategy(prices, data):    
    buy_price = []
    sell_price = []
    macd_signal = []
    signal = 0

    for i in range(len(data)):
        if data['macd'][i] > data['signal'][i]:
            if signal != 1:
                buy_price.append(prices[i])
                sell_price.append(np.nan)
                signal = 1
                macd_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                macd_signal.append(0)
        elif data['macd'][i] < data['signal'][i]:
            if signal != -1:
                buy_price.append(np.nan)
                sell_price.append(prices[i])
                signal = -1
                macd_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                macd_signal.append(0)
        else:
            buy_price.append(np.nan)
            sell_price.append(np.nan)
            macd_signal.append(0)
            
    return buy_price, sell_price, macd_signal
            
buy_price, sell_price, macd_signal = implement_macd_strategy(googl['close'], googl_macd)


Code Explanation: First, we are defining a function named ‘implement_macd_strategy’ which takes the stock prices (‘data’), and MACD data (‘data’) as parameters.


Inside the function, we are creating three empty lists (buy_price, sell_price, and macd_signal) in which the values will be appended while creating the trading strategy.


After that, we are implementing the trading strategy through a for-loop. Inside the for-loop, we are passing certain conditions, and if the conditions are satisfied, the respective values will be appended to the empty lists. If the condition to buy the stock gets satisfied, the buying price will be appended to the ‘buy_price’ list, and the signal value will be appended as 1 representing to buy the stock. Similarly, if the condition to sell the stock gets satisfied, the selling price will be appended to the ‘sell_price’ list, and the signal value will be appended as -1 representing to sell the stock.


Finally, we are returning the lists appended with values. Then, we are calling the created function and stored the values into their respective variables. The list doesn’t make any sense unless we plot the values. So, let’s plot the values of the created trading lists.


Step-6: Plotting the Trading Lists


In this step, we are going to plot the created trading lists to make sense out of them.


Python Implementation: