Automatischer Push: 46 Datei(en) geändert

- .claude/agents/planer.md
- .claude/skills/plan-executer-pruefung/SKILL.md
- .claude/workflows/plankritik.js
- .claude/workflows/planung.js
- .claude/workflows/pruefung.js
- .claude/workflows/verbrauch.js
- .claude/workflows/welle-mit-pruefung.js
- .vault-tools/plan-kopie/LIESMICH.md
- .vault-tools/plan-kopie/plan_zeichen.py
- .vault-tools/plan-kopie/rot_nachweis.py
- .vault-tools/trading-plattform-pruefung/LIESMICH.md
- .vault-tools/trading-plattform-pruefung/abendkontrolle.py
- … und 34 weitere

Erzeugt vom PostToolUse-Hook .vault-tools/auto-push/auto_push.py.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Pascal MichelandClaude Opus 5 committed 2026-09-25 04:00:02 +02:00
1 parent 67d674cd0b
commit ea278dc24f
46 files changed
+8403 -80

No files matched your search

@@ -0,0 +1,204 @@
"""Paket AF ohne Netz: Gegenfall und Gegenprobe der Fortsetzung zum Tagesstart.
Koordinator-Werkzeug, nicht Teil des Repos (Paket AF, 2026-09-24). Faehrt die echte
PaperSitzung des Arbeitsbaums mit der ausgelieferten config/paper.toml und ihrem Schalter
[auto_fortsetzen] gegen einen ProbeBroker, mit echten IEX-Kerzen aus einer JSON-Datei von
hole_kerzen.py (fuer jedes Instrument dieselben), einer ProbeUhr, einem Wegwerf-Register
mit der Neustartregel und einem Melder, der nur sammelt. Kein Netz, keine Order an Alpaca,
das echte Register bleibt unberuehrt.
Drei Faelle:
restposition vor der Oeffnung gestartet, Restposition beim Start -> nicht fortgesetzt
mitten 60 Marktminuten nach der Oeffnung gestartet, flach -> nicht fortgesetzt
flach vor der Oeffnung gestartet, flach -> fortgesetzt (die Gegenprobe)
Exit 0 heisst: Der Fall lief wie erwartet. Jede Abweichung ist eine Zeile BEFUND und Exit 2,
eine fehlende Voraussetzung Exit 3.
Aufruf (WSL, aus dem Arbeitsbaum): python af_gegenfall.py <kerzen.json> <fall>
"""
import json
import sys
import tempfile
from datetime import datetime
from datetime import timedelta
from pathlib import Path
sys.path.insert(0, str(Path.cwd()))
from trading.killswitch.killswitch import Killswitch # noqa: E402
from trading.killswitch.killswitch import lade_killswitch_config # noqa: E402
from trading.paper.agenten import agenten_je_instrument # noqa: E402
from trading.paper.agenten import beschreibung_je_instrument # noqa: E402
from trading.paper.agenten import lade_agent # noqa: E402
from trading.paper.config import enge_limits # noqa: E402
from trading.paper.config import lade_paper_config # noqa: E402
from trading.paper.kerzen import FesteKerzenquelle # noqa: E402
from trading.paper.kerzen import Kerze # noqa: E402
from trading.paper.probe import ProbeBroker # noqa: E402
from trading.paper.sitzung import PaperSitzung # noqa: E402
from trading.paper.uhr import ProbeUhr # noqa: E402
from trading.registry.registry import Registry # noqa: E402
from trading.risk.limits import lade_limits # noqa: E402
FAELLE = ("restposition", "mitten", "flach")
RESTPOSITION = 10
class SammelMelder:
"""Nimmt jede Meldung an und schickt nichts."""
aktiv = True
def __init__(self) -> None:
self.gesehen = []
def melde(self, meldung):
self.gesehen.append(meldung)
return None
class Protokoll:
"""Haelt die Zeilen im Speicher, durch json.dumps wie das echte Protokoll."""
def __init__(self) -> None:
self.zeilen = []
def schreibe(self, eintrag) -> None:
self.zeilen.append(json.loads(json.dumps(eintrag, ensure_ascii=False, default=str)))
def lies_kerzen(pfad: Path) -> list:
roh = json.loads(pfad.read_text(encoding="utf-8"))
return [
Kerze(
zeit=datetime.fromisoformat(k["zeit"]),
open=float(k["open"]),
high=float(k["high"]),
low=float(k["low"]),
close=float(k["close"]),
volume=float(k["volume"]),
)
for k in roh
]
def main() -> int:
if len(sys.argv) != 3 or sys.argv[2] not in FAELLE:
print(f"Aufruf: python af_gegenfall.py <kerzen.json> <{'|'.join(FAELLE)}>")
return 3
pfad, fall = Path(sys.argv[1]), sys.argv[2]
if not pfad.is_file():
print(f"VORAUSSETZUNG FEHLT: {pfad}")
return 3
kerzen = sorted(lies_kerzen(pfad), key=lambda kerze: kerze.zeit)
if not kerzen:
print(f"VORAUSSETZUNG FEHLT: keine Kerze in {pfad}")
return 3
config = lade_paper_config("config/paper.toml")
if not getattr(config, "auto_fortsetzen", False):
print("VORAUSSETZUNG FEHLT: config/paper.toml traegt [auto_fortsetzen] aktiv = true nicht")
return 3
limits = enge_limits(lade_limits("config/risk.toml"), config)
ks_config = lade_killswitch_config("config/killswitch.toml")
quelle = FesteKerzenquelle(kerzen)
if fall == "mitten":
beginn = kerzen[0].zeit + timedelta(minutes=60)
else:
beginn = kerzen[0].zeit - timedelta(minutes=30)
uhr = ProbeUhr(beginn)
rest = config.alle_instrumente[-1]
broker = ProbeBroker(
kerzenquelle=quelle,
uhr=uhr,
instrument_id=config.instrument_id,
instrumente=config.alle_instrumente,
positionen={rest: RESTPOSITION} if fall == "restposition" else None,
)
protokoll = Protokoll()
melder = SammelMelder()
with tempfile.TemporaryDirectory(prefix="af-gegenfall-") as wurzel:
ordner = Path(wurzel)
registry = Registry(db_pfad=ordner / "registry.db", artefakt_wurzel=ordner / "artifacts")
Killswitch.laden(broker=broker, registry=registry, config=ks_config)
agent, beschreibung = lade_agent(config, registry)
sitzung = PaperSitzung(
broker=broker,
kerzenquelle=quelle,
agent=agent,
agent_beschreibung=beschreibung_je_instrument(config, beschreibung),
agenten=agenten_je_instrument(config, agent),
registry=registry,
limits=limits,
killswitch_config=ks_config,
config=config,
uhr=uhr,
protokoll=protokoll,
protokoll_pfad="af-gegenfall.jsonl",
status_pfad=ordner / "status.json",
modus="probe",
soll_abbrechen=lambda: False,
melder=melder,
)
code = sitzung.laufe()
gespeichert = registry.killswitch_zustand()
ereignisse = registry.killswitch_ereignisse()
auto = [z for z in protokoll.zeilen if z.get("was") == "auto_fortsetzen"]
orders = [z for z in protokoll.zeilen if z.get("art") == "order"]
takte = [z for z in protokoll.zeilen if z.get("art") == "takt"]
print(f"Fall {fall}, {len(kerzen)} Kerzen ab {kerzen[0].zeit.isoformat()}, Beginn {beginn.isoformat()}")
print(f"Instrumente: {', '.join(config.alle_instrumente)}")
for zeile in auto:
print(f"auto_fortsetzen {zeile.get('ergebnis')} | {zeile.get('text')} | phase {zeile.get('phase')}")
for meldung in melder.gesehen:
print(f"Meldung [{meldung.prioritaet}] {meldung.titel}")
for zeile in meldung.text.split("\n"):
if zeile.startswith(("Killswitch", "Nicht selbst fortgesetzt", "Ausloeser", "Position")):
print(f" {zeile}")
print(f"Killswitch am Ende: {gespeichert.zustand}, Ausloeser {gespeichert.ausloeser}")
print(f"Killswitch-Ereignisse: {[(e.stufe, e.ausloeser) for e in ereignisse]}")
print(f"Takte {len(takte)}, Orders {len(orders)}, Exit {code}")
befunde = []
titel = [m.titel for m in melder.gesehen]
automatik = [e for e in ereignisse if e.ausloeser == "automatik"]
if fall == "flach":
if [z.get("ergebnis") for z in auto] != ["fortgesetzt"]:
befunde.append(f"auto_fortsetzen {[z.get('ergebnis') for z in auto]} statt ['fortgesetzt']")
if "Paper-Runner laeuft" not in titel or "Paper-Runner wartet" in titel:
befunde.append(f"Meldungen {titel}")
if len(automatik) != 1 or automatik[0].stufe != "fortsetzen":
befunde.append(f"Ereignisse mit Ausloeser automatik: {[(e.stufe, e.grund[:60]) for e in automatik]}")
if not orders:
befunde.append("keine Order -- fortgesetzt, aber nicht gehandelt")
if code != 0:
befunde.append(f"Exit {code} statt 0")
else:
if fall == "restposition":
erwartet = f"Konto nicht flach: {rest} {RESTPOSITION}"
else:
erwartet = "dieser Prozess ist nicht vor der Oeffnung gestartet"
if [z.get("text") for z in auto] != [f"nicht_fortgesetzt: {erwartet}"]:
befunde.append(f"auto_fortsetzen {[z.get('text') for z in auto]}")
wartet = [m for m in melder.gesehen if m.titel == "Paper-Runner wartet"]
if len(wartet) != 1 or f"Nicht selbst fortgesetzt: {erwartet}" not in wartet[0].text.split("\n"):
befunde.append(f"Meldungen {titel}")
if automatik:
befunde.append(f"{len(automatik)} Ereignis(se) mit Ausloeser automatik")
if orders:
befunde.append(f"{len(orders)} Order(s) ohne Fortsetzung")
if any(t.get("killswitch") != "angehalten" for t in takte):
befunde.append("ein Takt nicht angehalten")
erwarteter_code = 4 if fall == "restposition" else 0
if code != erwarteter_code:
befunde.append(f"Exit {code} statt {erwarteter_code}")
for befund in befunde:
print(f"BEFUND: {befund}")
if befunde:
return 2
print("ERGEBNIS: Fall wie erwartet")
return 0
if __name__ == "__main__":
sys.exit(main())