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