#!/usr/bin/env python3
"""Sucht ungefüllte Kurslücken (Gaps) im WTI-Tageschart 2026.

Gap = Vakuum zwischen Vortag und Folgetag:
  Gap UP   : Low(heute) > High(gestern)  → Lücke [High_gestern … Low_heute]
  Gap DOWN : High(heute) < Low(gestern)  → Lücke [High_heute … Low_gestern]
„Gefüllt" = ein SPÄTERER Bar handelt wieder in/durch das Vakuum (Kurs kehrt zur
Gap-Kante zurück). Ausgegeben werden die NOCH OFFENEN (ungefüllten) Gaps + das
Kursniveau, das sie schließen würde, und der Abstand zum aktuellen Kurs.
"""
import datetime as dt
import MetaTrader5 as mt5

OFF = 3 * 3600   # Broker UTC+3 → UTC (nur für die Datumsanzeige grob)

def main():
    if not mt5.initialize():
        print("MT5 init fehlgeschlagen:", mt5.last_error()); return
    sym = None
    for c in ("SpotCrude", "USOIL", "WTI", "XTIUSD"):
        if mt5.symbol_info(c): sym = c; break
    # genug D1-Bars holen (ganzes Jahr + Puffer)
    bars = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_D1, 0, 400)
    tick = mt5.symbol_info_tick(sym)
    price = (tick.bid + tick.ask) / 2 if tick else None
    mt5.shutdown()
    if bars is None:
        print("Keine Bars"); return

    rows = []
    for b in bars:
        d = dt.datetime.utcfromtimestamp(int(b["time"]) - OFF).date()
        rows.append({"date": d, "o": float(b["open"]), "h": float(b["high"]),
                     "l": float(b["low"]), "c": float(b["close"])})
    rows = [r for r in rows if r["date"].year == 2026]
    if len(rows) < 2:
        print("Zu wenig 2026-Daten"); return

    gaps = []
    for i in range(1, len(rows)):
        p, cur = rows[i - 1], rows[i]
        if cur["l"] > p["h"]:           # Gap UP
            gaps.append({"i": i, "date": cur["date"], "dir": "UP",
                         "lo": p["h"], "hi": cur["l"], "ref_close": p["c"]})
        elif cur["h"] < p["l"]:         # Gap DOWN
            gaps.append({"i": i, "date": cur["date"], "dir": "DOWN",
                         "lo": cur["h"], "hi": p["l"], "ref_close": p["c"]})

    # Fill-Status: handelt ein späterer Bar in das Vakuum zurück?
    for g in gaps:
        later = rows[g["i"] + 1:]
        if g["dir"] == "UP":
            # gefüllt, sobald ein späterer Low <= Gap-Unterkante (zurück in die Lücke)
            mn = min((r["l"] for r in later), default=price or g["hi"])
            g["filled"] = mn <= g["lo"]
            g["fill_target"] = g["lo"]          # Niveau, das die Lücke schließt
            g["partial"] = (not g["filled"]) and mn < g["hi"]
        else:
            mx = max((r["h"] for r in later), default=price or g["lo"])
            g["filled"] = mx >= g["hi"]
            g["fill_target"] = g["hi"]
            g["partial"] = (not g["filled"]) and mx > g["lo"]
        g["size"] = g["hi"] - g["lo"]

    openg = [g for g in gaps if not g["filled"]]
    print("=" * 78)
    print(f"  WTI ({sym}) 2026 — Gap-Analyse (D1)   aktueller Kurs ≈ {price:.2f}"
          if price else f"  WTI ({sym}) 2026 — Gap-Analyse (D1)")
    print(f"  {len(rows)} Handelstage · {len(gaps)} Gaps gesamt · "
          f"{len(openg)} NOCH OFFEN")
    print("=" * 78)
    if not openg:
        print("  Keine offenen Gaps — alle 2026er Lücken wurden gefüllt.")
        return
    print(f"  {'Datum':<12}{'Richtg':<7}{'Lücke von–bis':<18}{'Größe':>7}"
          f"{'Schließt bei':>14}{'Abstand':>10}  Status")
    for g in sorted(openg, key=lambda x: x["date"]):
        dist = (g["fill_target"] - price) if price else 0.0
        tag = "teilw. angelaufen" if g.get("partial") else "unberührt"
        zone = f"{g['lo']:.2f}–{g['hi']:.2f}"
        print(f"  {g['date'].isoformat():<12}{g['dir']:<7}{zone:<18}"
              f"{g['size']:>7.2f}{g['fill_target']:>14.2f}{dist:>+10.2f}  {tag}")
    print("\n  „Schließt bei\" = Kursniveau, das die Lücke füllt · "
          "Abstand = von dort zum aktuellen Kurs (+ = darüber, − = darunter)")

if __name__ == "__main__":
    main()
