feat(BT-002): add historical snapshot provider
This commit is contained in:
66
docs/BT-002_HISTORICAL_OPTIONS_SNAPSHOT_PROVIDER.md
Normal file
66
docs/BT-002_HISTORICAL_OPTIONS_SNAPSHOT_PROVIDER.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# 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.
|
||||
@@ -700,7 +700,8 @@ If this changes later, it must be a scenario-level parameter.
|
||||
## 5. Continuous-vs-listed quantity must be explicit
|
||||
|
||||
MVP synthetic runs may use `continuous_units`.
|
||||
BT-002 listed snapshot runs should support `listed_contracts` with contract-size rounding.
|
||||
The shipped BT-002 provider slice also remains `continuous_units`-only.
|
||||
`listed_contracts` with contract-size rounding is deferred to follow-up slice `BT-002A`.
|
||||
|
||||
Do not hide rounding rules inside providers.
|
||||
They belong in the position sizing logic and must be recorded in the result.
|
||||
@@ -714,9 +715,9 @@ Do not collapse the entire hedge economics into end-of-period payoff only.
|
||||
|
||||
Any missing snapshot/mark fallback must add:
|
||||
|
||||
- a run warning
|
||||
- a run or template warning
|
||||
- a template validation note
|
||||
- a deterministic result status if the template becomes incomplete
|
||||
- and, in a fuller follow-up slice, a deterministic result status if the template becomes incomplete
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ notes:
|
||||
- Pre-alpha policy: we may cut or replace old features without backward compatibility until alpha is declared.
|
||||
- Alpha migration policy: once alpha is declared, compatibility only needs to move forward; backward migrations are not required.
|
||||
priority_queue:
|
||||
- BT-002
|
||||
- BT-001C
|
||||
- EXEC-001
|
||||
- EXEC-002
|
||||
@@ -21,7 +20,9 @@ priority_queue:
|
||||
- DATA-001A
|
||||
- OPS-001
|
||||
- BT-003
|
||||
- BT-002A
|
||||
recently_completed:
|
||||
- BT-002
|
||||
- PORT-003
|
||||
- BT-003B
|
||||
- CORE-001D
|
||||
@@ -43,9 +44,9 @@ states:
|
||||
- OPS-001
|
||||
- EXEC-001
|
||||
- EXEC-002
|
||||
- BT-002
|
||||
- BT-003
|
||||
- BT-001C
|
||||
- BT-002A
|
||||
in_progress: []
|
||||
done:
|
||||
- DATA-001
|
||||
@@ -61,6 +62,7 @@ states:
|
||||
- EXEC-001A
|
||||
- BT-001
|
||||
- BT-001A
|
||||
- BT-002
|
||||
- BT-003A
|
||||
- BT-003B
|
||||
- CORE-001A
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
id: BT-002
|
||||
title: Historical Daily Options Snapshot Provider
|
||||
status: backlog
|
||||
priority: P2
|
||||
effort: L
|
||||
depends_on:
|
||||
- BT-001
|
||||
tags: [backtesting, data]
|
||||
summary: Support real daily historical options premiums in backtests.
|
||||
acceptance_criteria:
|
||||
- Historical provider abstraction supports point-in-time daily option snapshots.
|
||||
- Backtests can swap synthetic pricing for observed historical premiums.
|
||||
- Contract selection avoids lookahead bias.
|
||||
- Provider/data-quality tradeoffs are documented.
|
||||
@@ -0,0 +1,16 @@
|
||||
id: BT-002A
|
||||
title: Snapshot Ingestion and Listed Contract Sizing
|
||||
status: backlog
|
||||
priority: P3
|
||||
effort: M
|
||||
depends_on:
|
||||
- BT-002
|
||||
tags:
|
||||
- backtesting
|
||||
- data
|
||||
summary: Extend BT-002 from provider support to file-backed/external snapshot ingestion and listed-contract sizing semantics.
|
||||
acceptance_criteria:
|
||||
- Historical snapshot data can be loaded from a documented file-backed or external source, not only injected in-memory fixtures.
|
||||
- Snapshot-backed runs can size positions in listed contract units with explicit contract-size rounding rules.
|
||||
- Snapshot data-quality warnings and incomplete-run behavior are persisted/reportable, not only template-local warnings.
|
||||
- Provider configuration and snapshot-source assumptions are documented for reproducible runs.
|
||||
20
docs/roadmap/done/BT-002-historical-options-snapshots.yaml
Normal file
20
docs/roadmap/done/BT-002-historical-options-snapshots.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
id: BT-002
|
||||
title: Historical Daily Options Snapshot Provider
|
||||
status: done
|
||||
priority: P2
|
||||
effort: L
|
||||
depends_on:
|
||||
- BT-001
|
||||
tags:
|
||||
- backtesting
|
||||
- data
|
||||
summary: Backtests can now use a point-in-time historical options snapshot provider with exact-contract mark-to-market instead of synthetic-only option pricing.
|
||||
completed_notes:
|
||||
- Added shared historical position/mark provider hooks in `app/services/backtesting/historical_provider.py` so `BacktestService` can swap provider implementations while preserving the backtest engine flow.
|
||||
- Snapshot-backed runs still fail closed on `listed_contracts`; BT-002 ships observed snapshot pricing for `continuous_units` only, with listed-contract sizing explicitly deferred to `BT-002A`.
|
||||
- Added `DailyOptionsSnapshotProvider` with deterministic entry-day contract selection, exact-contract mark-to-market, and explicit carry-forward warnings when later marks are missing.
|
||||
- Updated `app/backtesting/engine.py` and `app/services/backtesting/service.py` so snapshot-backed runs and synthetic runs share the same scenario execution path.
|
||||
- Added focused regression coverage in `tests/test_backtesting_snapshots.py` for entry-day-only selection, observed snapshot marks, and no-substitution missing-mark fallback behavior.
|
||||
- Added provider/data-quality documentation in `docs/BT-002_HISTORICAL_OPTIONS_SNAPSHOT_PROVIDER.md`, including current limitations around precomputed mids, continuous-units sizing, and follow-up ingestion work.
|
||||
- Docker-served browser validation still passed on the affected historical routes after the engine/provider seam changes: `/health` returned OK and `tests/test_e2e_playwright.py` passed against the local Docker app.
|
||||
- While closing that browser loop, `/{workspace_id}/event-comparison` preset changes were corrected to preserve user-edited underlying units and only reset preset-driven template selection, matching the UI copy and stale-state behavior.
|
||||
Reference in New Issue
Block a user