trading-system-docs/modul-13-optimization.md

119 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Modul-13: Optimization
**Status: E2E-VERIFIZIERT (FREIGABE ausstehend)** · Container `Modul-13-Optimization` · Image `optimization:0.1.0`
## Zweck
Deterministische, reproduzierbare **Parameteroptimierung** für die V1.1-Strategie
(`trend_pullback_v1.1`, Modul-12). Findet robuste Parameter im erlaubten Suchraum
**ohne KI/LLM und ohne Live-Orders** — Ergebnis ist nur **CANDIDATE/RECOMMENDATION**,
es gibt keine automatische Parameterübernahme in Modul-05.
## Kernprinzipien
- **Deterministisch**: gleiche Eingaben → gleiche Kandidaten, Reihenfolge, run_hash (kein Zufall außer explizit via Seed).
- **Kein Lookahead**: IS/OOS strikt zeitlich getrennt; **OOS wird nie zur Selektion verwendet**.
- **IS/OOS strikt**: `is_ratio` (z.B. 0.7) teilt die Daten; OOS ausschließlich zur Validierung.
- **Robuste Bereiche > Einzelmax**: best = Kandidat mit bester kombinierter Score (nicht max net_pnl).
- **Reject-/Overfit-Logik**: zu wenige Trades, DD-Limit, IS/OOS-Degradation → Kandidat wird `rejected`.
- **Parametergrenzen zwingend**: Suchraum begrenzt auf `pullback_band_atr`, `trend_regime_window`, `rr_multiplier`.
- **Score-Gewichte**: Expectancy/R 0.30, PF 0.25, Max-Drawdown 0.20, Trades 0.10, Stabilität 0.15.
- **Synchron**: Request/Response über API (kein RabbitMQ).
## Architektur / Datenfluss
```
OHLCV (Modul-01 PostgreSQL, Tabelle ohlcv)
│ synchron, direkt nach Timestamp/Zeitraum
Modul-13-Optimization ── POST /optimization
├── core/split.py IS/OOS + Walk-Forward-Folds (kein Leakage)
├── core/optimizer.py Grid/Random-Suche, ScoreEngine, WF, Reject
├── core/scoring.py Multi-Metrik-Score (Gewichte siehe oben)
├── storage/storage.py Persistenz optimization_run/candidate/walk_forward
├── backtest_client.py synchroner Aufruf Modul-12 (POST /backtest)
└── api/main.py REST-API (intern, Port 55013)
```
API (Docker-intern, Port 55013, kein öffentliches Port-Mapping):
```
GET /health, /health/ready
POST /optimization Start (Grid/Random), synchron
GET /optimization Liste aller Runs
GET /optimization/{id} Run-Metadaten + Status
GET /optimization/{id}/candidates Kandidaten + Multi-Metrik-Ranking
GET /optimization/{id}/best bester ROBUSTER Kandidat (RECOMMENDATION/NO_RECOMMENDATION)
GET /optimization/{id}/walk-forward Walk-Forward-Folds
```
## Suchraum (V1.1 — NUR 3 optimierbare Parameter)
| Parameter | Suchraum (E2E) | Zweck |
|-----------|----------------|-------|
| `pullback_band_atr` | {0.2, 0.5, 0.8, 1.0} | ATR-Band um EMA20 (Pullback-Timing) |
| `trend_regime_window` | {60, 200, 300} | Fenster für strategie-internes Trend-Regime |
| `rr_multiplier` | {1.0, 2.0, 3.0} | Target = Entry + rr×Risiko |
Fix (nicht optimiert): `atr_period=14`, `regime_vol_override=false`, `min_risk_reward=1.0`,
ADX-/EMA-Perioden (14/20/30), `adx_trend_threshold=20`.
## Profile
| Profil | min_trades_is | min_trades_oos | max_drawdown_limit | max_oos_degradation | Zweck |
|--------|---------------|----------------|--------------------|--------------------|-------|
| **produktion** (Default) | 3 | 1 | 0.30 | 0.5 | Anti-Overfit, Produktion |
| **test** (`profile=test`) | 1 | 1 | — | — | nur Mindest-Trade-Schwellen gesenkt für technischen E2E; sonst identisch |
Das TEST-PROFIL senkt **ausschließlich** die Mindest-Trade-Schwellen (damit ein
Sweep über die kalibrierte synthetische Fixture genügend Kandidaten akzeptiert).
Produktive Anti-Overfit-Defaults bleiben unverändert. `profile` wird per API-Feld
übergeben; explizite Request-Werte (`min_trades_is/oos`) haben Vorrang.
## Eigene Tabellen (Migration)
- `optimization_run` — Run-Metadaten: optimization_id, strategy+version, symbol, timeframe,
optimizer_method, optimizer_version, param_space, seed, status, result (JSON), created/completed_at.
- `optimization_candidate` — Kandidaten: rank, params, backtest_run_id_is/oos, is_metrics,
oos_metrics, score, score_components, rejected, reject_reasons, flags, stability, robustness.
- `walk_forward_result` — WF-Folds: fold, params, is_start/end, oos_start/end, is/oos_metrics,
score, backtest_run_id_is/oos.
## Reject-/Overfit- und Recommendation-Verhalten
- Kandidat wird `rejected` wenn: zu wenige Trades (min_trades_is/oos), Drawdown über Limit,
OOS-Degradation zu hoch (Overfit), fachlich unbrauchbare Metriken (z.B. PF<=0/kein Trades).
- `/best` liefert:
- `recommendation=RECOMMENDATION` + `best_candidate` wenn ein fachlich brauchbarer robuster Bester existiert.
- `recommendation=NO_RECOMMENDATION` (statt leerem `best_candidate`), wenn das Kandidatenfeld
fachlich unbrauchbar ist — eine produktive Empfehlung wird nie aus unbrauchbaren Daten erzeugt.
## Tests
- `tests/test_optimization.py`: 12/12 grün (Grid deterministisch, Random-Seed, IS/OOS-Leak,
Walk-Forward korrekt + JSON-serialisierbar, Multi-Metrik-Ranking, Reject/Overfit, Robustheit,
Suchraum=3 Params, Reproduzierbarkeit, NO_RECOMMENDATION, TEST-PROFIL).
## E2E-Verifikation (VPS, 20.08.2026)
- **Suchraum-Differenzierung** (kalibrierte Fixture `M13_E2E`, 1314 Bars, alle 3 Params unterscheidbar):
- `pullback_band_atr`: 0.2→8 Trades, 0.5→910, 0.8/1.0→1 Trade.
- `trend_regime_window`: bei pba=0.5: 60→9 Trades vs 200/300→10 Trades.
- `rr_multiplier`: 1.0→810 Trades (net +828…+1046), 2.0/3.0→1 Trade.
- **12/12 E2E-Punkte über API bestätigt** (e2e_full.py):
Grid deterministisch, Random-Seed identisch, IS/OOS-Leak ausgeschlossen, WF-Folds zeitlich
korrekt + DB-persistiert (3 Folds), Multi-Metrik-Ranking, Reject-Logik (36 Kandidaten,
30 rejected / 6 accepted), best=robust + RECOMMENDATION, DB-Run/Candidates vollständig,
API list_runs, Reproduzierbarkeit (status COMPLETED), NO_RECOMMENDATION bei nur-1-Trade
Konfiguration (produktion-Profil).
- **Bugfix C**: Walk-Forward-Fold-Ergebnisse enthalten `datetime`-Timestamps → beim
`json.dumps` in `complete_run` nicht serialisierbar (422). Fix: ISO-String-Kopie im
Run-Result, datetime bleibt für `storage.insert_wf`. Verifiziert (3 WF-Folds in DB).
- Health `/health/ready``{"status":"ready","db":"ok"}` (M13 und M12). Keine Tracebacks.
- Port 55013 nur intern (`expose`), kein Host-Port-Binding (verifiziert).
## Compose / Betrieb
- Netzwerk `trading-modules`, DB-Host `Modul-01-PostgreSQL` (Modul-01), Backtest via
`Modul-12-Backtesting` (intern). Port 55013 nur intern (`expose`), `restart: unless-stopped`.
## Nächste Module
- **Modul-14 ff.** — noch Platzhalter (alpine), NICHT gestartet.
---
## Geändert
```
Geändert von: Rain Ocampo
Datum: 20.08.2026
Grund: Modul-13-Optimization dokumentiert — E2E-verifiziert (12/12 Punkte), Suchraum-Diffusion für alle 3 Params, TEST-PROFIL, NO_RECOMMENDATION, Bugfix C (WF-JSON-datetime). FREIGABE ausstehend.
```