Python Backtesting for Options Strategies

Backtesting is where an options strategy is built or exposed: the discipline of replaying historical data through your rules to measure the edge, the risk, and the costs - before your money is on the line. In Python, options backtesting means modelling greeks, time decay, IV, and multi-leg fills honestly. This guide walks through the components of a solid options backtest system, with code patterns you can adapt.

What the Backtester Must Model

  • Greeks and pricing: the option's value needs a pricing model (Black-Scholes or binomial for indices) with inputs - spot, strike, expiry days, IV, interest rate
  • Time decay: the position is marked to market every day, so theta shows up naturally
  • IV: supply implied volatility per strike per day (from historical option chain snapshots) rather than a single flat number
  • Multi-leg orders: spreads fill per leg with bid/ask and costs; single-fill shortcuts lie
  • Costs: brokerage, exchange charges, STT, slippage - apply to every simulated order and every roll

The Core Loop in Python

prices = load_ohlc(symbol)
chains = load_option_chains(symbol)     # per-date strike/IV tables
position = None
equity, trades = [], []
for day in dates:
    if position and position.expiry == day:      # roll/expire
        cash += settle(position)
        position = None
    signal = strategy(prices[:day], chains[:day])  # no look-ahead
    if signal and position is None:
        position = open_spread(signal, chains[day])  # legs, IV, costs
    equity.append(mark_to_market(position, chains[day]))

The two rules that make it honest: features use only data up to day (no future leakage), and closing prices + mid/next-bar fill only (no perfect fill fantasy).

The Costs of an Options Backtest

Options backtests undercharge in three silent places: (1) bid-ask spread - use mid plus half-spread, not the mid; (2) slippage on exits - stops are usually filled worse than entries; (3) margin and collateral - sold spread structures block capital that compounding must account for. An honest backtest runs the strategy with cost-modeled fills and reports net P&L per trade.

Metrics That Matter for Options

  • Win rate and avg win vs avg loss: option strategies are asymmetric; the ratio between them is the truth
  • Max drawdown and time to recover: a strategy that survives 30% DD but with 6 months of life is different from one that recovers in 30 days
  • Sharpe / Sortino: risk-adjusted; for short-vol strategies, check these survive the vol spikes
  • Trade count: fewer than ~50 trades is anecdote, not evidence
  • Tail sensitivity: run the scenario where IV jumps 40% or the index gaps 500 points - the strategy must survive the tail explicitly

Common Failures That Falsify a Backtest

  • Shuffling data or using future features (look-ahead leakage)
  • Ignoring bid-ask and costs on options legs
  • Overfitting the walk-forward window (fine-tuned to the exact past)
  • Survivorship bias - options universe that excludes delisted crashes
  • Using flat IV instead of per-strike/per-day IV surfaces

From Backtest to Confidence

  1. Start with a walk-forward engine on 3+ years of Indian index data with monthlies
  2. Add costs first; only trade a strategy whose net edge survives them
  3. Trade the strategy on paper or a micro position while the live loop records real fills
  4. Compare simulated vs actual slippage; adjust your fill model iteratively
  5. Journal every strategy: the index, dates, IV regime, and what the market taught you

Bottom Line

Python options backtesting is honest engineering: replay option chains with pricing, greeks, day-by-day time decay, per-strike IV, multi-leg fills, and real costs - then test the strategy's metrics and tail scenarios. The backtest is a filter, not a prophesy; the strategies that survive cost models, walk-forward validation, and paper-vs-live slippage checks become the trading**, the rest stay as education.

SEBI Disclaimer

Backtested performance is hypothetical and may differ materially from live results. This article is educational and is not investment advice.

Modeling the Multi-Leg Fill and the Spread Cost

An options backtester earns its keep in the fill model. A single-leg strategy can cheat with the theoretical mid; a multi-leg spread needs the legs filled at individually realistic prices - the bid for legs you sell, the ask for legs you buy, at the date's actual volatile depth. The honest structure models each leg at its own touch with a slippage term proportional to the leg's notional size relative to the recorded book, and re-prices the whole strategy at each rebalance date. The difference between mid-fill and touch-fill modelling on an iron condor is often the entire edge; the backtester that hides it is producing entertainment, not evidence.

Choosing the Underlying Data: Chain vs Synthesised IV

Two data diets exist. The rich one replays historical option chains with recorded premiums, strikes, and open interest - the most faithful, and the scarce one; the practical one synthesises option prices from spot, a volatility model, and Black-Scholes, which lets you backtest a decade of strikes that never traded. Every synthesised price inherits the model's assumptions, so mark the backtest's volatility input from realised series plus a cost markup, and validate the synthesis against real chain snapshots on a surviving year. The two diets answer different questions; the chain replays what would have happened, the synthesis replays what the model's world would have priced, and the truth sits somewhere honest between them.

The Exact Indian Cost Line Items

The rupee cost stack is non-negotiable and itemised: brokerage per executed order, exchange transaction charges on turnover, SEBI charges, GST on the fees, STT on the sell side, stamp duty on the buy, and the spread itself. On a small premium per unit the combined round-trip can approach or exceed a point on the strike's premium, which is why no options strategy backtests honestly without per-leg cost tabulation. The professional habit carries the fee schedule as a table in the backtester and re-runs the whole campaign when the broker changes pricing.

Distributing the Backtest Across Time Shards

Options curves are periods, not points. Split the campaign into calendar shards - a pre-regime year, a volatile year, a quiet year - and report the strategy's metrics per shard, because the aggregate curve hides which regime paid the bills. The iron condor shows positive PnL in the calm shard and a fat negative spike in the volatile one; a single headline number could sell either story. Shard reporting forces the judgment question the strategy must answer: can this strategy pay back its worst regime, and at what sized cost?

From Equity Curve to the R-Multiple Distribution

Convert the equity curve into a distribution of R-multiples - each trade's profit or loss expressed as a multiple of its initial risk - and the strategy becomes readable at a glance: the median R, the tail's fatness, and the percentage of losing weeks. This conversion replaces the hypnotic equity line with the distribution that actually governs position sizing. A strategy with a median R of 0.35 and a tail of -6R twice a year is a different position-sizing problem than one with a steady 0.8R and small tails, and the backtest that reports the multiple distribution has already done the sizing work for you.

  1. Fill every leg at its touch plus a size-proportional slippage.
  2. Prefer chain replays; validate any synthesised series against real snapshots.
  3. Itemise the full Indian cost stack per leg.
  4. Report metrics per calendar regime, not one aggregate number.
  5. Express results as R-multiples for position sizing.

Structure-Aware PnL: Greeks, Expiry, and the Assignment Net

An options backtester earns its keep by respecting structure: the PnL must flow through the contract's greeks from the trade date, the strike's delta must be computed against each session's own spot and volatility, and days to expiry must tick down in trading days, not calendar ones. The expiry mechanics decide the tail: pin-risk assignments settle each in-the-money leg at the closing settlement, so a spread's short leg assigned before market open reprices the entire structure in one gap. The backtest's edge is the settlement net: reconcile the premium collected, the STT charge on the sell leg, the assignment fill, and the rollover cost against the options chain data, session by session. A backtest whose fills respect contractual settlement rather than the close price is a backtest that names the gap risk before the live trade discovers it.