Skip to content

Historical Data for Expired Options

Expired options are where strategy truth is revealed.

Unlike live markets, expired contracts provide complete, finalised price paths — free from survivorship bias, rollovers, or regime ambiguity.

They are essential for:

  • payoff validation
  • Greek behaviour analysis
  • decay modelling
  • execution hindsight
  • post-expiry forensics

Nubra’s Historical Data API allows you to query fully expired option contracts with the same structure as live instruments.


Why Expired Options Matter

Option Symbol Formats (NSE)

1 Weekly Options (Jan–Sep)

Numeric month encoding

Weekly options expiring between January and September use a numeric month code in the symbol.

Format
<UNDERLYING><YY><M><DD><STRIKE><CE|PE>
Example
NIFTY2532426000CE
  • NIFTY → Underlying
  • 25 → Year (2025)
  • 3 → Month (March)
  • 24 → Expiry day
  • 26000 → Strike
  • CE → Call option

2 Weekly Options (Oct–Dec)

Alphabet month encoding

Weekly options expiring in the last quarter use alphabet month codes.

Format
<UNDERLYING><YY><M><DD><STRIKE><CE|PE>
Example
NIFTY25D1626000CE
  • D → December
  • N → November
  • O → October

3 Monthly Options

Standard monthly expiry

Monthly options use a 3-letter month abbreviation and do not include the expiry day.

Format
<UNDERLYING><YY><MMM><STRIKE><CE|PE>
Examples
NIFTY26NOV25500CE
HDFCBANK26MAY2380CE
  • NOV → November expiry
  • No expiry day encoded
  • Standard monthly contracts

Historical Charting for Expired Options

Below is a ready-to-use example that fetches historical data for an expired options contract and visualises:

  • OHLC candlesticks
  • cumulative volume
  • Greeks in separate panels

Because the contract is expired: - all data is complete - no rollovers occur - no real-time feeds are required

Simply change the SYMBOL, date range, or interval to analyse any expired option.

import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from nubra_python_sdk.marketdata.market_data import MarketData
from nubra_python_sdk.start_sdk import InitNubraSdk, NubraEnv


# ============================================================
# Nubra API
# ============================================================

nubra = InitNubraSdk(NubraEnv.PROD, env_creds=True)
md = MarketData(nubra)

SYMBOL = "NIFTY25DEC26000CE"

resp = md.historical_data({
    "exchange": "NSE",
    "type": "OPT",
    "values": [SYMBOL],
    "fields": [
        "open", "high", "low", "close",
        "cumulative_volume",
        "theta", "delta", "gamma", "iv_mid"
    ],
    "interval": "1d",
    "startDate": "2025-12-01T00:00:00.000Z",
    "endDate": "2025-12-30T00:00:00.000Z",
    "intraDay": False,
    "realTime": False
})


# ============================================================
# Helpers
# ============================================================

def ts_to_dt(ts):
    """Convert Nubra nanoseconds → datetime"""
    return pd.to_datetime(ts, unit="ns")


def series_to_df(series, col):
    if not series:
        return None
    return pd.DataFrame({
        "time": [ts_to_dt(x.timestamp) for x in series],
        col: [x.value for x in series],
    })


# ============================================================
# Parse Response
# ============================================================

chart = resp.result[0].values[0][SYMBOL]

df_price = pd.DataFrame({
    "time":  [ts_to_dt(x.timestamp) for x in chart.open],
    "open":  [x.value / 100 for x in chart.open],
    "high":  [x.value / 100 for x in chart.high],
    "low":   [x.value / 100 for x in chart.low],
    "close": [x.value / 100 for x in chart.close],
})

df_volume = series_to_df(chart.cumulative_volume, "volume")
df_delta  = series_to_df(chart.delta, "delta")
df_gamma  = series_to_df(chart.gamma, "gamma")
df_theta  = series_to_df(chart.theta, "theta")
df_iv     = series_to_df(chart.iv_mid, "iv")


# ============================================================
# Build Plotly Figure
# ============================================================

fig = make_subplots(
    rows=6,
    cols=1,
    shared_xaxes=True,
    vertical_spacing=0.02,
    row_heights=[0.44, 0.12, 0.11, 0.11, 0.11, 0.11],
    subplot_titles=[
        f"{SYMBOL} — OHLC",
        "Cumulative Volume",
        "Delta",
        "Gamma",
        "Theta",
        "Implied Volatility",
    ],
)

# OHLC
fig.add_trace(
    go.Candlestick(
        x=df_price["time"],
        open=df_price["open"],
        high=df_price["high"],
        low=df_price["low"],
        close=df_price["close"],
        increasing_line_color="#00c853",
        decreasing_line_color="#ff5252",
        name="OHLC",
    ),
    row=1, col=1
)

# Volume
if df_volume is not None:
    fig.add_trace(
        go.Bar(
            x=df_volume["time"],
            y=df_volume["volume"],
            marker_color="#546e7a",
            name="Volume",
        ),
        row=2, col=1
    )

# Greeks
if df_delta is not None:
    fig.add_trace(
        go.Scatter(
            x=df_delta["time"],
            y=df_delta["delta"],
            mode="lines+markers",
            name="Delta",
            line=dict(color="#42a5f5", width=2),
        ),
        row=3, col=1
    )

if df_gamma is not None:
    fig.add_trace(
        go.Scatter(
            x=df_gamma["time"],
            y=df_gamma["gamma"],
            mode="lines+markers",
            name="Gamma",
            line=dict(color="#ab47bc", width=2),
        ),
        row=4, col=1
    )

if df_theta is not None:
    fig.add_trace(
        go.Scatter(
            x=df_theta["time"],
            y=df_theta["theta"],
            mode="lines+markers",
            name="Theta",
            line=dict(color="#ffa726", width=2),
        ),
        row=5, col=1
    )

if df_iv is not None:
    fig.add_trace(
        go.Scatter(
            x=df_iv["time"],
            y=df_iv["iv"],
            mode="lines+markers",
            name="IV",
            line=dict(color="#26a69a", width=2),
        ),
        row=6, col=1
    )

fig.update_layout(
    template="plotly_dark",

    autosize=True,
    height=None,
    margin=dict(
        l=40,
        r=20,
        t=30,
        b=30
    ),

    hovermode="x unified",
    showlegend=False,
    xaxis_rangeslider_visible=False,
)



# ============================================================
# Render
# ============================================================

fig.show()
NEO Assistant