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 pricetype
: Option type (CE/PE)oi
: Open Interestltp
: Last Traded Price
Last updated