Home · Guides · How bid/ask data improves backtest quality

GUIDE · BID/ASK · BACKTESTING · CATALOG VERIFIED 2026-11-11

How bid/ask data improves backtest quality

Bid/ask data makes a backtest fill trades where they could really have filled: buy at the ask, sell at the bid, and pay the spread on every entry and exit. A backtest filled at the last traded price ignores that cost, lets you trade contracts nobody was quoting, and on expiry day can turn a losing strategy into a winning one. On Options Data, a NIFTY 1-minute bid/ask add-on for options and futures (1 Jan – 16 Oct 2026, bid and ask prices only) is available on request to NIFTY 2026 1-minute buyers (₹1,599) via the contact form.

NIFTY Options 1-minute 2026NIFTY option chain data Get free sample by email File schema

Get the free sample by email → check the columns

Dates on this page are read from the live file catalog, not typed by hand. Full matrix: /coverage.

NIFTY options on 10 Mar 2026: quote vs last traded price (bid/ask add-on and price files)

Minute (IST)ExpiryStrikeTypeBidAskLast traded (close)Note
11:0010 Mar 202624200CE73.9074.0573.40At the money; close below the bid
15:2010 Mar 202624250CE10.5510.6010.70Close above the ask
15:2010 Mar 202624250PE0.050.100.05Ask is twice the bid
11:0030 Mar 202626200PE1,891.751,911.201,905.55Deep in the money; no volume that minute
11:0030 Mar 202622200CE1,861.102,463.35no traded barQuote only; spread very wide

Columns in the NIFTY bid/ask add-on files

ColumnTypeMeaning
timestamptimestampMinute, IST wall-clock, same clock as the price files
expirystringContract expiry, e.g. 2026-03-10
strikeintStrike price (options only)
option_typestringCE or PE (options only)
bidfloatBest bid price
askfloatBest ask price

Why filling at the last traded price overstates results

A 1-minute bar records trades that printed. Its close is the last trade in that minute, which may have been a buyer lifting the ask or a seller hitting the bid. A backtest that buys and sells at close assumes you trade at the middle of the market for free, every time.

Three things go wrong. The spread disappears: each round trip should cost the gap between ask and bid, and option strategies trade often. Bounce looks like edge: a close that printed at the bid looks cheap and one that printed at the ask looks expensive, so mean-reversion rules appear to earn money that was only the spread. Stale prints look tradable: in a minute with no trade, the bar either is missing or repeats an old price, while the real quotes may have moved or widened.

The rows below are real NIFTY option rows from 10 Mar 2026, a weekly expiry day, with the quote and the traded close for the same contract and minute. The close can sit below the bid or above the ask, so it is not a price you could have traded at.

Realistic fills: buy at the ask, sell at the bid

The simplest honest fill model: a buy fills at the ask, a sell fills at the bid. The mid price (bid + ask) / 2 is a fair value for marking a position, not a fill price.

Use the quote of the bar after your signal, not the same bar. If a rule reads the 10:14 close, the earliest realistic fill is the 10:15 quote. This removes look-ahead and adds the one-minute delay a real order has.

Join the quotes to the price files on minute, expiry, strike and call/put. The code below maps the price file to the quote-file column names first.

import pandas as pd
def parse_expiry(s):
    s = s.astype(str)
    a = pd.to_datetime(s, format="%d-%b-%Y", errors="coerce")
    b = pd.to_datetime(s, format="%Y-%m-%d", errors="coerce")
    return a.fillna(b)

px = pd.read_parquet("NIFTY_OPT_1min_2026-03-10_exp2026-03-10.parquet")
# map layout A names to the layout B names the quote files use
px = px.rename(columns={"datetime": "timestamp", "expiry_date": "expiry",
                        "strike_price": "strike", "right": "option_type"})
px["option_type"] = px["option_type"].replace({"Call": "CE", "Put": "PE"})
px["expiry"] = parse_expiry(px["expiry"])

q = pd.read_parquet("NIFTY_OPT_BIDASK_1min_2026-03-10_exp2026-03-10.parquet")
q["expiry"] = parse_expiry(q["expiry"])

keys = ["timestamp", "expiry", "strike", "option_type"]
df = q.merge(px[keys + ["close", "volume"]], on=keys, how="left")
df["mid"] = (df["bid"] + df["ask"]) / 2
df["spread"] = df["ask"] - df["bid"]

# fill a signal on bar t at the quote of bar t+1, per contract
df = df.sort_values(keys[1:] + ["timestamp"])
g = df.groupby(keys[1:])
df["buy_fill"] = g["ask"].shift(-1)
df["sell_fill"] = g["bid"].shift(-1)

Spread cost: count it on every leg

Against the mid, each fill costs half the spread; a full round trip costs one whole spread. A four-leg iron condor opened and closed pays four spreads. Compare that with the edge per trade before trusting any result.

Spread is often more useful as a share of the mid than in points: spread / mid. A spread that is negligible on an at-the-money option can be most of the premium on a cheap out-of-the-money one. Exchange charges, taxes and brokerage come on top, so add your own cost model as well.

Slippage beyond the quoted price

The add-on holds bid and ask prices, not the quantity behind them, so it cannot tell you how much would have filled at the touch. Treat the quote as the best case for a small order. For larger orders, or fast markets, add a buffer beyond the quote, for example one or two ticks past the ask on buys and past the bid on sells, and check that results survive it.

A quick robustness test: rerun the backtest with fills at the far side of the quote plus one tick. If the strategy only works at the mid, it is unlikely to work live.

Filter out illiquid strikes

Deep in-the-money and far out-of-the-money options and later expiries are where last-traded prices mislead most: few trades, wide quotes. Use the quotes to decide what a strategy may trade at all, using only information known at that minute:

Require a two-sided quote: both bid and ask present and bid > 0. Cap the relative spread: skip contracts where spread / mid is above a threshold you choose, such as a few percent. Require recent trading: a non-zero volume in the price file for that minute or the minutes before it.

The far-strike rows in the table below show the problem: a deep in-the-money put whose quote was wide with no trade in that minute, and a call with a quote but no traded bar at all.

liquid = df[(df["bid"] > 0) & df["ask"].notna()
            & (df["spread"] / df["mid"] < 0.03)
            & (df["volume"] > 0)]

Expiry-day fills

On expiry day, premiums decay towards intrinsic value through the session. Options worth a few rupees still have a minimum tick, so the spread can be a large share of the price. In the 15:20 row below, the at-the-money put is quoted at 0.05 bid and 0.10 ask: buying at the ask costs twice the bid.

Strategies that sell cheap options late on expiry day, or buy them as lottery tickets, are the most sensitive to fill assumptions. Test them on bid/ask fills, and check what happens if a position must be closed in the last minutes instead of being held to settlement.

At-the-money options on the expiring weekly are usually quoted tightly; the 11:00 row below shows a spread of 0.15 on a premium near 74, yet the last traded price was still outside the quote.

Where the bid/ask add-on fits

The NIFTY 1-minute bid/ask add-on covers NIFTY options and futures from 1 Jan to 16 Oct 2026, with a best bid and best ask price for each contract each minute. It is available on request to NIFTY 2026 1-minute buyers (₹1,599) via the contact form, so the quotes line up with option price files you already hold.

The traded price files themselves are described on the NIFTY option chain data page and in the Parquet backtesting guide.

Exactly which fields are in the files

Options files

Column (layout A · layout B)TypeMeaning
datetime · timestamptimestampBar start time, IST wall-clock (e.g. 2026-09-10 09:15:00)
stock_code · —stringUnderlying code: NIFTY, or BSESEN for SENSEX
exchange_code · —stringNFO (NSE F&O) or BFO (BSE F&O)
product_type · —stringOptions
expiry_date · expirystringContract expiry (A: 15-SEP-2026, B: 2026-09-15)
strike_price · strikenumberStrike price
right · option_typestringA: Call / Put, B: CE / PE
open / high / low / closefloatTraded premium OHLC within the bar
volumeintVolume traded in the bar
open_interest · oiintOpen interest at that bar (SENSEX has gaps where it is 0 — see /free/schema)

Futures files

Column (layout A · layout B)TypeMeaning
datetime · timestamptimestampBar start time, IST wall-clock
stock_code · symbolstringUnderlying / contract code
expiry_date · expirystringFutures contract expiry
open / high / low / closefloatTraded price OHLC within the bar
volumeintVolume traded in the bar
open_interest · oiintOpen interest at that bar (SENSEX futures have 0-OI gaps — see /free/schema)

The price files hold traded OHLC, volume and open interest. Bid and ask prices are a separate add-on: best bid and best ask price per contract per minute, with no sizes or order book depth.

Format: Parquet. Check the price-file layout on the free sample and the schema page.

FAQ

Why is a backtest filled at the last traded price too optimistic?

The last traded price can be at the bid or the ask, so trading at it assumes no spread cost. It also hides minutes with no trades and wide quotes. Filling buys at the ask and sells at the bid removes that bias.

Should I fill at the mid price?

Use the mid to value open positions, not to fill orders. A market order buys at the ask and sells at the bid; a limit order at the mid may not fill at all.

Does the bid/ask add-on include sizes or market depth?

No. It has the best bid and best ask price for each contract each minute, without quantities or deeper levels. Add a slippage buffer for larger orders.

Which dates and instruments does the NIFTY bid/ask add-on cover?

NIFTY options and futures at 1-minute, from 1 Jan to 16 Oct 2026, bid and ask prices only. It is available on request to NIFTY 2026 1-minute buyers (₹1,599) via the contact form.

How do I filter out illiquid strikes?

Only trade contracts with a two-sided quote, a relative spread (ask minus bid, over mid) below a threshold you set, and recent traded volume. Use only data known at that minute.

Get the data

Free sample · Schema · Ask the AI for your data

NIFTY Options 1-minute 2026NIFTY option chain data Get free sample by email

Related guides

Index and commodity data only.