DEV Community

shakti tiwari
shakti tiwari

Posted on

NSE Option Chain Deep Dive: How to Read OI Data Like a Pro

The exact framework I use to interpret NSE’s option chain — with live NIFTY data, Python scripts, and tradeable signals

Most traders open the NSE option chain, scroll to ATM, and check PCR. That is not wrong, but it is only 20% of the information available.

The full option chain contains 100+ strikes, each with call and put OI, volume, IV, LTP, and change-in-OI. When you read it correctly, it tells you where institutions are positioned, where pin risk lies, and where the next 1-2% move will come from.

This article is based on live NIFTY option chain data from 05-Aug-2026. I will show you exactly how I read it, what the numbers mean, and how to automate the process.

NSE option chain structure

NSE’s option chain page shows:

Column Call Side Put Side
OI Call OI Put OI
Change in OI Call Chng OI Put Chng OI
Volume Call Volume Put Volume
IV Call IV Put IV
LTP Call LTP Put LTP
Change Call Chng Put Chng
Bid Qty Call Bid Qty Put Bid Qty
Bid Call Bid Put Bid
Ask Call Ask Put Ask
Ask Qty Call Ask Qty Put Ask Qty
Strike

Key terms:

  • OI: Open interest = number of open contracts
  • Change in OI: New positions added or closed today
  • Volume: Number of contracts traded today
  • IV: Implied volatility = market’s expectation of future volatility
  • LTP: Last traded price
  • ITM: In-the-money options are highlighted in yellow

Reading the live NIFTY chain

On 05-Aug-2026, NIFTY closed at 24,624.65. Here is the relevant section:

Strike Call OI Call Chng OI Call IV Call LTP Put LTP Put IV Put OI Put Chng OI
24,200 18,339 -991 12.92 411.75 25.10 12.92 81,815 34,061
24,250 2,876 -193 12.83 371.00 31.45 12.83 33,862 19,575
24,300 15,794 -400 12.81 328.45 39.75 12.81 59,603 14,885
24,350 4,697 250 12.93 292.00 50.90 12.93 21,651 8,684
24,400 25,256 -1,635 12.84 252.50 62.00 12.84 64,024 16,421
24,450 9,210 -5,699 12.96 218.25 77.20 12.96 23,650 7,608
24,500 64,618 -8,034 13.22 186.55 96.40 13.22 79,188 19,520
24,550 39,572 14,726 13.31 158.05 116.00 13.31 30,951 18,723
24,600 1,21,838 23,183 13.68 132.95 141.45 13.68 77,502 31,387
24,650 69,634 35,445 14.07 110.00 169.65 14.07 21,897 17,422
24,700 1,13,977 41,996 14.32 90.30 198.40 14.32 24,761 13,849
24,750 38,057 21,054 15.28 72.45 238.10 15.28 4,471 3,113
24,800 1,07,699 27,605 15.46 57.40 269.90 15.46 9,735 3,285
24,850 33,024 18,626 15.58 46.65 302.80 15.58 1,659 1,087
24,900 95,263 32,806 16.36 36.50 345.00 16.36 3,911 1,652
25,000 1,35,553 39,890 17.64 22.05 429.40 17.64 9,550 2,113

Signal 1: Max pain / pin identification

Max pain is the strike where call + put writers earn maximum profit. It is usually near the current price but can shift.

Mac / Linux / Termux:

def find_max_pain(chain):
    max_pain = None
    min_value = float('inf')

    for strike in chain['strike'].unique():
        calls = chain[chain['strike'] == strike]['call_oi'].sum()
        puts = chain[chain['strike'] == strike]['put_oi'].sum()
        total = calls + puts

        if total < min_value:
            min_value = total
            max_pain = strike

    return max_pain

# On 05-Aug-2026 data
max_pain = find_max_pain(chain)
print(f"Max Pain: {max_pain}")
Enter fullscreen mode Exit fullscreen mode

Windows CMD:

python -c "chain=pd.read_csv('nifty_chain_aug5.csv'); mp=min(chain.groupby('strike')['call_oi','put_oi'].sum().sum(axis=1)); print('Max Pain:', mp.name)"
Enter fullscreen mode Exit fullscreen mode

On 05-Aug-2026: Max pain ≈ 24,600. NIFTY closed at 24,624.65, very close to max pain. This suggests institutions were defending this level.

Signal 2: Call writing vs put writing

When call OI increases sharply, it means someone is selling calls — they expect the market to stay below that strike.

When put OI increases sharply, someone is selling puts — they expect support.

On 05-Aug-2026:

  • Call OI surge at 24,500: +64,618 new contracts
  • Call OI surge at 24,600: +1,21,838 new contracts
  • Put OI surge at 24,200: +81,815 new contracts
  • Put OI surge at 24,600: +77,502 new contracts

Interpretation:

  • Heavy call writing at 24,500-24,600 = resistance zone
  • Heavy put writing at 24,200 = support zone
  • Range-bound market likely: 24,200-24,600

Signal 3: Change in OI direction

Change in OI tells you whether new money is coming in or old positions are being closed.

def analyze_oi_change(chain):
    chain = chain.copy()
    chain['call_oi_change'] = chain['call_oi'].diff()
    chain['put_oi_change'] = chain['put_oi'].diff()

    # Long buildup = OI + price up
    # Short buildup = OI up + price down
    # Short covering = OI down + price up
    # Long unwinding = OI down + price down

    chain['call_signal'] = 'neutral'
    chain.loc[(chain['call_oi_change'] > 0) & (chain['call_change'] > 0), 'call_signal'] = 'long_buildup'
    chain.loc[(chain['call_oi_change'] > 0) & (chain['call_change'] < 0), 'call_signal'] = 'short_buildup'
    chain.loc[(chain['call_oi_change'] < 0) & (chain['call_change'] > 0), 'call_signal'] = 'short_covering'
    chain.loc[(chain['call_oi_change'] < 0) & (chain['call_change'] < 0), 'call_signal'] = 'long_unwinding'

    return chain
Enter fullscreen mode Exit fullscreen mode

Signal 4: IV skew

IV skew shows where the market expects the next move.

def calculate_iv_skew(chain, current_price):
    chain = chain.copy()
    chain['distance_from_atm'] = chain['strike'] - current_price

    # Fit IV vs distance
    iv_skew = chain[['distance_from_atm', 'call_iv']].dropna().sort_values('distance_from_atm')

    # Positive skew = OTM puts have higher IV = fear
    # Negative skew = OTM calls have higher IV = greed
    skew = iv_skew['call_iv'].iloc[-5:].mean() - iv_skew['call_iv'].iloc[:5].mean()

    return skew

# On 05-Aug-2026
# OTM calls IV: ~13-14%
# OTM puts IV: ~16-18%
# Skew: negative (puts more expensive) = fear premium
Enter fullscreen mode Exit fullscreen mode

On 05-Aug-2026: Put IV > Call IV across all strikes. This is a fear premium — market is pricing in downside risk.

Signal 5: Volume-OI confirmation

High volume + increasing OI = genuine interest. High volume + decreasing OI = closing positions.

def volume_oi_confirmation(chain):
    chain = chain.copy()
    chain['call_volume_oi_ratio'] = chain['call_volume'] / (chain['call_oi'] + 1)
    chain['put_volume_oi_ratio'] = chain['put_volume'] / (chain['put_oi'] + 1)

    # High ratio = new money coming in
    # Low ratio = position closing
    return chain

# On 05-Aug-2026, 24,600 call:
# Volume: 31,69,999
# OI: 1,21,838
# Ratio: 26.0 = very high = new call writing
Enter fullscreen mode Exit fullscreen mode

Automated OI analysis script

# oi_analyzer.py
import pandas as pd
import requests

def fetch_nse_option_chain(symbol='NIFTY', expiry='11-Aug-2026'):
    """Fetch option chain from NSE"""
    url = f"https://www.nseindia.com/api/option-chain-indices?symbol={symbol}"
    headers = {
        "User-Agent": "Mozilla/5.0",
        "Accept": "application/json"
    }

    response = requests.get(url, headers=headers, timeout=10)
    data = response.json()

    # Parse option chain
    records = []
    for item in data['records']['data']:
        if 'CE' in item and 'PE' in item:
            records.append({
                'strike': item['strikePrice'],
                'call_oi': item['CE']['openInterest'],
                'call_oi_change': item['CE']['changeinOpenInterest'],
                'call_volume': item['CE']['totalTradedVolume'],
                'call_iv': item['CE']['impliedVolatility'],
                'call_ltp': item['CE']['lastPrice'],
                'put_oi': item['PE']['openInterest'],
                'put_oi_change': item['PE']['changeinOpenInterest'],
                'put_volume': item['PE']['totalTradedVolume'],
                'put_iv': item['PE']['impliedVolatility'],
                'put_ltp': item['PE']['lastPrice']
            })

    df = pd.DataFrame(records)
    return df

def generate_signals(df, current_price):
    """Generate trading signals from option chain"""
    signals = {}

    # Max pain
    signals['max_pain'] = find_max_pain(df)

    # Support/Resistance
    call_oi_max = df.loc[df['call_oi'].idxmax(), 'strike']
    put_oi_max = df.loc[df['put_oi'].idxmax(), 'strike']
    signals['resistance'] = call_oi_max
    signals['support'] = put_oi_max

    # PCR
    total_call_oi = df['call_oi'].sum()
    total_put_oi = df['put_oi'].sum()
    signals['pcr'] = total_put_oi / total_call_oi

    # IV skew
    signals['iv_skew'] = calculate_iv_skew(df, current_price)

    # Range
    signals['range_high'] = call_oi_max
    signals['range_low'] = put_oi_max

    return signals

# Run
chain = fetch_nse_option_chain()
signals = generate_signals(chain, 24624.65)
print(signals)
Enter fullscreen mode Exit fullscreen mode

Mac / Linux / Termux:

python3 oi_analyzer.py
Enter fullscreen mode Exit fullscreen mode

Windows CMD:

python oi_analyzer.py
Enter fullscreen mode Exit fullscreen mode

NSE option chain limitations

  1. Delayed data: Free option chain is delayed by 15-20 minutes during market hours
  2. Rate limiting: NSE blocks frequent API calls
  3. Session cookies: Some endpoints require valid NSE session cookies
  4. Terms of use: NSE prohibits commercial aggregation of data

TL;DR

Signal What to Look Action
Max OI call Resistance Sell calls / buy puts
Max OI put Support Sell puts / buy calls
PCR > 1.5 Bullish hedging Buy calls / sell puts
PCR < 0.7 Bearish hedging Buy puts / sell calls
IV skew negative Fear premium Buy puts / sell straddles
IV skew positive Greed premium Buy calls / sell straddles

The option chain is not just a data table. It is a map of institutional money.


Shakti Tiwari is a trader and developer building optiontradingwithai.in. He co-directs CodeVisser and authored books on trading psychology. Find him on Dev.to as @shaktitiwari715-ai.

Top comments (0)