KI-Buch (Illab) ausgewertet: Workflow statt Signale, echte Luecke ist die Validierungsmethode

Zweites eingereichtes Buch, anderes Genre: Co-Piloten, No-Code-Bots, Scanner,
Agenten-Pipelines. Triage gegen den Projektstand: 10 von 12 Kapitel-Konzepten
sind gebaut (Copilot, Circuit-Breaker, Logs, Slippage-Messung, Dedup,
Tagesreport, Provider-Fallback) oder gemessen verworfen (Muster, Momentum,
Sentiment). Alt-Daten und Rebalancing sind fuer einen WTI-CFD gegenstandslos.

DIE EINE ECHTE LUECKE: Kap. 9.2 empfiehlt WALK-FORWARD, dieses Projekt prueft
mit ZWEI STICHPROBEN -- das Wort kommt in CLAUDE.md nie vor. Zwei Haelften haben
genau zwei Vorzeichen; ein Setup, das in der Haelfte aller Zeitbloecke negativ
ist, kann trotzdem 'beidhaelftig positiv' aussehen. Genau so ist das
P(break)-Modell ausgefallen (H1->H2 AUC 0,715, live 0,368).

Gemessen am Squeeze, gleiche Daten/Regel/Exit, variiert nur die Zeitaufteilung:
  2 Haelften: H1 -0,134 / H2 +0,036
  8 Bloecke:  - - - + + + - +   = 4 von 8 positiv, Spanne -0,301 .. +0,214
Die einzigen zwei Bloecke mit KI ohne Null sind BEIDE negativ. Kein Block ist
gesichert positiv. Die Haelften-Sicht verdeckt nichts, zeigt aber auch nichts.

Keine Code-Aenderung: die 2-Stichproben-Regel bleibt die Mindesthuerde (sie hat
28 Eingriffe korrekt abgewiesen), ist aber eine Aussage ueber zwei Regime, nicht
ueber Stabilitaet ueber die Zeit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-19 08:36:11 +02:00
co-authored by Claude Opus 5
parent adda5b0ccc
commit 5eec4517f8
4 changed files with 221 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
"""WALK-FORWARD statt 2 Stichproben -- versteckt unsere Methode Instabilitaet?
(User-Auftrag 2026-08-19: "nutze das PDF fuer neue Ideen", Illab Kap. 9.2)
DIE IDEE AUS DEM BUCH. Es empfiehlt fuer JEDEN neuen Indikator einen
**Walk-Forward-Backtest** (rollierendes Fenster, Zeitreihen-Kreuzvalidierung)
statt eines einmaligen Splits -- ausdruecklich gegen Overfitting. Dieses Projekt
prueft dagegen seit Monaten mit **ZWEI Haelften** ("2-Stichproben-Regel").
⚠⚠ WARUM DAS EINE ECHTE LUECKE IST, und nicht bloss eine andere Schreibweise:
zwei Haelften haben genau ZWEI Vorzeichen. Ein Setup, das in 4 von 8
aufeinanderfolgenden Bloecken negativ ist, kann trotzdem "beidhaelftig positiv"
aussehen -- es reicht, wenn die Gewinner in beiden Haelften gleich verteilt
liegen. Die Regel wuerde es durchwinken.
Das ist keine Theorie: der Live-Ausfall des P(break)-Modells (31.07. auf H1
gefittet, auf H2 mit AUC 0,715 bestaetigt -- dann live 0,368) ist exakt dieser
Fall. Der Split hat gehalten, die Zeit nicht.
WAS HIER GEMESSEN WIRD. Der **Squeeze** -- das einzige live laufende,
2-Stichproben-validierte Setup. Dieselben Daten, dieselbe Regel, derselbe Exit;
variiert wird NUR die Aufteilung der Zeitachse:
(a) 2 Haelften = die Projekt-Methode
(b) 8 Bloecke = walk-forward-artig, ~35 Tage je Block
Wenn (b) dasselbe Bild zeigt, ist die 2-Stichproben-Regel fuer diesen Fall
ausreichend und der Buch-Einwand traegt hier nicht. Zeigt (b) kippende
Vorzeichen, ist unsere Latte zu niedrig -- und zwar rueckwirkend fuer ALLES,
was je nach ihr freigegeben wurde.
AUFBAU. Echte `core.squeeze_scan`-Regel (kein Nachbau), LIVE-Parameter, Fill bei
BERUEHRUNG (die seit 06.08. als einzig ehrliche belegte Annahme), kanonischer
Exit `exit_model.LIVE`, Echtkosten aus dem Bar-Spread, 95-%-Bootstrap je Zelle.
⚠ GRENZE, vorab benannt: die Bloecke sind kuerzer, also ist jedes einzelne KI
breiter -- ein Block mit KI ueber der Null ist NICHT dasselbe wie eine Haelfte
mit KI ueber der Null. Ausgewertet wird deshalb das **Vorzeichenmuster** und die
Frage, ob die Haelften-Sicht es verdeckt, nicht der Einzelblock.
Aufruf: python backtest_walkforward.py [bars] [bloecke]
"""
import sys
import MetaTrader5 as mt5
from backtest_cost_gate import kennzahlen
from core.exit_model import LIVE, simulate
from core.squeeze_scan import atr_series, scan
# LIVE-Parameter (nicht die Modul-Defaults -- der ATR-Floor wurde gemessen von
# 0,12 auf 0,06 gesenkt; ihn hier zu vergessen erzeugt eine ANDERE Population,
# real passiert am 13.08.).
BOX_N = 10
K = 0.1
MULT = 2.5
ATRMIN = 0.06
def lauf(H, L, C, A, SP, lo, hi, ende):
"""Squeeze-Einstiege in [lo,hi), Fill bei Beruehrung, kanonischer Exit."""
entries = []
scan(H, L, C, A, SP, 1.0, lo, hi, MULT,
box_n=BOX_N, k=K, atr_min=ATRMIN,
exit_fn=lambda *a: 0.0,
on_entry=lambda j, d, lvl, atr, komp: (
entries.append((j, d, lvl, atr)) if komp else None))
rs = []
for j, d, lvl, atr in entries:
# ruhende Stop-Order am Level: fuellt, sobald der Kurs es BERUEHRT
f = None
for x in range(j, min(j + 13, ende)):
if (H[x] >= lvl) if d > 0 else (L[x] <= lvl):
f = x
break
if f is None or f + 1 >= ende:
continue
r = simulate(lvl, d, atr, H, L, C, f + 1, LIVE, timestop=True, use_tp=True)
rs.append(r - ((SP[f] if SP[f] > 0 else 0.0225) / atr))
return rs
def zeile(lbl, k):
if not k:
print(" {:<22} --".format(lbl))
return None
ci = k.get("ci")
m = ""
if ci:
m = (" [{:+.3f} .. {:+.3f}]".format(ci[0], ci[1])
+ ("" if ci[0] * ci[1] > 0 else " ~0"))
print(" {:<22} n={:>4} WR={:>3.0f}% OeR={:+.3f} PF={:.2f}{}"
.format(lbl, k["n"], k["wr"], k["oer"], k["pf"], m))
return k["oer"]
def main():
n = int(sys.argv[1]) if len(sys.argv) > 1 else 80000
nb = int(sys.argv[2]) if len(sys.argv) > 2 else 8
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]
SP = [float(b["spread"]) * point for b in bars]
A = atr_series(H, L, C)
N = len(C); ende = N - LIVE.max_hold - 1
print("=" * 92)
print(" WALK-FORWARD gegen 2-STICHPROBEN -- Squeeze, {} M5 ({} Bars)".format(sym, N))
print(" Fill bei BERUEHRUNG - kanonischer Exit - Echtkosten - 95-%-KI")
print("=" * 92)
print("\n (a) DIE PROJEKT-METHODE: 2 Haelften")
print(" " + "-" * 88)
mid = N // 2
zeile("H1", kennzahlen(lauf(H, L, C, A, SP, 0, mid, ende)))
zeile("H2", kennzahlen(lauf(H, L, C, A, SP, mid, ende, ende)))
print("\n (b) WALK-FORWARD: {} aufeinanderfolgende Bloecke (~{} Tage)"
.format(nb, round(ende / nb * 5 / 1440)))
print(" " + "-" * 88)
vz = []
for b in range(nb):
lo = int(b * ende / nb); hi = int((b + 1) * ende / nb)
o = zeile("Block {}/{}".format(b + 1, nb),
kennzahlen(lauf(H, L, C, A, SP, lo, hi, ende)))
if o is not None:
vz.append(o)
pos = sum(1 for x in vz if x > 0)
print("\n " + "=" * 88)
print(" Bloecke mit positivem OeR: {} von {}".format(pos, len(vz)))
print(" Vorzeichenfolge: "
+ " ".join("+" if x > 0 else "-" for x in vz))
if vz:
print(" Spanne OeR: {:+.3f} .. {:+.3f}"
.format(min(vz), max(vz)))
print(" " + "=" * 88)
print("\n LESART: 2 Haelften kennen nur ZWEI Vorzeichen. Kippen die Bloecke,")
print(" ist 'beidhaelftig positiv' erfuellbar, ohne dass das Setup stabil ist.")
if __name__ == "__main__":
main()