Vollpruefung aller 87 Skripte + Nachrechnung der verworfenen Modelle

Phase 1 - Lauffaehigkeit: 87 von 87 Skripten laufen fehlerfrei (8000 Bars,
Timeout 900 s). Kein Defekt durch die heutigen wave_rec-Aenderungen. Werkzeug:
run_all_backtests.py, probiert die uneinheitlichen Aufruf-Signaturen adaptiv
durch (Bar-Zahl / TF+Bars / zwei TFs+Bars).

Phase 2a - breakout_k: die dokumentierte Begruendung REPRODUZIERT NICHT.
k=0,3 war mit dem LEGACY_BE10-Exit (Breakeven 1,0) validiert. Erstmals mit dem
kanonischen Exit nachgerechnet (backtest_breakout_canon.py, echte
_confirm_breakout-Mechanik, 80k M5, 2 Halbjahre, Echtkosten):
  k=0,0  H1 -0,180/-569   H2 -0,054/-250
  k=0,3  H1 -0,170/-356   H2 -0,069/-217   (LIVE)
  k=0,5  H1 -0,142/-262   H2 -0,074/-208
  k=0,8  H1 -0,112/-166   H2 -0,032/ -73
  k=1,0  H1 -0,101/-130   H2 -0,004/  -8
k=0,5/0,8/1,0 schlagen den Live-Wert in BEIDEN Haelften (bis +226/+209 SR).

TROTZDEM keine Aenderung empfohlen: (a) alle Werte negativ (PF 0,70-0,99), es
ist eine Wahl zwischen Verlusten; (b) Optimum lag am Rand -> Gegenprobe bis
k=3,0 zeigt kein sauberes Optimum (H2: -8 -> -17 -> -20 -> -19); (c) gemessen
wurde das UNGEGATETE Signal, live laufen min_conf/HTF/Entry-Raum davor. Naechster
Schritt waere eine gegatete Neumessung, bevor ein Gate angefasst wird, das 63 %
der WARTEN erzeugt.

Phase 2b - verworfene Modelle bei vollen Bars: ALLE bleiben verworfen.
14 Klassen (ORB, Doppeltop, Volume Profile, Liquidity Sweeps, Konsolidierung,
Handbuch-Level, Marktstruktur, Momentum, Inter-Market, Chop-Gate,
Reversal-Lockout, S/R-Close-Signal, MACD/ADX/RSI, HTF-Winkel) - keine kippt
ins Positive. Bei ~14 Tests waere bei 5 % Fehlalarmquote ein falsch-positives
Ergebnis zu erwarten gewesen; es gab keins. Regel vorab fixiert.

Kein Freibrief fuer regelmaessiges Nachrechnen - die Begruendung gegen
woechentliche Laeufe steht unveraendert. Anlass war die einmalige
Signalaenderung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-04 12:15:33 +02:00
co-authored by Claude Opus 5
parent 9e6e81dd34
commit 80a5be7fe5
4 changed files with 302 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""Führt ALLE Backtest-/Analyse-Skripte aus und protokolliert das Ergebnis.
Zweck (User-Auftrag 2026-08-04): nach den Änderungen am Signal (Reversal aus,
`angle=`-Befund) prüfen, ob die Skripte noch laufen UND ob ihre Aussagen halten.
⚠ Die Aufruf-Signaturen sind NICHT einheitlich. Die meisten nehmen eine Bar-Zahl
als erstes Argument, einige aber einen Timeframe (`backtest_signal.py M5 3000`,
`backtest_htf_filter.py M5 M30 3000`). Ein Aufruf mit der falschen Signatur
quittiert mit `KeyError`/`ValueError` — das ist KEIN Defekt und darf nicht als
solcher gezählt werden. Deshalb probiert der Runner adaptiv mehrere Varianten
und meldet erst dann einen Fehler, wenn KEINE davon durchläuft.
Aufruf: python run_all_backtests.py [bars] [--only muster]
"""
from __future__ import annotations
import pathlib
import subprocess
import sys
import time
PY = sys.executable
HERE = pathlib.Path(__file__).parent
# Skripte, die kein eigenständiger Lauf sind (Bibliothek/Helfer) oder extrem lange
# laufen und separat gefahren werden.
SKIP = {
"run_all_backtests.py",
}
def varianten(n: int) -> list[list[str]]:
"""Aufruf-Varianten in der Reihenfolge, in der sie probiert werden."""
return [
[str(n)], # Standard: Bar-Zahl
["M5", str(n)], # TF + Bar-Zahl
["M5", "M30", str(n)], # zwei TFs + Bar-Zahl
[], # ohne Argumente (Defaults)
]
def main() -> None:
n = 6000
only = None
args = sys.argv[1:]
if args and args[0].isdigit():
n = int(args[0]); args = args[1:]
if len(args) >= 2 and args[0] == "--only":
only = args[1]
files = sorted(set(list(HERE.glob("backtest_*.py")) + list(HERE.glob("analyze_*.py"))))
files = [f for f in files if f.name not in SKIP]
if only:
files = [f for f in files if only in f.name]
print(f"{len(files)} Skripte · {n} Bars · Start {time.strftime('%H:%M:%S')}")
print("=" * 96)
ok, fail, slow = [], [], []
for i, f in enumerate(files, 1):
t0 = time.time()
erfolg = None
letzter_fehler = ""
for v in varianten(n):
try:
r = subprocess.run([PY, "-X", "utf8", f.name] + v, cwd=HERE,
capture_output=True, text=True, timeout=900,
encoding="utf-8", errors="replace")
except subprocess.TimeoutExpired:
letzter_fehler = "TIMEOUT (>900 s)"
continue
if r.returncode == 0:
erfolg = (v, r.stdout or "")
break
tail = [x for x in (r.stderr or "").splitlines() if x.strip()]
letzter_fehler = tail[-1][:110] if tail else f"exit {r.returncode}"
dt = time.time() - t0
if erfolg:
v, out = erfolg
zeilen = [x for x in out.splitlines() if x.strip()]
sig = " ".join(v) or "(ohne)"
print(f"[{i:>2}/{len(files)}] OK {f.name:<36} {dt:>6.1f}s args={sig}")
ok.append((f.name, sig, dt, zeilen[-1][:100] if zeilen else ""))
if dt > 120:
slow.append((f.name, dt))
else:
print(f"[{i:>2}/{len(files)}] FEHLER {f.name:<36} {dt:>6.1f}s {letzter_fehler}")
fail.append((f.name, letzter_fehler))
print("=" * 96)
print(f"OK: {len(ok)} FEHLER: {len(fail)} (Ende {time.strftime('%H:%M:%S')})")
if fail:
print("\nFEHLGESCHLAGEN:")
for name, err in fail:
print(f" {name:<36} {err}")
if slow:
print("\nLangsam (>120 s):")
for name, dt in sorted(slow, key=lambda x: -x[1]):
print(f" {name:<36} {dt:.0f}s")
if __name__ == "__main__":
main()