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;<B|A>;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 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-07-30 15:16:19 +02:00
co-authored by Claude Opus 4.8
parent 29f53c45c1
commit 7f2285ddb3
5 changed files with 176 additions and 65 deletions
+80
View File
@@ -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