Skip to main content

NIFTY Open Interest

Overview

This python code fetches and visualizes Open Interest (OI) profile for NIFTY options, helping traders identify support and resistance levels based on options market data.

Features

  • Real-time OI data for Call and Put options
  • Automatic ATM strike calculation
  • Batch processing with rate limiting
  • Interactive Plotly visualization
  • Error handling with retry logic

Full Python Code

"""
NIFTY 31 JUL 2025 – OI profile (10-req/s batches)
Author : OpenAlgo GPT
Updated : 2025-06-28
"""

print("🔁 OpenAlgo Python Bot is running.") # rule 13

import os, sys, re, time, asyncio, pandas as pd, plotly.graph_objects as go
from datetime import datetime
from openalgo import api

# ───────────── CONFIG (edit to suit) ─────────────────────────────────────
API_KEY = os.getenv("OPENALGO_API_KEY", "openalgo-api-key")
API_HOST = os.getenv("OPENALGO_API_HOST", "http://127.0.0.1:5000")

EXPIRY = "31JUL25" # ✅ option expiry
RADIUS = 20 # ± strikes
STEP = 100 # ✅ 100-pt strikes (was 50)
BATCH_SIZE = 10 # ✅ broker cap per second
BATCH_PAUSE = 2 # seconds to wait between batches
MAX_RETRIES = 1 # one retry on 429 / timeout
BACKOFF_SEC = 1.2

client = api(api_key=API_KEY, host=API_HOST)
if sys.platform.startswith("win"):
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

# ───────────── Helpers ───────────────────────────────────────────────────
def get_atm_strike(step: int = STEP) -> int:
q = client.quotes(symbol="NIFTY", exchange="NSE_INDEX")
print("Underlying Quote :", q) # rule 14
return int(round(q["data"]["ltp"] / step) * step)

_sym_rx = re.compile(r"^[A-Z]+(\d{2}[A-Z]{3}\d{2})(\d+)(CE|PE)$")
def parse_symbol(sym: str):
m = _sym_rx.match(sym)
return (int(m.group(2)), m.group(3)) if m else None

def fetch_sync(sym: str) -> dict | None:
"""Blocking quote call with a retry for 429 / timeout."""
for attempt in range(MAX_RETRIES + 1):
q = client.quotes(symbol=sym, exchange="NFO")
print(sym, "→", q) # rule 14
if q.get("status") == "success":
strike, opt = parse_symbol(sym)
if strike is None:
print("⚠️ Bad symbol", sym)
return None
return dict(strike=strike, type=opt,
oi=q["data"]["oi"], ltp=q["data"]["ltp"])
if (q.get("code") == 429 or q.get("error_type") == "timeout_error") and attempt < MAX_RETRIES:
time.sleep(BACKOFF_SEC)
return None

# ───────────── Batch-paced gather (≈5 req/s) ─────────────────────────────
async def gather_df() -> pd.DataFrame:
atm = get_atm_strike()
strikes = [atm + i*STEP for i in range(-RADIUS, RADIUS + 1)]
symbols = [f"NIFTY{EXPIRY}{k}{s}" for k in strikes for s in ("CE", "PE")]

rows: list[dict] = []
for i in range(0, len(symbols), BATCH_SIZE):
batch = symbols[i:i+BATCH_SIZE]
res = await asyncio.gather(*[asyncio.to_thread(fetch_sync, s) for s in batch])
rows.extend(r for r in res if r)
if i + BATCH_SIZE < len(symbols):
await asyncio.sleep(BATCH_PAUSE) # pace → 5 req/s

if not rows:
raise RuntimeError("All quotes failed – check API / symbols.")
return pd.DataFrame(rows)

# ───────────── Plot (Call = green, Put = red, both positive) ─────────────
def plot_oi(df: pd.DataFrame):
piv = (df.pivot(index="strike", columns="type", values=["oi", "ltp"])
.sort_index())
piv.columns = ["CE_OI", "PE_OI", "CE_LTP", "PE_LTP"]
piv = piv.reset_index()

fig = go.Figure()
fig.add_bar(
x=piv["strike"], y=piv["CE_OI"], # no minus sign
name="Call OI", marker_color="seagreen", # ✅ green
customdata=piv[["CE_OI", "CE_LTP"]],
hovertemplate="<b>%{x} CE</b><br>OI %{customdata[0]:,}"
"<br>LTP ₹%{customdata[1]:.2f}<extra></extra>"
)
fig.add_bar(
x=piv["strike"], y=piv["PE_OI"],
name="Put OI", marker_color="crimson", # ✅ red
customdata=piv[["PE_OI", "PE_LTP"]],
hovertemplate="<b>%{x} PE</b><br>OI %{customdata[0]:,}"
"<br>LTP ₹%{customdata[1]:.2f}<extra></extra>"
)
atm = get_atm_strike()
fig.add_vline(
x=piv.index[piv["strike"] == atm][0],
line_dash="dash", line_color="gray",
annotation_text=f"ATM {atm}", annotation_position="top"
)
fig.update_layout(
title=f"NIFTY {datetime.strptime(EXPIRY,'%d%b%y').strftime('%d %b %Y')} – OI Profile",
xaxis=dict(title="Strike", type="category"),
yaxis_title="Open Interest",
bargap=0.05, template="plotly_dark",
height=500, width=1250,
legend=dict(orientation="h", yanchor="bottom", y=1.02,
xanchor="right", x=1)
)
fig.show()

# ───────────── Runner (script / notebook) ────────────────────────────────
async def _main():
df = await gather_df()
print(df.head())
plot_oi(df)

def _in_nb() -> bool:
try:
import IPython
return IPython.get_ipython() is not None
except ImportError:
return False

if _in_nb():
await _main() # Jupyter
else:
asyncio.run(_main()) # script

Full Code Explaination

Configuration

Environment Variables

OPENALGO_API_KEY  = "your_api_key_here"
OPENALGO_API_HOST = "http://127.0.0.1:5000"

Parameters

EXPIRY        = "31JUL25"      # Option expiry date (DDMMMYY format)
RADIUS = 20 # Number of strikes above/below ATM
STEP = 100 # Strike price interval
BATCH_SIZE = 5 # API requests per batch
BATCH_PAUSE = 1.05 # Seconds between batches
MAX_RETRIES = 1 # Retry attempts for failed requests
BACKOFF_SEC = 1.2 # Backoff time for retries

Code Structure

1. ATM Strike Calculation

def get_atm_strike(step: int = STEP) -> int:
q = client.quotes(symbol="NIFTY", exchange="NSE_INDEX")
return int(round(q["data"]["ltp"] / step) * step)

Fetches current NIFTY spot price and rounds to nearest strike.

2. Symbol Parsing

_sym_rx = re.compile(r"^[A-Z]+(\d{2}[A-Z]{3}\d{2})(\d+)(CE|PE)$")
def parse_symbol(sym: str):
m = _sym_rx.match(sym)
return (int(m.group(2)), m.group(3)) if m else None

Extracts strike price and option type from symbol string.

3. Quote Fetching

def fetch_sync(sym: str) -> dict | None:
for attempt in range(MAX_RETRIES + 1):
q = client.quotes(symbol=sym, exchange="NFO")
if q.get("status") == "success":
strike, opt = parse_symbol(sym)
return dict(strike=strike, type=opt,
oi=q["data"]["oi"], ltp=q["data"]["ltp"])
if (q.get("code") == 429 or q.get("error_type") == "timeout_error") and attempt < MAX_RETRIES:
time.sleep(BACKOFF_SEC)
return None

Fetches quote data with retry logic for rate limits and timeouts.

4. Batch Data Collection

async def gather_df() -> pd.DataFrame:
atm = get_atm_strike()
strikes = [atm + i*STEP for i in range(-RADIUS, RADIUS + 1)]
symbols = [f"NIFTY{EXPIRY}{k}{s}" for k in strikes for s in ("CE", "PE")]

rows: list[dict] = []
for i in range(0, len(symbols), BATCH_SIZE):
batch = symbols[i:i+BATCH_SIZE]
res = await asyncio.gather(*[asyncio.to_thread(fetch_sync, s) for s in batch])
rows.extend(r for r in res if r)
if i + BATCH_SIZE < len(symbols):
await asyncio.sleep(BATCH_PAUSE)

return pd.DataFrame(rows)

Processes symbols in batches to respect API rate limits.

5. Visualization

def plot_oi(df: pd.DataFrame):
piv = df.pivot(index="strike", columns="type", values=["oi", "ltp"]).sort_index()
piv.columns = ["CE_OI", "PE_OI", "CE_LTP", "PE_LTP"]

fig = go.Figure()

# Call OI - Green bars
fig.add_bar(
x=piv["strike"], y=piv["CE_OI"],
name="Call OI", marker_color="seagreen",
customdata=piv[["CE_OI", "CE_LTP"]],
hovertemplate="<b>%{x} CE</b><br>OI %{customdata[0]:,}<br>LTP ₹%{customdata[1]:.2f}"
)

# Put OI - Red bars
fig.add_bar(
x=piv["strike"], y=piv["PE_OI"],
name="Put OI", marker_color="crimson",
customdata=piv[["PE_OI", "PE_LTP"]],
hovertemplate="<b>%{x} PE</b><br>OI %{customdata[0]:,}<br>LTP ₹%{customdata[1]:.2f}"
)

# ATM line
atm = get_atm_strike()
fig.add_vline(
x=piv.index[piv["strike"] == atm][0],
line_dash="dash", line_color="gray",
annotation_text=f"ATM {atm}"
)

fig.update_layout(
title=f"NIFTY {datetime.strptime(EXPIRY,'%d%b%y').strftime('%d %b %Y')} – OI Profile",
xaxis=dict(title="Strike", type="category"),
yaxis_title="Open Interest",
bargap=0.05, template="plotly_dark",
height=500, width=1250
)

fig.show()

Creates interactive bar chart with hover details.

Usage

In Jupyter Notebook

# Run all cells to generate OI profile
await _main() # Automatically detected and executed

As Python Script

# Run directly
python NiftyOI.py

Output

The script generates:

  • Interactive Plotly chart
  • Green bars for Call OI
  • Red bars for Put OI
  • Gray dashed line for ATM strike
  • Hover tooltips showing OI and LTP values

Error Handling

  • 429 errors: Automatic retry with backoff
  • Timeout errors: Retry with MAX_RETRIES limit
  • Invalid symbols: Skipped with warning
  • API failures: Graceful degradation

Data Structure

Output DataFrame contains:

  • strike: Strike price
  • type: Option type (CE/PE)
  • oi: Open Interest
  • ltp: Last Traded Price