In this Jupyter notebook, I've tried to explain how we can perform technical analysis in Python with technical indicators.
Indicators used for generating trade signals here are:
The basic strategy is to buy 100 stocks of Nifty Bank when the strategy generates a buy signal for any given indicator and sell 100 stocks after 'n' days.
Technical analysis is done for predicting short term price momentum of any asset class, hence I've decided to hold the stock only for a short term (60 days). You can go for any number of days and see what works out best!
This is merely a demonstration of how to implement and back-test technical analysis trading strategies in Python and not any investment advice.
# importing necessary libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import plotly.graph_objects as go
import pandas_datareader as web
import datetime
from datetime import date
%matplotlib inline
# Fetching Nifty Bank data from Yahoo! Finance
bank_nifty = web.get_data_yahoo('^NSEBANK',start = datetime.datetime(2015, 1, 2),
end = date.today())
bank_nifty.head()
High | Low | Open | Close | Volume | Adj Close | |
---|---|---|---|---|---|---|
Date | ||||||
2015-01-02 | 19118.849609 | 18752.199219 | 18752.199219 | 19057.800781 | 0.0 | 19057.800781 |
2015-01-05 | 19166.000000 | 18987.699219 | 19155.199219 | 19017.400391 | 0.0 | 19017.400391 |
2015-01-06 | 18874.599609 | 18388.349609 | 18874.599609 | 18430.750000 | 0.0 | 18430.750000 |
2015-01-07 | 18482.050781 | 18211.500000 | 18382.550781 | 18304.250000 | 0.0 | 18304.250000 |
2015-01-08 | 18752.300781 | 18486.199219 | 18587.099609 | 18701.400391 | 0.0 | 18701.400391 |
bank_nifty.tail()
High | Low | Open | Close | Volume | Adj Close | |
---|---|---|---|---|---|---|
Date | ||||||
2021-02-08 | 36466.449219 | 35872.250000 | 36073.851562 | 35983.648438 | 0.0 | 35983.648438 |
2021-02-09 | 36477.148438 | 35636.851562 | 36058.800781 | 36056.500000 | 0.0 | 36056.500000 |
2021-02-10 | 36227.199219 | 35428.148438 | 36042.351562 | 35783.101562 | 0.0 | 35783.101562 |
2021-02-11 | 36009.449219 | 35573.300781 | 35687.250000 | 35752.101562 | 0.0 | 35752.101562 |
2021-02-12 | 36322.800781 | 35700.250000 | 35736.601562 | 36275.351562 | 0.0 | 36275.351562 |
bank_nifty.index
DatetimeIndex(['2015-01-02', '2015-01-05', '2015-01-06', '2015-01-07', '2015-01-08', '2015-01-09', '2015-01-12', '2015-01-13', '2015-01-14', '2015-01-15', ... '2021-02-01', '2021-02-02', '2021-02-03', '2021-02-04', '2021-02-05', '2021-02-08', '2021-02-09', '2021-02-10', '2021-02-11', '2021-02-12'], dtype='datetime64[ns]', name='Date', length=1230, freq=None)
bank_nifty = bank_nifty.drop(columns=['Volume', 'Adj Close'])
bank_nifty.head()
High | Low | Open | Close | |
---|---|---|---|---|
Date | ||||
2015-01-02 | 19118.849609 | 18752.199219 | 18752.199219 | 19057.800781 |
2015-01-05 | 19166.000000 | 18987.699219 | 19155.199219 | 19017.400391 |
2015-01-06 | 18874.599609 | 18388.349609 | 18874.599609 | 18430.750000 |
2015-01-07 | 18482.050781 | 18211.500000 | 18382.550781 | 18304.250000 |
2015-01-08 | 18752.300781 | 18486.199219 | 18587.099609 | 18701.400391 |
# Plotting Bank Nifty Close Price over the years
fig = go.Figure()
fig.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.Close, name='Close'))
fig.update_layout(title='Price of Bank Nifty over the Years', xaxis_title='Date', yaxis_title='Price')
fig.show()
The technical indicators we're gonna be using here are:
Simple Moving Average is a technical indicator which calculates the average of 'Close' price of an underlying stock for a given period of time. eg. SMA_50 calculates the average of close price for the last 50 days.
Basic SMA strategy is that if the short term (fast) MA > long term (slow) MA, it's a signal to buy the stock. Similarly, if the short term (fast) MA < long term (slow) MA, it's a signal to sell the stock.
# Fast SMA (50 days)
bank_nifty['SMA_50'] = bank_nifty['Close'].rolling(50).mean()
# Slow SMA (200 days)
bank_nifty['SMA_200'] = bank_nifty['Close'].rolling(200).mean()
bank_nifty = bank_nifty.dropna()
bank_nifty.head()
High | Low | Open | Close | SMA_50 | SMA_200 | |
---|---|---|---|---|---|---|
Date | ||||||
2016-02-19 | 14395.849609 | 14207.349609 | 14243.450195 | 14344.200195 | 17028.070215 | 18231.565103 |
2016-02-25 | 13844.450195 | 13519.900391 | 13844.450195 | 13555.700195 | 16960.831211 | 18204.054600 |
2016-04-04 | 16278.500000 | 16046.500000 | 16236.900391 | 16190.599609 | 16940.355195 | 18189.920596 |
2016-05-25 | 17025.849609 | 16599.050781 | 16600.650391 | 16997.449219 | 16936.065195 | 18182.754092 |
2016-06-06 | 17754.150391 | 17636.900391 | 17710.449219 | 17671.400391 | 16946.562187 | 18179.589844 |
# Plotting daily close price, SMA_50 and SMA_200
fig_SMA = go.Figure()
fig_SMA.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.Close, name='Close'))
fig_SMA.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.SMA_50, name='SMA_50'))
fig_SMA.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.SMA_200, name='SMA_200'))
fig_SMA.update_layout(title='Price History of Bank Nifty and SMA',
xaxis_title='Date',
yaxis_title='Price')
fig_SMA.show()
# 'Buy' signal if Short SMA > Long SMA else 'Sell'
bank_nifty['Signal_SMA'] = np.where(bank_nifty['SMA_50'] > bank_nifty['SMA_200'], 1.0, 0.0)
bank_nifty['Position_SMA'] = bank_nifty['Signal_SMA'].diff()
bank_nifty.head()
High | Low | Open | Close | SMA_50 | SMA_200 | Signal_SMA | Position_SMA | |
---|---|---|---|---|---|---|---|---|
Date | ||||||||
2016-02-19 | 14395.849609 | 14207.349609 | 14243.450195 | 14344.200195 | 17028.070215 | 18231.565103 | 0.0 | NaN |
2016-02-25 | 13844.450195 | 13519.900391 | 13844.450195 | 13555.700195 | 16960.831211 | 18204.054600 | 0.0 | 0.0 |
2016-04-04 | 16278.500000 | 16046.500000 | 16236.900391 | 16190.599609 | 16940.355195 | 18189.920596 | 0.0 | 0.0 |
2016-05-25 | 17025.849609 | 16599.050781 | 16600.650391 | 16997.449219 | 16936.065195 | 18182.754092 | 0.0 | 0.0 |
2016-06-06 | 17754.150391 | 17636.900391 | 17710.449219 | 17671.400391 | 16946.562187 | 18179.589844 | 0.0 | 0.0 |
fig_SMA
fig_SMA.add_trace(go.Scatter(
mode= 'markers',
name='Buy',
x= bank_nifty[bank_nifty['Position_SMA']==1].index,
y = bank_nifty[bank_nifty['Position_SMA']==1].SMA_200,
marker=dict(
color='green',
size=15,
symbol='triangle-up')
)
)
fig_SMA.add_trace(go.Scatter(
mode= 'markers',
name='Sell',
x= bank_nifty[bank_nifty['Position_SMA']==-1].index,
y = bank_nifty[bank_nifty['Position_SMA']==-1].SMA_200,
marker=dict(
color='red',
size=15,
symbol='triangle-down'
)
)
)
Our general strategy using SMA is to buy and hold 100 stocks of Bank Nifty when a bullish signal is generated and sell after 60 days.
bank_nifty['Position_SMA'].value_counts()
0.0 1021 1.0 5 -1.0 4 Name: Position_SMA, dtype: int64
bank_nifty.columns.get_loc('Position_SMA')
7
bank_nifty.columns.get_loc('Close')
3
buyAmt = 0
sellAmt = 0
buyDates = np.array([])
for i in range(bank_nifty.shape[0]):
if bank_nifty.iloc[i, 7]==1:
buyAmt = buyAmt + bank_nifty.iloc[i, 3]*100
buyDates = np.append(buyDates, i)
print('Total Amount Invested:',buyAmt)
for i in range(bank_nifty.shape[0]):
for j in buyDates:
if i == j:
if (int(j)+60 < bank_nifty.shape[0]):
sellAmt = sellAmt + bank_nifty.iloc[int(j+60), 3]*100
else:
sellAmt = sellAmt + bank_nifty.iloc[int(j+10), 3]*100
total_SMA = sellAmt - buyAmt
print('Total Cumulative Returns:',total_SMA)
print('Returns in %:',(sellAmt/buyAmt)*100)
Total Amount Invested: 12984054.8828125 Total Cumulative Returns: 1697900.390625 Returns in %: 113.07681156579656
If we had followed this same strategy using SMA from 2015 till date, our total returns would be 113.07% i.e $10,000
invested using the same strategy would have generated $21,307
(as on 12/02/21).
The exponential moving average (EMA) is a technical chart indicator that tracks the price of an investment (like a stock or commodity) over time. The EMA is a type of weighted moving average (WMA) that gives more weighting or importance to recent price data.
The exponential moving average is designed to improve on the idea of a simple moving average (SMA) by giving more weight to the most recent price data, which is considered to be more relevant than older data. Since new data carries greater weight, the EMA responds more quickly to price changes than the SMA.
# Fast EMA (50 days)
bank_nifty['EMA_50'] = bank_nifty['Close'].ewm(span= 50, adjust=False).mean()
# Slow EMA (200 days)
bank_nifty['EMA_200'] = bank_nifty['Close'].ewm(span= 200, adjust=False).mean()
bank_nifty.head()
High | Low | Open | Close | SMA_50 | SMA_200 | Signal_SMA | Position_SMA | EMA_50 | EMA_200 | |
---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||
2016-02-19 | 14395.849609 | 14207.349609 | 14243.450195 | 14344.200195 | 17028.070215 | 18231.565103 | 0.0 | NaN | 14344.200195 | 14344.200195 |
2016-02-25 | 13844.450195 | 13519.900391 | 13844.450195 | 13555.700195 | 16960.831211 | 18204.054600 | 0.0 | 0.0 | 14313.278627 | 14336.354424 |
2016-04-04 | 16278.500000 | 16046.500000 | 16236.900391 | 16190.599609 | 16940.355195 | 18189.920596 | 0.0 | 0.0 | 14386.899057 | 14354.804625 |
2016-05-25 | 17025.849609 | 16599.050781 | 16600.650391 | 16997.449219 | 16936.065195 | 18182.754092 | 0.0 | 0.0 | 14489.273574 | 14381.099596 |
2016-06-06 | 17754.150391 | 17636.900391 | 17710.449219 | 17671.400391 | 16946.562187 | 18179.589844 | 0.0 | 0.0 | 14614.062860 | 14413.838907 |
# 'Buy' signal if Short SMA > Long SMA else 'Sell'
bank_nifty['Signal_EMA'] = np.where(bank_nifty['EMA_50'] > bank_nifty['EMA_200'], 1.0, 0.0)
bank_nifty['Position_EMA'] = bank_nifty['Signal_EMA'].diff()
bank_nifty.head()
High | Low | Open | Close | SMA_50 | SMA_200 | Signal_SMA | Position_SMA | EMA_50 | EMA_200 | Signal_EMA | Position_EMA | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||||
2016-02-19 | 14395.849609 | 14207.349609 | 14243.450195 | 14344.200195 | 17028.070215 | 18231.565103 | 0.0 | NaN | 14344.200195 | 14344.200195 | 0.0 | NaN |
2016-02-25 | 13844.450195 | 13519.900391 | 13844.450195 | 13555.700195 | 16960.831211 | 18204.054600 | 0.0 | 0.0 | 14313.278627 | 14336.354424 | 0.0 | 0.0 |
2016-04-04 | 16278.500000 | 16046.500000 | 16236.900391 | 16190.599609 | 16940.355195 | 18189.920596 | 0.0 | 0.0 | 14386.899057 | 14354.804625 | 1.0 | 1.0 |
2016-05-25 | 17025.849609 | 16599.050781 | 16600.650391 | 16997.449219 | 16936.065195 | 18182.754092 | 0.0 | 0.0 | 14489.273574 | 14381.099596 | 1.0 | 0.0 |
2016-06-06 | 17754.150391 | 17636.900391 | 17710.449219 | 17671.400391 | 16946.562187 | 18179.589844 | 0.0 | 0.0 | 14614.062860 | 14413.838907 | 1.0 | 0.0 |
# Plotting daily close price, EMA_50, EMA_200 and generating trade signals
fig_EMA = go.Figure()
fig_EMA.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.Close, name='Close'))
fig_EMA.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.EMA_50, name='EMA_50'))
fig_EMA.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.EMA_200, name='EMA_200'))
fig_EMA.update_layout(title='Price History of Bank Nifty and EMA',
xaxis_title='Date',
yaxis_title='Price')
fig_EMA.add_trace(go.Scatter(
mode= 'markers',
name='Buy',
x= bank_nifty[bank_nifty['Position_EMA']==1].index,
y = bank_nifty[bank_nifty['Position_EMA']==1].EMA_200,
marker=dict(
color='green',
size=15,
symbol='triangle-up')
)
)
fig_EMA.add_trace(go.Scatter(
mode= 'markers',
name='Sell',
x= bank_nifty[bank_nifty['Position_EMA']==-1].index,
y = bank_nifty[bank_nifty['Position_EMA']==-1].EMA_200,
marker=dict(
color='red',
size=15,
symbol='triangle-down'
)
)
)
fig_EMA.show()
Our general strategy using EMA is to buy and hold 100 stocks of Bank Nifty when a bullish signal is generated and sell after 60 days.
bank_nifty.columns.get_loc('Position_EMA')
11
buyAmt = 0
sellAmt = 0
buyDates = np.array([])
for i in range(bank_nifty.shape[0]):
if bank_nifty.iloc[i, 11]==1:
buyAmt = buyAmt + bank_nifty.iloc[i, 3]*100
buyDates = np.append(buyDates, i)
print('Total Amount Invested:',buyAmt)
for i in range(bank_nifty.shape[0]):
for j in buyDates:
if i == j:
if (int(j)+60 < bank_nifty.shape[0]):
sellAmt = sellAmt + bank_nifty.iloc[int(j+60), 3]*100
else:
sellAmt = sellAmt + bank_nifty.iloc[int(j+10), 3]*100
total_EMA = sellAmt - buyAmt
print('Total Cumulative Returns:',total_EMA)
print('Returns in %:',(sellAmt/buyAmt)*100)
Total Amount Invested: 12897879.8828125 Total Cumulative Returns: 1858885.15625 Returns in %: 114.41233112061401
If we had followed this same strategy using EMA from 2015 till date, our total returns would be 114.41% i.e $10,000
invested using the same strategy would have generated $21,441
(as on 12/02/21).
We can also see here that EMA generated better results as compared to SMA. This is because EMA gives more weightage to recent prices while in SMA we calculate the average of all 'Close' prices over a period of time.
from plotly.subplots import make_subplots
# Creating subplots for comparison between SMA and EMA strategy
fig_comp = make_subplots(rows = 1, cols=2)
fig_comp.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.Close, name='Close'), row=1, col=1)
fig_comp.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.SMA_50, name='SMA_50'), row=1, col=1)
fig_comp.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.SMA_200, name='SMA_200'), row=1, col=1)
fig_comp.add_trace(go.Scatter(
mode= 'markers',
name='Buy',
x= bank_nifty[bank_nifty['Position_SMA']==1].index,
y = bank_nifty[bank_nifty['Position_SMA']==1].SMA_200,
marker=dict(
color='green',
size=15,
symbol='triangle-up')
), row= 1, col=1
)
fig_comp.add_trace(go.Scatter(
mode= 'markers',
name='Sell',
x= bank_nifty[bank_nifty['Position_SMA']==-1].index,
y = bank_nifty[bank_nifty['Position_SMA']==-1].SMA_200,
marker=dict(
color='red',
size=15,
symbol='triangle-down'
)
), row=1, col=1
)
fig_comp.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.Close, name='Close'), row=1, col=2)
fig_comp.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.EMA_50, name='EMA_50'),row=1, col=2)
fig_comp.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.EMA_200, name='EMA_200'), row=1, col=2)
fig_comp.add_trace(go.Scatter(
mode= 'markers',
name='Buy',
x= bank_nifty[bank_nifty['Position_EMA']==1].index,
y = bank_nifty[bank_nifty['Position_EMA']==1].EMA_200,
marker=dict(
color='green',
size=15,
symbol='triangle-up')
), row=1, col=2
)
fig_comp.add_trace(go.Scatter(
mode= 'markers',
name='Sell',
x= bank_nifty[bank_nifty['Position_EMA']==-1].index,
y = bank_nifty[bank_nifty['Position_EMA']==-1].EMA_200,
marker=dict(
color='red',
size=15,
symbol='triangle-down'
)
), row= 1, col=2
)
fig_comp.update_layout(height=600, width=1100,
title_text="SMA and EMA Trading Strategies Comparison")
fig_comp = go.Figure()
fig_comp.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.Close, name= 'Close'))
fig_comp.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.SMA_50, name= 'SMA_50'))
fig_comp.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.EMA_50, name= 'EMA_50'))
fig_comp.update_layout(title='Price History of Bank Nifty, SMA_50 and EMA_50',
xaxis_title='Date',
yaxis_title='Price')
In the 1980s, John Bollinger, a long-time technician of the markets, developed the technique of using a moving average with two trading bands above and below it. Unlike a percentage calculation from a normal moving average, Bollinger Bands simply add and subtract a standard deviation calculation.
When stock prices continually touch the upper Bollinger Band, the prices are thought to be overbought, triggering a sell
signal; conversely, when they continually touch the lower band, prices are thought to be oversold, triggering a buy
signal.
# Calculating Bollinger Bands considering 20 days and 2 standard deviations
bank_nifty['Rolling Mean'] = bank_nifty['Close'].rolling(20).mean()
bank_nifty['Rolling Std'] = bank_nifty['Close'].rolling(20).std()
bank_nifty['Bollinger High'] = bank_nifty['Rolling Mean'] + (bank_nifty['Rolling Std']*2)
bank_nifty['Bollinger Low'] = bank_nifty['Rolling Mean'] - (bank_nifty['Rolling Std']*2)
bank_nifty.tail()
High | Low | Open | Close | SMA_50 | SMA_200 | Signal_SMA | Position_SMA | EMA_50 | EMA_200 | Signal_EMA | Position_EMA | Rolling Mean | Rolling Std | Bollinger High | Bollinger Low | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||||||||
2021-02-08 | 36466.449219 | 35872.250000 | 36073.851562 | 35983.648438 | 31345.757070 | 24549.750752 | 1.0 | 0.0 | 31179.455341 | 27082.369048 | 1.0 | 0.0 | 32679.554980 | 1708.202277 | 36095.959534 | 29263.150427 |
2021-02-09 | 36477.148438 | 35636.851562 | 36058.800781 | 36056.500000 | 31472.142070 | 24631.524004 | 1.0 | 0.0 | 31370.711994 | 27171.663883 | 1.0 | 0.0 | 32878.170020 | 1859.562708 | 36597.295436 | 29159.044603 |
2021-02-10 | 36227.199219 | 35428.148438 | 36042.351562 | 35783.101562 | 31603.876094 | 24709.099766 | 1.0 | 0.0 | 31543.746879 | 27257.349830 | 1.0 | 0.0 | 33067.380078 | 1955.437750 | 36978.255579 | 29156.504578 |
2021-02-11 | 36009.449219 | 35573.300781 | 35687.250000 | 35752.101562 | 31727.923125 | 24789.927021 | 1.0 | 0.0 | 31708.780396 | 27341.874723 | 1.0 | 0.0 | 33238.035156 | 2035.807455 | 37309.650067 | 29166.420246 |
2021-02-12 | 36322.800781 | 35700.250000 | 35736.601562 | 36275.351562 | 31861.249141 | 24870.898027 | 1.0 | 0.0 | 31887.861618 | 27430.765040 | 1.0 | 0.0 | 33423.070215 | 2137.955000 | 37698.980215 | 29147.160215 |
# Plotting Bollinger Bands
fig_bb = go.Figure()
fig_bb.add_trace(go.Scatter(x=bank_nifty.index, y=bank_nifty['Rolling Mean'], name='SMA_20'))
fig_bb.add_trace(go.Scatter(x=bank_nifty.index, y=bank_nifty.Close, name='Close'))
fig_bb.add_trace(go.Scatter(x=bank_nifty.index, y=bank_nifty['Bollinger High'], name='High Band'))
fig_bb.add_trace(go.Scatter(x=bank_nifty.index, y=bank_nifty['Bollinger Low'], name='Low Band'))
fig_bb.update_layout(title='Price History of Bank Nifty and Bollinger Bands')
fig_bb.show()
bank_nifty.tail()
High | Low | Open | Close | SMA_50 | SMA_200 | Signal_SMA | Position_SMA | EMA_50 | EMA_200 | Signal_EMA | Position_EMA | Rolling Mean | Rolling Std | Bollinger High | Bollinger Low | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||||||||
2021-02-08 | 36466.449219 | 35872.250000 | 36073.851562 | 35983.648438 | 31345.757070 | 24549.750752 | 1.0 | 0.0 | 31179.455341 | 27082.369048 | 1.0 | 0.0 | 32679.554980 | 1708.202277 | 36095.959534 | 29263.150427 |
2021-02-09 | 36477.148438 | 35636.851562 | 36058.800781 | 36056.500000 | 31472.142070 | 24631.524004 | 1.0 | 0.0 | 31370.711994 | 27171.663883 | 1.0 | 0.0 | 32878.170020 | 1859.562708 | 36597.295436 | 29159.044603 |
2021-02-10 | 36227.199219 | 35428.148438 | 36042.351562 | 35783.101562 | 31603.876094 | 24709.099766 | 1.0 | 0.0 | 31543.746879 | 27257.349830 | 1.0 | 0.0 | 33067.380078 | 1955.437750 | 36978.255579 | 29156.504578 |
2021-02-11 | 36009.449219 | 35573.300781 | 35687.250000 | 35752.101562 | 31727.923125 | 24789.927021 | 1.0 | 0.0 | 31708.780396 | 27341.874723 | 1.0 | 0.0 | 33238.035156 | 2035.807455 | 37309.650067 | 29166.420246 |
2021-02-12 | 36322.800781 | 35700.250000 | 35736.601562 | 36275.351562 | 31861.249141 | 24870.898027 | 1.0 | 0.0 | 31887.861618 | 27430.765040 | 1.0 | 0.0 | 33423.070215 | 2137.955000 | 37698.980215 | 29147.160215 |
bank_nifty['Position_BB'] = None
# Fill our newly created position column - set to sell (-1) when the price hits the upper band, and set to
# buy (1) when it hits the lower band
for row in range(len(bank_nifty)):
if (bank_nifty['Close'].iloc[row] > bank_nifty['Bollinger High'].iloc[row]) and (bank_nifty['Close'].iloc[row-1] < bank_nifty['Bollinger High'].iloc[row-1]):
bank_nifty['Position_BB'].iloc[row] = -1
if (bank_nifty['Close'].iloc[row] < bank_nifty['Bollinger Low'].iloc[row]) and (bank_nifty['Close'].iloc[row-1] > bank_nifty['Bollinger Low'].iloc[row-1]):
bank_nifty['Position_BB'].iloc[row] = 1
# Forward fill our position column to replace the "None" values with the correct long/short
# positions to represent the "holding" of our position forward through time
bank_nifty['Position_BB'].fillna(method='ffill',inplace=True)
bank_nifty.tail()
C:\Users\Tejas\anaconda3\lib\site-packages\pandas\core\indexing.py:1637: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
High | Low | Open | Close | SMA_50 | SMA_200 | Signal_SMA | Position_SMA | EMA_50 | EMA_200 | Signal_EMA | Position_EMA | Rolling Mean | Rolling Std | Bollinger High | Bollinger Low | Position_BB | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | |||||||||||||||||
2021-02-08 | 36466.449219 | 35872.250000 | 36073.851562 | 35983.648438 | 31345.757070 | 24549.750752 | 1.0 | 0.0 | 31179.455341 | 27082.369048 | 1.0 | 0.0 | 32679.554980 | 1708.202277 | 36095.959534 | 29263.150427 | -1.0 |
2021-02-09 | 36477.148438 | 35636.851562 | 36058.800781 | 36056.500000 | 31472.142070 | 24631.524004 | 1.0 | 0.0 | 31370.711994 | 27171.663883 | 1.0 | 0.0 | 32878.170020 | 1859.562708 | 36597.295436 | 29159.044603 | -1.0 |
2021-02-10 | 36227.199219 | 35428.148438 | 36042.351562 | 35783.101562 | 31603.876094 | 24709.099766 | 1.0 | 0.0 | 31543.746879 | 27257.349830 | 1.0 | 0.0 | 33067.380078 | 1955.437750 | 36978.255579 | 29156.504578 | -1.0 |
2021-02-11 | 36009.449219 | 35573.300781 | 35687.250000 | 35752.101562 | 31727.923125 | 24789.927021 | 1.0 | 0.0 | 31708.780396 | 27341.874723 | 1.0 | 0.0 | 33238.035156 | 2035.807455 | 37309.650067 | 29166.420246 | -1.0 |
2021-02-12 | 36322.800781 | 35700.250000 | 35736.601562 | 36275.351562 | 31861.249141 | 24870.898027 | 1.0 | 0.0 | 31887.861618 | 27430.765040 | 1.0 | 0.0 | 33423.070215 | 2137.955000 | 37698.980215 | 29147.160215 | -1.0 |
fig_bb.add_trace(go.Scatter(
mode= 'markers',
name='Buy',
x= bank_nifty[bank_nifty['Position_BB']==1].index,
y = bank_nifty[bank_nifty['Position_BB']==1].Close,
marker=dict(
color='green',
size=5,
symbol='triangle-up')
)
)
fig_bb.add_trace(go.Scatter(
mode= 'markers',
name='Sell',
x= bank_nifty[bank_nifty['Position_BB']==-1].index,
y = bank_nifty[bank_nifty['Position_BB']==-1].Close,
marker=dict(
color='red',
size=5,
symbol='triangle-down'
)
)
)
fig_bb.show()
Our general strategy using Bollinger Bands is to buy and hold 100 stocks of Bank Nifty when a bullish signal is generated and sell after 60 days.
bank_nifty.columns.get_loc('Position_BB')
16
bank_nifty['Position_BB'].value_counts()
-1.0 669 1.0 334 Name: Position_BB, dtype: int64
buyAmt = 0
sellAmt = 0
buyDates = np.array([])
for i in range(bank_nifty.shape[0]):
if bank_nifty.iloc[i, 16]==1:
buyAmt = buyAmt + bank_nifty.iloc[i, 3]*100
buyDates = np.append(buyDates, i)
# print(buyDates[1])
print('Total Amount Invested:',buyAmt)
for i in range(bank_nifty.shape[0]):
for j in buyDates:
if i == j:
if (int(j)+60 < bank_nifty.shape[0]):
sellAmt = sellAmt + bank_nifty.iloc[int(j+60), 3]*100
else:
sellAmt = sellAmt + bank_nifty.iloc[int(j+5), 3]*100
total_BB = sellAmt - buyAmt
print('Total Cumulative Returns:',total_BB)
print('Returns in %:',(sellAmt/buyAmt)*100)
Total Amount Invested: 842638084.1796875 Total Cumulative Returns: 23341230.078125 Returns in %: 102.77001841197905
If we had followed this same strategy using Bollinger Bands from 2015 till date, our total returns would be 102.77% i.e $10,000
invested using the same strategy would have generated $20,277
(as on 12/02/21).
MACD is a trend following momentum-indicator which shows relationship between the moving avearges of a security's market price. It is calculated by subtracting 26-day EMA (exponential moving average) from 12-day EMA.
The result of that calculation is the MACD line. A nine-day EMA of the MACD called the "signal line," is then plotted on top of the MACD line, which can function as a trigger for buy and sell signals. Traders may buy the security when the MACD crosses above its signal line and sell, or short the security when the MACD crosses below the signal line. Moving average convergence divergence (MACD) indicators can be interpreted in several ways, but the more common methods are crossovers, divergences, and rapid rises/falls.
macd = bank_nifty[['Close']]
macd.head()
Close | |
---|---|
Date | |
2016-02-19 | 14344.200195 |
2016-02-25 | 13555.700195 |
2016-04-04 | 16190.599609 |
2016-05-25 | 16997.449219 |
2016-06-06 | 17671.400391 |
macd['MACD'] = macd['Close'].ewm(span=12, adjust= False).mean() - macd['Close'].ewm(span=26, adjust= False).mean()
macd['Signal'] = macd['MACD'].ewm(span=9, adjust= False).mean()
bank_nifty[['Signal_9', 'MACD']] = macd[['Signal', 'MACD']]
bank_nifty.head()
C:\Users\Tejas\anaconda3\lib\site-packages\ipykernel_launcher.py:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy C:\Users\Tejas\anaconda3\lib\site-packages\ipykernel_launcher.py:2: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
High | Low | Open | Close | SMA_50 | SMA_200 | Signal_SMA | Position_SMA | EMA_50 | EMA_200 | Signal_EMA | Position_EMA | Rolling Mean | Rolling Std | Bollinger High | Bollinger Low | Position_BB | Signal_9 | MACD | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | |||||||||||||||||||
2016-02-19 | 14395.849609 | 14207.349609 | 14243.450195 | 14344.200195 | 17028.070215 | 18231.565103 | 0.0 | NaN | 14344.200195 | 14344.200195 | 0.0 | NaN | NaN | NaN | NaN | NaN | NaN | 0.000000 | 0.000000 |
2016-02-25 | 13844.450195 | 13519.900391 | 13844.450195 | 13555.700195 | 16960.831211 | 18204.054600 | 0.0 | 0.0 | 14313.278627 | 14336.354424 | 0.0 | 0.0 | NaN | NaN | NaN | NaN | NaN | -12.580057 | -62.900285 |
2016-04-04 | 16278.500000 | 16046.500000 | 16236.900391 | 16190.599609 | 16940.355195 | 18189.920596 | 0.0 | 0.0 | 14386.899057 | 14354.804625 | 1.0 | 1.0 | NaN | NaN | NaN | NaN | NaN | 9.681371 | 98.727084 |
2016-05-25 | 17025.849609 | 16599.050781 | 16600.650391 | 16997.449219 | 16936.065195 | 18182.754092 | 0.0 | 0.0 | 14489.273574 | 14381.099596 | 1.0 | 0.0 | NaN | NaN | NaN | NaN | NaN | 65.464535 | 288.597189 |
2016-06-06 | 17754.150391 | 17636.900391 | 17710.449219 | 17671.400391 | 16946.562187 | 18179.589844 | 0.0 | 0.0 | 14614.062860 | 14413.838907 | 1.0 | 0.0 | NaN | NaN | NaN | NaN | NaN | 149.937518 | 487.829449 |
bank_nifty['Signal_MACD'] = np.where(bank_nifty.loc[:, 'MACD'] > bank_nifty.loc[:, 'Signal_9'], 1.0, 0.0)
bank_nifty['Position_MACD'] = bank_nifty['Signal_MACD'].diff()
# Plotting daily close price, signal line, MACD and generating trade signals
fig_MACD = go.Figure()
fig_MACD.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.Close, name='Close'))
fig_MACD.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.Signal_9, name='MACD-EMA-9'))
fig_MACD.add_trace(go.Scatter(x= bank_nifty.index, y= bank_nifty.MACD, name='MACD'))
fig_MACD.update_layout(title='Price History of Bank Nifty and MACD',
xaxis_title='Date',
yaxis_title='Price')
fig_MACD.add_trace(go.Scatter(
mode= 'markers',
name='Buy',
x= bank_nifty[bank_nifty['Position_MACD']==1].index,
y = bank_nifty[bank_nifty['Position_MACD']==1].Close,
marker=dict(
color='green',
size=5,
symbol='triangle-up')
)
)
fig_MACD.add_trace(go.Scatter(
mode= 'markers',
name='Sell',
x= bank_nifty[bank_nifty['Position_MACD']==-1].index,
y = bank_nifty[bank_nifty['Position_MACD']==-1].Close,
marker=dict(
color='red',
size=5,
symbol='triangle-down'
)
)
)
fig_MACD.show()
Our general strategy using MACD is to buy and hold 100 stocks of Bank Nifty when a bullish signal is generated and sell after 60 days.
bank_nifty.columns.get_loc('Position_MACD')
20
bank_nifty['Position_MACD'].value_counts()
0.0 949 1.0 41 -1.0 40 Name: Position_MACD, dtype: int64
buyAmt = 0
sellAmt = 0
buyDates = np.array([])
for i in range(bank_nifty.shape[0]):
if bank_nifty.iloc[i, 20]==1:
buyAmt = buyAmt + bank_nifty.iloc[i, 3]*100
buyDates = np.append(buyDates, i)
print('Total Amount Invested:', buyAmt)
for i in range(bank_nifty.shape[0]):
for j in buyDates:
if i == j:
if (int(j)+60 < bank_nifty.shape[0]):
sellAmt = sellAmt + bank_nifty.iloc[int(j+60), 3]*100
else:
sellAmt = sellAmt + bank_nifty.iloc[int(j+8), 3]*100
total_MACD = sellAmt - buyAmt
print('Total Cumulative Returns:',total_MACD)
print('Returns in %:',(sellAmt/buyAmt)*100)
Total Amount Invested: 105009079.4921875 Total Cumulative Returns: 3458630.6640625 Returns in %: 103.29364915947083
If we had followed this same strategy using MACD from 2015 till date, our total returns would be 103.27% i.e $10,000
invested using the same strategy would have generated $20,327
(as on 12/02/21).
Conclusion: In this notebook, we have learnt how to predict stock prices using some popular technical indicators and back-tested to calculate returns over 5 years (2015-Present). We can also consider pairing other external factors with these technical indicators to improvise on our trading strategy and get much better returns.