From e48b8b6c1c52ffb6e4f20228404d38b23c171193 Mon Sep 17 00:00:00 2001 From: Rain Ocampo Date: Thu, 20 Aug 2026 13:53:47 +0000 Subject: [PATCH] Doku: Modul-13 E2E-verifiziert, V1.1-Spec deployt, Modul-12 auf 0.1.2 (20.08.2026) --- README.md | 12 +++- modul-12-backtesting.md | 8 ++- modul-13-optimization.md | 119 ++++++++++++++++++++++++++++++++++++ ports-reference.md | 8 ++- trend-pullback-v1.1-spec.md | 115 ++++++++++++++++++++++++++++++++++ 5 files changed, 256 insertions(+), 6 deletions(-) create mode 100644 modul-13-optimization.md create mode 100644 trend-pullback-v1.1-spec.md diff --git a/README.md b/README.md index 14e0da1..6f99bec 100644 --- a/README.md +++ b/README.md @@ -39,10 +39,12 @@ Jede Änderung folgt dem **Notation-Format**: Autor (`Alice` / `Rain Ocampo`) + | 09 | Execution-Service | ✅ FREIGEGEBEN (execution-service:0.1.0) | 55009 (intern) | | 10 | Trade-Journal | ✅ FREIGEGEBEN (trade-journal:0.1.0) | 55010 (intern) | | 11 | Analytics | ✅ FREIGEGEBEN (analytics-service:0.1.0) | 55011 (intern) | -| 12 | Backtesting | ✅ FREIGEGEBEN (backtesting:0.1.0) | 55012 (intern) | -| 13–17 | … | ⬜ Platzhalter (alpine) | 55013–55017 | +| 12 | Backtesting | ✅ FREIGEGEBEN (backtesting:0.1.2) | 55012 (intern) | +| 13 | Optimization | ✅ E2E-VERIFIZIERT (optimization:0.1.0) | 55013 (intern) | +| 14–17 | … | ⬜ Platzhalter (alpine) | 55014–55017 | -Details: siehe `modul-03-market-data.md` … `modul-12-backtesting.md` (Module 03–12 vollständig implementiert und freigegeben). Modul-13 ff. folgen. +Details: siehe `modul-03-market-data.md` … `modul-13-optimization.md` (Module 03–12 +vollständig implementiert und freigegeben; Modul-13 E2E-verifiziert). Modul-14 ff. folgen. --- @@ -75,3 +77,7 @@ Grund: README aktualisiert — Modul-04 und Modul-05 als healthy/freigegeben ein Geändert von: Rain Ocampo Datum: 20.08.2026 Grund: README-Modultabelle aktualisiert — Module 06–12 als freigegeben eingetragen; Modul-12-Backtesting als FREIGEGEBEN markiert. + +Geändert von: Rain Ocampo +Datum: 20.08.2026 +Grund: README-Modultabelle aktualisiert — Modul-12 auf backtesting:0.1.2 (V1.1), Modul-13 Optimization als E2E-VERIFIZIERT eingetragen. diff --git a/modul-12-backtesting.md b/modul-12-backtesting.md index e4f3f12..ea5db6a 100644 --- a/modul-12-backtesting.md +++ b/modul-12-backtesting.md @@ -1,6 +1,6 @@ # Modul-12: Backtesting -**Status: FREIGEGEBEN** · Container `Modul-12-Backtesting` · Image `backtesting:0.1.0` +**Status: FREIGEGEBEN** · Container `Modul-12-Backtesting` · Image `backtesting:0.1.2` (V1 + V1.1 parallel) ## Zweck Deterministische, reproduzierbare **historische Strategietests** auf echten @@ -113,7 +113,8 @@ Jede Tabelle FK auf `backtest_run(run_id) ON DELETE CASCADE`. nur intern (`expose`), `restart: unless-stopped`. Kein öffentliches Port-Mapping. ## Nächste Module -- **Modul-13: Optimization** — aktuell alpine-Platzhalter (Port 55013). NICHT gestartet. +- **Modul-13: Optimization** — Image `optimization:0.1.0`, E2E-verifiziert (12/12 Punkte). Details: `modul-13-optimization.md`. FREIGABE ausstehend. +- **V1.1-Strategie**: `trend_pullback_v1.1` (Image `backtesting:0.1.2`) — Spezifikation und Parameter: `trend-pullback-v1.1-spec.md`. --- ## Geändert @@ -122,3 +123,6 @@ Geändert von: Rain Ocampo Datum: 20.08.2026 Grund: Modul-12-Backtesting als FREIGEGEBEN markiert (deployt + E2E verifiziert: deterministisch, kein Lookahead, Stop/Target/Gap, LONG/SHORT, run_hash+data_hash). ``` +Geändert von: Rain Ocampo +Datum: 20.08.2026 +Grund: Modul-12-Doku aktualisiert — Image auf backtesting:0.1.2 (V1.1), V1-Kompatmodus bitgenau, Referenz auf V1.1-Spec und Modul-13. diff --git a/modul-13-optimization.md b/modul-13-optimization.md new file mode 100644 index 0000000..7de2458 --- /dev/null +++ b/modul-13-optimization.md @@ -0,0 +1,119 @@ +# 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→9–10, 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→8–10 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. +``` diff --git a/ports-reference.md b/ports-reference.md index 4f66dc3..2052a06 100644 --- a/ports-reference.md +++ b/ports-reference.md @@ -15,7 +15,9 @@ | 03 | Modul-03-Market-Data | FastAPI | 55003 (intern, `expose`) | **nein** (nur intern) | | 04 | Modul-04-Market-Regime | FastAPI | 55004 (intern, expose) | nein (nur intern) | | 05 | Modul-05-Strategy-Engine | FastAPI | 55005 (intern, expose) | nein (nur intern) | -| 06–17 | … | — | 55006–55017 | (Platzhalter) | +| 06–12 | Modul-06…12 | FastAPI | 55006–55012 (intern, expose) | nein (nur intern) | +| 13 | Modul-13-Optimization | FastAPI | 55013 (intern, expose) | nein (nur intern) | +| 14–17 | … | — | 55014–55017 | (Platzhalter) | > **SECURITY-FIX 20.08.2026:** Modul-01 (PostgreSQL) und Modul-02 (RabbitMQ) haben **keine Host-Port-Bindings** mehr. Die ehemaligen öffentlichen Ports **55432, 55672, 15672 sind geschlossen** und von außen nicht mehr erreichbar (verifiziert). Adminzugriff nur noch per SSH-Tunnel ins `trading-modules`-Netz oder `docker exec`. Alles läuft über `expose:` → nur im internen Docker-Netzwerk. @@ -51,3 +53,7 @@ Grund: Modul-01/02 von öffentlichen Ports auf interne `expose`-Bindings umgeste Geändert von: Rain Ocampo Datum: 20.08.2026 Grund: ports-reference aktualisiert — Modul-04 und Modul-05 von Platzhalter auf real (FastAPI, intern expose, nicht öffentlich) eingetragen. + +Geändert von: Rain Ocampo +Datum: 20.08.2026 +Grund: ports-reference aktualisiert — Modul-06–12 als real (FastAPI, intern) und Modul-13 Optimization (55013 intern) eingetragen. diff --git a/trend-pullback-v1.1-spec.md b/trend-pullback-v1.1-spec.md new file mode 100644 index 0000000..269b885 --- /dev/null +++ b/trend-pullback-v1.1-spec.md @@ -0,0 +1,115 @@ +# trend_pullback_v1.1 — Spezifikation + +> Status: **IMPLEMENTIERT + DEPLOYT** (`backtesting:0.1.2`, Modul-12), V1-Kompatibilitätsmodus bitgenau. +> Erstellt: 20.08.2026 (Rain Ocampo) · Aktualisiert: 20.08.2026 + +## 1. Motivation + +Die Produktions-Strategie `trend_pullback_v1` (Modul-05/12) ist unter den +**unveränderten** produktiven Regime-Schwellen fachlich widersprüchlich +(messbar): Ein TREND_UP-Regime verlangt Volatilität, der Pullback +`close < EMA20` verlangt ein flaches Plateau. Im 60-Bars-Produktionsfenster +(`regime_lookback=60`) schneiden sich `TREND_UP` und `close95% der `close ema_slow_l) UND adx_l >= adx_trend_threshold +trend_dir = DOWN wenn (ema_fast_l < ema_slow_l) UND adx_l >= adx_trend_threshold +sonst: kein Trade # KEIN atr_ratio-Check für die Richtung + +# --- Pullback-Timing (ATR-Band um EMA20) --- +atr_prev = ATR(prev_highs, prev_lows, prev_closes, atr_period) +ema20 = EMA20(prev_closes) +pullback_limit = ema20 + pullback_band_atr * atr_prev # LONG +pullback_limit = ema20 - pullback_band_atr * atr_prev # SHORT + +LONG wenn trend_dir==UP UND last_close>sma200 UND last_close < pullback_limit + UND last_close>prev_close UND swing_low=min_risk_reward +SHORT wenn trend_dir==DOWN UND last_close pullback_limit + UND last_closelast_close UND rr>=min_risk_reward +``` + +- `break_high` entfällt (Befund: `prev_high ≥ prev_close` ⇒ `break_high ⇒ close_up`; + bleibt als `close_up`/`close_down` erhalten — nicht restriktiv). +- Deterministisch: reine Listen-Berechnung, kein Zufall, kein Hidden-State. + +## 4. Parameter-Tabelle + +| Parameter | V1 produktiv (Code) | V1.1 Default | Min | Max | Zweck | V1.1 Optimierbar? | +|---|---|---|---|---|---|---| +| `pullback_band_atr` | — (strikt `< EMA20`) | **0.35** | 0.0 | 1.0 | ATR-Band um EMA20. `0.0`=strikt V1. | **Ja** | +| `atr_period` | 14 | **14** | 14 (fix) | 14 (fix) | ATR-Fenster für Pullback-Band | Nein (fix) | +| `trend_regime_window` | 60 (geteilter 60er) | **200** | 60 | 500 | Fenster für strategie-internes Trend-Regime | **Ja** | +| `regime_vol_override` | true (geteilt) | **false** | — | — | false=V1.1 strategie-eigene Richtung | Nein (fix) | +| `rr_multiplier` | 2.0 | **2.0** | 1.0 | 4.0 | Target = Entry + rr_multiplier×Risiko | **Ja** | +| `min_risk_reward` | 1.0 | **1.0** | 1.0 (min) | 3.0 | Mindest-RR; keine Kandidaten mit Chance/Risiko <1 | Nein (fix) | + +**Zusätzliche Trend-Parameter (produktiv, V1.1 fix — NICHT von Modul-13 optimieren):** + +| Parameter | V1 produktiv (Code) | V1.1 | Zweck | +|---|---|---|---| +| `adx_trend_threshold` | **20.0** (config.py:54) | fix 20.0 | ADX-Schwelle für echten Trend | +| `adx_period` | **14** (config.py:50) | fix 14 | ADX-Indikatorfenster | +| `ema_fast_period` | **20** (config.py:36) | fix 20 | Fast-EMA (Strategie-Pullback-Basis) | +| `ema_slow_period` | **30** (config.py:47) | fix 30 | Slow-EMA (Regime-Flanke) | + +## 5. V1-Kompatibilitätsmodus (bitgenau V1) + +``` +pullback_band_atr = 0.0 +regime_vol_override = true +trend_regime_window = 60 ++ alle übrigen V1-Parameter auf produktiven Defaults +``` +⇒ muss V1 **bitgenau** reproduzieren (Rückwärtskompatibilität; Backtest ohne +`strategy_params` bleibt fachlich identisch). **E2E-verifiziert**: V1-Kompatmodus +LONG+SHORT bitgenau (net 200.0, target r=2.0). + +## 6. Abgrenzung / No-Go + +- **Keine KI/LLM** in V1.1, keine Live-Orders. +- Produktions-Regime-Schwellen (`adx_trend_threshold=20`, `trend_min_ema_gap=0.02`, + `high_vol_atr_ratio=0.03`, `low_vol_atr_ratio=0.008`) werden **nicht verändert**. +- `atr_period`, ADX-/EMA-Perioden, `min_risk_reward` werden in dieser ersten + Optimierungsrunde **nicht** über Modul-13 optimiert. + +--- +## Geändert +``` +Geändert von: Rain Ocampo +Datum: 20.08.2026 +Grund: V1.1-Spec von ENTWURF auf DEPLOYT aktualisiert (backtesting:0.1.2); V1-Kompatmodus bitgenau E2E-verifiziert. +```