CuteMarkets Docs

Backtesting Framework

Framework guides for engineers building realistic options backtests with causal data, quote-aware fills, and robust validation.

Tip: open /docs/options-contract-selection.md directly for raw markdown (easy copy/paste into an LLM).

Quick definition: options contract selection is the process that turns an underlying signal into one tradable OCC ticker or a structured rejection, using only contracts and market evidence available at the simulated time.

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.

Why this matters

An options strategy can be right about direction and still choose an untradable contract. It can select an expiration that was not listed, choose a strike that was too far from the actual entry price, use a stale current-chain record, or ignore the spread that would have dominated the expected move. Contract selection is therefore part of the strategy, not a small implementation detail.

A selector should produce evidence. If it chooses a contract, it should explain why. If it rejects the setup, it should say whether the blocker was no listed expiry, no contract in the DTE window, missing quote, wide spread, weak open interest, missing pair leg, or another concrete reason.

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:

  1. Fetch listed contracts by underlying and as_of.
  2. Restrict to the DTE and expiration window.
  3. Restrict to the side needed by the strategy.
  4. Rank contracts using only fields available at the selection timestamp.
  5. Validate quote, spread, volume, and open-interest rules.
  6. 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:

CriterionWhy it mattersTypical implementation
DTEControls decay, gamma exposure, and historical availability.Filter to min/max DTE, then rank near target DTE.
MoneynessKeeps strike choice tied to the underlying price at entry.Rank by distance from entry underlying price or target moneyness.
DeltaUseful when Greeks are available point-in-time.Rank inside a target delta band after quote checks.
SpreadPrevents fills in contracts that are too expensive to cross.Reject spread above absolute or percent threshold.
Open interestScreens out structurally inactive contracts.Require minimum OI or rank among otherwise valid candidates.
Entry volumeConfirms activity around the trade window.Use trades or chain volume as supporting evidence.
Intrinsic/extrinsic mixAvoids poor payoff geometry for the setup.Reject contracts that are too deep ITM or too cheap to execute cleanly.

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_provider
  • no_contracts_in_dte_window
  • open_interest_below_min
  • quote_missing_near_entry
  • spread_above_max
  • entry_volume_below_min
  • vertical_pair_missing
  • debit_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.

Implementation example

bash
curl "https://api.cutemarkets.com/v1/options/contracts/?underlying_ticker=SPY&as_of=2026-04-17&expiration_date.gte=2026-04-17&expiration_date.lte=2026-04-24&limit=100" \
  -H "Authorization: Bearer YOUR_API_KEY"

After a selector chooses a candidate, fetch quote evidence for the exact OCC ticker. That second step is important because the contract being listed is not the same as the contract being executable.

bash
curl "https://api.cutemarkets.com/v1/options/quotes/O:SPY260417C00500000/?timestamp.gte=2026-04-17T13:30:00Z&timestamp.lt=2026-04-17T20:00:00Z&limit=500" \
  -H "Authorization: Bearer YOUR_API_KEY"

Review checklist

QuestionGood answer
Did the selector use a historical universe?Yes, contract discovery used as_of for the simulated date.
Did it choose from a valid expiration?Yes, expiration filters match the strategy DTE policy.
Did ranking depend on available state only?Yes, moneyness, delta, and quote fields were available at selection time.
Did it reject weak markets?Yes, missing quotes, stale quotes, wide spreads, and weak liquidity produce reason codes.
Can another developer replay the choice?Yes, the selected OCC ticker, inputs, filters, and request windows are preserved.

Read next

Next steps

Move from the docs into the product workflow

If you are evaluating the API rather than implementing a specific endpoint right now, the product pages map live and historical workflows for stocks, options, and WebSockets.