Python Tutorial: ROC

Download the accompanying IPython Notebook for this Tutorial from Github. 

Python streamlines tasks requiring multiple steps in a single block of code. For this reason, it is a great tool for querying and performing analysis on data.

Last Tutorial, we outlined steps for calculating Relative Strength Index (RSI).
In this Tutorial, we introduce a new technical indicator, the Rate of Change (ROC).

‘The only thing constant is change’

The Rate of Change (ROC) is a technical indicator of momentum that measures the percentage change in price between the current price and the price n periods in the past.

The Rate of Change (ROC) is calculated as follows:

ROC = ((Most recent closing price - Closing price n periods ago) / Closing price n periods ago) x 100

The Rate of Change (ROC) is classed as a momentum indicator because it measures strength of price momentum. For example, if a stock’s price at the close of trading today is 10, and the closing price five trading days prior was 7, then the Rate of Change (ROC) over that time frame is approximately 43, calculated as (10 – 7 / 7) x 100 = 42.85.

 Positive values indicate upward buying pressure or momentum, while negative values below zero indicate selling pressure or downward momentum. Increasing values in either direction, positive or negative, indicate increasing momentum, and decreasing values indicate waning momentum.

The Rate of Change (ROC) is also sometimes used to indicate overbought or oversold conditions for a security. Positive values that are greater than 30 are generally interpreted as indicating overbought conditions, while negative values lower than negative 30 indicate oversold conditions.

 Let’s use Python to compute the Rate of Change (ROC).

1.) Import modules.

import pandas as pd
import numpy as np
from pandas_datareader import data as web
import matplotlib.pyplot as plt
%matplotlib inline

2.) Define function for querying daily close.

def get_stock(stock,start,end):
     return web.DataReader(stock,'google',start,end)['Close']

3.) Define function for Rate of Change (ROC).

def ROC(df, n):  
    M = df.diff(n - 1)  
    N = df.shift(n - 1)  
    ROC = pd.Series(((M / N) * 100), name = 'ROC_' + str(n))   
    return ROC

How does the ROC function work?

3.a.) Function calculates difference in most recent closing price from closing price n periods ago. Sets the value to variable M.
#M = df.diff(n - 1)

3.b.) Function calculates closing price n periods ago. Sets the value to variable N.

#N = df.shift(n - 1)

3.c.) Function creates series called ROC that is ((M/N) * 100)

#ROC = pd.Series(((M / N) * 100), name = 'ROC_' + str(n))

3.d.) Function returns ROC

#return ROC

4.) Query daily close for ‘FB’ during 2016.

df = pd.DataFrame(get_stock('FB', '1/1/2016', '12/31/2016'))

5.) Run daily close through ROC function. Save series to new column in dataframe.

df['ROC'] = ROC(df['Close'], 12)
df.tail()

6.) Plot daily close and ROC.

df.plot(y=['Close'])
df.plot(y=['ROC'])

There you have it! We created our ROC indicator. Here’s the full code:

import pandas as pd
import numpy as np
from pandas_datareader import data as web
import matplotlib.pyplot as plt
%matplotlib inline

def get_stock(stock,start,end):
     return web.DataReader(stock,'google',start,end)['Close']
    
def ROC(df, n):  
    M = df.diff(n - 1)  
    N = df.shift(n - 1)  
    ROC = pd.Series(((M / N) * 100), name = 'ROC_' + str(n))   
    return ROC
    
df = pd.DataFrame(get_stock('FB', '1/1/2016', '12/31/2016'))
df['ROC'] = ROC(df['Close'], 12)
df.tail()

Capitalism 2.0 Will Include a Healthy Dose of Socialism | Eric Weinstein

 

 

‘We never saw that capitalism may be defeated by its own child, Technology’

‘Markets only really work when the value of something and the price of that good or service coincide’

‘What causes value and price to get out of alignment?’

‘Technology figures out the small size of market failures and make it rather large’

‘Musicians went from producing a private good, where price and value coincided, to in fact producing a public good’

‘The idea of taxing people to pay for a standing army and their steady diet of jazz and rock and roll probably didn’t make a lot of sense’

‘The portion of the pie that is private goods is likely to shrink’

‘Traditionally, technology has moved us from low value occupations to higher value occupations’

‘Almost all code can be broken into two kinds: code that runs once and never repeats, and code that loops over and over and over’

‘Unfortunately, what most jobs are, are some version of a loop’

‘Our technical training for occupations moves the entire population into the crosshairs of software’

‘All repetitive behaviors are in the crosshairs of software’

‘We still have the Rube Goldberg section of codes, where something happens once, never to be repeated’

‘A poem will be composed that will never need to be recomposed’

‘Most people do not see themselves as capable of doing these one off acts of inspiration, that will probably always be fairly highly rewarded’

‘They see themselves as needing a repetitive behavior on which they can build their families, hopes, and dreams’

‘That era may have changed’

‘So many souls will require respect, hope, freedom, and choice who may not be able to defend themselves in the market as our software and machines continue to get better and better’

‘This is reason why something like universal basic income comes out of a place as fiercely capitalistic as Silicon Valley’

‘These are the folks who are closest to seeing the destruction that their work may visit upon the population’

‘Technology is forcing those who are more familiar with it to become the most compassionate’

‘I’m rather confused about whether to be optimistic or pessimistic’

‘Our government is populated by people who come from softer disciplines, whether that is law or poli sci’

‘Very few people in government come from a hardcore technical background’

‘There are very few senators who can solve a partial differential equation or program a computer’

‘This is a terrible inversion of what should be happening’

‘The technical professions were turned into support roles for this leadership class’

‘During the 1950s there was a tremendous vogue for thinking of a technical intellectual elite who could in fact lead us to a more hopeful, technological, scientific tomorrow’

‘Somewhere along the line, that got lost’

‘We have the technical talent to build an optimistic future’

‘For whatever reason, we are so terrified now of those technical folks, that we keep attempting to subordinate them’

‘If you look at a society like China’s, China is certainly not falling for this trap–they are proceeding along a very different path’

‘Whether or not we understand where we are and make the correct decisions for an optimistic future, depends as to whether we have the right leadership class’

‘Do we realize that the technical people should have been making the decisions all along’

Quantopian: RSI Strategy Backtest

Relative Strength Index (RSI) Strategy Backtest

Contact: andrewshamlet@gmail.com // @andrewshamlet

Download the IPython Notebook that accompanies this Tutorial from Github. 

View the Quantopian Backtest here. 

Summary

  • The Relative Strength Index (RSI) is a momentum indicator that compares the magnitude of recent gains and losses over a specified time period. RSI values range from 0 to 100.
  • For this strategy, we buy $FB when the RSI is less than 30, and we will sell $FB when the RSI is greater than 70. The RSI will be calculated at a minutely frequency, as opposed to a daily frequency.
  • During 01/01/16 – 12/31/16,
    • The RSI Strategy produces 32.2% return, resulting in $3,220 pre-tax return.
    • FB Buy & Hold produces 10.0% return, resulting in $1,000 pre-tax return.
    • SPY Buy & Hold produces 12.0% return, resulting in $1,200 pre-tax return.
    • Compared to the SPY Buy & Hold, the RSI Strategy produces $2,220 Alpha whereas FB Buy & Hold produces ($200) Alpha, both on $10,000, principal.
  • During 05/19/12 – 12/31/16,
    • The RSI Strategy produces 147.4% return, resulting in $14,740 pre-tax return.
    • FB Buy & Hold produces 238.5% return, resulting in $23,850 pre-tax return.
    • SPY Buy & Hold produces 89.6% return, resulting in $8,960 pre-tax return.
    • Compared to SPY Buy & Hold, the RSI Strategy produces $5,780 Alpha whereas FB Buy & Hold produces $14,890 Alpha, both on $10,000 principal.
  • Thus, on the broader time horizon, FB Buy & Hold outperforms the RSI Strategy.
  • The question still stands: what about 2016 makes the RSI Strategy superior in performance to FB Buy & Hold?

 

Introduction 

In this post, we use Quantopian to build and backtest a Relative Strength Index (RSI) trading strategy.

 

Quantopian

About Quantopian:

Quantopian provides capital, education, data, a research environment, and a development platform to algorithm authors (quants). Quantopian provides everything a quant needs to create a strategy and profit from it.

Quantopian’s members include finance professionals, scientists, developers, and students from more than 180 countries from around the world. The members collaborate in our forums and in person at regional meetups, workshops, and QuantCon, Quantopian’s flagship annual event.”

In other words, Quantopian is a website where one can build, test, and deploy trading strategies, using Python.

 

Relative Strength Index

To review, the Relative Strength Index (RSI) is a momentum indicator that compares the magnitude of recent gains and losses over a specified time period to measure speed and change of price movements of a security. It is primarily used to identify overbought or oversold conditions in the trading of an asset.

RSI values range from 0 to 100.

The Relative Strength Index (RSI) is calculated as follows:

RSI = 100 - 100 / (1 + RS)

RS = Average gain of last 14 trading days / Average loss of last 14 trading days

 

Strategy

For this strategy, we buy $FB when the RSI is less than 30, and we will sell $FB when the RSI is greater than 70. The RSI will be calculated at a minutely frequency, as opposed to a daily frequency.

Trading Strategy

Buy - RSI < 30

Sell - RSI > 70

 

Code

Here is the Python code for the RSI Strategy.

import talib
import numpy as np
import pandas as pd

def initialize(context):
    context.stocks = symbols('FB')
    context.pct_per_stock = 1.0 / len(context.stocks)
    context.LOW_RSI = 30
    context.HIGH_RSI = 70
    
    set_benchmark(sid(42950))  
    
def handle_data(context, data):
    prices = data.history(context.stocks, 'price', 40, '1d')

    rsis = {}
    
    for stock in context.stocks:
        rsi = talib.RSI(prices[stock], timeperiod=14)[-1]
        rsis[stock] = rsi
        
        current_position = context.portfolio.positions[stock].amount
        
        if rsi > context.HIGH_RSI and current_position > 0 and data.can_trade(stock):
            order_target(stock, 0)

        elif rsi < context.LOW_RSI and current_position == 0 and data.can_trade(stock):
            order_target_percent(stock, context.pct_per_stock)

    record(FB_rsi=rsis[symbol('FB')])

At its foundation, Quantopian code is made up of three chunks: import modules, initialize, and handle_data.

1.) First we import the Talib, Numpy, and Pandas modules. As we’ll see, Talib streamlines the calculation of Technical Indicators.

import talib 
import numpy as np 
import pandas as pd

2.)  The initialize function:

def initialize(context): 
     context.stocks = symbols('FB') 
     context.pct_per_stock = 1.0 / len(context.stocks) 
     context.LOW_RSI = 30 
     context.HIGH_RSI = 70 

     set_benchmark(sid(42950)) 

2.a.) Define the security to trade, $FB.

context.stocks = symbols('FB') 

2.b.) Define the weight of each security. Since the RSI Strategy trades one security, the weight is 1.0. If there were two securities, the weight would be 0.5.

context.pct_per_stock = 1.0 / len(context.stocks) 

2.c.) Define the LOW_RSI value as 30

context.LOW_RSI = 30 

2.d.) Define the HIGH_RSI value as 70

context.HIGH_RSI = 70 

2.e.) Define the benchmark to which we will compare our strategy. In the example, the benchmark is set to $FB, essentially a buy and hold strategy. Remove ‘set_benchmark()’ to set the benchmark to the standard, ‘SPY’, or market rate.

set_benchmark(sid(42950)) 

3.)  The handle_data function:

def handle_data(context, data): 
     prices = data.history(context.stocks, 'price', 40, '1d') 

     rsis = {} 

     for stock in context.stocks: 
          rsi = talib.RSI(prices[stock], timeperiod=14)[-1] 
          rsis[stock] = rsi 

          current_position = context.portfolio.positions[stock].amount 

          if rsi > context.HIGH_RSI and current_position > 0 and data.can_trade(stock): 
               order_target(stock, 0) 

          elif rsi < context.LOW_RSI and current_position == 0 and data.can_trade(stock): 
               order_target_percent(stock, context.pct_per_stock) 

     record(FB_rsi=rsis[symbol('FB')])

3.a.) Query the ‘FB’ historical price data for the past 40 trading days.

prices = data.history(context.stocks, 'price', 40, '1d') 

3.b.) Create dictionary of RSI values.

rsis = {} 

3.c.) Create for loop for RSI calculation and order logic.

for stock in context.stocks: 

3.d.) Use Talib to calculate Relative Strength Index.

rsi = talib.RSI(prices[stock], timeperiod=14)[-1]

3.e.) Save Talib output to dictionary.

rsis[stock] = rsi 

3.f.) Save current portfolio positions in order to not execute too many/few orders.

current_position = context.portfolio.positions[stock].amount 

3.g.) Order logic: if RSI is greater than 70 and positions are greater than 0, then sell all positions.

if rsi > context.HIGH_RSI and current_position > 0 and data.can_trade(stock): 
               order_target(stock, 0) 

3.h.) Order logic: if RSI is less than 30 and positions are equal to 0, then buy positions equal to weight defined in initialize function.

elif rsi < context.LOW_RSI and current_position == 0 and data.can_trade(stock): 
               order_target_percent(stock, context.pct_per_stock) 

3.i.) Chart RSI data for $FB.

record(FB_rsi=rsis[symbol('FB')])

1 Year Performance

For the time period, 01/01/16 – 12/31/16

% Return Principal Pre-Tax Return Alpha
RSI Strategy 32.2% $10,000 $3,220 $2,220
FB Buy & Hold 10.0% $10,000 $1,000 ($200)
SPY Buy & Hold 12.0% $10,000 $1,200 N/A

 

We backtest the RSI Strategy with a $10,000 principal for the time period, 01/01/16 – 12/31/16. 

During 01/01/16 – 12/31/16,

  • The RSI Strategy produces 32.2% return, resulting in $3,220 pre-tax return.
  • FB Buy & Hold produces 10.0% return, resulting in $1,000 pre-tax return.
  • SPY Buy & Hold produces 12.0% return, resulting in $1,200 pre-tax return.
  • Compared to the SPY Buy & Hold, the RSI Strategy produces $2,220 Alpha whereas FB Buy & Hold produces ($200) Alpha, both on $10,000, principal.

 

 

Beyond 1 Year Performance

Yes, $2,220 Alpha on $10,000 principal is impressive.

Before we go and bet the farm, let’s see how the RSI Strategy performs over a longer time period.

Since the ‘FB’ IPO occurred on 05/18/12, we will backtest for the period 05/19/12 – 12/31/16.

For the time period, 05/19/12 – 12/31/16

% Return Principal Pre-Tax Return Alpha
RSI Strategy 147.4% $10,000 $14,740 $5,780
FB Buy & Hold 238.5% $10,000 $23,850 $14,890
SPY Buy & Hold 89.6% 10,000 $8,960 N/A

During 05/19/12 – 12/31/16,

  • The RSI Strategy produces 147.4% return, resulting in $14,740 pre-tax return.
  • FB Buy & Hold produces 238.5% return, resulting in $23,850 pre-tax return.
  • SPY Buy & Hold produces 89.6% return, resulting in $8,960 pre-tax return.
  • Compared to SPY Buy & Hold, the RSI Strategy produces $5,780 Alpha whereas FB Buy & Hold produces $14,890 Alpha, both on $10,000 principal.

Thus, on the broader time horizon, FB Buy & Hold outperforms the RSI Strategy.

 

Concluding Thought

Over the long term, money would go further with the FB Buy & Hold strategy.

The question still stands: what about 2016 makes the RSI Strategy superior in performance to FB Buy & Hold?

Until next time!

Python Tutorial: RSI

Download the accompanying IPython Notebook for this Tutorial from Github. 

Python streamlines tasks requiring multiple steps in a single block of code. For this reason, it is a great tool for querying and performing analysis on data.

Last Tutorial, we outlined steps for calculating Price Channels.

In this Tutorial, we introduce a new technical indicator, the Relative Strenght Index (RSI).

The Relative Strength Index (RSI) is a momentum indicator developed by noted technical analyst Welles Wilder, that compares the magnitude of recent gains and losses over a specified time period to measure speed and change of price movements of a security. It is primarily used to identify overbought or oversold conditions in the trading of an asset.

The Relative Strength Index (RSI) is calculated as follows:

RSI = 100 - 100 / (1 + RS)

RS = Average gain of last 14 trading days / Average loss of last 14 trading days

RSI values range from 0 to 100.

Traditional interpretation and usage of the RSI is that RSI values of 70 or above indicate that a security is becoming overbought or overvalued, and therefore may be primed for a trend reversal or corrective pullback in price. On the other side, an RSI reading of 30 or below is commonly interpreted as indicating an oversold or undervalued condition that may signal a trend change or corrective price reversal to the upside.

Let’s use Python to compute the Relative Strenght Index (RSI).

1.) Import modules (numpy included).

import pandas as pd
import numpy as np
from pandas_datareader import data as web
import matplotlib.pyplot as plt
%matplotlib inline

2.) Define function for querying daily close.

def get_stock(stock,start,end):
 return web.DataReader(stock,'google',start,end)['Close']

3.) Define function for RSI.

def RSI(series, period):
 delta = series.diff().dropna()
 u = delta * 0
 d = u.copy()
 u[delta > 0] = delta[delta > 0]
 d[delta < 0] = -delta[delta < 0]
 u[u.index[period-1]] = np.mean( u[:period] ) #first value is sum of avg gains
 u = u.drop(u.index[:(period-1)])
 d[d.index[period-1]] = np.mean( d[:period] ) #first value is sum of avg losses
 d = d.drop(d.index[:(period-1)])
 rs = pd.stats.moments.ewma(u, com=period-1, adjust=False) / \
 pd.stats.moments.ewma(d, com=period-1, adjust=False)
 return 100 - 100 / (1 + rs)

How does the RSI function work?

– 3.a.) Function creates two series of daily differences.

– 3.b.) One series is daily positive differences, i.e. gains.

– 3.c.) One series is daily negative difference, i.e. losses.

– 3.d.) Average daily positive differences for the period specified.

– 3.e.) Average daily negative difference for the period specified.

– 3.f.) RS is set equal to Exponential Moving Average of daily positive differences for the period sepcified / Exponential Moving Average of daily positive differences for the period sepcified.

– 3.g) Return 100 – 100 / (1 + RS)

 4.) Query daily close for ‘FB’ during 2016.

df = pd.DataFrame(get_stock('FB', '1/1/2016', '12/31/2016'))

5.) Run daily close through RSI function. Save series to new column in dataframe.

df['RSI'] = RSI(df['Close'], 14)
df.tail()

6.) Plot daily close and RSI.

df.plot(y=['Close'])
df.plot(y=['RSI'])

There you have it! We created our RSI indicator. Here’s the full code:

import pandas as pd
import numpy as np
from pandas_datareader import data as web
import matplotlib.pyplot as plt
%matplotlib inline

def get_stock(stock,start,end):
 return web.DataReader(stock,'google',start,end)['Close']
 
def RSI(series, period):
 delta = series.diff().dropna()
 u = delta * 0
 d = u.copy()
 u[delta > 0] = delta[delta > 0]
 d[delta < 0] = -delta[delta < 0]
 u[u.index[period-1]] = np.mean( u[:period] ) #first value is sum of avg gains
 u = u.drop(u.index[:(period-1)])
 d[d.index[period-1]] = np.mean( d[:period] ) #first value is sum of avg losses
 d = d.drop(d.index[:(period-1)])
 rs = pd.stats.moments.ewma(u, com=period-1, adjust=False) / \
 pd.stats.moments.ewma(d, com=period-1, adjust=False)
 return 100 - 100 / (1 + rs)
 
df = pd.DataFrame(get_stock('FB', '1/1/2016', '12/31/2016'))
df['RSI'] = RSI(df['Close'], 14)
df.tail()

Python Tutorial: MACD Signal Line & Centerline Crossovers

Python streamlines tasks requiring multiple steps in a single block of code. For this reason, it is a great tool for querying and performing analysis on data.

Last post, we outlined steps for calculating a stock’s MACD indicator.

In this post, we take MACD a step further by introducing Signal Line and Centerline Crossovers.

Signal Line Crossovers

Signal Line is defined as:

Signal Line: 9-day EMA of MACD Line

Signal line crossovers are the most common MACD signals. The signal line is a 9-day EMA of the MACD Line. As a moving average of the indicator, it trails the MACD and makes it easier to spot MACD turns. A bullish crossover occurs when the MACD turns up and crosses above the signal line. A bearish crossover occurs when the MACD turns down and crosses below the signal line. Crossovers can last a few days or a few weeks, it all depends on the strength of the move.

Let’s use Python to compute the Signal Line.

1.  Start with the MACD Tutorial code.

import pandas.io.data as web 
import pandas as pd 
%matplotlib inline 
import matplotlib.pyplot as plt 

names = ['FB'] 

def get_px(stock, start, end): 
     return web.get_data_yahoo(stock, start, end)['Adj Close'] 
px = pd.DataFrame({n: get_px(n, '1/1/2016', '1/17/2017') for n in names}) 
px['26 ema'] = pd.ewma(px["FB"], span=26) 
px['12 ema'] = pd.ewma(px["FB"], span=12) 
px['MACD'] = (px['12 ema'] - px['26 ema'])

2. Compute the 9 Day Exponential Moving Average of MACD.

px['Signal Line'] = pd.ewma(px['MACD'], span=9)

3. Create Signal Line Crossover Indicator. When MACD > Signal Line, 1. When MACD < Signal Line, 0.

px['Signal Line Crossover'] = np.where(px['MACD'] > px['Signal Line'], 1, 0)
px['Signal Line Crossover'] = np.where(px['MACD'] < px['Signal Line'], -1, px['Signal Line Crossover'])

Centerline Crossovers

Centerline crossovers are the next most common MACD signals. A bullish centerline crossover occurs when the MACD Line moves above the zero line to turn positive. This happens when the 12-day EMA of the underlying security moves above the 26-day EMA. A bearish centerline crossover occurs when the MACD moves below the zero line to turn negative. This happens when the 12-day EMA moves below the 26-day EMA.

Centerline crossovers can last a few days or a few months. It all depends on the strength of the trend. The MACD will remain positive as long as there is a sustained uptrend. The MACD will remain negative when there is a sustained downtrend.

4. Create Centerline Crossover Indicator. When MACD > 0, 1. When MACD < 0, 0.

px['Centerline Crossover'] = np.where(px['MACD'] > 0, 1, 0)
px['Centerline Crossover'] = np.where(px['MACD'] < 0, -1, px['Centerline Crossover'])

Plotting Crossovers

Last post, we posed the question: ‘When would you enter the position 😕 ?’

Now that we understand Signal Line Crossovers, let’s propose that we enter the position, ‘buy’, on 1, and we exit the position, ‘sell’, on -1.

5. Create Buy/Sell Indicator, based on Signal Line Crossovers. Multiply by 2 to increase size of indicator when plotted, so ‘buy’ on 2 and ‘sell’ on -2.

px['Buy Sell'] = (2*(np.sign(px['Signal Line Crossover'] - px['Signal Line Crossover'].shift(1))))

6. Plot close price, MACD & Signal Line, and Signal Line & Centerline Crossovers.

px.plot(y=['FB'], title='Close')
px.plot(y= ['MACD', 'Signal Line'], title='MACD & Signal Line')
px.plot(y= ['Centerline Crossover', 'Buy Sell'], title='Signal Line & Centerline Crossovers', ylim=(-3,3))

There you have it! We created MACD Signal Line and Centerline Crossovers, and based on the Crossovers, plotted ‘buy’ and ‘sell’ indicators.

Based on the entry and exit points, can you calculate the P&L? Stay tuned to find out.

Here’s the full code:

import pandas.io.data as web 
import pandas as pd 
%matplotlib inline 
import matplotlib.pyplot as plt 

names = ['FB'] 

def get_px(stock, start, end): 
     return web.get_data_yahoo(stock, start, end)['Adj Close'] 
px = pd.DataFrame({n: get_px(n, '1/1/2016', '1/17/2017') for n in names}) 
px['26 ema'] = pd.ewma(px["FB"], span=26) 
px['12 ema'] = pd.ewma(px["FB"], span=12) 
px['MACD'] = (px['12 ema'] - px['26 ema'])
px['Signal Line'] = pd.ewma(px['MACD'], span=9)
px['Signal Line Crossover'] = np.where(px['MACD'] > px['Signal Line'], 1, 0)
px['Signal Line Crossover'] = np.where(px['MACD'] < px['Signal Line'], -1, px['Signal Line Crossover'])
px['Centerline Crossover'] = np.where(px['MACD'] > 0, 1, 0)
px['Centerline Crossover'] = np.where(px['MACD'] < 0, -1, px['Centerline Crossover'])
px['Buy Sell'] = (2*(np.sign(px['Signal Line Crossover'] - px['Signal Line Crossover'].shift(1))))

px.plot(y=['FB'], title='Close')
px.plot(y= ['MACD', 'Signal Line'], title='MACD & Signal Line')
px.plot(y= ['Centerline Crossover', 'Buy Sell'], title='Signal Line & Centerline Crossovers', ylim=(-3,3))

Python Tutorial: MACD (Moving Average Convergence/Divergence)

Python streamlines tasks requiring multiple steps in a single block of code. For this reason, it is a great tool for querying and performing analysis on data.

In this post, we outline steps for calculating a stock’s MACD indicator. But first, what is MACD (Moving Average Convergence/Divergence)?

Developed by Gerald Appel in the late seventies, MACD is one of the simplest and most effective momentum indicators available. MACD turns two trend-following indicators, moving averages, into a momentum oscillator by subtracting the longer moving average from the shorter moving average. As a result, MACD offers the best of both worlds: trend following and momentum.

To calculate MACD, the formula is:

MACD: (12-day EMA - 26-day EMA)

EMA stands for Exponential Moving Average.

With that background, let’s use Python to compute MACD.

1. Start with the 30 Day Moving Average Tutorial code.

import pandas as pd
import pandas.io.data as web

stocks = ['FB']
def get_stock(stock, start, end):
     return web.get_data_yahoo(stock, start, end)['Adj Close']
px = pd.DataFrame({n: get_px(n, '1/1/2016', '12/31/2016') for n in names})
px

2. Compute the 26 Day Exponential Moving Average. We must call the column by the stock ticker.

px['26 ema'] = pd.ewma(px["FB"], span=26)

3. Then the 12 Day Exponential Moving Average.

px['12 ema'] = pd.ewma(px["FB"], span=12)

4. Subtract the 26 Day EMA from the 12 Day EMA, arriving at the MACD.

px['MACD'] = (px['12 ema'] - px['26 ema'])

5. Plot close price against MACD.

px.plot(y= ['FB'], title='FB')
px.plot(y= ['MACD'], title='MACD')

There you have it! We created our MACD indicator. Here’s the full code:

import pandas.io.data as web
import pandas as pd
%matplotlib inline
import matplotlib.pyplot as plt

names = ['FB']
def get_px(stock, start, end): 
     return web.get_data_yahoo(stock, start, end)['Adj Close']
px = pd.DataFrame({n: get_px(n, '1/1/2016', '1/17/2017') for n in names})
px['26 ema'] = pd.ewma(px["FB"], span=26)
px['12 ema'] = pd.ewma(px["FB"], span=12)
px['MACD'] = (px['12 ema'] - px['26 ema'])
px.plot(y= ['FB'], title='FB')
px.plot(y= ['MACD'], title='MACD')

So when would you enter the position 😕 ?