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
python
"""
NIFTY 28 AUG 2025 - OI profile (10-req/s batches)
Author : Tradeboard GPT
Updated : 2025-06-28
"""
print("Tradeboard Python Bot is running.")
import os, sys, re, time, asyncio, pandas as pd, plotly.graph_objects as go
from datetime import datetime
from tradeboard import api
# ------------------------- CONFIG (edit to suit) --------------------------
API_KEY = os.getenv("TRADEBOARD_API_KEY", "tradeboard-api-key")
API_HOST = os.getenv("TRADEBOARD_API_HOST", "http://127.0.0.1:5000")
EXPIRY = "28AUG25" # option expiry (DDMMMYY)
RADIUS = 20 # strikes above and below ATM
STEP = 100 # 100-pt strike interval
BATCH_SIZE = 10 # quotes per batch
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)
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)
if q.get("status") == "success":
parsed = parse_symbol(sym)
if parsed is None:
print("Bad symbol", sym)
return None
strike, opt = parsed
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 (about 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: 10 quotes / 2s
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",
customdata=piv[["CE_OI", "CE_LTP"]],
hovertemplate="<b>%{x} CE</b><br>OI %{customdata[0]:,}"
"<br>LTP Rs.%{customdata[1]:.2f}<extra></extra>"
)
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 Rs.%{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():
asyncio.ensure_future(_main()) # Jupyter already runs an event loop
else:
asyncio.run(_main()) # scriptFull Code Explaination
Configuration
Environment Variables
python
TRADEBOARD_API_KEY = "your_api_key_here"
TRADEBOARD_API_HOST = "http://127.0.0.1:5000"Parameters
python
EXPIRY = "28AUG25" # Option expiry date (DDMMMYY format)
RADIUS = 20 # Number of strikes above/below ATM
STEP = 100 # Strike price interval
BATCH_SIZE = 10 # API requests per batch
BATCH_PAUSE = 2 # Seconds between batches
MAX_RETRIES = 1 # Retry attempts for failed requests
BACKOFF_SEC = 1.2 # Backoff time for retriesCode Structure
1. ATM Strike Calculation
python
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
python
_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 NoneExtracts strike price and option type from symbol string.
3. Quote Fetching
python
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":
parsed = parse_symbol(sym)
if parsed is None:
return None
strike, opt = parsed
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 NoneFetches quote data with retry logic for rate limits and timeouts.
4. Batch Data Collection
python
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
python
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 Rs.%{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 Rs.%{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
python
# Run all cells to generate the OI profile.
# The runner detects the notebook and schedules _main() on the running loop.
await _main()As Python Script
bash
python NiftyOI.pyOutput
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
