Files
AH-Oil-Trader/analyze_verdict_calibration.py
Axel HocksandClaude Opus 4.8 d61c4a3634 Gesamtempfehlung-Paket: 2 Rollouts, 3 Anzeige-Features, 2 Messungen (v=114)
- Entry-Raum-Gate 0,6→1,0 (Messung lag vor: beide Hälften besser)
- Konfidenz-Kalibrierung gemessen (analyze_verdict_calibration.py): conf
  INVERTIERT zwischen Regimen → keine P(Erfolg)-Aufwertung, kein Konf-Sizing
- verdict_votes-Logging (1×/min) für spätere Copilot/Elliott-Entscheidung
- Order-Dialog zeigt eigenen Ausrichtungs-Split (36/40 Trades ohne Signal: −423€)
- Live-Kosten-Chip (Spread/ATR) im Verdict
- P(break)×Entry-Raum-Freigabe gemessen VERWORFEN (Flip in jeder Schwelle,
  backtest_entryroom_pbreak.py) — 13. verworfener Signal-Eingriff
- News-Konflikt-Chip + recommendations.news_score wieder befüllt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 20:57:56 +02:00

120 lines
5.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Kalibrierung der Gesamtempfehlung (2026-07-24, Idee 2 „Verdict ans eigene
Erfolgsrezept halten"): Ist die angezeigte Konfidenz (conf_pct) PRÄDIKTIV — sagt
conf=80 mehr Erfolg voraus als conf=55? Und: sind die unbelegten Verdict-Stimmen
(KI-Copilot ai_sentiment, news_score) prädiktiv?
Methodik wie beim P(break)-Modell (dem kalibrierten Erfolgsfall):
- `recommendations` (LONG/SHORT) aus der DB, Sampling ≥15 min Abstand
(Autokorrelation dämpfen — die Empfehlung ändert sich minütlich kaum).
- Forward-Return = Signalrichtung × (Close[t+H] Close[t0]) / ATR(M5, t0)
für Horizonte 1 h und 2 h (M5-Bars von MT5; rec.timestamp = lokale Epoch,
Bar-Zeit = Broker-Zeit → 3 h).
- Bins über conf_pct → n / WR(fwd>0) / Ø-fwdR je Bin, in ZWEI Zeitraum-Hälften
(Sign-Stabilität wie immer).
- Copilot: ai_sentiment (LONG/bullish=+1, SHORT/bearish=1) → Ø-fwdR in
Copilot-Richtung (unabhängig vom Wave-Signal!). News: Vorzeichen news_score.
Kalibriert = monoton steigende Ø-fwdR über die conf-Bins, in BEIDEN Hälften.
"""
import sqlite3, datetime as dt
import MetaTrader5 as mt5
_BROKER_OFF = 3*3600
_SAMPLE_S = 15*60 # Mindestabstand zwischen zwei gewerteten Empfehlungen
_H1, _H2 = 12, 24 # Forward-Horizonte in M5-Bars (1 h / 2 h)
_ATR_P = 14
_BINS = [(0,45),(45,55),(55,65),(65,75),(75,85),(85,101)]
def _atr_series(H, L, C, p=_ATR_P):
t=[0.0]
for i in range(1,len(C)): t.append(max(H[i]-L[i],abs(H[i]-C[i-1]),abs(L[i]-C[i-1])))
return [(sum(t[max(1,i-p+1):i+1])/max(1,len(t[max(1,i-p+1):i+1]))) if i else None for i in range(len(C))]
def _dirnum(s):
if not s: return 0
s = str(s).strip().upper()
if s in ("LONG","BULLISH"): return 1
if s in ("SHORT","BEARISH"): return -1
return 0
def line(lbl, rows, key):
v=[r[key] for r in rows]
if not v: return f" {lbl:<22} —"
n=len(v); wr=100*sum(1 for x in v if x>0)/n
return f" {lbl:<22} n={n:>5} WR={wr:>3.0f}% ØR={sum(v)/n:+.3f}"
def main():
mt5.initialize()
sym = next((c for c in ("SpotCrude","USOIL","WTI","XTIUSD") if mt5.symbol_info(c)), None)
bars = None
for req in (100000, 80000, 60000, 40000):
bars = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, req)
if bars is not None and len(bars) > 2000: break
mt5.shutdown()
H=[float(b["high"]) for b in bars]; L=[float(b["low"]) for b in bars]
C=[float(b["close"]) for b in bars]
T=[int(b["time"])-_BROKER_OFF for b in bars] # → echte UTC-Epoch
A=_atr_series(H,L,C)
t2i = {t:i for i,t in enumerate(T)}
con = sqlite3.connect("oil_widget_history.db"); con.row_factory=sqlite3.Row
recs = con.execute(
"SELECT timestamp, signal, conf_pct, ai_sentiment, news_score "
"FROM recommendations WHERE signal IN ('LONG','SHORT') ORDER BY timestamp"
).fetchall()
ev=[]; last_ts=0
for r in recs:
ts=r["timestamp"]
if ts-last_ts < _SAMPLE_S: continue
bar_t = (ts//300)*300 # auf M5-Raster runden
i = t2i.get(bar_t)
if i is None or i+_H2 >= len(C) or not A[i]: continue
last_ts=ts
d = 1 if r["signal"]=="LONG" else -1
atr=max(A[i],0.06)
ev.append(dict(ts=ts, conf=r["conf_pct"] or 0, d=d,
ai=_dirnum(r["ai_sentiment"]),
news=(r["news_score"] if r["news_score"] is not None else None),
f1=(C[i+_H1]-C[i])*d/atr, f2=(C[i+_H2]-C[i])*d/atr))
if not ev:
print("Keine matchbaren Empfehlungen (History-Überlappung prüfen)."); return
mid_ts = ev[len(ev)//2]["ts"]
halves=[("H1 (alt)",[e for e in ev if e["ts"]<mid_ts]),
("H2 (neu)",[e for e in ev if e["ts"]>=mid_ts])]
def span(rows):
return (f"{dt.datetime.fromtimestamp(rows[0]['ts']):%d.%m.%y}"
f"{dt.datetime.fromtimestamp(rows[-1]['ts']):%d.%m.%y}")
print("="*84)
print(f" Verdict-Kalibrierung — {sym}, {len(ev)} gesampelte Signale (≥15 min Abstand)")
print(f" fwd-Return in Signalrichtung, ×ATR(M5) · Horizonte 1 h/2 h")
print("="*84)
for lbl, rows in halves:
print(f"\n{lbl} ({span(rows)}, n={len(rows)}):")
print(" conf_pct-Bins (Horizont 2 h) — kalibriert = ØR steigt monoton:")
for lo,hi in _BINS:
sub=[e for e in rows if lo<=e["conf"]<hi]
print(line(f"conf {lo}{hi-1}", sub, "f2"))
print(" KI-Copilot (eigene Richtung, unabhängig vom Wave-Signal):")
for tag,dd in (("Copilot LONG",1),("Copilot SHORT",-1)):
sub=[dict(f2=e["f2"]*e["d"]*dd) for e in rows if e["ai"]==dd]
print(line(tag, sub, "f2"))
print(" News-Score (Richtung des Scores, |score|≥0.3):")
for tag,dd in (("News bullisch",1),("News bärisch",-1)):
sub=[dict(f2=e["f2"]*e["d"]*dd) for e in rows
if e["news"] is not None and (e["news"]>=0.3 if dd>0 else e["news"]<=-0.3)]
print(line(tag, sub, "f2"))
print("\n Lesart: conf kalibriert → höhere Bins klar bessere ØR in BEIDEN Hälften.")
print(" Copilot/News prädiktiv → 'LONG'-Zeile positiv UND 'SHORT'-Zeile positiv")
print(" (jeweils fwd in der EIGENEN Richtung gemessen).")
if __name__ == "__main__":
main()