67 lines
2.5 KiB
Markdown
67 lines
2.5 KiB
Markdown
# BT-002 Historical Options Snapshot Provider
|
|
|
|
## What shipped
|
|
|
|
BT-002 adds a point-in-time historical options snapshot provider for backtests.
|
|
|
|
The new provider lives in `app/services/backtesting/historical_provider.py` and plugs into the same `BacktestService` / engine flow as the existing synthetic provider.
|
|
|
|
## Provider contract
|
|
|
|
The snapshot provider exposes the same backtest-facing behaviors as the synthetic provider:
|
|
|
|
- load underlying daily closes for the scenario window
|
|
- validate `ProviderRef`
|
|
- open positions at scenario start using only the entry-day snapshot
|
|
- mark open positions later using the exact same contract identity
|
|
|
|
This lets backtests swap:
|
|
|
|
- synthetic pricing: `synthetic_v1 / synthetic_bs_mid`
|
|
- observed snapshot pricing: `daily_snapshots_v1 / snapshot_mid`
|
|
|
|
## Contract-selection rules
|
|
|
|
The provider uses explicit, deterministic, point-in-time rules:
|
|
|
|
1. filter to the entry-day option chain only
|
|
2. keep contracts with expiry at or beyond the target expiry date
|
|
3. choose the nearest eligible expiry
|
|
4. within that expiry, choose the nearest strike to the target strike
|
|
5. on equal-distance strike ties:
|
|
- puts prefer the higher strike
|
|
- calls prefer the lower strike
|
|
|
|
These rules avoid lookahead bias because later snapshots are not consulted for entry selection.
|
|
|
|
## Daily mark-to-market rules
|
|
|
|
After entry, the provider marks positions using the exact same `contract_key`.
|
|
|
|
It does **not** silently substitute a different strike or expiry when the original contract is missing.
|
|
|
|
Current fallback policy:
|
|
|
|
1. use the exact same contract from the same-day snapshot
|
|
2. if missing before expiry, carry forward the previous mark for that same contract and emit a warning
|
|
3. if the valuation date is at or after expiry, settle to intrinsic value and close the position
|
|
|
|
## Data-quality tradeoffs
|
|
|
|
The current BT-002 slice intentionally keeps the data model simple:
|
|
|
|
- snapshots are assumed to provide a precomputed daily `mid`
|
|
- the provider does not currently derive mids from bid/ask pairs
|
|
- missing exact-contract marks are explicit warnings, not silent substitutions
|
|
- the engine currently still supports `continuous_units` sizing for snapshot-backed runs
|
|
|
|
## Known limitations / follow-up
|
|
|
|
This slice does **not** yet include:
|
|
|
|
- file-backed or external ingestion of real historical snapshot datasets
|
|
- listed-contract rounding / contract-size-aware position sizing
|
|
- persistent run-status objects beyond template-level warnings
|
|
|
|
Those follow-ups should remain explicit roadmap work rather than being implied by BT-002.
|