Any backtester can show you an equity curve. Whether you should trust it depends entirely on how the trades underneath were simulated. This page describes exactly what the btoption engine does — data, fills, management, settlement — and lists the things it deliberately does not model. We'd rather you find the limitations here than in your account.
Backtests run on end-of-day historical options chains from ORATS, going back to 2007, topped up nightly with fresh end-of-day data for supported tickers. For every trading day, every expiration and every strike we store the actual closing bid and ask— not just a mid price — plus delta, implied volatility, and the underlying's close.
One row per strike per day. That means the engine sees the market once a day, at the close. It does not have intraday data, and it won't pretend to: there is no tick-level simulation here, and strategies that depend on intraday behavior (0 DTE in particular) are rejected rather than approximated.
Most backtesters fill you at mid. Real fills are worse than mid, so the results are flattered before you've even looked at them. Our fill model crosses 75% of the bid-ask spread: when you buy, you pay bid + 0.75 × spread; when you sell, you receive ask − 0.75 × spread.
Example: an option quoted 1.00 × 1.20.
Mid would fill you at 1.10.
btoption buys at 1.15 and sells at 1.05.
That's deliberately pessimistic for liquid chains and roughly honest for wider ones. Open positions are marked at mid for P&L and rule evaluation; only actual entries and exits pay the spread. Entries fill at the close of the same day the entry condition is met.
When your strategy says “sell the 16-delta put, 45 DTE”, the engine looks at expirations within ±10 days of your DTE target and picks the strike whose delta is closest to 16 — the same imperfect match you'd get trading a real chain, where the exact 16-delta strike rarely exists.
If a leg can't be filled — no quote, no bid, nothing in the DTE window — the position simply doesn't open that day. Multi-leg positions are all-or-nothing: either every leg fills or no trade happens. The engine never invents a quote to keep a backtest going.
Every open position is checked once per trading day, against that day's closing quotes, in a fixed order: profit target first, then time- and roll-based management rules, then stop loss, then expiration handling. First rule that triggers wins, and the exit fills at that day's actual quoted price through the same 75%-spread model — never at your theoretical trigger level.
Two consequences worth understanding. If the underlying gaps through your stop, you exit at the real (worse) end-of-day price, just like a stop-market order. But a stop level that's breached intraday and recovers by the close is never seen at all — daily data can't catch it, so we don't pretend it can.
Rolls close every leg at the quoted price and re-enter the new position the same day, with the same strike-selection rules. A roll that can't fill doesn't happen.
Positions held to expiration are cash-settled at intrinsic value against the underlying's closing price: an option that expires in the money is worth exactly its intrinsic value, one that expires out of the money is worth zero.
The engine does not simulate share assignment. A covered call or cash-secured put that finishes in the money settles in cash rather than delivering a stock position — economically close at expiry, but it means post-assignment share P&L (and wheel-style continuation on the assigned shares) isn't part of the simulation. Early assignment isn't modeled either.
Say your strategy is: sell a 16-delta SPY iron condor at 45 DTE, close at 50% of max profit, stop at 2× the credit. On each trading day with no open position, the engine:
Every trade in your results table went through exactly this process. Same strategy, same window, same data — same result, every run. There is no randomness in the simulation.
Commissions and fees.
Results are gross P&L. At a typical $0.65 per contract per side, a high-frequency strategy loses a meaningful slice of its edge to fees — check your trade count and do that math before trusting a thin edge.
Intraday price action.
Everything happens at the close. Intraday stop breaches, morning gaps that recover, same-day entry-and-exit — invisible by construction.
Share assignment and early assignment.
ITM expirations settle in cash at intrinsic value. No stock positions, no dividend-driven early exercise.
Dividends and corporate actions.
Prices are as recorded historically; splits and dividends aren’t separately adjusted for.
Liquidity beyond the quote.
A strike needs a live bid to be selected, and the fill model charges you most of the spread — but there’s no volume or open-interest filter. On illiquid underlyings, real fills could be worse than simulated ones.
Position sizing and margin.
You trade a fixed number of contracts per position. The engine doesn’t compound, model buying power, or block trades a broker would reject. The equity curve is cumulative dollar P&L; return figures use a $10,000 reference capital.
Near-worthless exits.
Options quoted below $0.05 at exit are treated as closing for zero — a small simplification that slightly flatters short-premium exits.
These are real limitations and we'd rather state them than have you discover them. The practical read: btoption is built to tell you whether a strategy's logic held up historically — not to predict your fills to the cent. If an edge only survives with zero commissions and perfect mid fills, it isn't an edge, and this engine is designed to expose that rather than hide it.
Judge the engine yourself.
Run a backtest and open the trade list — every fill, every roll, every exit is there to inspect. Free, no account.
Run a backtest →New to backtesting? Start with the options backtesting guide.