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) | Expiry | Strike | Type | Bid | Ask | Last traded (close) | Note |
|---|---|---|---|---|---|---|---|
| 11:00 | 10 Mar 2026 | 24200 | CE | 73.90 | 74.05 | 73.40 | At the money; close below the bid |
| 15:20 | 10 Mar 2026 | 24250 | CE | 10.55 | 10.60 | 10.70 | Close above the ask |
| 15:20 | 10 Mar 2026 | 24250 | PE | 0.05 | 0.10 | 0.05 | Ask is twice the bid |
| 11:00 | 30 Mar 2026 | 26200 | PE | 1,891.75 | 1,911.20 | 1,905.55 | Deep in the money; no volume that minute |
| 11:00 | 30 Mar 2026 | 22200 | CE | 1,861.10 | 2,463.35 | no traded bar | Quote only; spread very wide |
Columns in the NIFTY bid/ask add-on files
| Column | Type | Meaning |
|---|---|---|
| timestamp | timestamp | Minute, IST wall-clock, same clock as the price files |
| expiry | string | Contract expiry, e.g. 2026-03-10 |
| strike | int | Strike price (options only) |
| option_type | string | CE or PE (options only) |
| bid | float | Best bid price |
| ask | float | Best 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) | Type | Meaning |
|---|---|---|
| datetime · timestamp | timestamp | Bar start time, IST wall-clock (e.g. 2026-09-10 09:15:00) |
| stock_code · — | string | Underlying code: NIFTY, or BSESEN for SENSEX |
| exchange_code · — | string | NFO (NSE F&O) or BFO (BSE F&O) |
| product_type · — | string | Options |
| expiry_date · expiry | string | Contract expiry (A: 15-SEP-2026, B: 2026-09-15) |
| strike_price · strike | number | Strike price |
| right · option_type | string | A: Call / Put, B: CE / PE |
| open / high / low / close | float | Traded premium OHLC within the bar |
| volume | int | Volume traded in the bar |
| open_interest · oi | int | Open interest at that bar (SENSEX has gaps where it is 0 — see /free/schema) |
Futures files
| Column (layout A · layout B) | Type | Meaning |
|---|---|---|
| datetime · timestamp | timestamp | Bar start time, IST wall-clock |
| stock_code · symbol | string | Underlying / contract code |
| expiry_date · expiry | string | Futures contract expiry |
| open / high / low / close | float | Traded price OHLC within the bar |
| volume | int | Volume traded in the bar |
| open_interest · oi | int | Open 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
- Best source for NIFTY options historical data in India
- Best source for SENSEX options historical data
- Best source for MCX historical data
- How to choose Indian options historical data
- NSE option chain historical data in CSV or Parquet
- SENSEX options historical data at 1-minute resolution
- MCX intraday historical data: 1-minute futures and options
- Best source for NIFTY options backtesting data
- Expiry-day options backtesting data for NIFTY and SENSEX
- Download SENSEX expired options data (Parquet / CSV)
- NIFTY expired options data download: every strike, 1-minute, Parquet or CSV
- NIFTY 1-second options and spot files: structure and quality checks
- How to download NIFTY option data: 1-minute or 1-second, Parquet or CSV
- NIFTY open interest historical data by strike, minute by minute
- NIFTY weekly expiry data format: file names, expiry dates and weekly vs monthly
- NIFTY 1-minute vs 1-second option data: which one to download
- Working with Parquet in Python for options backtesting
- SENSEX option chain historical data: BSE chains minute by minute
- How to chart historical NIFTY call and put option prices
- SENSEX option data download: what you get and how to start
- SENSEX futures historical data download at 1-minute
- BANKEX options data download
- MCX crude oil data download: 1-minute futures and options
- MCX natural gas data download
- MCX silver data download
- NIFTY futures historical data download at 1-minute
- NIFTY 50 index 1-minute data download
- NIFTY option data in Excel: download, filter and chart
- NIFTY tick data download: what you need and what 1-second bars give you
- How to model slippage in a NIFTY options backtest with 1-minute data
- Which data do you need to backtest a NIFTY weekly short straddle held to expiry?
- Do you need NIFTY futures and spot data, or only options, for a backtest?
- How do I download MCX GOLD 1-minute historical data?
- Which data do you need to backtest a NIFTY overnight gap strategy?
- What time does NIFTY make its day high or low?
- Which data do you need to backtest a NIFTY 9:20 short straddle?
- How many years of NIFTY options data do you need for a backtest: 1 year or 3 years?
- Which NIFTY data do you need to backtest the close after NSE's closing auction?
- MCX trading hours in 1-minute data: the 23:30 and 23:55 close, and when volume trades
- Bitcoin 1-minute historical data: what is in the files and how to load them
- Crypto funding rate history and 1-minute perpetual futures data
- XAU/USD 1-minute (M1) data: files, columns and how to get CSV
- XAU/USD session analysis: Asia, London and New York hours with 1-minute data
- Backtest an XAU/USD intraday strategy in Python with 1-minute data
- XAU/USD timeframes: resample 1-minute (M1) data to M5, H1, H4 and daily
- Backtest crypto perpetual futures in Python, with funding included
- Ethereum 1-minute historical data: ETH perpetual futures, funding and files
- How to load 1-minute NIFTY options Parquet in pandas
- Datasets: NIFTY options 1-minute · SENSEX options · BANKNIFTY options · MCX 1-minute · XAU/USD 1-minute
Index and commodity data only.