Nifty Options Backtesting with Greeks — Free Python Guide
By Shakti Tiwari — Nifty Option Trader, Research Analyst & XGBoost Expert
Backtesting Nifty options is harder than backtesting the index, because an option's value depends on more than price direction — it depends on time decay, volatility, and the Greeks. But you can do it for free in Python. This guide shows how to reconstruct option prices from ^NSEI index data using Black-Scholes, compute the Greeks with py_vollib, and backtest a simple options strategy without paid data. Code-first, no fake numbers.
The Core Problem: Options Data Is Expensive
Clean historical Nifty options chains (per-strike, per-expiry, tick-by-tick) are sold by vendors. For free research, the practical approach is to model option prices from the freely available index level and an implied-volatility assumption. This won't match every real fill, but it's rigorous enough to test the logic of a strategy — theta harvesting, delta exposure, etc.
Step 1: Install Free Tooling
pip install yfinance pandas numpy py_vollib
py_vollib is a fast, free Black-Scholes / Greeks library. We'll price European options — Nifty options are European-style, so this is the correct model.
Step 2: Get Nifty Spot History Free
import yfinance as yf
import pandas as pd, numpy as np
spot = yf.download("^NSEI", start="2020-01-01", end="2024-12-31", auto_adjust=True)
spot = spot["Close"].dropna()
Step 3: Price a Nifty Option and Its Greeks
Black-Scholes needs spot S, strike K, time to expiry t (years), risk-free rate r, volatility sigma, and flag c/p.
from py_vollib.black_scholes import black_scholes as bs
from py_vollib.black_scholes.greeks.analytical import delta, gamma, theta, vega
S = float(spot.iloc[-1])
K = round(S / 50) * 50 # ATM strike, Nifty steps by 50
t = 7 / 365 # weekly expiry, 7 days
r = 0.065 # ~India risk-free proxy; verify current rate
sigma = 0.14 # implied vol assumption — see Step 4
price = bs("c", S, K, t, r, sigma)
print("CE price:", round(price, 2))
print("Delta:", round(delta("c", S, K, t, r, sigma), 4))
print("Gamma:", round(gamma("c", S, K, t, r, sigma), 6))
print("Theta/day:", round(theta("c", S, K, t, r, sigma), 4))
print("Vega:", round(vega("c", S, K, t, r, sigma), 4))
Theta is negative for a long option — the daily bleed. That's the number an option seller wants on their side.
Step 4: Where to Get Implied Volatility for Free
A backtest is only as good as its vol input. Options:
-
India VIX as a proxy for at-the-money Nifty IV. Yahoo carries it as
^INDIAVIX. Divide by 100 to get a decimal sigma. - Realized volatility from index returns as a fallback when VIX history has gaps.
vix = yf.download("^INDIAVIX", start="2020-01-01", end="2024-12-31")["Close"] / 100
# align vix to spot dates and use as time-varying sigma
Using time-varying IV (not a flat 14%) makes the backtest far more honest, because option prices explode when VIX spikes.
Step 5: Backtest a Weekly Short-Straddle (Theta) Idea
Sell an ATM call and put each Monday, hold to Friday expiry, repeat. This is a classic theta strategy — and a classic way to blow up if you ignore tail risk. Let's model it.
def price_leg(S, K, t, r, sigma, flag):
return bs(flag, S, K, max(t, 1e-6), r, sigma)
# pseudo-loop: each week, entry Monday, settle at Friday intrinsic value
pnl = []
weekly = spot.resample("W-FRI").last().dropna()
for i in range(len(weekly) - 1):
S0 = float(weekly.iloc[i]); S1 = float(weekly.iloc[i+1])
K = round(S0 / 50) * 50
sig = 0.14 # replace with aligned VIX value
credit = price_leg(S0, K, 5/365, r, sig, "c") + price_leg(S0, K, 5/365, r, sig, "p")
payout = max(S1 - K, 0) + max(K - S1, 0) # intrinsic at expiry
pnl.append(credit - payout) # short straddle: keep credit, pay intrinsic
pnl = pd.Series(pnl)
print("Weeks:", len(pnl), "Win rate:", round((pnl > 0).mean(), 3))
Step 6: Read the Risk Honestly
Short straddles win most weeks and lose big in crashes. Look at the tail, not the average.
print("Worst week:", round(pnl.min(), 1))
print("Mean weekly P&L:", round(pnl.mean(), 1))
print("Std:", round(pnl.std(), 1))
Run it and you'll typically see a high win rate with rare, brutal losers — the exact shape that lures retail sellers and then ruins the unhedged ones. I'm not quoting the numbers because they depend entirely on your vol input and date range; a fixed figure would be fake.
Critical Caveats for Nifty Options
- Model prices ≠ real fills. Black-Scholes with a flat or VIX-proxy sigma ignores the volatility smile — real OTM options trade richer. Treat this as logic-testing, not P&L truth.
- Lot size and margin. Nifty options trade in lots; short straddles require large SPAN margin. Model position sizing realistically.
- Costs. Brokerage, STT (higher on the sell side of options), and slippage matter enormously for high-frequency theta strategies.
- For real historical chains, use Zerodha Kite Connect historical API (paid) or NSE's own data; validate any modeled backtest against real data before trusting it.
Takeaway
- You can backtest Nifty options logic for free by modeling prices with
py_vollibBlack-Scholes from^NSEI. - Use
^INDIAVIXas a time-varying IV input — a flat sigma is unrealistic. - Greeks (delta, gamma, theta, vega) tell you why a position makes or loses money; theta is the seller's edge and the buyer's cost.
- Short-premium strategies show high win rates with fat tail losses — judge the worst week, not the average.
- Modeled prices are for logic-testing; validate against real Kite/NSE chains before risking capital.
Related: My book Option Trading with AI: XGBoost, Transformers & Quantized Models for the Retail Nifty Trader goes deeper on combining Greeks with ML for Nifty options.
Disclaimer: This article is for education only and is not financial, investment, or trading advice. SEBI-registered research rules apply — verify everything before acting.
🔗 Get the book on Amazon: https://www.amazon.in/dp/B0H9ZNTBPK
📢 Daily Nifty analysis on Telegram: https://t.me/shaktitrade
📧 Email: shaktitiwari715@gmail.com
Top comments (0)