Modul-12: Backtesting deployt + E2E verifiziert (deterministisch, kein Lookahead, Stop/Target/Gap)
This commit is contained in:
parent
6f039d0b43
commit
ed9fd19cbb
1 changed files with 119 additions and 0 deletions
119
modul-12-backtesting.md
Normal file
119
modul-12-backtesting.md
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
# Modul-12: Backtesting
|
||||||
|
|
||||||
|
**Status: FREIGEGEBEN** · Container `Modul-12-Backtesting` · Image `backtesting:0.1.0`
|
||||||
|
|
||||||
|
> Hinweis: Dieser Modul ist **deployt und E2E-verifiziert**. Die Freigabe-Markierung
|
||||||
|
> wird durch den Nutzer gesetzt; dieses Dokument beschreibt den verifizierten Stand.
|
||||||
|
|
||||||
|
## Zweck
|
||||||
|
Deterministische, reproduzierbare **historische Strategietests** auf echten
|
||||||
|
OHLCV-Daten aus **Modul-03** (PostgreSQL). Gleiche Strategie-Logik wie
|
||||||
|
**Modul-05** (`trend_pullback_v1`) und identische Regime-Engine wie **Modul-04** —
|
||||||
|
der Backtest entscheidet exakt so wie Live/Paper. **V1 ohne KI/ML, ohne Live-Orders,
|
||||||
|
ohne Broker-Ausführung.** Modular erweiterbar für weitere Strategien (Breakout, ORB,
|
||||||
|
Mean-Reversion) über gemeinsame `Strategy`-Schnittstelle + Registry.
|
||||||
|
|
||||||
|
## Architektur / Datenfluss
|
||||||
|
|
||||||
|
```
|
||||||
|
OHLCV (Modul-01 PostgreSQL, Tabelle ohlcv)
|
||||||
|
│ synchron, direkt nach Timestamp/Zeitraum (KEIN /history?limit)
|
||||||
|
▼
|
||||||
|
Modul-12-Backtesting ── POST /backtest
|
||||||
|
│
|
||||||
|
├── core/engine.py deterministische Backtest-Loop (kein Lookahead)
|
||||||
|
├── strategies/ trend_pullback_v1 (1:1 zu Modul-05)
|
||||||
|
├── regime/engine.py Regime-Engine (1:1 zu Modul-04)
|
||||||
|
├── storage/storage.py Persistenz backtest_run/trade/equity
|
||||||
|
└── api/main.py REST-API (intern, Port 55012)
|
||||||
|
|
||||||
|
API (Docker-intern, Port 55012):
|
||||||
|
/health, /health/ready
|
||||||
|
POST /backtest Backtest starten (synchron, returns Ergebnis)
|
||||||
|
GET /backtest/{run_id} Run-Metadaten
|
||||||
|
GET /backtest/{run_id}/status Status + run_hash/data_hash
|
||||||
|
GET /backtest/{run_id}/trades gespeicherte Trades
|
||||||
|
GET /backtest/{run_id}/equity Equity-Kurve
|
||||||
|
GET /backtest alle Runs
|
||||||
|
POST /backtest/{run_id}/rebuild (idempotenter Rebuild aus DB)
|
||||||
|
GET /strategies registrierte Strategien + Versionen
|
||||||
|
```
|
||||||
|
|
||||||
|
Kein RabbitMQ: Backtest ist ein synchroner, request/response-Aufruf. Port `55012`
|
||||||
|
ist **nur intern** (`expose:`), kein öffentliches Port-Mapping.
|
||||||
|
|
||||||
|
## Datenquelle
|
||||||
|
- OHLCV direkt aus PostgreSQL (`ohlcv`), Spalten `provider, symbol, timeframe, ts,
|
||||||
|
open, high, low, close, volume`.
|
||||||
|
- Abfrage **nach Timestamp/Zeitraum** (`start_date`..`end_date`), nicht via
|
||||||
|
`/history?limit=N` — vollständige, deterministische Datenbasis.
|
||||||
|
- Mindestanzahl Candles: `min_candles_required` (default 220), sonst Fehler.
|
||||||
|
|
||||||
|
## Bias-Schutz / Realismus (kein Lookahead)
|
||||||
|
- **Entry am OPEN der Folgewandle** nach dem Signal (nie zum bekannten Signal-Close).
|
||||||
|
- **Regime & Strategie** werden nur auf **abgeschlossenen** Candles (bis Candle i)
|
||||||
|
ausgewertet — keine zukünftige Information.
|
||||||
|
- **Intrabar Stop/Target konservativ**: Stop wird VOR Target geprüft (kein Lookahead
|
||||||
|
durch Intrabar-Reihenfolge). Falls beide in einer Candle getroffen, gewinnt Stop
|
||||||
|
(Worst-Case).
|
||||||
|
- **Gap-Handling**: Überspringt der Open den Stop/Target, Fill zum Gap-Open
|
||||||
|
(realistisch schlechter).
|
||||||
|
- **Gebühren/Spread/Slippage** werden auf Entry UND Exit angewendet (verschlechtern
|
||||||
|
den Fill).
|
||||||
|
- **R-Multiple** bezieht sich auf das **Geldrisiko** `qty * |entry − stop|` (nicht
|
||||||
|
Preisdifferenz) — `expectancy_r` daher korrekt (vorheriger Bug: 31431 → korrekt 2.0).
|
||||||
|
|
||||||
|
## Eingaben (POST /backtest)
|
||||||
|
- Symbol, Asset-Klasse, Provider, Timeframe, Start-/Enddatum
|
||||||
|
- Strategy + Version (`trend_pullback_v1`, default `1.0.0`)
|
||||||
|
- Initiales Kapital, Risk % (Risk-basierte Positionsgröße)
|
||||||
|
- Gebühren (fix + %), Spread (Preispunkte), Slippage (Preispunkte)
|
||||||
|
- Optionale Limits: `max_positions`, `max_qty`
|
||||||
|
|
||||||
|
## Eigene Tabellen (Migration `migrations/001_backtest.sql`)
|
||||||
|
- `backtest_run` — Run-Metadaten: run_id, strategy+version, symbol, timeframe,
|
||||||
|
status (COMPLETED/…), data_hash, run_hash, Parameter (JSON), Datenzeitraum.
|
||||||
|
- `backtest_trade` — einzelne Trades: direction (LONG/SHORT), qty, entry/exit-Preis,
|
||||||
|
entry/exit_ts, signal_ts, reason (target/stop/force_close), regime, gross_pnl,
|
||||||
|
fee, pnl, r (R-Multiple), strategy_version.
|
||||||
|
- `backtest_equity` — Equity-Kurve (Zeitstempel, Equity, offene Positionen).
|
||||||
|
|
||||||
|
Jede Tabelle FK auf `backtest_run(run_id) ON DELETE CASCADE`.
|
||||||
|
|
||||||
|
## Determinismus / Reproduzierbarkeit
|
||||||
|
- Eindeutige `run_id` + deterministischer `run_hash` + `data_hash`.
|
||||||
|
- `run_hash`: Hash über Strategy+Version+Parameter+Datenzeitraum+data_hash.
|
||||||
|
- `data_hash`: Hash über die geladenen OHLCV-Daten.
|
||||||
|
- **Gleiche Daten + gleiche Version + gleiche Parameter → identische Trades, Equity,
|
||||||
|
Kennzahlen und identischer run_hash** (E2E-verifiziert).
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
- `tests/test_backtest.py`: 12/12 grün (deterministisch, LONG/SHORT, fees, slippage,
|
||||||
|
lookahead, stop/target, gaps, unzureichende Daten, Strategy-Version/Parameter,
|
||||||
|
Rebuild-Identität).
|
||||||
|
- `tests/fixtures.py`: deterministische Fixture-Factory.
|
||||||
|
- `tests/m12_fixtures.py`: erzeugt E2E-Fixtures (BT_PULL LONG, BT_PULL_S SHORT) für
|
||||||
|
die VPS-ohlcv-Tabelle.
|
||||||
|
- `tests/determinism_check.py` / `determinism_diff.py`: E2E-Determinismus-Nachweis
|
||||||
|
(identische Trades/Equity/Kennzahlen/Hashes).
|
||||||
|
|
||||||
|
## E2E-Verifikation (VPS, 20.08.2026)
|
||||||
|
- **LONG** (BT_PULL, TREND_UP): 1 Trade, winrate 1.0, expectancy_r 1.94, net_pnl
|
||||||
|
196.59, max_drawdown 0.0001, r=1.94, reason=target.
|
||||||
|
- **SHORT** (BT_PULL_S, TREND_DOWN): 1 Trade, short_count 1, net_pnl 194.96.
|
||||||
|
- `signal_ts 15:00 → entry_ts 16:00` (Einstieg am Open der Folgewandle — Lookahead
|
||||||
|
praktisch verifiziert).
|
||||||
|
- run_hash + data_hash gesetzt (Beispiel: `bc6e2553…` / `d76a7549…`).
|
||||||
|
- **Determinismus**: zwei identische Backtests → identischer run_hash, data_hash,
|
||||||
|
Metrics, Trades (normalisiert), Equity-Curve.
|
||||||
|
- **Stop/Target/Gap**: Engine-`_evaluate_exit` geprüft — Gap-Down unter Stop → Fill
|
||||||
|
zum Gap-Open; Target bei high≥target; Stop vor Target (konservativ); SHORT Gap-Up
|
||||||
|
→ Fill zum Gap-Open.
|
||||||
|
- Health `/health` 200, `/health/ready` 200; keine öffentlichen Ports.
|
||||||
|
|
||||||
|
## Compose / Betrieb
|
||||||
|
- Netzwerk `trading-modules`, DB-Host `Modul-01-PostgreSQL` (Modul-01), Port 55012
|
||||||
|
nur intern (`expose`), `restart: unless-stopped`. Kein öffentliches Port-Mapping.
|
||||||
|
|
||||||
|
## Nächste Module
|
||||||
|
- **Modul-13: Optimization** — aktuell alpine-Platzhalter (Port 55013). NICHT gestartet.
|
||||||
Loading…
Reference in a new issue