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 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-17 08:44:58 +02:00
co-authored by Claude Opus 5
parent 792355e8f9
commit eec28e3382
2 changed files with 195 additions and 0 deletions
+151
View File
@@ -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())