#!/usr/bin/env python3 """Laeuft der Hyperliquid-Kurs dem Pepperstone-Kurs VORAUS? (User-Frage 2026-08-17) Wenn HL fuehrt, waere das eine echte Vorhersagequelle — und die einzige im Projekt, die nicht aus dem Pepperstone-Kurs selbst abgeleitet ist. ⚠ Im Projekt steht dazu bereits ein Befund („Pepperstone fuehrt, Peak k=-1 ~ 6 s, HL-Richtung trifft nach 6 s zu 51 %, n=545"). Der stammt aus einer kleinen Stichprobe; hier wird auf **245.000 Ticks / 20,4 Tagen** neu gerechnet. ⚠⚠ DREI FALLEN, die das Ergebnis sonst erzeugen statt messen: (1) **Geschlossener Broker.** 32 % der Zeit steht `mt5_mid` still (Wochenende). Ein stehender Kurs erzeugt zwangslaeufig den Eindruck, HL laufe voraus. -> Nur Ticks, bei denen sich MT5 in den letzten `_LEBT_S` bewegt hat. (2) **Basis-Korrektur.** `hl_mid + basis` ist bei stehendem Broker ZIRKULAER (dokumentiert 02.08.) — sie rekonstruiert den eingefrorenen MT5-Kurs. -> Gerechnet wird auf ROHEN Kursen. (3) **Niveaus statt Renditen.** Zwei kointegrierte Preisreihen korrelieren immer nahe 1 — das sagt nichts ueber Vorlauf. -> Nur RENDITEN, und keine ueber eine Datenluecke hinweg. ⚠ Und die oekonomische Huerde: ein Vorlauf muss die Kosten schlagen. Der Pepperstone-Spread liegt bei ~0,0225 $ auf ~82 $ = **~2,7 bp**. Ein Effekt von 0,2-1,0 bp (so lag der HL-Orderbuch-Befund) ist damit nicht handelbar. Aufruf: python analyze_leadlag_hl.py """ from __future__ import annotations import math import random import sqlite3 import sys DB = r"C:\Users\ah\Downloads\HyperLiquid-WTI Trader\leadlag.db" _LEBT_S = 180.0 # MT5 gilt als „lebt", wenn es sich so lange bewegt hat _MAXLUECKE = 15.0 # Renditen nie ueber groessere Luecken bilden _SPREAD_BP = 2.7 # Pepperstone-Spread ~0,0225 $ auf ~82 $ def lade(): db = sqlite3.connect(DB) rows = db.execute( "SELECT ts,hl_mid,mt5_mid FROM ticks WHERE hl_mid>0 AND mt5_mid>0 " "ORDER BY ts").fetchall() # (1) nur Phasen, in denen der Broker WIRKLICH laeuft letzte_bewegung, vor = None, None lebt = [] for ts, hl, mt in rows: if vor is None or mt != vor: letzte_bewegung = ts vor = mt lebt.append(letzte_bewegung is not None and (ts - letzte_bewegung) <= _LEBT_S) return rows, lebt def renditen(rows, lebt): """(ts, r_hl, r_mt5) — nur bei lebendem Broker und ohne Luecken-Spruenge.""" out = [] for i in range(1, len(rows)): if not (lebt[i] and lebt[i - 1]): continue dt_ = rows[i][0] - rows[i - 1][0] if not (0 < dt_ <= _MAXLUECKE): continue h0, h1 = rows[i - 1][1], rows[i][1] m0, m1 = rows[i - 1][2], rows[i][2] if h0 <= 0 or m0 <= 0: continue out.append((rows[i][0], (h1 - h0) / h0, (m1 - m0) / m0)) return out def korr(a, b): n = len(a) if n < 30: return None ma, mb = sum(a) / n, sum(b) / n va = sum((x - ma) ** 2 for x in a) vb = sum((x - mb) ** 2 for x in b) if va <= 0 or vb <= 0: return None cov = sum((a[i] - ma) * (b[i] - mb) for i in range(n)) return cov / math.sqrt(va * vb) def kreuzkorr(rs, kmax=10): """corr(r_hl[i], r_mt5[i+k]). k>0 = HL FUEHRT · k<0 = Pepperstone fuehrt.""" hl = [x[1] for x in rs]; mt = [x[2] for x in rs] out = [] for k in range(-kmax, kmax + 1): if k >= 0: a, b = hl[:len(hl) - k], mt[k:] else: a, b = hl[-k:], mt[:len(mt) + k] c = korr(a, b) if c is not None: out.append((k, c, len(a))) return out def richtung(rs, vor_n, nach_n, schwelle_bp): """ENTKOPPELT: nicht ueberlappende Fenster. Bewegt sich HL im Fenster deutlich, geht MT5 im NAECHSTEN Fenster in dieselbe Richtung?""" tr = ges = 0 bewegungen = [] schritt = vor_n + nach_n for i in range(0, len(rs) - schritt, schritt): # ⚠ nicht ueberlappend h = sum(rs[i + j][1] for j in range(vor_n)) if abs(h) * 1e4 < schwelle_bp: continue m = sum(rs[i + vor_n + j][2] for j in range(nach_n)) ges += 1 if (h > 0) == (m > 0): tr += 1 bewegungen.append(m * 1e4 * (1 if h > 0 else -1)) # bp in HL-Richtung if ges < 20: return None r = random.Random(11) bs = sorted(sum(1 if r.random() < tr / ges else 0 for _ in range(ges)) / ges for _ in range(2000)) ueberschuss = sum(bewegungen) / len(bewegungen) return {"n": ges, "tr": 100 * tr / ges, "lo": 100 * bs[50], "hi": 100 * bs[1950], "bp": ueberschuss} def main(): rows, lebt = lade() rs = renditen(rows, lebt) print(f"\n {len(rows)} Ticks · davon bei LEBENDEM Broker verwertbar: {len(rs)} " f"({100*len(rs)/len(rows):.0f} %)") print(f" Abtastung ~6 s → ein Lag-Schritt k entspricht ~6 Sekunden\n") mid = len(rs) // 2 for lbl, teil in (("GESAMT", rs), ("H1", rs[:mid]), ("H2", rs[mid:])): kk = kreuzkorr(teil) best = max(kk, key=lambda x: abs(x[1])) k0 = next(c for k, c, _ in kk if k == 0) print(f" ── {lbl} (n={len(teil)}) ──") print(f" gleichzeitig (k=0): r = {k0:+.4f} ← Kontrolle: beide handeln WTI") print(f" staerkster Lag: k = {best[0]:+d} (~{best[0]*6:+d} s) " f"r = {best[1]:+.4f} → " f"{'HL fuehrt' if best[0] > 0 else 'PEPPERSTONE fuehrt' if best[0] < 0 else 'kein Vorlauf'}") zeile = " Profil k=-3..+3: " + " ".join( f"{k:+d}:{c:+.3f}" for k, c, _ in kk if -3 <= k <= 3) print(zeile + "\n") print(" ── RICHTUNGSTEST (entkoppelt, nicht ueberlappend) ──────────────") print(" „HL hat sich bewegt — geht Pepperstone danach mit?\"\n") print(f" {'Vorlauf':>8}{'Horizont':>10}{'Schwelle':>10}{'n':>7}" f"{'Treffer':>9}{'95-%-KI':>18}{'Ø Bewegung':>13}") for vor_n, nach_n in ((1, 1), (1, 2), (2, 2), (5, 5)): for s in (0.5, 1.0, 2.0): r = richtung(rs, vor_n, nach_n, s) if not r: continue flag = "" if r["lo"] <= 50 <= r["hi"] else (" ✅" if r["lo"] > 50 else " ❌") print(f" {vor_n*6:>6}s{nach_n*6:>9}s{s:>9.1f}bp{r['n']:>7}" f"{r['tr']:>8.1f}% [{r['lo']:>5.1f} … {r['hi']:>5.1f}]" f"{r['bp']:>+11.2f}bp{flag}") print(f"\n ⚠ Oekonomische Huerde: der Pepperstone-Spread ist ~{_SPREAD_BP} bp.") print(" Eine Ø-Bewegung darunter ist nicht handelbar, egal wie die " "Trefferquote aussieht.") return 0 if __name__ == "__main__": raise SystemExit(main())