Files
AH-Oil-Trader/measurement_reminder.py
T
Axel HocksandClaude Opus 4.8 770f994fd1 Timer-Erinnerung fuer alle ausstehenden Messungen + MQL5 v1.26
1) measurement_reminder.py + Task OilMeasurementReminder (taeglich 18:00):
   meldet per Popup, welche Messung genug Daten hat. Jeder Punkt mit PRUEFBARER
   Bedingung (Datenmenge/Datum), Meldung je Punkt 1x (Marker in .reminders/).
   Status: python measurement_reminder.py --status

   Direkt zwei FAELLIGE Punkte gefunden:
   - P(break)-Prognose-Genauigkeit: 2213 ausgewertete Vorhersagen (Schwelle 500),
     lag seit 2026-07-23 unbeachtet herum
   - Chartmuster-Kontrolltest (generischer Breakout): jederzeit machbar

   Noch sammelnd: verdict_votes 24%, AUTOSIG-Trades 0, M30-Level-Validierung 27%,
   M1-History 7%, News-Score 8%.

2) MQL5 v1.26: Beschriftungen UND Pfeile stehen jetzt ganz rechts am Chartrand,
   auf einer Linie mit dem Kurs-Label (User: "genau so angeordnet wie der aktuelle
   Kurs"). RightTime() rechnet das Ende des Shift-Bereichs aus
   (CHART_WIDTH_IN_BARS x InpShiftPct) statt fix 2 Kerzen hinter der letzten Bar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 19:09:01 +02:00

195 lines
8.4 KiB
Python
Raw 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
"""measurement_reminder.py — erinnert per Windows-Popup an FÄLLIGE Messungen.
Im Projekt sammeln mehrere Module still Daten für spätere Auswertungen
(`pbreak_predictions`, `verdict_votes`, `candles_m1`, AUTOSIG-/SQUEEZE-Trades …).
Ohne Erinnerung bleiben die liegen — genau das ist passiert: die P(break)-Prognose-
Genauigkeit war seit 2026-07-23 „später auswertbar" und hatte am 2026-07-30 bereits
2213 ausgewertete Zeilen.
Jeder Eintrag hat eine PRÜFBARE Fälligkeitsbedingung (Datenmenge oder Datum), nicht
nur ein Datum — so wird gemeldet, wenn die Messung wirklich belastbar ist.
Gemeldet wird je Messung genau einmal (Marker-Datei); ist sie erledigt, trägt man
sie hier aus oder löscht den Marker für eine Wiedervorlage.
Nutzung: python measurement_reminder.py (echt, mit Popup)
python measurement_reminder.py --dry-run (Statusliste, kein Popup/Marker)
python measurement_reminder.py --status (alle Punkte + Fortschritt)
"""
from __future__ import annotations
import os
import sqlite3
import sys
from datetime import datetime, timezone
HERE = os.path.dirname(os.path.abspath(__file__))
DB = os.path.join(HERE, "oil_widget_history.db")
MARKER_DIR = os.path.join(HERE, ".reminders")
DRY = "--dry-run" in sys.argv
STATUS = "--status" in sys.argv
def _q(sql: str, default=0):
try:
with sqlite3.connect(DB, timeout=5) as c:
r = c.execute(sql).fetchone()
return r[0] if r and r[0] is not None else default
except Exception:
return default
def _days_since(iso: str) -> float:
try:
return (datetime.now() - datetime.fromisoformat(iso)).total_seconds() / 86400
except Exception:
return 0.0
# ── Die offenen Messungen ────────────────────────────────────────────────────
# have = aktueller Stand · need = Schwelle · unit = Anzeigeeinheit
# cmd = was zu tun ist, wenn fällig
CHECKS = [
{
"key": "pbreak_accuracy",
"title": "P(break)-Prognose-Genauigkeit auswerten",
"why": "Seit 2026-07-23 wird jede Level-Vorhersage mitgeloggt und gegen den "
"echten Ausgang geprüft. Zeigt, ob das Modell live so trifft wie im "
"Backtest (AUC 0,72) — und ob die M30-Umstellung es verbessert hat.",
"have": lambda: _q("SELECT COUNT(*) FROM pbreak_predictions WHERE outcome IS NOT NULL"),
"need": 500, "unit": "ausgewertete Vorhersagen",
"cmd": "GET /api/pbreak_accuracy?period=all · history.pbreak_accuracy()",
},
{
"key": "verdict_votes",
"title": "Verdict-Module auf Prädiktivität prüfen",
"why": "`verdict_votes` loggt seit 2026-07-24 jede Modul-Stimme + Forward-"
"Verlauf. Damit lässt sich datenbasiert entscheiden, welches Modul "
"im Konsens bleibt, welches Gewicht bekommt — und welches raus muss "
"(wie TU 2026-07-06). Auch die Doppelzählung M30/H1 wird hier messbar.",
"have": lambda: _q("SELECT COUNT(*) FROM verdict_votes"),
"need": 25000, "unit": "geloggte Verdicts (~3 Wochen)",
"cmd": "analog analyze_verdict_calibration.py, aber auf verdict_votes",
},
{
"key": "autosig_b4",
"title": "Auto-Signal-Entry: B4-Monitor (trägt er live?)",
"why": "Gemessen NICHT tragfähig (H1 0,05…−0,18, H2 positiv = Regime-Kippen), "
"auf ausdrücklichen Wunsch trotzdem gebaut. Erwartung: im aktuellen "
"Regime ~+0,14 R/Trade, im ungünstigen 0,05. Driftet es negativ weg → "
"`auto_signal=false` (B5-Rückbau).",
"have": lambda: _q("SELECT COUNT(*) FROM trades WHERE setup LIKE 'AUTOSIG%' "
"AND pnl IS NOT NULL"),
"need": 20, "unit": "AUTOSIG-Trades",
"cmd": "Statistik-Tab · setup LIKE 'AUTOSIG%' gegen ØR +0,14 halten",
},
{
"key": "m30_levels",
"title": "M30-Level-Umstellung live gegenprüfen",
"why": "Seit 2026-07-30 kommen S/R-Level + P(break) aus M30 statt M5 "
"(Backtest: R-Ertrag ~2×, AUC 0,721). Live gegenprüfen: bringt der "
"S/R-Auto-Close jetzt mehr pro Trade als vorher?",
"have": lambda: _q("SELECT COUNT(*) FROM trades WHERE closed_by='sr_close' "
"AND pnl IS NOT NULL AND exit_time >= "
"strftime('%s','2026-07-30')"),
"need": 30, "unit": "S/R-Closes seit der Umstellung",
"cmd": "sr_close-Trades vor/nach 2026-07-30 vergleichen (ØEUR, Trefferquote)",
},
{
"key": "patterns_control",
"title": "Chartmuster: Kontrolltest gegen generischen Breakout",
"why": "backtest_patterns.py zeigte positiven ØR — aber die Ziel-Trefferquote "
"ist nur 1338 %, der Ertrag kommt vom Trailing-Exit. Es fehlt die "
"Kontrolle: gleicher Exit auf einem BELIEBIGEN Breakout. Erst danach "
"ist 'Muster wirkt' belegt oder widerlegt.",
"have": lambda: 1, "need": 1, "unit": "jederzeit machbar (nur Rechenzeit)",
"cmd": "backtest_patterns.py um eine Kontrollgruppe erweitern",
},
{
"key": "m1_squeeze",
"title": "M1-Squeeze (Variante B) backtesten",
"why": "Der Squeeze läuft auf M5. Ob M1 früher/besser erkennt, ist offen — "
"braucht genug eigene M1-History (candles_m1, Broker-History rollt weg).",
"have": lambda: (lambda mn, mx: (mx - mn) / 86400 if mn else 0)(
_q("SELECT MIN(time) FROM candles_m1"), _q("SELECT MAX(time) FROM candles_m1")),
"need": 180, "unit": "Tage M1-History",
"cmd": "backtest_breakout_squeeze.py auf M1-Basis aus candles_m1",
},
{
"key": "news_score",
"title": "News-Score auf Prädiktivität prüfen",
"why": "`recommendations.news_score` wird seit 2026-07-24 wieder befüllt. "
"Die Kalibrier-Messung hatte damals zu wenige Fälle (n=4/6). Mit "
"genug Zeilen ist messbar, ob der News-Flow überhaupt etwas vorhersagt "
"— aktuell ist er nur Kontext-Chip.",
"have": lambda: _days_since("2026-07-24"),
"need": 90, "unit": "Tage Sammlung",
"cmd": "Forward-Return nach news_score-Bucket, 2 Hälften",
},
]
def _popup(title: str, text: str) -> None:
if DRY or STATUS:
print(f"[POPUP] {title}\n{text}")
return
try:
import ctypes
ctypes.windll.user32.MessageBoxW(0, text, title, 0x40 | 0x1000)
except Exception as e:
print(f"Popup fehlgeschlagen: {e}\n{text}")
def main() -> None:
os.makedirs(MARKER_DIR, exist_ok=True)
due, pending = [], []
for c in CHECKS:
try:
have = float(c["have"]())
except Exception:
have = 0.0
ready = have >= c["need"]
marker = os.path.join(MARKER_DIR, c["key"])
if ready and (STATUS or DRY or not os.path.exists(marker)):
due.append((c, have))
elif ready:
pending.append((c, have, "gemeldet"))
else:
pending.append((c, have, "sammelt"))
if STATUS or DRY:
print("=" * 78)
print(" AUSSTEHENDE MESSUNGEN — Status")
print("=" * 78)
for c, have in due:
print(f"\n ✅ FÄLLIG: {c['title']}")
print(f" {have:.0f}/{c['need']} {c['unit']}")
print(f" → {c['cmd']}")
for c, have, st in pending:
pct = min(100, have / c["need"] * 100) if c["need"] else 0
print(f"\n{c['title']} [{st}]")
print(f" {have:.0f}/{c['need']} {c['unit']} ({pct:.0f} %)")
if not due:
print("\n (nichts fällig)")
return
if not due:
return
lines = []
for c, have in due:
lines.append(f"• {c['title']}\n {have:.0f}/{c['need']} {c['unit']}\n"
f" {c['why']}\n -> {c['cmd']}")
text = ("Diese Messungen haben jetzt genug Daten:\n\n"
+ "\n\n".join(lines)
+ "\n\nStatus aller Punkte:\n"
" python measurement_reminder.py --status")
_popup(f"Oil-Bot: {len(due)} Messung(en) faellig", text)
for c, _ in due:
try:
open(os.path.join(MARKER_DIR, c["key"]), "w").close()
except Exception:
pass
if __name__ == "__main__":
main()