# Backtesting Framework

This knowledge base explains how to design a realistic options backtesting framework. It is written for engineers who want to build or audit a simulator, not for readers looking for a trading recommendation.

The public reference implementation is [cutebacktests](https://github.com/cutemarkets/cutebacktests). It packages an intraday/options runtime, opening-range profile helpers, provider adapters, persistence, and robustness utilities. [cute-intraday-option-strats](https://github.com/cutemarkets/cute-intraday-option-strats) is a narrower strategy package on top of that runtime.

## Architecture spine

A credible framework keeps five responsibilities separate:

| Layer | Job |
|---|---|
| Data model | Store point-in-time underlyings, contracts, quotes, trades, bars, and coverage metadata. |
| Replay engine | Walk the market forward using only information available at each simulated timestamp. |
| Strategy layer | Turn historical state into a setup, signal timestamp, direction, and exit policy. |
| Instrument expression | Convert an underlying setup into an option contract or structure. |
| Research layer | Run folds, holdouts, diagnostics, and portfolio aggregation without changing simulator rules. |

This separation is the main design rule. A strategy should not know how the option provider paginates. A contract selector should not compute signal features. A research sweep should not rewrite fill semantics mid-run. When those boundaries blur, it becomes hard to tell whether a result came from edge, leakage, or a convenient simulator shortcut.

## Minimum viable framework

The smallest useful options framework needs:

- Historical contract discovery by simulated date, not today's chain.
- Underlying bars for signal generation and option quotes or bars for execution.
- A replay loop that makes signal decisions before entry decisions.
- A contract selector with explicit DTE, moneyness, spread, open-interest, and volume rules.
- A fill model that records the side of the market used and rejects untradable conditions.
- A trade log with enough metadata to explain skipped trades and filled trades.
- Walk-forward or holdout tests that run the same engine rules outside the selection window.

In `cutebacktests`, the public shape is intentionally explicit:

```python
from datetime import datetime

from cutebacktests import (
    IntradayOptionsBacktestConfig,
    IntradayOptionsBacktester,
    get_opening_range_profile,
)

profile = get_opening_range_profile("c4_long_only_rr15")
config = IntradayOptionsBacktestConfig(
    ticker="SPY",
    start=datetime(2025, 1, 1),
    end=datetime(2025, 1, 31),
    return_trade_log=True,
    **profile.to_intraday_strategy_kwargs(),
)
```

The concrete profile can change. The framework standards should not.

## Private-to-public naming

Some early internal research code used legacy names tied to its original project history. The public extraction uses behavior-based names instead: `IntradayOptionsBacktester`, `IntradayOptionsBacktestConfig`, `IntradayStrategyConfig`, `cutebacktests.backtest.intraday_options`, and `cutebacktests.strategies.intraday`. Public docs should use the public names.

## Read next

- [Backtesting Data Model](/docs/backtesting-data-model)
- [Backtesting Engine Loop](/docs/backtesting-engine-loop)
- [Options Contract Selection](/docs/options-contract-selection)
- [Backtesting Execution Realism](/docs/backtesting-execution-realism)
- [Backtesting Robustness](/docs/backtesting-robustness)
- [Backtesting Test Plan](/docs/backtesting-test-plan)
- [cutebacktests](/docs/cutebacktests)
