User-Wunsch: "Kennzeiche die Liquiditaetslinie mit einem Pfeil in welche richtung
die linie den Kurs treibt. Zeichne mir eine Trendlnie in pink im mt5 auf basis
der liquiditaet ein".
(a) Pfeil je Wand in Wirkrichtung (Lesart "die Wand haelt": Nachfrage stuetzt hoch,
Angebot deckelt runter); groessere Wand dicker + "<<". CSV-Format erweitert:
LQ;<B|A>;preis;size;<U|D>;<1|0>.
WICHTIG: Pfeilrichtung ist INTERPRETATION, nicht gemessen -- die Gegenthese
("Liquidity Grab", Kurs laeuft ZUR Wand) ist genauso verbreitet. Klaert
analyze_bookflow.py Frage 3.
(b) Pinke Liquiditaets-Trendlinie LT;t1;p1;t2;p2;<U|D>: Regression ueber den
VOLUMENGEWICHTETEN Wand-Schwerpunkt der letzten 60 min. Zeigt, wohin das
Gewicht der RUHENDEN Orders wandert -- nicht der gehandelte Kurs.
Zeiten im Bot in Broker-Zeit umgerechnet, Preise basis-korrigiert.
HL /api/walls liefert jetzt arrow_bid/arrow_ask/dominant/trend.
Verifiziert: LQ;B;84.182;2441;U;1 / LQ;A;84.223;2441;D;0 /
LT;1785427175;84.135;1785430664;83.978;D in sr_levels.csv. Indikator kompiliert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
91 lines
4.4 KiB
Python
91 lines
4.4 KiB
Python
"""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"),
|
||
# Wirkrichtung je Wand (Pfeil im Chart) + größere Wand.
|
||
# ⚠ Lesart „die Wand hält" — INTERPRETATION, nicht gemessen.
|
||
"arrow_bid": d.get("arrow_bid") or "up",
|
||
"arrow_ask": d.get("arrow_ask") or "down",
|
||
"dominant": d.get("dominant"),
|
||
# Liquiditäts-Trendlinie (Regression über den volumen-
|
||
# gewichteten Wand-Schwerpunkt): {t1,p1,t2,p2,dir,slope_per_h}
|
||
# — Zeiten sind LOKALE Epoch, der Aufrufer rechnet sie in
|
||
# Broker-Zeit um (MT5-Charts brauchen Broker-Zeit).
|
||
"trend": d.get("trend")}
|
||
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
|