#!/usr/bin/env python3
"""Sagt das Hyperliquid-ORDERBUCH den Pepperstone-Kurs vorher? (User 2026-08-17)

Im Projekt ist das Verdict-Modul „Orderbuch" am 06.08. entmachtet worden (ueber
2.688 Episoden beidhaelftig NEGATIV, 49 % Treffer). Hier wird die Frage neu und
sauber gestellt — auf **196.712 Buch-Schnappschuessen / 17,4 Tagen**, mit dem
MT5-Kurs in derselben Zeile.

⚠⚠ DER ENTSCHEIDENDE KONTROLLTEST (heute erst moeglich): am 17.08. wurde
gemessen, dass **Pepperstone dem HL-Kurs VORAUSLAEUFT** (r(k=-1)=+0,455 gegen
r(k=+1)=+0,020). Ein Orderbuch-Signal, das nur die BEREITS GESCHEHENE
Pepperstone-Bewegung spiegelt, sieht in einer naiven Auswertung praediktiv aus.
Deshalb wird hier IMMER beides berichtet:
    RUECKWAERTS  = Zusammenhang mit der VERGANGENEN MT5-Bewegung  (reaktiv?)
    VORWAERTS    = Zusammenhang mit der KUENFTIGEN               (praediktiv?)
Nur wenn vorwaerts deutlich mehr steht als rueckwaerts, ist es Vorhersage.

WEITERE FALLEN, alle behandelt:
 · **Geschlossener Broker** (~1/3 der Zeit) muss raus — sonst ist die kuenftige
   Rendite strukturell 0 und alles sieht „stabil" aus.
 · **Ueberlappende Fenster**: bei 6-s-Abtastung und 300-s-Horizont teilen sich
   50 Beobachtungen dasselbe Fenster. Das effektive n ist ~n/50 — genau der
   Fehler, der am 07.08. elf Hypothesen ausgeloest hat. -> nicht ueberlappend.
 · **Quintile je HAELFTE** bilden, nicht global — sonst ist die Bucket-Zugehoerig-
   keit mit der Zeit konfundiert (die Lehre aus `analyze_hl_funding.py`).
 · **Oekonomische Huerde**: Pepperstone-Spread ~0,0225 $ auf ~82 $ = **~2,7 bp**.
   Alles darunter ist von den Kosten gefressen, egal wie sauber es aussieht.

Aufruf:  python analyze_orderbook_pred.py
"""
from __future__ import annotations

import math
import random
import sqlite3

DB = r"C:\Users\ah\Downloads\HyperLiquid-WTI Trader\orderflow.db"
_LEBT_S = 180.0
_SPREAD_BP = 2.7
_MERKMALE = ("imb1", "imb5", "imb20")


def lade():
    db = sqlite3.connect(DB)
    sp = ",".join(_MERKMALE)
    rows = db.execute(
        f"SELECT ts,{sp},wall_bid_sz,wall_ask_sz,mt5_mid FROM book "
        "WHERE mt5_mid>0 ORDER BY ts").fetchall()
    letzte, vor, out = None, None, []
    for r in rows:
        mt = r[-1]
        if vor is None or mt != vor:
            letzte = r[0]
        vor = mt
        if letzte is not None and (r[0] - letzte) <= _LEBT_S:
            out.append(r)
    return rows, out


def ci_mittel(xs, n=2000, seed=13):
    if len(xs) < 20:
        return None, None
    r = random.Random(seed); m = len(xs)
    b = sorted(sum(xs[r.randrange(m)] for _ in range(m)) / m for _ in range(n))
    return b[int(.025 * n)], b[int(.975 * n)]


def stichprobe(rows, idx, schritt, zurueck):
    """NICHT ueberlappende Faelle: (merkmal, rendite_vorwaerts_bp,
    rendite_rueckwaerts_bp). `schritt` = Beobachtungen je Horizont."""
    out = []
    for i in range(zurueck, len(rows) - schritt, schritt):
        # Luecken-Schutz: die Zeitspanne muss zum Horizont passen
        if rows[i + schritt][0] - rows[i][0] > schritt * 20:
            continue
        if rows[i][0] - rows[i - zurueck][0] > zurueck * 20:
            continue
        m0, m1, mv = rows[i][-1], rows[i + schritt][-1], rows[i - zurueck][-1]
        if m0 <= 0 or mv <= 0:
            continue
        x = rows[i][idx]
        if x is None:
            continue
        out.append((x, (m1 - m0) / m0 * 1e4, (m0 - mv) / mv * 1e4))
    return out


def quintile(paare, feld):
    """Quintile JE Stichprobe (nicht global) — sonst Zeit-Konfundierung."""
    s = sorted(paare, key=lambda x: x[0])
    k = max(1, len(s) // 5)
    return [s[i * k:(i + 1) * k] for i in range(5)]


def main():
    alle, rows = lade()
    print(f"\n  {len(alle)} Buch-Schnappschuesse · bei LEBENDEM Broker verwertbar: "
          f"{len(rows)} ({100*len(rows)/len(alle):.0f} %)")
    print(f"  Abtastung ~6 s · oekonomische Huerde: Spread ~{_SPREAD_BP} bp\n")
    mid = len(rows) // 2
    haelften = (("H1", rows[:mid]), ("H2", rows[mid:]))

    for mi, name in enumerate(_MERKMALE, start=1):
        print(f"  ══ MERKMAL {name} " + "═" * 52)
        for hor_s, schritt in ((60, 10), (300, 50)):
            print(f"    Horizont {hor_s} s (nicht ueberlappend, {schritt} Beob.)")
            for lbl, teil in haelften:
                pa = stichprobe(teil, mi, schritt, schritt)
                if len(pa) < 100:
                    print(f"      {lbl}: zu wenige Faelle ({len(pa)})"); continue
                qs = quintile(pa, mi)
                unten, oben = qs[0], qs[-1]
                mv_u = sum(p[1] for p in unten) / len(unten)
                mv_o = sum(p[1] for p in oben) / len(oben)
                # RUECKWAERTS-Kontrolle: reagiert das Buch nur auf Vergangenes?
                rb_u = sum(p[2] for p in unten) / len(unten)
                rb_o = sum(p[2] for p in oben) / len(oben)
                dif = [p[1] for p in oben] + [-p[1] for p in unten]
                lo, hi = ci_mittel(dif)
                spanne = (mv_o - mv_u) / 2
                sig = ("⚠ KI enthaelt 0" if lo is None or (lo <= 0 <= hi)
                       else ("✅" if lo > 0 else "❌ invers"))
                print(f"      {lbl}  n={len(pa):>5}  "
                      f"VORWAERTS unten {mv_u:>+6.2f} / oben {mv_o:>+6.2f} bp  "
                      f"→ Spanne {spanne:>+5.2f} bp  "
                      f"[{lo:+.2f} … {hi:+.2f}]  {sig}")
                print(f"            RUECKWAERTS (Kontrolle) unten {rb_u:>+6.2f} / "
                      f"oben {rb_o:>+6.2f} bp  → Spanne {(rb_o-rb_u)/2:>+5.2f} bp")
            print()

    print("  ── LESEHILFE ──────────────────────────────────────────────────")
    print("    · „Spanne\" = halber Abstand zwischen oberstem und unterstem")
    print("      Imbalance-Quintil, in Basispunkten. Das ist das, was ein")
    print("      Handelssignal maximal einbringen koennte.")
    print(f"    · Sie muss den Spread von ~{_SPREAD_BP} bp SCHLAGEN, nicht nur > 0 sein.")
    print("    · Ist die RUECKWAERTS-Spanne aehnlich gross oder groesser, spiegelt")
    print("      das Buch nur die schon geschehene Bewegung — dann ist es")
    print("      REAKTIV, nicht praediktiv (Pepperstone fuehrt HL, s. 17.08.).")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
