Options Contract Selection
Options contract selection is where many backtests become unrealistic. The signal may be generated on the underlying, but the traded instrument is an option contract with its own listing history, liquidity, spread, expiration, and strike geometry.
Selection inputs
A contract selector should receive:
- underlying ticker
- trade date
- signal direction
- selection timestamp
- underlying price at entry decision time
- DTE window and target DTE
- option type or structure mode
- moneyness, delta, or strike ranking rules
- spread, volume, open-interest, and quote-quality limits
- provider status and data coverage metadata
It should return either a selected contract or a structured rejection payload. Silent failure is not acceptable because the rejection mix is part of the research evidence.
Historical availability first
Selection must start from contracts that existed on the simulated date. A safe sequence is:
- Fetch listed contracts by underlying and
as_of. - Restrict to the DTE and expiration window.
- Restrict to the side needed by the strategy.
- Rank contracts using only fields available at the selection timestamp.
- Validate quote, spread, volume, and open-interest rules.
- Store the final selected symbol and every rejection reason.
When a framework supports both single-leg and vertical structures, select the primary leg first, then select the paired leg from the same causal universe. Do not backfill a cleaner structure from a later chain.
Ranking rules
Common ranking criteria:
| Criterion | Why it matters |
|---|---|
| DTE | Controls decay, gamma exposure, and historical availability. |
| Moneyness | Keeps strike choice tied to the underlying price at entry. |
| Delta | Useful when greeks are available point-in-time. |
| Spread | Prevents fills in contracts that are too expensive to cross. |
| Open interest | Screens out structurally inactive contracts. |
| Entry volume | Confirms activity around the trade window. |
| Intrinsic/extrinsic mix | Avoids selecting contracts with poor payoff geometry for the setup. |
The selector should prefer deterministic ranking. If two contracts tie, use a stable tie-breaker such as expiration, strike distance, symbol, and provider order.
Cache keys
Contract selection caches are useful but dangerous. Include every value that can affect the result:
- ticker and trade date
- direction and option type
- contract status
- min, target, and max DTE
- entry underlying price or moneyness bucket
- spread and liquidity thresholds
- quote-ranking mode
- selection timestamp when quote-aware ranking is used
- structure mode and paired-leg settings
If the entry underlying price changes, the best strike can change. If the selection timestamp changes, the quote-aware rank can change. Both belong in the cache key.
Rejection payloads
Good rejection reasons are specific:
no_contract_providerno_contracts_in_dte_windowopen_interest_below_minquote_missing_near_entryspread_above_maxentry_volume_below_minvertical_pair_missingdebit_to_width_ratio_too_high
Specific rejections make research debuggable. They also let a later tool explain whether a strategy failed because the signal was weak or because the market was not tradable.
Read next: Backtesting Data Model, Backtesting Execution Realism, and Contracts.