Fetching live option chain from NSE in pandas dataframe

What is Option chain

Option chain of a security is a single table which shows premium price, open interest, volume traded etc for both put and call options and for every strike price

Install required packages

pip install requests bs4 pandas

Getting started

We import the required modules

import requests
from bs4 import BeautifulSoup
import numpy as np
import pandas as pd

URL for option chain

url = "https://www1.nseindia.com/live_market/dynaContent/live_watch/option_chain/optionKeys.jsp"

Below headers are required for NSE to respond. Without these headers the request will just time out.

headers = {
        "Host": "www1.nseindia.com",
        "Referer": "https://www1.nseindia.com/live_market/dynaContent/live_watch/option_chain/optionKeys.jsp",
        "X-Requested-With": "XMLHttpRequest",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36",
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
        }
r = requests.get(url, headers=headers)

Open chrome and go to https://www1.nseindia.com/live_market/dynaContent/live_watch/option_chain/optionKeys.jsp . Press Ctrl+U to view the source. You can see that the table tag has an id - octable. We can extract the table using this id as below-

bs = BeautifulSoup(r.text)
table = bs.find("table", {"id": "octable"})
# Get all rows
table_rows = table.find_all('tr')

Extract the text in each cell

l = []
for tr in table_rows:
    td = tr.find_all('td')
    if td:
        row = [tr.text for tr in td]
        l.append(row)

Lets write a function which tries to convert the cell to numpy.float64 and if it fails because of - in many cells, it will return numpy.nan

def np_float(x):
    try:
        y = x.lstrip().rstrip().replace(',','')
        return np.float64(y)
    except:
        return np.nan
    

Loop thru each cell to convert to a array of numpy.float64

arr = []
for r in l:
    row = [np_float(x) for x in r]
    arr.append(row)

Convert the array to a dataframe and provide column headers manually

df = pd.DataFrame(arr[:-1]) # Leave last row of summary
df.columns = ["CE Chart", "CE OI", "CE Change in OI", "CE Volume", "CE IV", "CE LTP", "CE Net Change", "CE Bid Qty", "CE Bid Price", "CE Ask Price", "CE Ask Quantity",
             "Strike Price",
             "PE Bid Qty", "PE Bid Price", "PE Ask Price", "PE Ask Qty", "PE Net Change", "PE LTP", "PE IV", "PE Volume", "PE Change in OI", "PE OI", "PE Chart"]

Here’s how the output looks like

print(df.head())
   CE Chart  CE OI  CE Change in OI  CE Volume  CE IV  CE LTP  CE Net Change  \
0       NaN    NaN              NaN        NaN    NaN     NaN            NaN   
1       NaN    NaN              NaN        NaN    NaN     NaN            NaN   
2       NaN    NaN              NaN        NaN    NaN     NaN            NaN   
3       NaN    NaN              NaN        NaN    NaN     NaN            NaN   
4       NaN    NaN              NaN        NaN    NaN     NaN            NaN   

   CE Bid Qty  CE Bid Price  CE Ask Price  ...  PE Bid Price  PE Ask Price  \
0      7350.0       3110.30       3844.05  ...          0.25           0.4   
1      7350.0       3065.20       3786.65  ...          0.10           0.5   
2      7350.0       3019.80       3732.70  ...          0.20           0.5   
3      7350.0       2975.00       3674.90  ...          0.10           0.6   
4      7350.0       2929.55       3618.65  ...          0.30           0.4   

   PE Ask Qty  PE Net Change  PE LTP  PE IV  PE Volume  PE Change in OI  \
0      1725.0          -0.35    0.30  98.28     4032.0            300.0   
1      3000.0            NaN     NaN    NaN        NaN              NaN   
2      3000.0        -198.35    0.25  93.68     1680.0              NaN   
3       600.0            NaN     NaN    NaN        NaN              NaN   
4      1800.0          -0.15    0.35  93.10     1175.0           3225.0   

    PE OI  PE Chart  
0   450.0       NaN  
1     NaN       NaN  
2     NaN       NaN  
3     NaN       NaN  
4  5550.0       NaN  

[5 rows x 23 columns]

Fetching option chain for any stock/index for any expiry date

If you want to see the option chain for a security other than the default NIFTY, below is the URL format- https://www1.nseindia.com/live_market/dynaContent/live_watch/option_chain/optionKeys.jsp?segmentLink=17&instrument=OPTSTK&symbol=SBIN&date=30JUL2020

Observe the parameters at the end. Now let’s start putting everything together in function

import requests
from bs4 import BeautifulSoup
import numpy as np
import pandas as pd
from urllib.parse import urlencode

def np_float(x):
    try:
        y = x.lstrip().rstrip().replace(',','')
        return np.float64(y)
    except:
        return np.nan

def option_chain(symbol, instrument, date_="-"):
    base_url = "https://www1.nseindia.com/live_market/dynaContent/live_watch/option_chain/optionKeys.jsp?"
    parameters = {
        "segmentLink": 17,
        "instrument": instrument,
        "symbol": symbol,
        "date": date_
    }
    url = base_url + urlencode(parameters)
    headers = {
        "Host": "www1.nseindia.com",
        "Referer": "https://www1.nseindia.com/live_market/dynaContent/live_watch/option_chain/optionKeys.jsp",
        "X-Requested-With": "XMLHttpRequest",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36",
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
        }
    r = requests.get(url, headers=headers)
    
    bs = BeautifulSoup(r.text)
    table = bs.find("table", {"id": "octable"})
    # Get all rows
    table_rows = table.find_all('tr')
    
    l = []
    for tr in table_rows:
        td = tr.find_all('td')
        if td:
            row = [tr.text for tr in td]
            l.append(row)
    
    arr = []
    for r in l:
        row = [np_float(x) for x in r]
        arr.append(row)
    
    df = pd.DataFrame(arr[:-1])
    df.columns = ["CE Chart", "CE OI", "CE Change in OI", "CE Volume", "CE IV", "CE LTP", "CE Net Change", "CE Bid Qty", "CE Bid Price", "CE Ask Price", "CE Ask Quantity",
                 "Strike Price",
                 "PE Bid Qty", "PE Bid Price", "PE Ask Price", "PE Ask Qty", "PE Net Change", "PE LTP", "PE IV", "PE Volume", "PE Change in OI", "PE OI", "PE Chart"]
    return df
    

Let us now try to use this to get BANKNIFTY option chain

print(option_chain("BANKNIFTY", "OPTIDX"))
     CE Chart    CE OI  CE Change in OI  CE Volume  CE IV   CE LTP  \
0         NaN     20.0             20.0        6.0    NaN  8071.25   
1         NaN      NaN              NaN        NaN    NaN      NaN   
2         NaN      NaN              NaN        NaN    NaN      NaN   
3         NaN      NaN              NaN        NaN    NaN      NaN   
4         NaN      NaN              NaN        NaN    NaN      NaN   
..        ...      ...              ...        ...    ...      ...   
112       NaN  78820.0          14980.0    18094.0  41.29     3.25   
113       NaN  10100.0           5000.0     1787.0  41.39     2.65   
114       NaN   3120.0           3120.0      333.0  42.27     2.55   
115       NaN      NaN              NaN        NaN    NaN      NaN   
116       NaN  36660.0          36660.0     3100.0  45.81     3.35   

     CE Net Change  CE Bid Qty  CE Bid Price  CE Ask Price  ...  PE Bid Price  \
0          4390.85        40.0       8069.40       8207.15  ...          1.30   
1              NaN      2400.0       7548.45       8812.25  ...          0.35   
2              NaN        40.0       7794.35       8094.75  ...          1.30   
3              NaN      2400.0       7369.30       8590.55  ...           NaN   
4              NaN      2400.0       7275.05       8489.00  ...           NaN   
..             ...         ...           ...           ...  ...           ...   
112          -3.90       820.0          3.25          3.30  ...       2990.65   
113          -3.10       160.0          2.65          3.20  ...       2973.75   
114         -24.50        40.0          2.35          3.90  ...       3069.30   
115            NaN      1200.0          2.55          4.00  ...       3176.25   
116         -16.85       800.0          2.55          3.30  ...       3375.45   

     PE Ask Price  PE Ask Qty  PE Net Change  PE LTP   PE IV  PE Volume  \
0            1.55       480.0          -1.00    1.30  119.58      330.0   
1            2.95       200.0            NaN     NaN     NaN        NaN   
2            1.65       140.0           0.25    1.65  118.69       32.0   
3             NaN         NaN            NaN     NaN     NaN        NaN   
4             NaN         NaN            NaN     NaN     NaN        NaN   
..            ...         ...            ...     ...     ...        ...   
112       3134.20       200.0            NaN     NaN     NaN        NaN   
113       3463.30       500.0            NaN     NaN     NaN        NaN   
114       3484.20       500.0            NaN     NaN     NaN        NaN   
115       3597.65       500.0            NaN     NaN     NaN        NaN   
116       3548.25        40.0            NaN     NaN     NaN        NaN   

     PE Change in OI   PE OI  PE Chart  
0             3900.0  4580.0       NaN  
1                NaN     NaN       NaN  
2              240.0   360.0       NaN  
3                NaN     NaN       NaN  
4                NaN     NaN       NaN  
..               ...     ...       ...  
112              NaN     NaN       NaN  
113              NaN     NaN       NaN  
114              NaN     NaN       NaN  
115              NaN     NaN       NaN  
116              NaN     NaN       NaN  

[117 rows x 23 columns]

SBIN option chain (Please change the expiry date to the latest future expiry dates, it does not seem to work on past expiry dates)

print(option_chain("SBIN", "OPTSTK", "30JUL2020").tail())
    CE Chart      CE OI  CE Change in OI  CE Volume  CE IV  CE LTP  \
52       NaN  5751000.0         732000.0     3355.0  63.13    0.65   
53       NaN  1308000.0         540000.0     1138.0  64.01    0.45   
54       NaN   405000.0          84000.0      159.0  69.34    0.35   
55       NaN  1371000.0          66000.0      116.0  73.94    0.30   
56       NaN   999000.0         180000.0      231.0  77.81    0.25   

    CE Net Change  CE Bid Qty  CE Bid Price  CE Ask Price  ...  PE Bid Price  \
52          -0.15    195000.0          0.65          0.70  ...         54.25   
53          -0.05     30000.0          0.40          0.45  ...         63.30   
54          -0.05      9000.0          0.30          0.35  ...         73.20   
55            NaN    111000.0          0.25          0.30  ...         82.60   
56            NaN     33000.0          0.25          0.30  ...         92.80   

    PE Ask Price  PE Ask Qty  PE Net Change  PE LTP  PE IV  PE Volume  \
52         55.20      3000.0           2.95    53.5  51.18       12.0   
53         66.10     30000.0            NaN    62.3    NaN        NaN   
54         75.80     30000.0            NaN    72.5    NaN        NaN   
55         85.75     30000.0            NaN    92.5    NaN        NaN   
56         95.80     33000.0            NaN    93.7    NaN        NaN   

    PE Change in OI     PE OI  PE Chart  
52          24000.0  273000.0       NaN  
53              NaN   42000.0       NaN  
54              NaN   18000.0       NaN  
55              NaN   45000.0       NaN  
56              NaN  210000.0       NaN  

[5 rows x 23 columns]