#!/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()