"""Mutanten der Abendkontrolle und des Rueckwegs: jeder muss seinen Angriffstreiber rot machen. Koordinator-Werkzeug, nicht fuers Repo. Jeder Mutant laeuft auf einer Wegwerfkopie dieses Verzeichnisses. In WSL (Venv) die Mutanten von abendkontrolle_wsl.py, unter Windows die von abendkontrolle.py und rueckweg_aapl.py. Aufruf: python abendkontrolle_mutanten.py Exit 0, wenn keiner ueberlebt. Ein Mutant, der das Verhalten nicht aendert, ist keine Luecke (AI/Regelwerk/pruefwerkzeuge.md, Abschnitt 8). """ import shutil import subprocess import sys import tempfile from pathlib import Path HIER = Path(__file__).resolve().parent WSL_MUTANTEN = [ ("Exit-Vergleich verkehrt", "abendkontrolle_wsl.py", 'if satz["exit"] != "0":', 'if satz["exit"] == "9":'), ("Handelstag immer wahr", "abendkontrolle_wsl.py", 'return any(eintrag["date"] == tag for eintrag in liste)', "return True"), ("gesperrt nicht geprueft", "abendkontrolle_wsl.py", 'if zeile[0] == "gesperrt":', 'if zeile[0] == "nie":'), ("unbekannter Zustand still", "abendkontrolle_wsl.py", "elif zeile[0] not in KILLSWITCH_ZUSTAENDE:", "elif False:"), ("Broker-Schwelle", "abendkontrolle_wsl.py", "if offen[-1] != 0:", "if offen[-1] > 5:"), ("Probe zaehlt mit", "abendkontrolle_wsl.py", 'glob(f"{tag}T*-paper.jsonl")', 'glob(f"{tag}T*.jsonl")'), ("Maske aus", "abendkontrolle_wsl.py", " text = muster.sub(ersatz, text)", " pass"), ("nie-aktiv aus", "abendkontrolle_wsl.py", 'if satz.get("killswitch") == "aktiv":', "if True:"), ("Bericht nicht geprueft", "abendkontrolle_wsl.py", "if not bericht.is_file():\n k.befund", "if False:\n k.befund"), ("Failed-Zeile aus", "abendkontrolle_wsl.py", 'if "Failed with result" in text:', 'if "nie da" in text:'), ("nur letzter Lauf im Journal", "abendkontrolle_wsl.py", "for nummer, satz in enumerate(enden, start=1):", "for nummer, satz in list(enumerate(enden, start=1))[-1:]:"), ("Melder-Rueckgabe verworfen", "abendkontrolle_wsl.py", 'return "gesendet" if fehler is None else f"nicht gesendet: {fehler}"', 'return "gesendet"'), ("Ende je Protokoll nicht bewertet", "abendkontrolle_wsl.py", " if ende.get(\"grund\") != \"boersenschluss\":\n k.befund(f\"{lauf['datei']}", " if False:\n k.befund(f\"{lauf['datei']}"), ("wartezeit im Journal mitgezaehlt", "abendkontrolle_wsl.py", 'enden = [satz for satz in alle if satz["grund"] != "wartezeit"]', "enden = alle"), ("Lauf nach Schluss als Handelslauf", "abendkontrolle_wsl.py", 'return bool(lauf["ende"]) and lauf["ende"].get("grund") == "wartezeit" and lauf["takte"] == 0', "return False"), ("Fragen nicht voneinander getrennt", "abendkontrolle_wsl.py", " except Exception as ausnahme:\n self.befund(f\"{name} nicht geprueft", " except ZeroDivisionError as ausnahme:\n self.befund(f\"{name} nicht geprueft"), # Seit Paket IB2: der Teil ibkr. ("IBKR Ordner egal", "abendkontrolle_wsl.py", " if not ordner.is_dir():\n k.befund(f\"Betriebsordner", " if False:\n k.befund(f\"Betriebsordner"), ("IBKR Marke egal", "abendkontrolle_wsl.py", ' if marke != "ibkr":', " if False:"), ("IBKR gesperrt egal", "abendkontrolle_wsl.py", 'if zustand == "gesperrt":', 'if zustand == "nie":'), ("IBKR Register nicht gelesen", "abendkontrolle_wsl.py", ' k.schritt("IBKR-Killswitch", pruefe_register_ibkr, ordner, k)', " pass"), ("IBKR Schlussposition egal", "abendkontrolle_wsl.py", ' elif ende.get("position") != 0:', " elif False:"), ("IBKR Lauf ohne ende still", "abendkontrolle_wsl.py", ' if ende is None:\n # Im Alpaca-Teil meldet das', ' if False:\n # Im Alpaca-Teil meldet das'), ("IBKR Zeitgeber zaehlt nicht", "abendkontrolle_wsl.py", 'if not dateien and zeitgeber is not None and zeitgeber != "active":', "if not dateien:"), ("IBKR nicht gestartet als Befund", "abendkontrolle_wsl.py", 'k.hinweis(f"{NICHT_GESTARTET} (', 'k.befund(f"{NICHT_GESTARTET} ('), ("IBKR Positionen egal", "abendkontrolle_wsl.py", " if positionen != 0:", " if positionen > 5:"), ("IBKR Orders egal", "abendkontrolle_wsl.py", " if orders != 0:", " if False:"), ("IBKR fehlende Kontozeile egal", "abendkontrolle_wsl.py", " if positionen is None or orders is None:", " if False:"), ("IBKR Kontonummer sichtbar", "abendkontrolle_wsl.py", 'return maskiere(KONTONUMMER.sub("[konto]", text))', "return maskiere(text)"), ("IBKR Handelsfenster egal", "abendkontrolle_wsl.py", " return not IBKR_SPERRE_VON <= (ny.hour, ny.minute) < IBKR_SPERRE_BIS", " return True"), ("IBKR Journal mit Alpaca-Dienst", "abendkontrolle_wsl.py", "pruefe_journal(text, k, IBKR_DIENST)", "pruefe_journal(text, k)"), ("IBKR Muster aus", "abendkontrolle_wsl.py", " if muster in text:", " if False:"), ("IBKR Titel ohne Praefix", "abendkontrolle_wsl.py", 'IBKR_TITEL = "IBKR "', 'IBKR_TITEL = ""'), ("IBKR Meldeweiche aus", "abendkontrolle_wsl.py", "melde(IBKR_TITEL + titel if ibkr else titel,", "melde(titel,"), ] WIN_MUTANTEN = [ ("keine Ergebnisdatei", "abendkontrolle.py", '(ablage / f"{tag}{endung}.json").write_text(', '(ablage / f"{tag}{endung}.nix").write_text('), ("Wochenende mit", "abendkontrolle.py", "if tag < erster_tag or tag.weekday() >= 5:", "if tag < erster_tag:"), # Seit Paket IB2: der Teil ibkr. ("IBKR in die Alpaca-Datei", "abendkontrolle.py", 'IBKR_ENDUNG = "-ibkr"', 'IBKR_ENDUNG = ""'), ("IBKR mit Alpaca-Kopf", "abendkontrolle.py", ' "ibkr": "Abendkontrolle IBKR (automatisch)",', ' "ibkr": "Abendkontrolle Trading Plattform (automatisch)",'), ("IBKR nicht gestartet unsichtbar", "abendkontrolle.py", ' if fakten.get("nicht_gestartet") and not befunde:', " if False:"), ("IBKR ohne eigenen Starttag", "abendkontrolle.py", "erster_tag=IBKR_ERSTER_TAG, endung=IBKR_ENDUNG", "endung=IBKR_ENDUNG"), ("mit --ohne-wsl beide Teile", "abendkontrolle.py", '(["alpaca"] if argumente.ohne_wsl else list(TEILE))', "list(TEILE)"), ("IBKR-Zeile ohne Konto", "abendkontrolle.py", ' konto = fakten.get("konto") or {}', " konto = {}"), ("Teil ungeprueft", "abendkontrolle.py", 'if ergebnis.get("teil", "alpaca") != teil:', "if False:"), ("heute vor 22:30", "abendkontrolle.py", "if tag == jetzt.date() and jetzt.time() < FRUEHESTENS:", "if False:"), ("Befund ohne Markierung", "abendkontrolle.py", "**{len(befunde)} Befund(e)** #needs-review", "{len(befunde)} Befund(e)"), ("CRLF vergessen", "abendkontrolle.py", 'cr = "\\r" if any(z.endswith("\\r") for z in zeilen) else ""', 'cr = ""'), ("Formpruefung aus", "abendkontrolle.py", 'if not isinstance(ergebnis, dict) or not isinstance(ergebnis.get("befunde"), list):', "if False:"), ("Ueberschrift nur exakt", "abendkontrolle.py", "if z == UEBERSCHRIFT or z.startswith(UEBERSCHRIFT + \" \")), None)", "if z == UEBERSCHRIFT), None)"), ("Abbruch gilt als erledigt", "abendkontrolle.py", 'if ergebnis.get("exit") != 2:', "if True:"), ("nicht atomar", "abendkontrolle.py", " os.replace(neu, notiz)", " notiz.write_bytes(text.encode(\"utf-8\"))"), ("Zeit egal", "rueckweg_aapl.py", "if jetzt < FRUEHESTENS:", "if False:"), ("Runner egal", "rueckweg_aapl.py", " if runner_aktiv(runner_vorgabe):", " if False and runner_aktiv(runner_vorgabe):"), ("unlesbar gilt als steht", "rueckweg_aapl.py", 'raise RuntimeError(f"Zustand des Runners nicht lesbar: {zustand!r}")', "return False"), ("schmutziger Ordner egal", "rueckweg_aapl.py", " else:\n if geaendert:", " else:\n if False:"), ("folge egal", "rueckweg_aapl.py", 'if MEHRERE not in ziel.read_text(encoding="utf-8"):', "if False:"), ("Ergebnisdatei egal", "rueckweg_aapl.py", 'if (ablage / "rueckweg.json").exists():', "if False:"), ("kein Commit", "rueckweg_aapl.py", '("add", ZIEL.as_posix()), ("commit", "-m", NACHRICHT)', '("add", ZIEL.as_posix()),'), ("halb getauscht gilt als fertig", "rueckweg_aapl.py", " if not geaendert:\n return \"config/paper.toml ist schon", " if True:\n return \"config/paper.toml ist schon"), ("Schluesselvergleich aus", "rueckweg_aapl.py", " if fehlend:", " if False:"), ("Zweig egal", "rueckweg_aapl.py", " if zweig != ZWEIG:", " if False:"), ("Wertevergleich aus", "rueckweg_aapl.py", " elif neu[name] != alt[name]:", " elif False:"), ("Gegenrichtung aus", "rueckweg_aapl.py", " elif name not in alt:", " elif False:"), ] TREIBER = { "abendkontrolle_wsl.py": "abendkontrolle_angriffe.py", "abendkontrolle.py": "abendkontrolle_angriffe.py", "rueckweg_aapl.py": "rueckweg_aapl_angriffe.py", } mutanten = WIN_MUTANTEN if sys.platform == "win32" else WSL_MUTANTEN ueberlebt = [] for name, datei, alt, neu in mutanten: with tempfile.TemporaryDirectory() as tmp: ziel = Path(tmp) / "pruefung" shutil.copytree(HIER, ziel, ignore=shutil.ignore_patterns("__pycache__")) pfad = ziel / datei text = pfad.read_text(encoding="utf-8") if text.count(alt) != 1: print(f"ANKER {text.count(alt)}-mal: {name}") ueberlebt.append(name) continue pfad.write_text(text.replace(alt, neu), encoding="utf-8", newline="\n") lauf = subprocess.run([sys.executable, str(ziel / TREIBER[datei])], capture_output=True, text=True, timeout=1800, encoding="utf-8", errors="replace", cwd=tmp) faelle = [z for z in lauf.stdout.splitlines() if z.startswith("FEHL")] rot = lauf.returncode != 0 print(f"{'ROT (gefangen)' if rot else 'GRUEN (UEBERLEBT)'} {name}: {[f[:110] for f in faelle[:2]]}") if not rot: ueberlebt.append(name) print(f"ERGEBNIS: {len(ueberlebt)} ueberlebt von {len(mutanten)} {ueberlebt}") sys.exit(1 if ueberlebt else 0)