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]>
This commit is contained in:
1 parent
ea278dc24f
commit
c115a13190
11 files changed
+1848
-37
No files matched your search
@@ -0,0 +1,136 @@
|
||||
"""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())
|
||||
Reference in new issue
Block a user