From 7f2285ddb3ee318b077471f3fe74afe24ec71425 Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Thu, 30 Jul 2026 15:16:19 +0200 Subject: [PATCH] MT5-Chart: echte Liquiditaets-Waende statt Chartmuster-Linien (Indikator v1.12) User-Wunsch: "zeichne mir die liquiditaetslinien in den mt5 ein. entferne die chartmuster linien aus dem mt5". - core/hl_walls.py: holt GET /api/walls vom HL-Dashboard (groesstes L2-Level je Seite, basis-korrigiert auf CFD-Niveau). Fail-safe: HL aus -> keine Linien, kein Fehler; Cache 10s, Daten >60s alt werden verworfen, Log nur 1x. - engine._write_levels_file: PT-Zeilen raus, LQ;;preis;size rein. - SR_Levels.mq5 v1.12: PT-Handler entfernt, LQ-Handler (dick gestrichelt + Groessen-Label), InpShowWalls/InpWallBidColor/InpWallAskColor. Kompiliert. - config: export_hl_walls=true, hl_walls_url. Zwei Fallen dokumentiert: (1) Basis-Differenz HL vs CFD ~0,45$ -- ungerechnet laegen die Linien falsch. (2) 127.0.0.1 statt localhost (IPv6 -> 2066ms statt 19-46ms, lief in den 2s-Timeout). Verifiziert: LQ-Zeilen erscheinen in sr_levels.csv (B 83.619/3120, A 83.665/1721), plausibel zwischen R 83.721 und S 83.516. Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 31 ++++++++++++--- core/config.py | 9 +++++ core/engine.py | 27 ++++++++----- core/hl_walls.py | 80 +++++++++++++++++++++++++++++++++++++++ mql5/SR_Levels.mq5 | 94 ++++++++++++++++++++++------------------------ 5 files changed, 176 insertions(+), 65 deletions(-) create mode 100644 core/hl_walls.py diff --git a/CLAUDE.md b/CLAUDE.md index 6b2be75..95dbf40 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1097,12 +1097,31 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche: PWA-Chart. **Für das Desktop-Terminal: Datei-Bridge** — `engine._write_levels_file` (im `_run_analysis`, ~5 s, `[trading] export_mql5_levels=true`) schreibt atomar `\MQL5\Files\sr_levels.csv` (`SYM;…` / `R;preis` / `S;preis` / **`CH;; - t1;p1;t2;p2`** = Regressionskanal-Anker / **`PT;name;dir;trigger;target;status`** = - erkanntes Chartmuster (2026-07-27, top 2 aus `patterns.snapshot`); Quelle - **`_draw_levels`** (S/R) + **`structure.channel_line`** (Kanal) + **`self.patterns`** - (Muster), Indikator-Farben blau DodgerBlue/DeepSkyBlue, Muster rot/grün); - der Indikator **`mql5/SR_Levels.mq5`** (**v1.11** — zeichnet jetzt auch die Muster: - Trigger-/Nackenlinie kräftig + gepunktete Ziel-Linie + Label; in `MQL5\Indicators\`, **braucht + t1;p1;t2;p2`** = Regressionskanal-Anker / **`LQ;;preis;size`** = ECHTE + Liquiditäts-Wand, s. u.); Quelle **`_draw_levels`** (S/R) + + **`structure.channel_line`** (Kanal) + **`self.hl_walls`** (Liquidität), + Indikator-Farben blau DodgerBlue/DeepSkyBlue, Wände grün/orangerot. + ⚠ **Chartmuster-Linien (`PT;…`) 2026-07-30 auf User-Wunsch ENTFERNT** („entferne die + chartmuster linien aus dem mt5" — Chart war zu voll); die Muster-Kachel + das + PWA-Chart-Overlay bleiben unverändert, nur der MT5-Export/Indikator-Handler ist raus. + **ECHTE Liquiditäts-Wände im MT5-Chart (User-Wunsch 2026-07-30 „zeichne mir die + liquiditätslinien in den mt5 ein"):** `core/hl_walls.py` holt vom **HL-Dashboard** + (`GET http://127.0.0.1:8001/api/walls`, `[trading] export_hl_walls=true`) das + **größte L2-Orderbuch-Level je Seite** — die einzigen Linien im Chart, die NICHT aus + Pivots geschätzt sind, sondern **tatsächlich ruhende Orders** zeigen (Pepperstone = + Market Maker, liefert kein Buch). ⚠ **Basis-Korrektur zwingend:** HL `xyz:CL` notiert + **~0,45 $ unter** dem CFD (anderer Kontrakt/Contango) — die Umrechnung passiert + serverseitig im HL-Projekt (Median über 60 Snapshots), hier kommt schon + `bid_mt5`/`ask_mt5` an; fehlt die Basis, wird NICHT gezeichnet. ⚠ **`127.0.0.1`, nicht + `localhost`** (IPv6-Auflösung → 2066 ms statt 19–46 ms, lief in den 2-s-Timeout) und + der HL-Endpoint liest **nur seine DB** (live-`fetch_l2_book` dauerte ~2 s = Timeout). + Fail-safe: HL-Server aus → keine Linien, kein Fehler (Log 1×, dann still), Cache 10 s, + Daten älter als 60 s werden verworfen. **REINE ANZEIGE** — ob HL-Buch-Signale auf den + CFD übertragbar sind, wird gerade gemessen (`analyze_bookflow.py` im HL-Projekt, + Datensammlung seit 2026-07-30, Urteil in ~2 Wochen). Der Indikator + **`mql5/SR_Levels.mq5`** (**v1.12** — `LQ`-Handler: dick gestrichelt + Größen-Label + „Liquidität NACHFRAGE/ANGEBOT ", `InpShowWalls`/`InpWallBidColor`/ + `InpWallAskColor`; PT-Handler entfernt; in `MQL5\Indicators\`, **braucht `OnCalculate` als No-op** sonst Compile-Error, F7-kompilieren, auf den Chart ziehen) liest die S/R-Linien per Timer (OBJ_HLINE) UND den **M30-Regressionskanal als 3 diagonale Trendlinien** (OBJ_TREND, nach rechts verlängert, oben/unten gestrichelt/ diff --git a/core/config.py b/core/config.py index eb42950..0e67649 100644 --- a/core/config.py +++ b/core/config.py @@ -384,6 +384,15 @@ DEFAULT_CONFIG = { # REGIME-Schutz (hilft im schlechten, kostet im guten). 2. Bestätigung von # `backtest_adverse_nosignal.py`. 0 = aus. "adverse_15min_atr": "0.5", + # ECHTE Liquiditäts-Wände von Hyperliquid im MT5-Chart zeichnen (User-Wunsch + # 2026-07-30). Holt `GET /api/walls` vom HL-Dashboard (Port 8001) — das größte + # L2-Orderbuch-Level je Seite, BASIS-KORRIGIERT auf CFD-Niveau (HL notiert + # ~0,45 $ tiefer). Anders als R/S nicht aus Pivots geschätzt, sondern echte + # ruhende Orders. REINE ANZEIGE — Übertragbarkeit auf den CFD wird gerade + # gemessen (`analyze_bookflow.py` im HL-Projekt). Fail-safe: HL-Server aus → + # keine Linien, kein Fehler. false = aus. + "export_hl_walls": "true", + "hl_walls_url": "http://127.0.0.1:8001/api/walls", }, # (Der frühere `[setups]`-Block — Per-Setup-Toggles für den Dry-Run-Auto-Trader — # ist entfernt, 2026-07-23: verwaist seit dem Entfernen des Dry-Run-Traders, wurde diff --git a/core/engine.py b/core/engine.py index 7f8ee06..08b5ee0 100644 --- a/core/engine.py +++ b/core/engine.py @@ -153,6 +153,13 @@ class TradingEngine: self.elliott = ElliottAnalyzer(mt5.TIMEFRAME_M5) self.structure = MarketStructure() # Marktstruktur (M30) — reine Anzeige self.patterns = PatternDetector() # visuelle Chartmuster (M30) — reine Anzeige + # Echte Liquiditäts-Wände vom HL-Orderbuch (Anzeige im MT5-Chart, kein Signal). + # Braucht das HL-Dashboard auf 8001; ist es aus, bleibt der Chart unverändert. + from core.hl_walls import HLWalls + _t2 = self.cfg["trading"] + self.hl_walls = HLWalls( + url=_t2.get("hl_walls_url", "http://localhost:8001/api/walls"), + enabled=_t2.get("export_hl_walls", "true").lower() == "true") self.candles = CandleLogger(HISTORY_DB_FILE) # M1-Candle-Log (Daten-Sammlung) # News: eigener Fetch-Loop (versorgt UI /api/news UND Agent-Kontext) tcfg = self.cfg["translation"] @@ -1926,16 +1933,18 @@ class TradingEngine: lines.append(f"CH;{kind};{cl['t1']};{pts[0]};{cl['t2']};{pts[1]}") except Exception: pass - # Erkannte Chartmuster (reine Anzeige): Trigger/Nackenlinie + Ziel als - # horizontale Linien + Label. Format PT;name;dir;trigger;target;status - # (target leer = Ausbruch offen, z. B. symmetrisches Dreieck). + # Chartmuster-Linien (PT;…) 2026-07-30 auf User-Wunsch ENTFERNT — die + # Kachel/PWA-Anzeige bleibt, im MT5-Chart wurde es zu voll. Der Indikator + # (v1.12) hat den PT-Handler ebenfalls nicht mehr. + # ECHTE Liquiditäts-Wände von Hyperliquid (größtes L2-Level je Seite, + # basis-korrigiert auf CFD-Niveau) — Format LQ;;preis;size. + # Anders als R/S NICHT aus Pivots geschätzt, sondern tatsächlich ruhende + # Orders. REINE ANZEIGE (Übertragbarkeit auf den CFD wird gerade gemessen). try: - pats = (self.patterns.snapshot() or {}).get("patterns") or [] - for m in pats[:2]: - tgt = m.get("target") - lines.append(f"PT;{m.get('name','')};{m.get('dir','')};" - f"{m.get('trigger')};{tgt if tgt is not None else ''};" - f"{m.get('status','')}") + w = self.hl_walls.walls() + if w: + lines.append(f"LQ;B;{w['bid']};{w['bid_sz']:.0f}") + lines.append(f"LQ;A;{w['ask']};{w['ask_sz']:.0f}") except Exception: pass # Zonen NICHT mehr aus der (statischen) [zones]-Config — die dynamische diff --git a/core/hl_walls.py b/core/hl_walls.py new file mode 100644 index 0000000..681d32a --- /dev/null +++ b/core/hl_walls.py @@ -0,0 +1,80 @@ +"""core/hl_walls.py — ECHTE Liquiditäts-Wände von Hyperliquid für den MT5-Chart. + +Holt vom HL-Dashboard (Port 8001, `GET /api/walls`) das größte L2-Orderbuch-Level je +Seite — **basis-korrigiert auf das Pepperstone-Preisniveau** — und stellt es dem +Level-Export (`engine._write_levels_file` → `sr_levels.csv` → `SR_Levels.mq5`) bereit. + +WARUM (User-Wunsch 2026-07-30): Alle S/R-Linien im Projekt sind aus **Pivots +geschätzt** („wo war mal ein Docht"). Das hier ist die einzige Quelle mit +**tatsächlich ruhenden Orders** — Pepperstone ist Market Maker und liefert kein +Orderbuch, Hyperliquid schon (20 Levels je Seite). + +⚠ Zwei Dinge im Blick behalten: + 1. **Basis-Differenz**: HL `xyz:CL` notiert ~0,45 $ unter dem CFD (anderer + Kontrakt/Contango). Die Umrechnung passiert SERVERSEITIG im HL-Projekt + (Median über die letzten 60 Snapshots) — hier kommt schon `bid_mt5`/`ask_mt5` + an. Fehlt die Basis (zu wenige Snapshots), wird NICHT gezeichnet. + 2. **Anzeige, KEIN Signal.** Ob HL-Orderbuch-Signale auf den CFD übertragbar sind, + ist eine offene Frage und wird gerade gemessen (`analyze_bookflow.py` im + HL-Projekt). Bis dahin fließt hier nichts in Empfehlung/Trade ein. + +Fail-safe: kurzer Timeout, Ergebnis gecacht; ist der HL-Server aus, liefert +`walls()` einfach None und der Chart bleibt unverändert. +""" +from __future__ import annotations +import json +import threading +import time +import urllib.request + +from core.logger import get_logger + +log = get_logger("hlwalls") + +_TIMEOUT_S = 2.0 # der MT5-Loop darf nicht am HTTP hängen +_TTL_S = 10.0 # Cache — die Wände wandern langsamer als der Export-Takt (~5 s) +_MAX_AGE_S = 60.0 # älter = HL-Feed steht → nicht mehr zeichnen + + +class HLWalls: + def __init__(self, url: str = "http://127.0.0.1:8001/api/walls", + enabled: bool = False): + self.url = url + self.enabled = bool(enabled) + self._lock = threading.Lock() + self._cache: dict | None = None + self._cache_ts = 0.0 + self._fail_logged = False + + def walls(self) -> dict | None: + """{'bid','bid_sz','ask','ask_sz','basis','imb5'} auf MT5-Preisniveau — + oder None (aus / HL nicht erreichbar / Basis unbekannt / Daten zu alt).""" + if not self.enabled: + return None + now = time.time() + with self._lock: + if self._cache is not None and now - self._cache_ts < _TTL_S: + return self._cache + data = None + try: + with urllib.request.urlopen(self.url, timeout=_TIMEOUT_S) as r: + d = json.loads(r.read().decode()) + if d.get("ok") and d.get("bid_mt5") and d.get("ask_mt5"): + # Feed-Alter prüfen: ein stehengebliebenes Buch darf nicht als + # aktuelle Liquidität gezeichnet werden. + age = now - float(d.get("ts") or 0) + if age <= _MAX_AGE_S: + data = {"bid": float(d["bid_mt5"]), "ask": float(d["ask_mt5"]), + "bid_sz": float(d.get("bid_sz") or 0), + "ask_sz": float(d.get("ask_sz") or 0), + "basis": d.get("basis"), "imb5": d.get("imb5")} + self._fail_logged = False + except Exception as e: + if not self._fail_logged: # nur 1× melden, nicht jeden Tick + log.info(f"HL-Wände nicht abrufbar ({e}) — Chart bleibt ohne " + f"Liquiditätslinien (HL-Dashboard auf 8001 läuft nicht?)") + self._fail_logged = True + with self._lock: + self._cache = data + self._cache_ts = now + return data diff --git a/mql5/SR_Levels.mq5 b/mql5/SR_Levels.mq5 index 5d864ee..32f81c4 100644 --- a/mql5/SR_Levels.mq5 +++ b/mql5/SR_Levels.mq5 @@ -12,9 +12,13 @@ //| 4) Der Bot muss laufen (schreibt die CSV alle ~5 s). | //+------------------------------------------------------------------+ #property copyright "Oil Trading Bot" -#property version "1.11" -// v1.11: zeichnet zusätzlich erkannte Chartmuster (PT;… aus der CSV) — Trigger-/ -// Nackenlinie (rot/grün) + gepunktete Ziel-Linie + Label. Reine Anzeige. +#property version "1.12" +// v1.12 (2026-07-30): Chartmuster-Linien (PT;…) ENTFERNT (User-Wunsch, Chart war zu +// voll — Kachel/PWA-Anzeige bleibt). NEU: ECHTE Liquiditäts-Wände (LQ;…) aus +// dem Hyperliquid-L2-Orderbuch, basis-korrigiert auf CFD-Niveau — die einzigen +// Linien im Chart, die nicht aus Pivots geschätzt sind, sondern tatsächlich +// ruhende Orders zeigen (dick gestrichelt + Größen-Label). Reine Anzeige. +// v1.11: Chartmuster-Linien (jetzt entfernt). // ⚠ nach dem Update den Indikator NEU mit F7 kompilieren. #property indicator_chart_window #property indicator_plots 0 @@ -29,6 +33,9 @@ input color InpRangeColor = clrGoldenrod; // Range-Farbe input bool InpRangeFill = false; // Range füllen (aus = nur Umriss) input bool InpShowChannel = true; // Regressionskanal (M30) zeichnen input color InpChanColor = clrYellow; // Kanal-Farbe +input bool InpShowWalls = true; // echte Liquiditäts-Wände (Hyperliquid-Orderbuch) +input color InpWallBidColor = clrMediumSpringGreen; // Nachfrage-Wand (Bid, Käufer) +input color InpWallAskColor = clrOrangeRed; // Angebots-Wand (Ask, Verkäufer) const string PFX = "SRB_"; // Präfix S/R-Linien (aus Datei) const string RPFX = "SRR_"; // Präfix dynamische Range (aus Chart) @@ -244,63 +251,50 @@ void Redraw() } continue; } - // Chartmuster: PT;name;dir;trigger;target;status → kräftige Trigger-/Nacken- - // linie (rot=bear/grün=bull/grau=neutral) + gepunktete Ziel-Linie + Labels. - // REINE ANZEIGE (im Bot als Signal noch unbelegt). - if(typ == "PT") + // ECHTE Liquiditäts-Wände: LQ;;preis;size (B = Nachfrage/Bid-Wand, + // A = Angebot/Ask-Wand). Quelle = Hyperliquid-L2-Orderbuch, größtes Level je + // Seite, im Bot BASIS-KORRIGIERT auf CFD-Niveau (HL notiert ~0,45 $ tiefer). + // ⚠ Das ist die EINZIGE Linie im Chart, die nicht aus Pivots geschätzt ist, + // sondern tatsächlich ruhende Orders zeigt. Dick + gestrichelt, damit sie sich + // klar von den dünnen S/R-Linien abhebt. REINE ANZEIGE (kein Signal). + // (Chartmuster-Linien „PT;…" wurden 2026-07-30 auf User-Wunsch entfernt.) + if(typ == "LQ") { - if(k < 4) continue; - string pname = p[1]; - string pdir = p[2]; - double ptrig = StringToDouble(p[3]); - double ptgt = (k > 4 && StringLen(p[4]) > 0) ? StringToDouble(p[4]) : 0.0; - if(ptrig <= 0) continue; - color pcol = (pdir == "bull" ? clrLime : (pdir == "bear" ? clrRed : clrSilver)); - // Trigger-/Nackenlinie (WIDTH 2 → hebt sich von den dünnen S/R-Linien ab) - string nm = PFX + "PT_" + IntegerToString(idx); - if(ObjectCreate(0, nm, OBJ_HLINE, 0, 0, ptrig)) + if(k < 3) continue; + if(!InpShowWalls) continue; + string side = p[1]; + double lpx = StringToDouble(p[2]); + double lsz = (k > 3) ? StringToDouble(p[3]) : 0.0; + if(lpx <= 0) continue; + bool isBid = (side == "B"); + color lcol = isBid ? InpWallBidColor : InpWallAskColor; + string nm = PFX + "LQ_" + side; + if(ObjectCreate(0, nm, OBJ_HLINE, 0, 0, lpx)) { - ObjectSetInteger(0, nm, OBJPROP_COLOR, pcol); - ObjectSetInteger(0, nm, OBJPROP_STYLE, STYLE_SOLID); + ObjectSetInteger(0, nm, OBJPROP_COLOR, lcol); + ObjectSetInteger(0, nm, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(0, nm, OBJPROP_WIDTH, 2); ObjectSetInteger(0, nm, OBJPROP_BACK, true); ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false); } - string lbl = (ptgt > 0) - ? pname + " -> Ziel " + DoubleToString(ptgt, (int)_Digits) - : pname + " (Trigger " + DoubleToString(ptrig, (int)_Digits) + ")"; - string tn = PFX + "TPT_" + IntegerToString(idx); - if(ObjectCreate(0, tn, OBJ_TEXT, 0, CenterTime(), ptrig)) + else + ObjectMove(0, nm, 0, 0, lpx); + string llbl = (isBid ? "Liquidität NACHFRAGE " : "Liquidität ANGEBOT ") + + DoubleToString(lsz, 0); + string ltn = PFX + "TLQ_" + side; + if(ObjectCreate(0, ltn, OBJ_TEXT, 0, CenterTime(), lpx)) { - ObjectSetString (0, tn, OBJPROP_TEXT, lbl); - ObjectSetInteger(0, tn, OBJPROP_COLOR, pcol); - ObjectSetInteger(0, tn, OBJPROP_FONTSIZE, 9); - ObjectSetInteger(0, tn, OBJPROP_ANCHOR, ANCHOR_LOWER); - ObjectSetInteger(0, tn, OBJPROP_SELECTABLE, false); + ObjectSetString (0, ltn, OBJPROP_TEXT, llbl); + ObjectSetInteger(0, ltn, OBJPROP_COLOR, lcol); + ObjectSetInteger(0, ltn, OBJPROP_FONTSIZE, 9); + ObjectSetInteger(0, ltn, OBJPROP_ANCHOR, ANCHOR_LOWER); + ObjectSetInteger(0, ltn, OBJPROP_SELECTABLE, false); } - // Ziel-Linie (gepunktet) + Label, falls Ziel bekannt - if(ptgt > 0) + else { - string tg = PFX + "PTG_" + IntegerToString(idx); - if(ObjectCreate(0, tg, OBJ_HLINE, 0, 0, ptgt)) - { - ObjectSetInteger(0, tg, OBJPROP_COLOR, pcol); - ObjectSetInteger(0, tg, OBJPROP_STYLE, STYLE_DOT); - ObjectSetInteger(0, tg, OBJPROP_WIDTH, 1); - ObjectSetInteger(0, tg, OBJPROP_BACK, true); - ObjectSetInteger(0, tg, OBJPROP_SELECTABLE, false); - } - string tgn = PFX + "TPTG_" + IntegerToString(idx); - if(ObjectCreate(0, tgn, OBJ_TEXT, 0, CenterTime(), ptgt)) - { - ObjectSetString (0, tgn, OBJPROP_TEXT, "Ziel " + pname); - ObjectSetInteger(0, tgn, OBJPROP_COLOR, pcol); - ObjectSetInteger(0, tgn, OBJPROP_FONTSIZE, 8); - ObjectSetInteger(0, tgn, OBJPROP_ANCHOR, ANCHOR_LOWER); - ObjectSetInteger(0, tgn, OBJPROP_SELECTABLE, false); - } + ObjectMove(0, ltn, 0, CenterTime(), lpx); + ObjectSetString(0, ltn, OBJPROP_TEXT, llbl); } - idx++; continue; } // ("Z;"-Zonen-Zeilen schreibt der Bot nicht mehr — die dynamische Handels-