# 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.

<figure><img src="/files/cA7I8b40rmdI27VDPNOO" alt=""><figcaption></figcaption></figure>

### 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        = "28AUG25"      # ✅ 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

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

#### Parameters

```python
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

```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 None
```

Extracts 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":
            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

```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 ₹%{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

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

#### As Python Script

```python
# 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.openalgo.in/trading-platform/python/visualization/nifty-open-interest.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
