Automatischer Push: 32 Datei(en) geändert

- .claude/agents/executer.md
- .claude/agents/planer.md
- .claude/agents/pruefer.md
- .claude/agents/recherche.md
- .claude/skills/plan-executer-pruefung/SKILL.md
- .claude/workflows/executer-welle.js
- .claude/workflows/plankritik.js
- .claude/workflows/pruefung.js
- .claude/workflows/welle-mit-pruefung.js
- .gitignore
- .vault-tools/regelwerk-check/ENTWURF.md
- .vault-tools/regelwerk-check/regelwerk_check.py
- … und 20 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-24 04:00:02 +02:00
1 parent d7ffc9a5b5
commit 67d674cd0b
32 files changed
+1618 -88

No files matched your search

@@ -0,0 +1,182 @@
"""Lesender Probeaufruf fuer Paket M: GET /v2/assets, trades/latest und bars mit mehreren Symbolen.
Koordinator-Werkzeug, nicht Teil des Repos. Anlass: Paket M (zehn Aktien, 2026-09-25) haengt an drei
Antwortformen, die im Projekt nie gemessen wurden -- GET /v2/assets/{symbol}, GET /v2/stocks/trades/latest
mit mehreren Symbolen und das Seitenverhalten von GET /v2/stocks/bars mit mehreren Symbolen. Das Werkzeug
belegt sie an genau dem Code, der sie liest (AlpacaBroker.anlage, AlpacaBroker.letzte_kurse,
AlpacaKerzenquelle.kerzen_mehrerer), und zusaetzlich roh: Schluessel und Typen, nie Werte ausser den
Wahrheitswerten der Startpruefung, Zaehlungen und dem Alter des letzten Trades.
Nur GET, kein schreibender Aufruf. Liest die Geheimnisdatei ueber den Aufloeser des Projekts
(TRADING_GEHEIMNISSE, sonst config/secrets.toml) und gibt die Schluessel nie aus.
Aufruf (WSL, aus einem Arbeitsbaum mit Paket M, Venv):
python m_probeaufruf.py <handelstag JJJJ-MM-TT> <kennung> [<kennung> ...]
Exit 0: alle Formen wie angenommen. 2: eine Abweichung oder ein Starthindernis. 1: Ausnahme.
"""
import sys
import tomllib
from datetime import UTC
from datetime import datetime
from pathlib import Path
import requests
sys.path.insert(0, str(Path.cwd()))
from trading import geheimnisse # noqa: E402
from trading.broker.alpaca import DATEN_URL # noqa: E402
from trading.broker.alpaca import TRADING_URL # noqa: E402
from trading.broker.alpaca import AlpacaBroker # noqa: E402
from trading.broker.schnittstelle import zu_symbol # noqa: E402
from trading.paper.kerzen import HANDELSZONE # noqa: E402
from trading.paper.kerzen import AlpacaKerzenquelle # noqa: E402
from trading.paper.kerzen import handelsbeginn # noqa: E402
from trading.paper.kerzen import handelsende # noqa: E402
FEED = "iex"
# Klein genug, dass zehn Symbole mal 390 Minuten mehrere Seiten brauchen.
KLEINE_SEITE = 1000
MAX_SEITEN = 50
def typ(wert) -> str:
return type(wert).__name__
def iso_z(zeitpunkt: datetime) -> str:
return zeitpunkt.astimezone(UTC).isoformat().replace("+00:00", "Z")
def zaehle_seiten(sitzung, kopf: dict, parameter: dict) -> tuple[dict[str, int], int, int]:
"""Kerzen je Symbol ueber alle Seiten, Zahl der Seiten, Seiten mit mehr als einem Symbol."""
parameter = dict(parameter)
je_symbol: dict[str, int] = {}
seiten = 0
gemischt = 0
while seiten < MAX_SEITEN:
antwort = sitzung.get(f"{DATEN_URL}/v2/stocks/bars", headers=kopf, params=parameter, timeout=30)
seiten += 1
if antwort.status_code != 200:
raise RuntimeError(f"GET /v2/stocks/bars antwortete {antwort.status_code}")
nutzlast = antwort.json()
bars = nutzlast.get("bars") or {}
if len(bars) > 1:
gemischt += 1
for symbol, liste in bars.items():
je_symbol[symbol] = je_symbol.get(symbol, 0) + len(liste or [])
token = nutzlast.get("next_page_token")
if not token:
break
parameter["page_token"] = token
return je_symbol, seiten, gemischt
def main() -> int:
if len(sys.argv) < 3:
print(__doc__)
return 1
datum = sys.argv[1]
kennungen = sys.argv[2:]
symbole = [zu_symbol(kennung) for kennung in kennungen]
werte = tomllib.loads(geheimnisse.pfad().read_text(encoding="utf-8"))["alpaca"]
kopf = {"APCA-API-KEY-ID": werte["key_id"], "APCA-API-SECRET-KEY": werte["secret_key"]}
sitzung = requests.Session()
sitzung.trust_env = False
abweichungen = 0
# 1. GET /v2/assets roh fuer das erste Symbol: nur Schluessel und Typen.
antwort = sitzung.get(f"{TRADING_URL}/v2/assets/{symbole[0]}", headers=kopf, timeout=20)
print(f"GET /v2/assets/{symbole[0]}: HTTP {antwort.status_code}")
roh = antwort.json() if antwort.status_code == 200 else {}
if isinstance(roh, dict):
print(f" schluessel und typen: {dict((k, typ(v)) for k, v in sorted(roh.items()))}")
for feld, erwartet in (("symbol", "str"), ("tradable", "bool"), ("shortable", "bool"), ("easy_to_borrow", "bool")):
gefunden = typ(roh.get(feld)) if isinstance(roh, dict) and feld in roh else "fehlt"
if gefunden != erwartet:
print(f"ABWEICHUNG: Feld {feld} ist {gefunden}, erwartet {erwartet}")
abweichungen += 1
# 2. anlage() je Instrument ueber den Code des Projekts.
broker = AlpacaBroker(
werte["key_id"], werte["secret_key"], universum=tuple(kennungen), feed=FEED, timeout=20.0
)
for kennung in kennungen:
anlage = broker.anlage(kennung)
print(
f"anlage {kennung}: tradable {anlage.handelbar}, shortable {anlage.leerverkaufbar}, "
f"easy_to_borrow {anlage.leicht_leihbar}"
)
if not (anlage.handelbar and anlage.leerverkaufbar and anlage.leicht_leihbar):
print(f"STARTHINDERNIS: {kennung} haelt die Startpruefung einer Folge mit Short nicht")
abweichungen += 1
# 3. trades/latest roh und ueber den Code.
antwort = sitzung.get(
f"{DATEN_URL}/v2/stocks/trades/latest",
headers=kopf,
params={"symbols": ",".join(symbole), "feed": FEED},
timeout=20,
)
print(f"GET /v2/stocks/trades/latest mit {len(symbole)} Symbolen: HTTP {antwort.status_code}")
roh = antwort.json() if antwort.status_code == 200 else {}
trades = roh.get("trades") if isinstance(roh, dict) else None
print(f" schluessel oben: {sorted(roh) if isinstance(roh, dict) else typ(roh)}, trades ist {typ(trades)}")
if not isinstance(trades, dict) or sorted(trades) != sorted(symbole):
gesehen = sorted(trades) if isinstance(trades, dict) else typ(trades)
print(f"ABWEICHUNG: trades traegt {gesehen} statt {sorted(symbole)}")
abweichungen += 1
kurse = broker.letzte_kurse(kennungen)
jetzt = datetime.now(UTC)
for kennung in kennungen:
kurs = kurse.get(kennung)
alter = "keiner" if kurs is None else f"vor {(jetzt - kurs.zeit).total_seconds():.0f} s"
print(f"letzter trade {kennung}: {alter}")
# 4. Kerzen des Handelstags: gebuendelt ueber den Code gegen einzeln ueber den Code.
mitternacht = datetime.strptime(datum, "%Y-%m-%d").replace(tzinfo=HANDELSZONE)
bis = handelsende(mitternacht)
quelle = AlpacaKerzenquelle(werte["key_id"], werte["secret_key"], feed=FEED, timeout=30.0)
gebuendelt = quelle.kerzen_mehrerer(kennungen, bis)
for kennung in kennungen:
einzeln = quelle.kerzen_des_tages(kennung, bis)
gleich = [k.zeit for k in einzeln] == [k.zeit for k in gebuendelt[kennung]]
print(
f"kerzen {kennung}: gebuendelt {len(gebuendelt[kennung])}, einzeln {len(einzeln)}"
+ ("" if gleich else " ABWEICHUNG")
)
if not gleich:
abweichungen += 1
# 5. Seitenverhalten roh: eine grosse Seite gegen viele kleine, je Symbol dieselbe Summe.
grundparameter = {
"symbols": ",".join(symbole),
"timeframe": "1Min",
"start": iso_z(handelsbeginn(bis)),
"end": iso_z(bis),
"feed": FEED,
"adjustment": "raw",
}
gross, seiten_gross, _ = zaehle_seiten(sitzung, kopf, grundparameter | {"limit": 10_000})
klein, seiten_klein, gemischt = zaehle_seiten(sitzung, kopf, grundparameter | {"limit": KLEINE_SEITE})
summe = sum(gross.values())
print(f"seiten mit limit 10000: {seiten_gross}, kerzen zusammen {summe}")
print(f"seiten mit limit {KLEINE_SEITE}: {seiten_klein}, davon mit mehr als einem symbol: {gemischt}")
if gross != klein:
print(f"ABWEICHUNG: je Symbol ueber die kleinen Seiten {klein}, ueber die grosse {gross}")
abweichungen += 1
if summe > KLEINE_SEITE and seiten_klein == 1:
print("HINWEIS: limit gilt offenbar je Symbol und nicht ueber alle; der Code sammelt in beiden Faellen richtig")
if abweichungen:
print(f"ERGEBNIS: {abweichungen} Abweichung(en) oder Starthindernis(se)")
return 2
print("ERGEBNIS: alle Formen wie angenommen")
return 0
if __name__ == "__main__":
try:
sys.exit(main())
except Exception as ausnahme: # noqa: BLE001 - ein Absturz ist selbst ein Befund
print(f"BEFUND: das Werkzeug ist abgebrochen: {ausnahme!r}")
sys.exit(1)