Simple example of how to use NSEpy with backtrader


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.

Downloading the data

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.

In [5]:
#%matplotlib inline
from datetime import date
import pandas as pd
    nifty = pd.read_csv('data/nifty17years_withPE.csv')
    print('Read from disk successful')
    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']
Trying to reading from disk
Read from disk successful

Load the data in backtrader and run the strategy

In [12]:
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',

Defining the strategy

Defining the strategy described in introduction.

In [20]:
import backtrader as bt

class PEInvesting(bt.SignalStrategy):
    def log(self, txt, dt=None):

    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:
            # Use 100% of the cash to buy nifty
        if self.pe[0] > 24:
            # Sell everything

Running the defined strategy

Run the strategy, Get final portfolio value and plot the whole backtesting.

In [21]:
cerebro = bt.Cerebro()

# Set our desired cash start

print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
print('Final value is %.2f times the initial investment'%(cerebro.broker.getvalue()/1000000.0))
Final Portfolio Value: 10450659.40
Final value is 10.45 times the initial investment
[[<matplotlib.figure.Figure at 0x7f43edbf7c50>]]

Next steps

Play with different possible strategies, eg.

  • Try different values of PE for buying and selling
  • Try a formula to map relation between P/E value and percentage allocation

In next notebook I'll try to backtest for systematic investing (SIP) where we will invest every month and try different strategies for SIP.