diff --git a/CLAUDE.md b/CLAUDE.md index 17a1806..1dd68f2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6161,6 +6161,52 @@ Neumessung. Sie bleiben unverändert und reproduzierbar; wo ihre Schlussfolgerun relevant wird, gehört sie live-treu **neu gerechnet** (Muster: `backtest_auto_signal_norev.py`). +## ⚠⚠ DIE M15-KARTE HAT SICH ZUM ERSTEN MAL SELBST GEMESSEN (2026-08-12) + +Anlass: User-Frage „könntest du mir auch eine **M5** · Setup-Bereitschaft bauen? +Ist das sinnvoll?" — die Antwort hängt daran, ob die BESTEHENDE Karte trägt. +Seit dem 08.08. misst sie sich selbst (`m15_states`), 2.346 ausgewertete Zustände. + +⚠⚠ **DIE KARTE ZEIGTE 60 % — UND DAS WAR IRREFÜHREND.** +| | | +|---|---| +| Anzeige (roh, letzte 200 Zeilen) | **60 %** | +| **entkoppelt** (max 1 je Level+Stunde) | **53–54 %**, KI **[46 … 63 %]** ⚠ enthält 50 | +| davon **grün + gerichtet** (der handlungsrelevante Zustand) | **45,5 %** | +Zwei unabhängige Gründe: +**(1) Geclustert.** `m15_accuracy` nahm `ORDER BY id DESC LIMIT 200` ohne +Entkopplung. Der Zustand wechselt **~990×/Tag** — 200 Zeilen sind rund fünf +Stunden DERSELBEN Marktlage. Aus 2.302 rohen Zeilen bleiben **114 unabhängige**. +**(2) Regime.** Das Messfenster (10.–12.08.) lief **+4,93 $** (78,74 → 83,68). +Roh liest die Karte „auf" zu **62 %** richtig und „ab" zu **46 %** — das ist der +Trend, nicht die Karte. +✅ **BEHOBEN:** `m15_accuracy` entkoppelt jetzt (Schlüssel Level+Stunde, wie +`core.stichprobe.schluessel_level_stunde`) und meldet beide Zahlen — +„114 unabh. Zustände aus 2302". Angezeigt werden jetzt **53 %** statt 60 %. +⚠ Dieselbe Cluster-Falle hat im Projekt schon die Fälligkeitsbedingung +`pbreak_accuracy_v2` um das ZEHNFACHE danebenliegen lassen und am 07.08. elf +Hypothesen ausgelöst. Sie ist hier dieselbe — und sie steckte in einer Zeile, +die ich am 08.08. selbst gebaut habe. + +✅ **ANTWORT AUF DIE M5-FRAGE: NEIN, jetzt nicht.** +· **Die bestehende Karte ist nicht belegt** (KI enthält 50, handlungsrelevanter + Zustand 45,5 %). Eine zweite nach demselben Muster zu bauen, bevor die erste + ein Urteil hat, ist genau das Vorgehen, das dieses Projekt sonst zurückweist. +· **M5 wäre teurer**: Spread/ATR **0,193** gegen 0,104 auf M15 — die Kostenseite + ist der einzige gemessene Vorteil der M15-Karte. +· **Die M5-Information ist grösstenteils schon da**: der Squeeze-Zustand + (armed/active) steht in der Meldungen-Karte, Gate ③b rechnet **bereits auf M5** + (`room_info`), ebenso die P(break)-Merkmale. Die „M15-Karte" ist längst ein + Hybrid (M30-Level · ATR_M15 · M5-Raum/P(break)). +· **Doppelte Drift-Fläche**: allein diese Woche steckten in der EINEN Karte drei + Defekte (Bias als Richtungsgeber · Ziel in der verbotenen Richtung · fehlende + Raum-Zeile). Eine zweite verdoppelt die Stellen für die Veto→Richtung-Falle. +⚠ **OFFEN, benannt:** die Karte **flackert** — ~990 Zustandswechsel/Tag, also +alle 1,5 min. Das ist selbst ein Befund und der nächste sinnvolle Schritt. +✅ **Bedingung für ein Ja:** wenn die entkoppelte Trefferquote bei ≳350 Fällen +(dieselbe Latte wie `pbreak_accuracy_v2`) über 50 % liegt und das Intervall die +Null-Linie ausschliesst. Vorher ist eine zweite Karte Zierde, kein Werkzeug. + ## ✅✅ SYSTEMZUSTAND IM TAGESREPORT (2026-08-11) — der fehlende letzte Meter **Selbständige Empfehlung nach der Gesamtprüfung, vom User beauftragt.** diff --git a/core/history.py b/core/history.py index c2aa751..dd44f28 100644 --- a/core/history.py +++ b/core/history.py @@ -636,26 +636,51 @@ class HistoryLogger: ⚠ Gegen 50 % zu lesen, nicht gegen 0. Und ⚠ die Karte behauptet KEINE freie Richtung — gemessen wird nur, ob die BEDINGTE Lesart am Level - („Abprall -> eher abwaerts") getroffen hat.""" + („Abprall -> eher abwaerts") getroffen hat. + + ⚠⚠ ENTKOPPELT seit 2026-08-12. Vorher nahm die Abfrage schlicht die + letzten N Zeilen (`ORDER BY id DESC LIMIT ?`). Die sind NICHT unabhaengig: + der Zustand wechselt ~990x/Tag, 200 Zeilen sind also rund fuenf Stunden + DERSELBEN Marktlage, vielfach gezaehlt. Die Karte zeigte dadurch **60 %**, + entkoppelt sind es **54,4 %** mit dem Intervall [46 … 63] — also nicht von + der Muenze zu unterscheiden. + ⚠ Dieselbe Cluster-Falle hat im Projekt schon die Faelligkeitsbedingung + `pbreak_accuracy_v2` um das ZEHNFACHE danebenliegen lassen (07.08.) und + elf Hypothesen ausgeloest (07.08.). Sie ist hier dieselbe. + ⚠ Schluessel = (Level auf 2 Stellen, Stunde) — identisch zu + `core.stichprobe.schluessel_level_stunde`, damit beide Auswertungen + dieselbe Definition benutzen. + """ try: with self._lock, self._connect() as conn: - rows = conn.execute(""" - SELECT hit30, hit60, ret30 FROM m15_states + # ⚠ Grosszuegig lesen und DANACH entkoppeln — sonst waeren nach der + # Entkopplung nur noch eine Handvoll Faelle uebrig. + raw = conn.execute(""" + SELECT hit30, hit60, ret30, level, ts FROM m15_states WHERE done=1 AND lesart IN ('auf','ab') ORDER BY id DESC LIMIT ? - """, (int(last_n),)).fetchall() + """, (int(last_n) * 40,)).fetchall() offen = conn.execute( "SELECT COUNT(*) FROM m15_states WHERE done IS NULL " "AND lesart IN ('auf','ab')").fetchone()[0] except Exception: return {"n": 0} + seen, rows = set(), [] + for r in raw: + k = (round(r[3] or 0.0, 2), int((r[4] or 0) // 3600)) + if k in seen: + continue + seen.add(k) + rows.append(r) + if len(rows) >= int(last_n): + break n = len(rows) if not n: - return {"n": 0, "offen": offen} + return {"n": 0, "offen": offen, "roh": len(raw)} h30 = [r[0] for r in rows if r[0] is not None] h60 = [r[1] for r in rows if r[1] is not None] r30 = [r[2] for r in rows if r[2] is not None] - return {"n": n, "offen": offen, + return {"n": n, "offen": offen, "roh": len(raw), "wr30": round(100 * sum(h30) / len(h30)) if h30 else None, "wr60": round(100 * sum(h60) / len(h60)) if h60 else None, "avg30": round(sum(r30) / len(r30), 3) if r30 else None} diff --git a/web/app.js b/web/app.js index 1e6c253..24262a7 100644 --- a/web/app.js +++ b/web/app.js @@ -323,11 +323,19 @@ function renderM15(s) { // ── ⑥ Selbstmessung: die Karte zeigt ihre EIGENE Trefferquote ──────── // ⚠ Gegen 50 % lesen. Gemessen wird die BEDINGTE Lesart am Level, nicht // eine freie Prognose — die ist gemessen unmöglich (AUC 0,52 in-sample). + // ⚠⚠ ENTKOPPELT seit 2026-08-12. Vorher stand hier die ROHE Quote der letzten + // 200 Zeilen — bei ~990 Zustandswechseln/Tag sind das fünf Stunden derselben + // Marktlage, vielfach gezählt. Angezeigt wurden dadurch **60 %**, entkoppelt + // sind es **53 %**. Dieselbe Cluster-Falle hat im Projekt schon eine + // Fälligkeitsbedingung um das Zehnfache danebenliegen lassen. + // ⚠ `n` ist jetzt die ENTKOPPELTE Zahl (max 1 je Level+Stunde), `roh` die + // gelesene — beide werden genannt, damit die Schrumpfung sichtbar bleibt. const tk = s.m15_track || {}; const tEl = $("m15-track"); if (tk.n >= 20 && tk.wr30 != null) { tEl.className = "m15-item " + (tk.wr30 >= 55 ? "ok" : tk.wr30 <= 45 ? "warn" : ""); - tEl.textContent = `⑥ eigene Trefferquote (${tk.n} Zustände): ${tk.wr30} % auf 30′` + tEl.textContent = `⑥ eigene Trefferquote (${tk.n} unabh. Zustände` + + (tk.roh ? ` aus ${tk.roh}` : "") + `): ${tk.wr30} % auf 30′` + (tk.wr60 != null ? ` · ${tk.wr60} % auf 60′` : "") + ` · Ø ${tk.avg30 == null ? "—" : (tk.avg30 >= 0 ? "+" : "") + f(tk.avg30, 3)}×ATR`; } else { diff --git a/web/index.html b/web/index.html index 2c53536..a21e124 100644 --- a/web/index.html +++ b/web/index.html @@ -6,7 +6,7 @@ Oil · MT5 - + @@ -354,6 +354,6 @@ - +