Hyperliquid-Kurs neben dem Broker-Kurs + G/V-Schaetzung bei geschlossenem Markt

User: "wenn Pepperstone closed ist trotzdem Kurs und Liquiditaet von
Hyperliquid einbinden" -> nachgeschaerft zu "blende den HL-Kurs NEBEN den
Pepperstone-Kurs ein" (ehrlicher als ein stiller Tausch) -> plus "bei
geschlossenem Markt auch den G/V mit HL berechnen".

Snapshot: hl_live {mid_mt5, hl_mid, basis, age_s, bid/ask, bid_sz/ask_sz,
imb5, trend, slope_per_h, vs_mt5} - IMMER befuellt. mid_mt5 = HL-Mid +
gemessene Basis, also direkt mit dem Broker-Kurs vergleichbar. Dazu
market_closed, tick_age_s, pnl_hl.
core/hl_walls.py liefert jetzt zusaetzlich hl_mid/mid_mt5/age_s.

Dashboard v=129: eigene Header-Box "HL" neben KURS mit Tooltip (Rohkurs,
Basis, Alter, Abweichung). Bei geschlossenem Markt: Label "KURS · ZU",
Session-Zeile "MARKT ZU", und das G/V wechselt auf die HL-Schaetzung, mit
"≈" markiert.

MT5 v1.34: HL;<mt5_aequiv>;<alter>;<bid>;<ask>;<U|D|-> -> bernsteinfarbenes
Label unter Kurs und G/V (InpShowHL/InpHLColor).

DREI SACKGASSEN dokumentiert, damit sie niemand wiederholt:
1) tick_local_ts ist die ABHOLZEIT, nicht das Tick-Alter.
2) tick_server_ts minus _broker_offset_s() ist ZIRKULAER - die
   Offset-Funktion leitet den Versatz selbst aus dem letzten Tick ab und
   misst bei geschlossenem Markt die Veraltung (-7,41 h statt +3 h); das
   Alter hebt sich auf (-347 s).
3) pnl / Kursbewegung als EUR-je-Punkt ergab 155 statt ~86, weil
   trader.pnl den Wochenend-Swap enthaelt.

Verwendet wird stattdessen: Markt zu = der Kurs hat sich seit 180 s nicht
BEWEGT (_last_bid/_last_bid_move_ts). Und der G/V nutzt die vorhandene,
korrekte trader.live_pnl(bid, ask) mit dem HL-Kurs +- halbem Spread.

Live verifiziert: market_closed=True nach 198 s, Broker 86.333 eingefroren,
HL 86.349 (7 s alt), Broker-P&L -15,02 -> HL-Schaetzung -14,37 EUR.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-01 09:31:01 +02:00
co-authored by Claude Opus 5
parent 35f9b567c8
commit 7427c36922
7 changed files with 264 additions and 9 deletions
+104
View File
@@ -81,6 +81,9 @@ _SQUEEZE_REENTRY_COOLDOWN_S = 180.0 # nach JEDEM Close eines Trades (Notfall/SL
# gegen die Open→Instant-Close-Kaskade bei zu engem Stop
# (real 2026-07-27 15:39: 3-€-Stop killte Entry #1 in 16 s,
# Squeeze noch aktiv → sofortiger Re-Entry; 2026-07-28)
_TICK_STALE_S = 180 # kein Tick seit X s → Markt gilt als geschlossen.
# Absichtlich tick-basiert statt kalenderbasiert: deckt
# Wochenende, Feiertage UND Broker-Ausfall mit einer Regel.
_TRADE_MARKS = 6 # so viele letzte Trades im MQL5-Chart markieren
# (Kerze umranden: Eröffnung hellgrün, Close dunkelgrün)
_STARTUP_GRACE_S = 60.0 # nach Bot-Start schließt der Bot 60 s lang KEINEN
@@ -321,6 +324,10 @@ class TradingEngine:
self._open_pos_dir = None # Richtung der aktuell offenen Position (LONG/SHORT)
self._pos_close_ts = 0.0 # Zeit + Richtung des letzten Positions-Close (JEDE
self._news_block_logged = None # Dedup für die News-Blackout-Logzeile
# Markt-zu-Erkennung über die letzte KURSBEWEGUNG (s. `snapshot`) — die
# Tick-Zeitstempel taugen dafür nicht (Abholzeit bzw. zirkulärer Offset).
self._last_bid = None
self._last_bid_move_ts = 0.0
# Auto-Flip-Close (User-Wunsch 2026-07-31) — ⚠ GEMESSEN NEGATIV in beiden
# Hälften (`backtest_flipclose2.py`), s. `_check_flip_close`. Default-Schwelle
# 0,5×ATR = die mildeste der gemessenen Varianten.
@@ -2367,6 +2374,20 @@ class TradingEngine:
f"Konsens {_b:+.2f}")
except Exception:
pass
# ── HYPERLIQUID-ERSATZKURS bei geschlossenem Markt (User 2026-08-01):
# HL;<mt5_aequivalent>;<alter_s>;<bid>;<ask>;<trend U|D|-> — der Indikator
# zeigt ihn UNTER dem (eingefrorenen) MT5-Kurs, sobald kein Tick mehr
# kommt. HL (`xyz:CL`) handelt 24/7, Pepperstone nicht; ohne das sieht man
# am Wochenende einen Freitags-Kurs und merkt es nicht.
try:
_w = self.hl_walls.walls()
if _w and _w.get("mid_mt5") is not None:
_tr = (_w.get("trend") or {}).get("dir")
lines.append(f"HL;{_w['mid_mt5']:.3f};{float(_w.get('age_s') or 0):.0f};"
f"{_w.get('bid')};{_w.get('ask')};"
f"{'U' if _tr == 'up' else 'D' if _tr == 'down' else '-'}")
except Exception:
pass
# G/V der offenen Position, direkt unter dem Kurs (User-Wunsch 2026-07-30):
# PL;wert;waehrung. **NETTO** wie im Dashboard — bei Gewinn abzüglich der
# WHT-Quellensteuer, die der Broker einbehält (`[trading] wht_pct`); bei
@@ -2999,6 +3020,74 @@ class TradingEngine:
stop_approach = self._stop_approach_hint(market, position, wave_snap)
# einmal holen — Verdict-Stimme UND Snapshot-Feld nutzen dasselbe Objekt
_pat_snap = self.patterns.snapshot()
# ── Marktstatus + Hyperliquid-Kurs (User 2026-08-01) ─────────────────
# ⚠ ZWEI Sackgassen bei der Markt-zu-Erkennung, beide verworfen:
# (1) `tick_local_ts` ist die lokale ABHOLZEIT — erneuert sich bei jedem
# Abruf, auch wenn der Broker denselben alten Tick liefert.
# (2) `tick_server_ts` minus `trader._broker_offset_s()` ist ZIRKULÄR: die
# Offset-Funktion leitet den Zeitzonen-Versatz selbst aus dem letzten
# Tick ab (`off = tick.time - now`). Bei geschlossenem Markt misst sie
# damit die Tick-Veraltung statt der Zeitzone (real 7,41 h statt +3 h)
# und das Alter hebt sich rechnerisch auf (Ergebnis: 347 s).
# Verwendet wird daher der direkte Beleg: **wann hat sich der Kurs zuletzt
# BEWEGT?** Kein Zeitzonen-, Kalender- oder Feiertagswissen nötig.
_now = time.time()
try:
_b = (market or {}).get("bid")
if _b is not None:
if self._last_bid is None or abs(float(_b) - self._last_bid) > 1e-9:
self._last_bid = float(_b)
self._last_bid_move_ts = _now
except Exception:
pass
_tick_age = (round(_now - self._last_bid_move_ts)
if self._last_bid_move_ts else None)
_mkt_closed = bool(_tick_age is not None and _tick_age > _TICK_STALE_S)
# HL-Kurs IMMER mitliefern (User: „blende den Hyperliquid-Kurs NEBEN den
# Pepperstone-Kurs ein") — nicht nur bei geschlossenem Markt. So sieht man
# laufend die Abweichung; am Wochenende ist es der einzige lebende Kurs.
_hl_live = None
try:
_w = self.hl_walls.walls()
if _w and _w.get("mid_mt5") is not None:
_hl_live = {
"mid_mt5": _w["mid_mt5"], "hl_mid": _w.get("hl_mid"),
"basis": _w.get("basis"), "age_s": _w.get("age_s"),
"bid": _w.get("bid"), "ask": _w.get("ask"),
"bid_sz": _w.get("bid_sz"), "ask_sz": _w.get("ask_sz"),
"imb5": _w.get("imb5"),
"trend": (_w.get("trend") or {}).get("dir"),
"slope_per_h": (_w.get("trend") or {}).get("slope_per_h"),
"vs_mt5": (round(_w["mid_mt5"] - float(market["bid"]), 3)
if (market or {}).get("bid") else None),
}
except Exception:
pass
# ── G/V mit HL-Kurs, wenn der Broker eingefroren ist (User 2026-08-01) ──
# Bei geschlossenem Markt steht `position.pnl` still. Die Schätzung setzt den
# HL-Kurs (basis-korrigiert, also MT5-Niveau) in `trader.live_pnl()` ein — die
# BEREITS VORHANDENE, korrekte Umrechnung über `tick_size`/`tick_value` inkl.
# Swap und Währungsumrechnung.
# ⚠ Erster Versuch leitete den EUR-je-Punkt-Faktor aus `pnl / Kursbewegung`
# ab — das ergab 155 statt ~86 EUR je 1,00 und Lot, weil `pnl` den über das
# Wochenende aufgelaufenen SWAP enthält. Eine Kennzahl aus einer anderen
# Kennzahl zurückzurechnen ist hier schlicht falsch, wenn zusätzliche Terme
# drinstecken.
# ⚠ Bleibt eine SCHÄTZUNG: HL ist ein anderer Kontrakt (Basis herausgerechnet,
# aber nicht exakt), und der Spread am Montags-Open ist unbekannt.
_pnl_hl = None
try:
if _mkt_closed and _hl_live and position and position.get("ticket"):
_mid = float(_hl_live["mid_mt5"])
_sp = float((market or {}).get("spread") or 0.0)
_v = self.trader.live_pnl(_mid - _sp / 2.0, _mid + _sp / 2.0)
if _v is not None:
_pnl_hl = round(_v, 2)
except Exception:
pass
snap = {
"ts": time.time(),
"connected": self._connected,
@@ -3085,6 +3174,21 @@ class TradingEngine:
# Wirtschaftskalender: {blackout, next, enabled, n} — Anzeige + Grund,
# warum der Bot gerade keine autonome Order eröffnet.
"calendar": self.calendar.snapshot(),
# ── MARKT ZU + HYPERLIQUID-ERSATZKURS (User-Wunsch 2026-08-01) ──────
# Pepperstone handelt WTI nicht am Wochenende; der Snapshot zeigt dann
# den EINGEFRORENEN Freitags-Kurs, ohne dass man es sieht (der grüne
# Punkt im Header ist die VERBINDUNG, nicht der Marktstatus). Hyperliquid
# (`xyz:CL`) läuft dagegen 24/7 weiter.
# `market_closed` wird NICHT aus dem Kalender abgeleitet, sondern aus dem
# ALTER DES LETZTEN TICKS — das deckt Wochenende, Feiertage und
# Broker-Ausfälle mit derselben Regel ab, ohne Kalenderpflege.
# `hl_live.mid_mt5` = HL-Mid + gemessene Basis (HL notiert ~0,45 $ unter
# dem CFD, anderer Kontrakt) → direkt mit dem MT5-Kurs vergleichbar.
"market_closed": _mkt_closed,
"tick_age_s": _tick_age,
"hl_live": _hl_live,
# G/V-Schaetzung auf HL-Basis, nur bei geschlossenem Markt (sonst None)
"pnl_hl": _pnl_hl,
}
# ── Entry-Checkliste je Richtung (User-Vorgabe 2026-07-30: „blende einen
# Hinweis ein, wenn die Checkliste gegen meine Eröffnung ist"). Wird im
+15 -1
View File
@@ -79,7 +79,21 @@ class HLWalls:
# 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")}
"trend": d.get("trend"),
# ── Ergänzt 2026-08-01 (User: „blende den Hyperliquid-
# Kurs neben den Pepperstone-Kurs ein"): der HL-MITTELKURS
# auf MT5-Niveau. HL `xyz:CL` notiert wegen anderem
# Kontrakt/Contango ~0,45 $ unter dem CFD — `basis` ist die
# serverseitig gemessene Differenz (Median über 60
# Snapshots), `mid_mt5` also direkt mit dem Broker-Kurs
# vergleichbar. Nützlich vor allem am Wochenende: HL
# handelt 24/7, Pepperstone nicht.
"hl_mid": (float(d["hl_mid"])
if d.get("hl_mid") is not None else None),
"mid_mt5": (round(float(d["hl_mid"])
+ float(d.get("basis") or 0.0), 3)
if d.get("hl_mid") is not None else None),
"age_s": round(age, 1)}
self._fail_logged = False
except Exception as e:
if not self._fail_logged: # nur 1× melden, nicht jeden Tick