HL-Kurs: Rohkurs anzeigen + Basis bei stehendem Broker einfrieren

User-Fund: app.hyperliquid.xyz zeigt WTIOIL-USDC bei 81,392, das Dashboard
86,424. Dahinter steckten zwei Fehler.

(1) Falsches Label (vom 01.08.): angezeigt wurde mid_mt5 = HL + Basis, also
der auf Pepperstone-Niveau umgerechnete Wert - unter der Beschriftung
"HYPERLIQUID KURS". Jetzt steht dort der ROHE HL-Kurs (exakt die Zahl von der
HL-Seite); das CFD-Aequivalent ist in den Tooltip gewandert.

(2) Die Basis-Korrektur war bei geschlossenem Markt ZIRKULAER. Gegenstueck im
HL-Projekt (eigener Commit): die Basis wird jetzt auf dem letzten Stand mit
lebendem Broker eingefroren und als basis_stale durchgereicht.
Belegt aus leadlag.db: Basis bei lebendem Broker konstant +0,42...+0,58, nach
dem Freeze +5,03. Der Wochenend-Move war echt (So 03:00->05:00 von 85,51 auf
80,84, danach 13 h stabil um 81 bei ~600 Messungen/h).

Materielle Folge: die offene SHORT-Position (0,57 ab 86,163, TP 84,123) wurde
mit -15,02 EUR angezeigt; mit eingefrorener Basis sind es +211 EUR, und der TP
liegt 2,3 $ ueber dem echten Niveau. Vorbehalt bleibt (Oracle-Perp) -> weiter "≈".

Ausserdem dokumentiert: restart_server.bat hat zweimal still NICHT neu
gestartet. Eine korrekt ausgelieferte app.js?v=N belegt NICHTS ueber den
geladenen Python-Code (statische Dateien werden je Request von der Platte
gelesen). Zuverlaessig nur: Prozess killen, direkt starten, danach an einem
NEUEN Snapshot-Feld pruefen.

v=132.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-02 17:42:28 +02:00
co-authored by Claude Opus 5
parent d18d8712db
commit 89e4af4561
5 changed files with 90 additions and 13 deletions
+55 -5
View File
@@ -130,8 +130,16 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
das Oil-Backend im **VORDERGRUND** (Zeile 40) — also **nicht** mit Output-Umleitung das Oil-Backend im **VORDERGRUND** (Zeile 40) — also **nicht** mit Output-Umleitung
aus PowerShell aufrufen (`& .\restart_server.bat *>&1 | …`): der Aufruf blockiert bis aus PowerShell aufrufen (`& .\restart_server.bat *>&1 | …`): der Aufruf blockiert bis
zum Timeout und der abgeschnittene Tail zeigt ein irreführendes „===== Server beendet zum Timeout und der abgeschnittene Tail zeigt ein irreführendes „===== Server beendet
=====", obwohl der Server läuft. Entweder in einem eigenen Fenster starten oder danach =====", obwohl der Server läuft.
gezielt prüfen (`/api/health` + `Get-NetTCPConnection -LocalPort 8000`). Auto-Start: ⚠⚠ **NACHTRAG 2026-08-02: der Batch hat ZWEIMAL still NICHT neu gestartet** (auch als
`Start-Process cmd /c restart_server.bat -WindowStyle Minimized`). Der alte Prozess lief
weiter — und weil statische Dateien bei jedem Request frisch von der Platte gelesen
werden, **belegt eine korrekt ausgelieferte `app.js?v=N` GAR NICHTS** über den geladenen
Python-Code. Zuverlässig ist nur: Port-8000-Prozess killen und direkt starten
(`Start-Process python -ArgumentList '-X','utf8','server.py' -WorkingDirectory …`),
danach **an einem NEUEN Snapshot-Feld prüfen**, dass der Python-Code wirklich neu ist
(hier: `hl_live.basis_stale` fehlte → alter Code entlarvt). Zum Schluss verifizieren,
dass **genau eine** Instanz je Port läuft. Auto-Start:
Aufgabe `OilTradingServer` (Login, versteckt via VBS). Aufgabe `OilTradingServer` (Login, versteckt via VBS).
**Startet seit 2026-07-30 BEIDE Server** (User-Vorgabe): erst das **Startet seit 2026-07-30 BEIDE Server** (User-Vorgabe): erst das
**Hyperliquid-Dashboard** (Port 8001, versteckt via dessen `start_hidden.vbs`, **Hyperliquid-Dashboard** (Port 8001, versteckt via dessen `start_hidden.vbs`,
@@ -2201,7 +2209,7 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
Signal → **Hinweis-Dialog „trotzdem eröffnen?"** (überschreibbar). CLOSE bei Signal → **Hinweis-Dialog „trotzdem eröffnen?"** (überschreibbar). CLOSE bei
**negativem** P&L → Sicherheitsabfrage; im Plus/Breakeven direkt. Sonst keine **negativem** P&L → Sicherheitsabfrage; im Plus/Breakeven direkt. Sonst keine
Token-Dialoge (`require_token=false`). Audio braucht 1× Nutzer-Tap. Token-Dialoge (`require_token=false`). Audio braucht 1× Nutzer-Tap.
- Asset-Version aktuell **v=131** (in `web/index.html` hochzählen, siehe Workflows). - Asset-Version aktuell **v=132** (in `web/index.html` hochzählen, siehe Workflows).
Schriftgrößen 2026-07-24 global **+4px** (2× je +2px auf User-Wunsch; Body-Basis Schriftgrößen 2026-07-24 global **+4px** (2× je +2px auf User-Wunsch; Body-Basis
14→18px). ⚠ Betrifft in `style.css` sowohl `font-size:Npx` (71×) ALS AUCH die 14→18px). ⚠ Betrifft in `style.css` sowohl `font-size:Npx` (71×) ALS AUCH die
`font:<weight> Npx/…`-**Shorthand** (3×: `.ms-chip`/`.ms-bos-lbl`/`.sqm-badge` `font:<weight> Npx/…`-**Shorthand** (3×: `.ms-chip`/`.ms-bos-lbl`/`.sqm-badge`
@@ -2216,8 +2224,50 @@ Liquidität von Hyperliquid einbinden" → nachgeschärft zu „blende den HL-Ku
den Pepperstone-Kurs ein" (ehrlicher: man sieht die Abweichung, statt dass still den Pepperstone-Kurs ein" (ehrlicher: man sieht die Abweichung, statt dass still
getauscht wird) → plus „bei geschlossenem Markt auch den **G/V** mit HL berechnen". getauscht wird) → 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, ### ⚠⚠ Die Basis-Korrektur war am Wochenende ZIRKULÄR (Fix 2026-08-02)
imb5, trend, slope_per_h, vs_mt5}` — IMMER befüllt, nicht nur am Wochenende.
**User-Fund:** „Auf app.hyperliquid.xyz steht WTIOIL-USDC bei 81,392, im Dashboard 86,424."
Beides war erklärbar — und dahinter steckten **zwei** Fehler.
**(1) Falsches Label (mein Fehler vom 01.08.).** Angezeigt wurde `mid_mt5` = HL + Basis,
also der auf Pepperstone-Niveau umgerechnete Wert — unter der Beschriftung „HYPERLIQUID
KURS". Vorher hieß das Feld nur „HL" (unklar, aber nicht falsch); die Ausschreibung machte
daraus eine unzutreffende Behauptung. **Jetzt steht dort der ROHE HL-Kurs** (exakt die
Zahl von der HL-Seite), das CFD-Äquivalent wanderte in den Tooltip.
**(2) Der eigentliche Bug: die Basis saugt bei stehendem Broker den HL-Move auf.**
Die Basis ist `Median(mt5_mid hl_mid)` über die letzten 60 Snapshots — **ohne Prüfung,
ob `mt5_mid` überhaupt noch lebt.** Steht Pepperstone (Wochenende/Feiertag), friert
`mt5_mid` ein, während HL 24/7 weiterläuft → die „Basis" wächst exakt um den HL-Move, und
`hl_mid + basis` rekonstruiert wieder den **EINGEFRORENEN Broker-Kurs**. Die Korrektur
wird zirkulär — also genau dann wertlos, wenn man sie braucht.
**Beleg aus `leadlag.db`** (beide Kurse nebeneinander, 46k Ticks):
| Zeitraum | Basis |
|---|---|
| 27.31.07., Broker **live** | **+0,42 … +0,58** (bemerkenswert stabil) |
| ab 31.07. 22:55 (Freeze), Broker **steht** | 1,07 → **+5,03** |
Real angezeigt: **86,414** statt der echten ~81,89 — Abstand zum eingefrorenen Broker-Bid
nur noch **0,081**. Der Wochenend-Move war dabei ECHT, kein dünnes Buch: So 03:00→05:00
von 85,51 auf 80,84, danach 13 h stabil um 81 bei ~600 Messungen/Stunde.
**Fix (HL-Projekt, `server.py` `/api/walls`):** Der Endpoint sucht jetzt die **letzte
echte Broker-Bewegung** (Rückwärtslauf über `_BASIS_LOOKBACK`=40 000 Zeilen ≈ 2,3 Tage —
muss ein ganzes Wochenende überspannen) und bildet die Basis aus dem Fenster **davor**.
Steht der Broker länger als `_MT5_STALE_S`=180 s (gleiche Schwelle wie
`engine._TICK_STALE_S`), ist die Basis **eingefroren** und der Endpoint liefert
`basis_stale: true` + `mt5_still_s`. Verifiziert an den Echtdaten: letzte Bewegung
31.07. 22:55:06 (= der eingefrorene Tick), Basis **+0,4765** statt +5,0285, Antwortzeit
109 ms (Timeout des Oil-Bots ist 2 s).
**Materielle Folge, nicht nur Kosmetik:** Die offene SHORT-Position (0,57 Lots ab
86,163, TP 84,123) wurde mit **15,02 €** angezeigt; mit eingefrorener Basis sind es
**+211 €**, und der TP lag 2,3 $ ÜBER dem echten Niveau. Die HL-G/V-Schätzung war vorher
zwangsläufig falsch, weil sie auf demselben zirkulären Wert rechnete.
**Vorbehalt bleibt:** HL ist ein Oracle-basierter HIP-3-Perp — dass er 81,3 zeigt, heißt
nicht, dass Pepperstone dort öffnet. Richtung belastbar, Höhe nicht. Deshalb weiter `≈`.
- **Snapshot:** `hl_live {mid_mt5, hl_mid, basis, basis_stale, age_s, bid, ask, bid_sz,
ask_sz, imb5, trend, slope_per_h, vs_mt5}` — IMMER befüllt, nicht nur am Wochenende.
`mid_mt5` = HL-Mid + gemessene Basis (HL `xyz:CL` notiert wegen anderem Kontrakt `mid_mt5` = HL-Mid + gemessene Basis (HL `xyz:CL` notiert wegen anderem Kontrakt
~0,45 $ unter dem CFD; die Basis kommt serverseitig aus dem HL-Projekt als Median ~0,45 $ unter dem CFD; die Basis kommt serverseitig aus dem HL-Projekt als Median
über 60 Snapshots) → direkt mit dem Broker-Kurs vergleichbar. über 60 Snapshots) → direkt mit dem Broker-Kurs vergleichbar.
+4
View File
@@ -3090,6 +3090,10 @@ class TradingEngine:
_hl_live = { _hl_live = {
"mid_mt5": _w["mid_mt5"], "hl_mid": _w.get("hl_mid"), "mid_mt5": _w["mid_mt5"], "hl_mid": _w.get("hl_mid"),
"basis": _w.get("basis"), "age_s": _w.get("age_s"), "basis": _w.get("basis"), "age_s": _w.get("age_s"),
# Basis auf dem letzten Stand mit lebendem Broker eingefroren
# (Fix 2026-08-02, s. `core/hl_walls.py`) — ohne das war die
# Umrechnung bei geschlossenem Markt zirkulär.
"basis_stale": _w.get("basis_stale"),
"bid": _w.get("bid"), "ask": _w.get("ask"), "bid": _w.get("bid"), "ask": _w.get("ask"),
"bid_sz": _w.get("bid_sz"), "ask_sz": _w.get("ask_sz"), "bid_sz": _w.get("bid_sz"), "ask_sz": _w.get("ask_sz"),
"imb5": _w.get("imb5"), "imb5": _w.get("imb5"),
+11
View File
@@ -93,6 +93,17 @@ class HLWalls:
"mid_mt5": (round(float(d["hl_mid"]) "mid_mt5": (round(float(d["hl_mid"])
+ float(d.get("basis") or 0.0), 3) + float(d.get("basis") or 0.0), 3)
if d.get("hl_mid") is not None else None), if d.get("hl_mid") is not None else None),
# ⚠ `basis_stale` (2026-08-02): der Broker tickt nicht mehr,
# die Basis ist auf dem letzten Stand mit LEBENDEM Markt
# eingefroren. Vorher rechnete die Gegenseite die Basis
# blind aus den letzten 60 Snapshots — bei stehendem Broker
# sog sie den kompletten HL-Wochenend-Move auf und
# `hl_mid + basis` ergab wieder den EINGEFRORENEN Broker-
# Kurs (real 86,41 statt 81,89, Differenz zum Broker-Bid
# nur 0,081 = zirkulär). Alles, was daraus abgeleitet wird,
# ist bei `basis_stale` eine SCHÄTZUNG und wird so markiert.
"basis_stale": bool(d.get("basis_stale")),
"mt5_still_s": d.get("mt5_still_s"),
"age_s": round(age, 1)} "age_s": round(age, 1)}
self._fail_logged = False self._fail_logged = False
except Exception as e: except Exception as e:
+18 -6
View File
@@ -500,16 +500,28 @@ function render(d) {
// Hyperliquid daneben: basis-korrigiert (= MT5-Niveau), mit Abweichung im Tooltip. // Hyperliquid daneben: basis-korrigiert (= MT5-Niveau), mit Abweichung im Tooltip.
const hl = d.hl_live || null; const hl = d.hl_live || null;
const hlEl = $("hdr-hl"); const hlEl = $("hdr-hl");
if (hlEl && hl && hl.mid_mt5 != null) { if (hlEl && hl && hl.hl_mid != null) {
// ⚠ Angezeigt wird der ROHE HL-Kurs (Fix 2026-08-02, User-Fund): hier stand
// vorher `mid_mt5` = HL + Basis, also der auf Pepperstone-Niveau umgerechnete
// Wert. Unter der Beschriftung „HYPERLIQUID KURS" ist das schlicht falsch — der
// Nutzer verglich mit app.hyperliquid.xyz (81,392) und sah bei uns 86,424.
// Jetzt steht hier exakt die Zahl, die auch auf der HL-Seite steht; das
// CFD-Äquivalent (für den Broker-Vergleich) wandert in den Tooltip.
const arr = hl.trend === "up" ? "▲" : hl.trend === "down" ? "▼" : ""; const arr = hl.trend === "up" ? "▲" : hl.trend === "down" ? "▼" : "";
hlEl.textContent = fmt(hl.mid_mt5) + (arr ? " " + arr : ""); hlEl.textContent = fmt(hl.hl_mid) + (arr ? " " + arr : "");
const dv = hl.vs_mt5; const dv = hl.vs_mt5;
hlEl.className = d.market_closed ? "hl-price" hlEl.className = d.market_closed ? "hl-price"
: (dv != null && Math.abs(dv) >= 0.15) ? "hl-price" : ""; : (dv != null && Math.abs(dv) >= 0.15) ? "hl-price" : "";
hlEl.title = `Hyperliquid xyz:CL ${fmt(hl.hl_mid)} + gemessene Basis ` hlEl.title = `Hyperliquid xyz:CL — Rohkurs ${fmt(hl.hl_mid)}, `
+ `${hl.basis >= 0 ? "+" : ""}${fmt(hl.basis, 3)} = ${fmt(hl.mid_mt5)} · ` + `${fmt(hl.age_s, 0)} s alt.\n`
+ `${fmt(hl.age_s, 0)} s alt` + `Auf Pepperstone-Niveau: ${fmt(hl.hl_mid)} + Basis `
+ (dv != null ? ` · ggü. Broker ${dv >= 0 ? "+" : ""}${fmt(dv, 3)}` : ""); + `${hl.basis >= 0 ? "+" : ""}${fmt(hl.basis, 3)} = ${fmt(hl.mid_mt5)}`
+ (dv != null ? ` (ggü. Broker ${dv >= 0 ? "+" : ""}${fmt(dv, 3)})` : "")
+ (hl.basis_stale
? `\n⚠ Broker steht — Basis ist auf dem letzten Stand mit lebendem Markt `
+ `eingefroren. Ohne das würde die Umrechnung nur den eingefrorenen `
+ `Broker-Kurs reproduzieren.`
: "");
} else if (hlEl) { } else if (hlEl) {
hlEl.textContent = "—"; hlEl.className = ""; hlEl.title = ""; hlEl.textContent = "—"; hlEl.className = ""; hlEl.title = "";
} }
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="theme-color" content="#0d1117"> <meta name="theme-color" content="#0d1117">
<title>Oil · MT5</title> <title>Oil · MT5</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<link rel="stylesheet" href="style.css?v=131"> <link rel="stylesheet" href="style.css?v=132">
</head> </head>
<body> <body>
<!-- Modul-Menü (ganz oben) --> <!-- Modul-Menü (ganz oben) -->
@@ -249,6 +249,6 @@
<div id="toast" class="toast hidden"></div> <div id="toast" class="toast hidden"></div>
<script src="lightweight-charts.standalone.production.js?v=114"></script> <script src="lightweight-charts.standalone.production.js?v=114"></script>
<script src="app.js?v=131"></script> <script src="app.js?v=132"></script>
</body> </body>
</html> </html>