Technical indicators Moving Average Convergence and Divergence (MACD)


Background

Moving average convergence/divergence (MACD) is a technical indicator to help investors identify price trends, measure trend momentum, and identify market entry points for buying and selling, it shows the relationship between two exponential moving averages (EMAs) of a security’s price.

The MACD line is calculated by subtracting the 26-period EMA from the 12-period EMA. A 9-day EMA of the MACD line is called the signal line, plotted on top of the MACD line, which can function as a trigger for buy or sell signals. When the MACD line crosses above the signal line it indicates a buy signal, when the MACD line crosses below the signal line it is the sell signal.

Python Implementation

import pandas as pd
import sqlite3

# load S&P500 data from stored database
sp500_db = sqlite3.connect(database="sp500_data.sqlite")

df = pd.read_sql_query(sql="SELECT * FROM SP500",
                       con=sp500_db,
                       parse_dates={"Date"})
df.head()

indexDateTickerAdj CloseCloseHighLowOpenVolumegarmin_klass_volrsiatr
002014-04-29A35.04953438.12589338.30472237.17453438.1187404688612.0-0.002274NaNNaN
112014-04-29AAL33.47674235.50999835.65000234.97000135.2000018994200.0-0.000788NaNNaN
222014-04-29AAPL18.63333321.15464221.28500021.05392821.205000337377600.0-0.006397NaNNaN
332014-04-29ABBV34.03498551.36999951.52999950.75999850.9399995601300.0-0.062705NaNNaN
442014-04-29ABT31.83151838.54000138.72000138.25999838.3699994415600.0-0.013411NaNNaN
# remove irrelevant columns
df = df.drop('index', axis=1)
df.head()

DateTickerAdj CloseCloseHighLowOpenVolumegarmin_klass_volrsiatr
02014-04-29A35.04953438.12589338.30472237.17453438.1187404688612.0-0.002274NaNNaN
12014-04-29AAL33.47674235.50999835.65000234.97000135.2000018994200.0-0.000788NaNNaN
22014-04-29AAPL18.63333321.15464221.28500021.05392821.205000337377600.0-0.006397NaNNaN
32014-04-29ABBV34.03498551.36999951.52999950.75999850.9399995601300.0-0.062705NaNNaN
42014-04-29ABT31.83151838.54000138.72000138.25999838.3699994415600.0-0.013411NaNNaN
# remove GEV and SOLV which were newly added (no sufficient data for MACD calculation)
df = df.drop(df[(df['Ticker'] =='GEV') | (df['Ticker'] == 'SOLV')].index)
import pandas_ta

# calculate MACD
def compute_macd(close):
    macd = pandas_ta.macd(close=close, length=20).iloc[:,0]
    return macd.sub(macd.mean()).div(macd.std())

# calculate MACD signal
def compute_macd_signal(close):
    macd_signal = pandas_ta.macd(close=close, length=20).iloc[:,2]
    return macd_signal.sub(macd_signal.mean()).div(macd_signal.std())

df = df.set_index(['Date', 'Ticker'])
df['macd'] = df.groupby(level=1, group_keys=False)['Adj Close'].apply(compute_macd)
df['macd signal'] = df.groupby(level=1, group_keys=False)['Adj Close'].apply(compute_macd_signal)

df.tail()

Adj CloseCloseHighLowOpenVolumegarmin_klass_volrsiatrmacdmacd signal
DateTicker
2024-04-25XYL130.610001130.610001131.199997128.100006129.619995963600.00.00026461.4827450.6653060.3551460.255276
YUM141.559998141.559998142.169998140.389999141.9799961693100.00.00007665.6681630.3225660.5508300.285784
ZBH119.750000119.750000121.349998118.769997120.7099991078800.00.00020635.636078-0.350196-0.889200-0.646480
ZBRA292.529999292.529999293.290009271.630005274.359985674700.00.00135556.1722720.500501-0.391439-0.242456
ZTS153.360001153.360001153.589996150.039993150.9700014567200.00.00017839.8096191.374957-3.202379-3.584353
# update the database
df = df.reset_index()
df.to_sql(name="SP500",
          con=sp500_db,
          if_exists="replace",
          index=True)
sp500_db.close()
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import warnings
warnings.filterwarnings('ignore')

# select AAPL
aapl = df[df['Ticker'] == 'AAPL'].set_index('Date')

# only select the data from 2023-01-01
aapl_new = aapl[aapl.index > datetime(2023,1,1)]
aapl_new['macd histogram'] = aapl_new['macd'] - aapl['macd signal']

# create a plotly figure
fig = make_subplots(rows=2, cols=1)

# adjust close
fig.add_trace(go.Scatter(x=aapl_new.index, y=aapl_new['Adj Close'], mode='lines', name='Adjust Close'), row=1, col=1)

# MACD and signal lines
fig.add_trace(go.Scatter(x=aapl_new.index, y=aapl_new['macd'], mode='lines', name='MACD'), row=2, col=1)
fig.add_trace(go.Scatter(x=aapl_new.index, y=aapl_new['macd signal'], mode='lines', name='MACD Signal'), row=2, col=1)

# histogram
fig.add_trace(go.Bar(x=aapl_new.index, y=aapl_new['macd histogram'], name='MACD Histogram',
                marker_color=['green' if val >= 0 else 'red' for val in aapl_new['macd histogram']]), row=2, col=1)


# customize the chart
fig.update_xaxes(rangeslider=dict(visible=False))
fig.update_layout(plot_bgcolor='#efefff', font_size=10,width=900)
fig.update_layout(title="AAPL Moving Average Convergence/Divergence")
# show the chart
fig.show()

AAPL Moving Average Convergence Divergence

Reference

What is MACD? by Brian Dolan on Investopedia


Author: wenvenn
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source wenvenn !