Doku: Modul-13 E2E-verifiziert, V1.1-Spec deployt, Modul-12 auf 0.1.2 (20.08.2026)

This commit is contained in:
Rain Ocampo 2026-08-20 13:53:47 +00:00
parent 720d10084e
commit c8cf6c2472
5 changed files with 256 additions and 6 deletions

View file

@ -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) |
| 1317 | … | ⬜ Platzhalter (alpine) | 5501355017 |
| 12 | Backtesting | ✅ FREIGEGEBEN (backtesting:0.1.2) | 55012 (intern) |
| 13 | Optimization | ✅ E2E-VERIFIZIERT (optimization:0.1.0) | 55013 (intern) |
| 1417 | … | ⬜ Platzhalter (alpine) | 5501455017 |
Details: siehe `modul-03-market-data.md``modul-12-backtesting.md` (Module 0312 vollständig implementiert und freigegeben). Modul-13 ff. folgen.
Details: siehe `modul-03-market-data.md``modul-13-optimization.md` (Module 0312
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 0612 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.

View file

@ -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.

119
modul-13-optimization.md Normal file
View file

@ -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→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.
```

View file

@ -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) |
| 0617 | … | — | 5500655017 | (Platzhalter) |
| 0612 | Modul-06…12 | FastAPI | 5500655012 (intern, expose) | nein (nur intern) |
| 13 | Modul-13-Optimization | FastAPI | 55013 (intern, expose) | nein (nur intern) |
| 1417 | … | — | 5501455017 | (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-0612 als real (FastAPI, intern) und Modul-13 Optimization (55013 intern) eingetragen.

115
trend-pullback-v1.1-spec.md Normal file
View file

@ -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 `close<EMA20` nur in
**14 von 1620** Kerzen (m13_evidence.py, Messwerte 20.08.2026). Die
Vol-Override (`HIGH_VOL`/`LOW_VOL`) kippt das Regime bei jedem sinnvollen
Pullback (>95% der `close<EMA20`-Kerzen werden `LOW_VOL`).
**V1.1 trennt Richtung und Pullback-Timing.** Richtung wird aus einem
strategie-internen, längeren Fenster berechnet (nicht durch Vol-Override
kippbar); Volatilität fließt nur noch ins Pullback-Timing (ATR-Band um EMA20)
ein.
## 2. Kernentscheidungen
1. **V1.1 bleibt zustandslos.**
Trendrichtung wird **jede Bar neu** aus dem aktuellen Fenster berechnet.
**Kein Sticky-State.** Kein Hidden-State. Begründung: einfacher, transparenter
und reproduzierbarer (deterministisch nach Seed/Replay).
2. **Kein kurzfristiger Vol-Override auf die Richtung.**
`regime_vol_override=false` (V1.1-Default): Die Trend-Richtung wird
strategie-intern berechnet und **nicht** durch `HIGH_/LOW_VOLATILITY`
überschrieben. Volatilität beeinflusst nur das Pullback-Timing (Band).
3. **Optimierungsraum für V1.1 bewusst klein.** Erste Iteration optimiert
ausschließlich `pullback_band_atr`, `trend_regime_window`, `rr_multiplier`.
ADX-/EMA-Perioden und `atr_period` bleiben **fix** auf produktiven V1-Werten.
## 3. Pseudo-Regel (Variante A, lookahead-frei)
Strategie-intern, jede Bar, aus geordneten `prev_*`-Serien (kein Lookahead,
kein versteckter Zustand):
```
# --- Trend-Richtung (Fenster = trend_regime_window) ---
ema_fast_l = EMA(prev_closes, ema_fast_period, lookback=trend_regime_window)
ema_slow_l = EMA(prev_closes, ema_slow_period, lookback=trend_regime_window)
adx_l = ADX(prev_highs, prev_lows, prev_closes, adx_period, lookback=trend_regime_window)
trend_dir = UP wenn (ema_fast_l > 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<last_close UND rr>=min_risk_reward
SHORT wenn trend_dir==DOWN UND last_close<sma200 UND last_close > pullback_limit
UND last_close<prev_close UND swing_high>last_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.
```