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:
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())
|
||||
Reference in new issue
Block a user