From eec28e33823a54ad739637506ef870f540ec255e Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Mon, 17 Aug 2026 08:44:58 +0200 Subject: [PATCH] Pfeil "naechste Kerze" gemessen: Effekt existiert, ist aber 50-100x zu klein User: der Pfeil an der aktuellen Kerze zeigt die Abprall-Wahrscheinlichkeit des S/R -- er soll stattdessen die Richtung der naechsten Kerze schaetzen. Die Beobachtung stimmt und ist Absicht: live steht AR;L;81.815;81.983;62% Abprall -- der L-Pfeil, seit v1.32 bewusst als BEDINGTE, kalibrierte Groesse gebaut (haelt DIESES Level, AUC 0,65 oos) statt als freie Prognose. Trotzdem neu gemessen statt abgelehnt: die bisherigen Richtungs-Messungen liefen ueber 60-360 Minuten, die naechste M5-Kerze ist ein kuerzerer Horizont mit eigener Mikrostruktur. 80k M5-Bars, 2 Halbjahre, 5 Praediktoren: EMA-Trend 49,6 % / 49,6 % Momentum 3 Bars 48,3 % / 48,3 % letzte Kerze fort 48,6 % / 48,8 % letzte Kerze UMKEHR 51,4 % / 51,2 % <- beide KI ueber 50 langer Docht 49,5 % / 49,3 % Es GIBT einen Effekt, und zwar den umgekehrten: 1-Bar-Mean-Reversion trifft beidhaelftig ueber 50 %. Trendfolge und Momentum liegen darunter. ABER die oekonomische Bedingung faellt katastrophal durch: die Oe-Bewegung in Vorhersagerichtung ist +-0,0002 $, der halbe Spread 0,0100 $ -- der Effekt ist 50-100x zu klein. Eine M5-Kerze bewegt sich im Median 0,0400 $, der Spread ist 0,0200 $ = 50 % davon. URTEIL: der Pfeil bleibt. Ein "naechste Kerze"-Pfeil waere technisch wahr und praktisch wertlos -- und muesste nach der Messung GEGEN die letzte Kerze zeigen, was jeder Mensch als Fehler lesen wuerde. EIGENER MESSFEHLER, dokumentiert weil er eine Klasse ist: der erste Lauf zaehlte unveraenderte Schlusskurse als Fehltreffer, wodurch ein Praediktor UND sein Gegenteil beide unter 50 % lagen (43,4 + 45,8 = 89,2) -- arithmetisch unmoeglich und damit der Verraeter. Real sind ~11 % der M5-Kerzen unveraendert. Merksatz: wenn A und Nicht-A sich nicht zu 100 % addieren, ist die Messung kaputt, nicht der Markt. Nichts am Live-System geaendert. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 44 +++++++++++ backtest_naechste_kerze.py | 151 +++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 backtest_naechste_kerze.py diff --git a/CLAUDE.md b/CLAUDE.md index 8795860..9b65235 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6062,6 +6062,50 @@ circuit_breaker` verifiziert, genau eine Instanz je Port. Entscheidung; und die 44 Backtests mit eigener Exit-Kopie (Migrations-Regel: ihre publizierten Zahlen würden sich verschieben). +### ⚠⚠ „DER PFEIL SOLL DIE NÄCHSTE KERZE SCHÄTZEN" — gemessen: geht, zahlt aber nicht (`backtest_naechste_kerze.py`, 2026-08-17) + +User: der Pfeil an der aktuellen Kerze zeigt die Abprall-Wahrscheinlichkeit des +S/R — er soll stattdessen die Richtung der nächsten Kerze schätzen. +✅ **Die Beobachtung stimmt und ist Absicht.** Live steht in der CSV +`AR;L;81.815;81.983;62% Abprall` — der **L**-Pfeil, seit v1.32 (31.07.) bewusst +so gebaut: er zeigt eine **bedingte, kalibrierte** Grösse (hält DIESES Level?, +AUC 0,65 oos) statt einer freien Prognose. +⚠ Trotzdem neu gemessen statt abgelehnt: die bisherigen Richtungs-Messungen +liefen über **60–360 Minuten**. Die **nächste M5-Kerze** ist ein anderer, +kürzerer Horizont — auf Ein-Bar-Ebene gibt es Mikrostruktur-Effekte, die dort +anders wirken können. + +**80k M5-Bars, 2 Halbjahre, 5 Prädiktoren, 95-%-Intervall je Zelle:** +| Prädiktor | H1 Treffer | H2 Treffer | Ø Bewegung in Richtung | +|---|---|---|---| +| EMA-Trend (Wellen-Signal) | 49,6 % | 49,6 % | −0,0008 / +0,0004 $ | +| Momentum 3 Bars | 48,3 % ❌ | 48,3 % ❌ | ~0 | +| letzte Kerze **setzt fort** | 48,6 % ❌ | 48,8 % ❌ | ~0 | +| **letzte Kerze KEHRT UM** | **51,4 % ✅** | **51,2 % ✅** | **−0,0001 / +0,0002 $** | +| langer Docht (Lehrbuch) | 49,5 % | 49,3 % | +0,0010 / −0,0014 $ | + +✅ **Es GIBT einen Effekt — und zwar den umgekehrten:** die 1-Bar-**Mean +Reversion** trifft in BEIDEN Hälften über 50 %, beide Intervalle schliessen die +50 aus. Trendfolge und Momentum sind dagegen **unter** 50. +⚠⚠ **ABER die ökonomische Bedingung fällt katastrophal durch: die Ø-Bewegung in +Vorhersagerichtung ist ±0,0002 $ — der halbe Spread beträgt 0,0100 $.** Der +Effekt ist damit **50–100× zu klein**. Eine M5-Kerze bewegt sich im Median nur +**0,0400 $**, der Spread ist **0,0200 $ = 50 % davon.** +**Trefferquote real, Geld keins:** Mean Reversion fängt viele kleine Rücksetzer +und wird von den wenigen Fortsetzungen überfahren. + +✅ **URTEIL: der Pfeil bleibt, wie er ist.** Ein „nächste Kerze"-Pfeil wäre +**technisch wahr und praktisch wertlos** — und gefährlicher als nützlich, weil er +autoritativ aussieht. ⚠ Dazu käme, dass er nach der Messung **gegen** die letzte +Kerze zeigen müsste (Mean Reversion), was jeder Mensch als Fehler lesen und +übersteuern würde. +⚠⚠ **EIGENER MESSFEHLER auf dem Weg, dokumentiert weil er eine Klasse ist:** der +erste Lauf zählte **unveränderte** Schlusskurse als Fehltreffer — dadurch lagen +ein Prädiktor UND sein eigenes Gegenteil beide unter 50 % (43,4 + 45,8 = 89,2), +was arithmetisch unmöglich ist und den Fehler verriet. Real sind **~11 %** der +M5-Kerzen unverändert. **Merksatz: wenn A und Nicht-A sich nicht zu 100 % +addieren, ist die Messung kaputt, nicht der Markt.** + ### ✅ S/R-AUTO-CLOSE WIEDER AN (User-Entscheidung 2026-08-11) Frage war: „sollten wir Trades bei einem S/R-Bounce automatisch schliessen?" diff --git a/backtest_naechste_kerze.py b/backtest_naechste_kerze.py new file mode 100644 index 0000000..4ea394e --- /dev/null +++ b/backtest_naechste_kerze.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +"""Ist die Richtung der NAECHSTEN Kerze vorhersagbar? (User-Frage 2026-08-17) + +Der Pfeil an der aktuellen Kerze zeigt heute die ABPRALL-Wahrscheinlichkeit des +naechsten S/R-Levels (`AR;L;…`) — so ist er seit v1.32 (31.07.) entworfen. Der +User moechte stattdessen die Richtung der naechsten Kerze. + +⚠⚠ WARUM DAS NICHT EINFACH UMGEBAUT WIRD. Die freie Richtungsfrage ist im +Projekt mehrfach als Muenzwurf gemessen (`analyze_reversal.py`: AUC 0,499-0,509 +out-of-sample und **in-sample nur 0,52** — die Merkmale enthalten keine +Richtungsinformation; Live-Richtung 48-51 % ueber 1.071 Episoden). Genau deshalb +zeigt der Pfeil eine BEDINGTE Groesse (haelt DIESES Level?), die out-of-sample +kalibriert ist (AUC 0,65). + +⚠ ABER: gemessen wurden bisher Horizonte von 60-360 Minuten. Die **naechste +M5-Kerze** ist ein anderer, kuerzerer Horizont — auf Ein-Bar-Ebene gibt es +Mikrostruktur-Effekte (Mean Reversion), die dort anders wirken koennen. Das ist +offen und wird hier gemessen, statt aus Analogie abgelehnt. + +VORAB FIXIERTE REGEL: ein Praediktor taugt, wenn er + (1) in BEIDEN Haelften eine Trefferquote > 50 % hat, + (2) deren 95-%-Intervall die 50 % ausschliesst, und + (3) die mittlere Bewegung in Vorhersagerichtung den SPREAD schlaegt. +Punkt (3) ist die haerteste Huerde: eine M5-Kerze bewegt sich im Median ~0,10 $, +der Spread ist ~0,0225 $ — eine Trefferquote knapp ueber 50 % zahlt ihn nicht. + +Aufruf: python backtest_naechste_kerze.py [bars] +""" +from __future__ import annotations + +import random +import sys + +import MetaTrader5 as mt5 + +from core.squeeze_scan import atr_series + + +def ema(v, p): + k = 2 / (p + 1); o = [v[0]] + for x in v[1:]: + o.append(o[-1] + k * (x - o[-1])) + return o + + +def anteil_ci(tr, n, seed=5, w=2000): + if n < 30: + return None, None + r = random.Random(seed); p = tr / n + b = sorted(sum(1 if r.random() < p else 0 for _ in range(n)) / n for _ in range(w)) + return 100 * b[int(.025 * w)], 100 * b[int(.975 * w)] + + +def main(): + n = int(sys.argv[1]) if len(sys.argv) > 1 else 80000 + mt5.initialize() + sym = next((c for c in ("SpotCrude", "USOIL", "WTI", "XTIUSD") + if mt5.symbol_info(c)), None) + bars = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, n) + point = mt5.symbol_info(sym).point + 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]; O = [float(b["open"]) for b in bars] + SP = [float(b["spread"]) * point for b in bars] + A = atr_series(H, L, C) + ef, es = ema(C, 12), ema(C, 50) + + beweg = sorted(abs(C[i + 1] - C[i]) for i in range(len(C) - 1)) + med_bew = beweg[len(beweg) // 2] + med_sp = sorted(SP)[len(SP) // 2] + print(f"\n {sym} · {len(C)} M5-Bars") + print(f" Median-Bewegung einer M5-Kerze: {med_bew:.4f} $ · Median-Spread " + f"{med_sp:.4f} $ = {100*med_sp/med_bew:.0f} % davon") + print(f" ⚠ Eine Trefferquote muss diesen Anteil SCHLAGEN, nicht nur > 50 % sein.\n") + + # Praediktoren: jeder gibt +1 / -1 / 0 fuer die Richtung der NAECHSTEN Kerze + def p_ema(i): # Trendfolge (das Wellen-Signal) + d = ef[i] - es[i] + return 0 if not A[i] or abs(d) < 0.15 * A[i] else (1 if d > 0 else -1) + + def p_mom3(i): # Momentum-Fortsetzung + m = C[i] - C[i - 3] + return 0 if not A[i] or abs(m) < 0.1 * A[i] else (1 if m > 0 else -1) + + def p_kerze(i): # letzte Kerze setzt sich fort + b = C[i] - O[i] + return 0 if b == 0 else (1 if b > 0 else -1) + + def p_kerze_geg(i): # letzte Kerze kehrt um (Mean Reversion auf 1 Bar) + return -p_kerze(i) + + def p_docht(i): # langer Docht oben -> abwaerts (Lehrbuch) + sp = H[i] - L[i] + if sp <= 0 or not A[i] or sp < 0.5 * A[i]: + return 0 + ob = H[i] - max(O[i], C[i]); un = min(O[i], C[i]) - L[i] + if ob > 2 * un and ob > 0.3 * sp: + return -1 + if un > 2 * ob and un > 0.3 * sp: + return 1 + return 0 + + praed = (("EMA-Trend (Wellen-Signal)", p_ema), + ("Momentum 3 Bars", p_mom3), + ("letzte Kerze setzt fort", p_kerze), + ("letzte Kerze kehrt um", p_kerze_geg), + ("langer Docht (Lehrbuch)", p_docht)) + + mid = len(C) // 2 + print(f" {'Praediktor':<28}{'Haelfte':>8}{'n':>8}{'Treffer':>9}" + f"{'95-%-KI':>16}{'Ø Bew. in Richtung':>20}") + for name, fn in praed: + for lbl, lo, hi in (("H1", 60, mid), ("H2", mid, len(C) - 2)): + tr = ges = unentschieden = 0; summe = 0.0 + for i in range(lo, hi): + if not A[i]: + continue + d = fn(i) + if d == 0: + continue + bew = (C[i + 1] - C[i]) * d + # ⚠⚠ UNVERAENDERTE Schlusskurse AUSSCHLIESSEN. Der erste Entwurf + # zaehlte sie als Fehltreffer — dadurch lagen ein Praediktor UND + # sein eigenes Gegenteil beide unter 50 % (43,4 + 45,8 = 89,2), + # was arithmetisch unmoeglich ist und den Fehler verriet. + # Real sind ~11 % der M5-Kerzen unveraendert. + if bew == 0: + unentschieden += 1 + continue + ges += 1; summe += bew + if bew > 0: + tr += 1 + if ges < 100: + continue + u, o = anteil_ci(tr, ges) + q = 100 * tr / ges + flag = "" if u is None or (u <= 50 <= o) else (" ✅" if u > 50 else " ❌") + print(f" {name:<28}{lbl:>8}{ges:>8}{q:>8.1f}%" + f" [{u:>4.1f} … {o:>4.1f}]{summe/ges:>+17.4f} $" + flag) + print() + + print(" ── LESEHILFE ──────────────────────────────────────────────────") + print(f" Die Spalte ganz rechts ist die Ø-Bewegung in Vorhersagerichtung.") + print(f" Sie muss den halben Spread ({med_sp/2:.4f} $) schlagen, damit ein") + print(f" Trade ueberhaupt kostendeckend waere — und den ganzen ({med_sp:.4f} $)") + print(f" fuer Ein- UND Ausstieg.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())