Files
claude-regelwerk/.vault-tools/trading-plattform-pruefung/ha_totmann_wsl.py
T
Pascal MichelandClaude Opus 5 c115a13190 Automatischer Push: 11 Datei(en) geändert
- .vault-tools/trading-plattform-pruefung/LIESMICH.md
- .vault-tools/trading-plattform-pruefung/abendkontrolle.py
- .vault-tools/trading-plattform-pruefung/abendkontrolle_angriffe.py
- .vault-tools/trading-plattform-pruefung/abendkontrolle_mutanten.py
- .vault-tools/trading-plattform-pruefung/abendkontrolle_wsl.py
- .vault-tools/trading-plattform-pruefung/browserlauf.py
- .vault-tools/trading-plattform-pruefung/browserlauf_zugang.py
- .vault-tools/trading-plattform-pruefung/ha_totmann.py
- .vault-tools/trading-plattform-pruefung/ha_totmann_wsl.py
- .vault-tools/trading-plattform-pruefung/scan_push.sh
- AI/Regelwerk/codeworkflow.md

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

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-26 04:00:02 +02:00

137 lines
5.5 KiB
Python

"""WSL-Teil von ha_totmann.py: Thema, Token und Handelskalender, nur ueber die Pipe (Paket Z2).
Laeuft in WSL mit dem Python der Trading Plattform (~/venvs/trading; gebraucht wird nur die
Standardbibliothek). Die Geheimnisdatei der Plattform liegt nur hier. Dieses Skript liest
daraus [ntfy] thema und token und [alpaca] key_id und secret_key, fragt den Kalender von
Alpaca (GET /v2/calendar, lesend, ohne Proxy aus der Umgebung) und schreibt genau eine
JSON-Zeile auf stdout: thema, token, kalender.
Gestartet wird es nur von ha_totmann.py unter Windows, das stdout als Pipe liest und davon
nie etwas ausgibt. Von Hand gestartet stuende das Thema auf der Konsole -- deshalb gibt es
keinen Aufruf dafuer in LIESMICH.md. stderr traegt nur Befunde ohne Werte: Feldnamen und
Laengen.
ha_totmann.py importiert diese Datei auch unter Windows, fuer die Regeln (zugang,
pruefe_kalender, ausgabe) und fuer seine Aktion trocken; beim Import wird nichts gelesen.
Aufruf (nur durch ha_totmann.py): python ha_totmann_wsl.py [--geheimnisse PFAD] [--tage 400]
Exit: 0 fertig, 2 Ausnahme, 3 Voraussetzung fehlt.
"""
import argparse
import json
import re
import sys
import tomllib
import urllib.parse
import urllib.request
from datetime import date
from datetime import timedelta
from pathlib import Path
GEHEIMNISSE = "/home/claude/.config/trading-plattform/secrets.toml"
ALPACA_KALENDER = "https://paper-api.alpaca.markets/v2/calendar"
ZEITLIMIT_S = 30
THEMA_MUSTER = re.compile(r"^[A-Za-z0-9_-]{1,64}$")
TOKEN_MUSTER = re.compile(r"^[A-Za-z0-9_-]{1,128}$")
KALENDER_FELDER = (
("date", re.compile(r"^\d{4}-\d{2}-\d{2}$")),
("open", re.compile(r"^\d{2}:\d{2}$")),
("close", re.compile(r"^\d{2}:\d{2}$")),
)
class Befund(Exception):
"""Eine Voraussetzung fehlt. Der Text nennt Feldnamen und Laengen, nie einen Wert."""
def zugang(geheim: dict) -> tuple[str, str]:
"""Thema und Token aus dem Abschnitt [ntfy]; der Token darf fehlen, das Thema nicht."""
ntfy = geheim.get("ntfy") or {}
if not isinstance(ntfy, dict):
raise Befund("[ntfy] ist kein Abschnitt")
thema = str(ntfy.get("thema") or "").strip()
token = str(ntfy.get("token") or "").strip()
if not THEMA_MUSTER.match(thema):
raise Befund(f"[ntfy] thema fehlt oder passt nicht zur Regel (Laenge {len(thema)})")
if token and not TOKEN_MUSTER.match(token):
raise Befund(f"[ntfy] token passt nicht zur Regel (Laenge {len(token)})")
return thema, token
def pruefe_kalender(daten: object) -> list[dict]:
"""Je Handelstag date, open und close; jede andere Form ist ein Befund."""
if not isinstance(daten, list):
raise Befund("Kalender: Antwort ist keine Liste")
tage = []
for eintrag in daten:
if not isinstance(eintrag, dict):
raise Befund("Kalender: ein Eintrag ist keine Tabelle")
for feld, muster in KALENDER_FELDER:
wert = eintrag.get(feld)
if not isinstance(wert, str) or not muster.match(wert):
raise Befund(f"Kalender: Feld {feld} nicht lesbar")
tage.append({feld: eintrag[feld] for feld, _muster in KALENDER_FELDER})
return tage
def ausgabe(thema: str, token: str, kalender: list[dict]) -> str:
"""Die eine Zeile fuer die Pipe. ASCII, damit kein Zeilentrenner darin stehen kann."""
return json.dumps(
{"thema": thema, "token": token, "kalender": kalender},
ensure_ascii=True,
separators=(",", ":"),
)
def kalender_holen(alpaca: dict, tage: int) -> list[dict]:
"""GET /v2/calendar von heute bis heute plus tage, lesend, ohne Proxy aus der Umgebung."""
key_id = str(alpaca.get("key_id") or "")
secret_key = str(alpaca.get("secret_key") or "")
if not key_id or not secret_key:
raise Befund("[alpaca] key_id oder secret_key fehlt")
heute = date.today()
parameter = urllib.parse.urlencode(
{"start": heute.isoformat(), "end": (heute + timedelta(days=tage)).isoformat()}
)
anfrage = urllib.request.Request(
f"{ALPACA_KALENDER}?{parameter}",
headers={"APCA-API-KEY-ID": key_id, "APCA-API-SECRET-KEY": secret_key},
)
oeffner = urllib.request.build_opener(urllib.request.ProxyHandler({}))
with oeffner.open(anfrage, timeout=ZEITLIMIT_S) as antwort:
return pruefe_kalender(json.loads(antwort.read().decode("utf-8")))
def main(argv=None) -> int:
zerleger = argparse.ArgumentParser(
description="WSL-Teil von ha_totmann.py; nur ueber die Pipe benutzen.",
allow_abbrev=False,
)
zerleger.add_argument("--geheimnisse", default=GEHEIMNISSE)
zerleger.add_argument("--tage", type=int, default=400)
argumente = zerleger.parse_args(argv)
try:
pfad = Path(argumente.geheimnisse)
if not pfad.is_file():
raise Befund("die Geheimnisdatei liegt nicht am erwarteten Ort")
geheim = tomllib.loads(pfad.read_text(encoding="utf-8"))
thema, token = zugang(geheim)
alpaca = geheim.get("alpaca") or {}
if not isinstance(alpaca, dict):
raise Befund("[alpaca] ist kein Abschnitt")
kalender = kalender_holen(alpaca, argumente.tage)
except Befund as befund:
print(f"BEFUND {befund}", file=sys.stderr)
return 3
except Exception as ausnahme:
# Nur der Typ: Eine Ausnahme kann eine Adresse oder einen Kopf mit Schluessel tragen.
print(f"ABBRUCH: {type(ausnahme).__name__}", file=sys.stderr)
return 2
print(ausgabe(thema, token, kalender))
return 0
if __name__ == "__main__":
sys.exit(main())