Initial commit: Oil Trading Bot (MT5, WTI)
Headless FastAPI-Backend (server.py + core/engine.py) mit Mobile-PWA (web/), Strategie-/Backtest-Suite und Doku. Secrets, DB, Logs und Laufzeit-State sind via .gitignore ausgeschlossen; Config-Vorlage: oil_widget_config.ini.example. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# Exit-Simulation (Befund B: Initial-SL-Breite)
|
||||
|
||||
`backtest_exit.py` — misst, ob ein **weiterer Initial-SL** netto mehr bringt,
|
||||
unter originalgetreuer Nachbildung des Exit-Ablaufs aus `core/trailing.py`.
|
||||
|
||||
## Mechanik (nachgebildet)
|
||||
|
||||
- Initial-SL = **X×ATR** (getestete Variable).
|
||||
- **Teil-Exit 50 %** bei +1,5×ATR (einmalig).
|
||||
- Phasen: **Init** (<0,3×ATR → Initial-SL halten) · **Trail** (SL = HW∓1,5×ATR,
|
||||
Breakeven-Floor ab +0,6×ATR) · **Lock** (≥3,5×ATR → enger). Phasen-Ratsche.
|
||||
- Pessimistische Intrabar-Annahme: **Gegenlauf vor Mitlauf** → überschätzt den
|
||||
Nutzen eines weiten SL NICHT.
|
||||
- PnL in **R** (ATR-Vielfache). Fester Init-TP (+3,5×ATR) weggelassen — der
|
||||
Runner-Exit läuft praktisch über den Trailing-SL (konservativ, dominant).
|
||||
|
||||
> Schlüssel: Der weite Init-SL wirkt **nur in der Init-Phase**. Sobald Trailing
|
||||
> greift, kappt HW∓1,5×ATR ihn ohnehin. Er schützt also genau die Trades, die
|
||||
> **sofort** gegen den Einstieg laufen (die „Frühstopps").
|
||||
|
||||
## Ergebnis (M5+M30, ~28 Tage, 3585 Signale)
|
||||
|
||||
| Init-SL | Treffer | Ø-R | Summe-R | PF | Ø-Verlierer | Worst-R | Init-Stops |
|
||||
|--------:|--------:|----:|--------:|---:|-----------:|--------:|-----------:|
|
||||
| 1,2 | 37 % | 0,169 | 607 | 1,33 | −1,09 | −1,20 | 37 % |
|
||||
| 1,5 | 39 % | 0,182 | 654 | 1,33 | −1,29 | −1,50 | 31 % |
|
||||
| **2,0** | **43 %** | **0,223** | **798** | **1,39** | −1,56 | −2,00 | 25 % |
|
||||
| 2,5 | 46 % | 0,255 | 914 | 1,43 | −1,78 | −2,50 | 21 % |
|
||||
| 4,0 | 51 % | 0,308 | 1104 | 1,49 | −2,28 | −4,00 | 14 % |
|
||||
|
||||
**Befund:** Ø-R, PF, Treffer steigen monoton mit der SL-Breite, Frühstopps sinken.
|
||||
**Aber:** der größte Einzelverlust wächst 1:1 (Worst-R = −X) → Tail-/Drawdown-Risiko.
|
||||
|
||||
## Bewertung
|
||||
|
||||
- Das aktuelle Band **1,2–1,5×ATR ist zu eng** (deckt sich mit MAE ~1,7×ATR und
|
||||
37 %/31 % Frühstopps). Wechsel auf **~2,0×ATR** ist der ausgewogene Punkt:
|
||||
+32 % Ø-R, PF 1,33→1,39, Frühstopps 37→25 %, Worst-Case auf 2,0×ATR begrenzt.
|
||||
- **Nicht** auf 3–4×ATR: Durchschnitte steigen weiter, aber die großen Einzel-
|
||||
verluste treiben Drawdown/Ruin-Risiko; Stichprobe ist zudem ein Trend-Regime
|
||||
(„länger halten" bevorzugt).
|
||||
- ⚠ **Money-Management:** ein weiterer SL = mehr Risiko/Trade in EUR bei gleicher
|
||||
Lotgröße. Für konstantes EUR-Risiko die Lots ~proportional senken (1,5→2,0 ≈ −25 %).
|
||||
|
||||
## Aufruf
|
||||
`python backtest_exit.py [n_bars]` (Default 8000 M5-Bars)
|
||||
@@ -0,0 +1,68 @@
|
||||
# Gesamtempfehlung (Verdict-Panel)
|
||||
|
||||
Grafischer Empfehlungsbereich ganz oben im Mobile-Dashboard (`web/`), der die
|
||||
Module zu **einer** Aussage zusammenfasst.
|
||||
|
||||
## Wer berechnet was
|
||||
|
||||
- **Backend (`core/engine.py` → `_verdict`)** baut die Aggregation und legt sie
|
||||
als `verdict` in den Snapshot (`/api/snapshot`, `/ws`). Eine Quelle der
|
||||
Wahrheit — das Frontend rendert nur.
|
||||
- **Frontend (`web/app.js` → `renderVerdict`)** zeichnet Ring, Bias-Balken und
|
||||
Modul-Chips. Markup in `web/index.html` (`#card-verdict`), Stil in
|
||||
`web/style.css` (`.vd-*`).
|
||||
|
||||
## Logik
|
||||
|
||||
**Headline = die validierte Welle** (`wave_signal`) — das einzige Modul mit per
|
||||
Backtest belegtem Edge. Sie bestimmt LONG/SHORT/WARTEN und die Order-Freigabe.
|
||||
Die übrigen Module bilden nur einen **gewichteten Konsens** (Anzeige, kein
|
||||
Eingriff in die Order-Logik).
|
||||
|
||||
Votes je Modul: `+1` LONG · `−1` SHORT · `0` neutral. Gewichte:
|
||||
|
||||
| Modul | Gewicht | Quelle |
|
||||
|-------|---------|--------|
|
||||
| Welle | **3,0** | `wave_signal.signal` (validierter Edge) |
|
||||
| M30 | 1,5 | `wave.htf_trend` (EMA12/50 der M30 — exakt das Signal, das die Welle nutzt) |
|
||||
| H1 | 1,5 | `wave.h1_trend` (EMA12/50 der H1) |
|
||||
| KI-Copilot | 1,0 *(nur mit Bias)* | `agent.advisory.bias` — ohne advisory/Bias **Gewicht 0** |
|
||||
| Elliott/FVG | 1,0 *(nur mit Ziel)* | Richtung zum `elliott.target` — ohne Ziel **Gewicht 0** |
|
||||
| Squeeze | 2,0 *(nur `active`)* | `wave.squeeze` — armed/keine Kompression **Gewicht 0** |
|
||||
|
||||
(TradersUnion wurde 2026-07-06 aus dem Konsens entfernt — nur noch Anzeige.)
|
||||
|
||||
```
|
||||
bias = Σ(gewicht · vote) / Σ(gewicht) → −1 … +1
|
||||
```
|
||||
|
||||
⚠ **Stimmberechtigt sind nur Module mit Aussage** (Fix 2026-07-15): Module ohne
|
||||
anwendbares Signal (Squeeze ohne Ausbruch, Elliott ohne Ziel, KI aus) bekommen
|
||||
Gewicht 0 und stehen NICHT im Nenner — vorher dämpfte z. B. das fast immer
|
||||
neutrale Squeeze-Gewicht (2,0) die Nadel dauerhaft um ~×0,8 Richtung Mitte.
|
||||
„Flach" bei Welle/M30/H1 bleibt dagegen eine ECHTE Neutral-Stimme (mit Gewicht).
|
||||
|
||||
`bias` steuert die **Nadel** im Balken (−1 = ganz SHORT/links, +1 = ganz
|
||||
LONG/rechts: `left% = (bias+1)/2·100`). `agree/total` = wie viele der **anderen**
|
||||
stimmberechtigten Module (ohne die Welle selbst — die zählte sich bis 2026-07-15
|
||||
mit) die Headline-Richtung teilen. Der **Konfidenz-Ring** zeigt `wave.conf_pct`
|
||||
als conic-gradient in Richtungsfarbe.
|
||||
|
||||
> ⚠ Wichtig: Für M30/H1 werden die **EMA12/50-Signale der Welle** genutzt
|
||||
> (`htf_trend`/`h1_trend`), NICHT das Regressions-Winkelmaß `market.angles` —
|
||||
> die beiden können sich widersprechen, das Panel muss zur Empfehlung passen.
|
||||
|
||||
## Snapshot-Feld `verdict`
|
||||
|
||||
```json
|
||||
{
|
||||
"headline": "SHORT", "conf": 90, "bias": -1.0, "agree": 3, "total": 3,
|
||||
"votes": [{"name": "Welle", "vote": -1, "weight": 3.0, "detail": "SHORT · 90%"}, …]
|
||||
}
|
||||
```
|
||||
|
||||
## Pflege
|
||||
|
||||
Neues Modul ins Panel → Vote/Gewicht in `_verdict` ergänzen; das Frontend rendert
|
||||
`votes` generisch. Bei JS/CSS-Änderung Asset-Version `v=N` in `index.html`
|
||||
hochzählen. Siehe `CLAUDE.md`.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Risiko-Management (Positionsgröße & Stops)
|
||||
|
||||
Stand: 2026-07-08.
|
||||
|
||||
## Positionsgröße
|
||||
|
||||
> **Aktiv (User-Vorgabe): margin-basiert 95 %.** `calc_lots` setzt die Lots auf
|
||||
> ~95 % der freien Margin (`[trading] margin_buffer_pct=95`, `risk_pct=0`). ⚠ Das
|
||||
> ist faktisch All-in: **~7 %+ Konto-Risiko pro Trade** (gemessen
|
||||
> `backtest_sizing.py`: 36 % Wahrscheinlichkeit eines >80 %-Drawdowns). Bewusste
|
||||
> Entscheidung — 2026-07-06 kurz auf risiko-basiert 1,5 % gestellt, dann auf
|
||||
> 95 % Margin zurück. Der harte Broker-SL wird trotzdem immer gesetzt.
|
||||
|
||||
Das **risiko-basierte** Sizing (`calc_lots_risk`) ist gebaut, gemessen und
|
||||
jederzeit aktivierbar (`[trading] risk_pct = 1.5` + Restart). Es wählt die Lots
|
||||
so, dass der **Verlust beim Initial-SL ≈ `risk_pct` % der Equity** ist:
|
||||
|
||||
```
|
||||
val_per_price = tick_value / tick_size # € je 1.0 Preis je 1 Lot
|
||||
lots = (Equity × risk_pct/100) / (SL_Distanz × val_per_price)
|
||||
```
|
||||
|
||||
- **SL zuerst:** `_send_locked` berechnet erst den SL (`_calc_sl_tp`, Band
|
||||
1,8–2,2×ATR), dann die Lots aus der **echten** SL-Distanz.
|
||||
- **Margin bleibt Deckel:** nie mehr Lots als die freie Margin (× Buffer) erlaubt.
|
||||
- **Fallback:** liefert `calc_lots_risk` 0 (fehlende Daten), greift `calc_lots`.
|
||||
- Vergleich (102-€-Konto, SL 2×ATR≈0,53): 95 % Margin ≈ 0,15 L → −7 € (6,8 %)
|
||||
am SL · 1,5 % Risiko ≈ 0,03 L → −1,40 € (1,4 %).
|
||||
|
||||
## Stops & Auto-Close
|
||||
|
||||
- **Harter Broker-SL auf jeder Bot-Order** (`req["sl"]`) — server-seitig, wirkt
|
||||
auch bei Bot-/Verbindungsausfall. Band 1,8–2,2×ATR (Ziel 2,0; gemessen
|
||||
`backtest_exit.py`); Breakeven ab +1,3×ATR; Trailing Init/Trail/Lock
|
||||
(Parameter gemessen optimal, `backtest_trailing.py`; ATR-Floor 0,06,
|
||||
`backtest_atrfloor.py`).
|
||||
- **Adoptierte Fremd-Trades** (in MT5 manuell eröffnet) bekommen beim Erkennen
|
||||
einen **Schutz-SL** nachgerüstet (`_refresh_locked`), falls keiner gesetzt ist.
|
||||
- **Notfall-Stop (P&L ≤ −Wert) + Gewinn-Ziel (P&L ≥ +Wert):** server-seitiger
|
||||
Auto-Close (`engine._check_auto_close`, 1-s-Loop, wirkt bei gesperrtem Handy).
|
||||
Notfall-Stop wird beim Öffnen automatisch auf den letzten Wert armiert
|
||||
(Start `[trading] auto_emergency_loss`, Default 10 — ⚠ bei ~100-€-Konto sind
|
||||
10 € ≈ 10 % Equity, das ist der faktische Risiko-Deckel). „aus"-Button
|
||||
deaktiviert auch den Auto-Arm.
|
||||
- **SL/TP sind im Snapshot + Web sichtbar** (Position-Kachel) — fehlender SL wird
|
||||
rot „⚠ kein SL gesetzt!" markiert.
|
||||
|
||||
## Bewusst NICHT umgesetzt
|
||||
|
||||
- **Tagesverlust-Schalter / Circuit Breaker:** vom User **abgelehnt** — nicht
|
||||
einbauen (s. CLAUDE.md).
|
||||
- **Signal-Filter als Risiko-Schutz:** 5× gemessen wirkungslos/schädlich
|
||||
(Winkel, HTF-Winkel, Konfluenz-Strafe, Gegen-H1-Reversal, ER-Chop-Gate).
|
||||
Schutz läuft über Stops + Sizing, nicht über das Signal.
|
||||
Reference in New Issue
Block a user