diff --git a/CLAUDE.md b/CLAUDE.md index bc9e268..6147723 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 aus PowerShell aufrufen (`& .\restart_server.bat *>&1 | …`): der Aufruf blockiert bis 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 - gezielt prüfen (`/api/health` + `Get-NetTCPConnection -LocalPort 8000`). Auto-Start: + =====", obwohl der Server läuft. + ⚠⚠ **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). ⚠ **Startet seit 2026-07-30 BEIDE Server** (User-Vorgabe): erst das **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 **negativem** P&L → Sicherheitsabfrage; im Plus/Breakeven direkt. Sonst keine 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 14→18px). ⚠ Betrifft in `style.css` sowohl `font-size:Npx` (71×) ALS AUCH die `font: 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 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, - imb5, trend, slope_per_h, vs_mt5}` — IMMER befüllt, nicht nur am Wochenende. +### ⚠⚠ Die Basis-Korrektur war am Wochenende ZIRKULÄR (Fix 2026-08-02) + +**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 ~0,45 $ unter dem CFD; die Basis kommt serverseitig aus dem HL-Projekt als Median über 60 Snapshots) → direkt mit dem Broker-Kurs vergleichbar. diff --git a/core/engine.py b/core/engine.py index 03a44ea..ecd89cb 100644 --- a/core/engine.py +++ b/core/engine.py @@ -3090,6 +3090,10 @@ class TradingEngine: _hl_live = { "mid_mt5": _w["mid_mt5"], "hl_mid": _w.get("hl_mid"), "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_sz": _w.get("bid_sz"), "ask_sz": _w.get("ask_sz"), "imb5": _w.get("imb5"), diff --git a/core/hl_walls.py b/core/hl_walls.py index 9f67201..29045fb 100644 --- a/core/hl_walls.py +++ b/core/hl_walls.py @@ -93,6 +93,17 @@ class HLWalls: "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), + # ⚠ `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)} self._fail_logged = False except Exception as e: diff --git a/web/app.js b/web/app.js index da062cd..528e79d 100644 --- a/web/app.js +++ b/web/app.js @@ -500,16 +500,28 @@ function render(d) { // Hyperliquid daneben: basis-korrigiert (= MT5-Niveau), mit Abweichung im Tooltip. const hl = d.hl_live || null; 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" ? "▼" : ""; - hlEl.textContent = fmt(hl.mid_mt5) + (arr ? " " + arr : ""); + hlEl.textContent = fmt(hl.hl_mid) + (arr ? " " + arr : ""); const dv = hl.vs_mt5; hlEl.className = d.market_closed ? "hl-price" : (dv != null && Math.abs(dv) >= 0.15) ? "hl-price" : ""; - hlEl.title = `Hyperliquid xyz:CL ${fmt(hl.hl_mid)} + gemessene Basis ` - + `${hl.basis >= 0 ? "+" : ""}${fmt(hl.basis, 3)} = ${fmt(hl.mid_mt5)} · ` - + `${fmt(hl.age_s, 0)} s alt` - + (dv != null ? ` · ggü. Broker ${dv >= 0 ? "+" : ""}${fmt(dv, 3)}` : ""); + hlEl.title = `Hyperliquid xyz:CL — Rohkurs ${fmt(hl.hl_mid)}, ` + + `${fmt(hl.age_s, 0)} s alt.\n` + + `Auf Pepperstone-Niveau: ${fmt(hl.hl_mid)} + Basis ` + + `${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) { hlEl.textContent = "—"; hlEl.className = ""; hlEl.title = ""; } diff --git a/web/index.html b/web/index.html index 7a5e281..4d3e1eb 100644 --- a/web/index.html +++ b/web/index.html @@ -6,7 +6,7 @@ Oil · MT5 - + @@ -249,6 +249,6 @@ - +