This is a simple example to show how you can use P/E ratio to make investment decision. Our investment of choice is NIFTY index for simplicity (Though you cannot invest directly in the NIFTY, you can invest in any one of 11 Exchange traded funds which are based on NIFTY)
Lets get into tools and strategies now. To downloading index historical data we are going to use NSEpy. For back-testing our strategy we are using backtrader. Install dependencies - pip install nsepy backtrader
Our strategy is simple, allocate 100% of your funds to NIFTY when P/E ratio of NIFTY is below say 20 and sell all your holdings and cash out if P/E ratio is above 24. Rational - Buy low and sell high and we use P/E estimate how high or low the index is. P/E ratio tells us how undervalued or overvalued the stock/index. Higher the P/E ratio, more overpriced the stock is.
Download the data from NSE takes a significan time and you do not want to wait everytime you tweak your strategies. Therefore we download the data and save it to disk.
#%matplotlib inline
from datetime import date
import pandas as pd
try:
nifty = pd.read_csv('data/nifty17years_withPE.csv')
print('Read from disk successful')
except:
print('Downloading from NSE')
nifty = get_history('NIFTY', date(2000, 1, 1), date(2017, 10, 31), index=True)
pe = get_index_pe_history('NIFTY', date(2000, 1, 1), date(2017, 10, 31))
nifty['PE'] = pe['P/E']
nifty.to_csv('data/nifty17years_withPE.csv')
from backtrader.feeds import GenericCSVData
"""
By default downloaded data only has datetime, Open, High, Low, Close, Volume and Turnover.
As we are adding one more parameter "PE", we can no longer use GenericCSVData reader provided by
backtrader library without modification to base class.
"""
# Define the new parameter
class GenericCSV_PE(GenericCSVData):
# Add a 'pe' line to the inherited ones from the base class
lines = ('pe',)
# add the parameter to the parameters inherited from the base class
params = (('pe', 8),)
# Declare position of each column in csv file
data = GenericCSV_PE(dataname='data/nifty17years_withPE.csv',
dtformat=('%Y-%m-%d'),
datetime=0,
high=1,
low=2,
open=3,
close=4,
volume=5,
pe=7,
openinterest=-1,
#fromdate=date(2017,1,1),
#todate=date(2017,1,10)
)
Defining the strategy described in introduction.
import backtrader as bt
class PEInvesting(bt.SignalStrategy):
def log(self, txt, dt=None):
pass
def __init__(self):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
self.pe = self.datas[0].pe
def next(self):
curdate = self.datetime.date(ago=0)
if self.pe[0] < 21:
self.log(self.dataclose[0])
# Use 100% of the cash to buy nifty
self.order_target_percent(target=1.0)
if self.pe[0] > 24:
self.log(self.dataclose[0])
# Sell everything
self.order_target_percent(target=0)
Run the strategy, Get final portfolio value and plot the whole backtesting.
cerebro = bt.Cerebro()
# Set our desired cash start
cerebro.broker.setcash(1000000.0)
cerebro.adddata(data)
cerebro.addstrategy(PEInvesting)
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
print('Final value is %.2f times the initial investment'%(cerebro.broker.getvalue()/1000000.0))
cerebro.plot()
Play with different possible strategies, eg.
In next notebook I'll try to backtest for systematic investing (SIP) where we will invest every month and try different strategies for SIP.