Automatischer Push: 19 Datei(en) geändert

- .vault-tools/plan-kopie/LIESMICH.md
- .vault-tools/plan-kopie/rot_nachweis.py
- .vault-tools/plan-kopie/welle_vergleich.py
- .vault-tools/plan-kopie/welle_vergleich_treiber.py
- .vault-tools/trading-plattform-pruefung/LIESMICH.md
- .vault-tools/trading-plattform-pruefung/angriffe.py
- .vault-tools/trading-plattform-pruefung/browserlauf.py
- .vault-tools/trading-plattform-pruefung/browserlauf_zugang.py
- .vault-tools/trading-plattform-pruefung/c_eigenlauf.py
- .vault-tools/trading-plattform-pruefung/d_eigenlauf.py
- .vault-tools/trading-plattform-pruefung/handelstag_auswertung.py
- .vault-tools/trading-plattform-pruefung/ntfy_probe_server.py
- … und 7 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-19 04:00:01 +02:00
1 parent 371a1f5d2a
commit 395642c480
19 files changed
+1636 -32

No files matched your search

@@ -0,0 +1,115 @@
"""Vorpruefung vor dem Start des Paper-Runners: liest nur, startet nichts, aendert nichts.
Koordinator-Werkzeug zu Teil 10, nicht fuers Repo. Fragen und Herkunft der Erwartung:
1. Laeuft schon ein Runner? Projektvorgabe (Doppelstart endet mit Exit 2; hier vorher sichtbar).
2. Wie steht der Killswitch? Nur Auskunft: Der Runner-Start stellt nach der Neustartregel immer
auf angehalten. Ein Befund ist allein 'gesperrt'. Gelesen aus
data/registry.db, nur lesend.
3. Ist das Paper-Konto glatt? Projektvorgabe (der Tag beginnt ohne Position und ohne offene Order).
4. Was sagt die Marktuhr des Brokers? Quelle: GET /v2/clock (is_open, next_open, next_close).
5. Wie weit liegt die eigene Uhr daneben? Quelle: Date-Kopf der Broker-Antwort gegen die eigene Uhr;
mehr als 30 Sekunden waeren fuer TOTP und Minutentakt ein Problem.
Was es nicht sieht: ob das Dashboard laeuft (das fragt der Koordinator unter Windows ab), ob der
IEX-Feed frisch liefert (dafuer handabruf_kerzen.py), und den Code-Stand (dafuer git im Projektordner).
Eine fehlende Voraussetzung ist ein Befund (Exit 3), jede Abweichung ein Befund (Exit 2).
Liest config/secrets.toml aus dem Projektordner und gibt die Schluessel nie aus.
Aufruf aus dem Projektordner (WSL, Venv): python vorstart_pruefung.py
"""
import sqlite3
import subprocess
import sys
import tomllib
from datetime import UTC, datetime
from email.utils import parsedate_to_datetime
from pathlib import Path
import requests
HANDEL_URL = "https://paper-api.alpaca.markets"
def main() -> int:
befunde: list[str] = []
laeufer = subprocess.run(["pgrep", "-af", "scripts/paper.py"], capture_output=True, text=True).stdout.strip()
eigene = [z for z in laeufer.splitlines() if "pgrep" not in z]
print(f"1. laufende runner: {len(eigene)}")
for zeile in eigene:
print(" ", zeile[:140])
if eigene:
befunde.append("es laeuft schon ein Runner")
db = Path("data/registry.db")
if not db.is_file():
print("VORAUSSETZUNG FEHLT: data/registry.db (aus dem Projektordner starten)")
return 3
verbindung = sqlite3.connect(f"file:{db.as_posix()}?mode=ro", uri=True)
try:
zeile = verbindung.execute("select zustand, seit, ausloeser from killswitch_zustand").fetchone()
finally:
verbindung.close()
# Kein Befund, gleich wie er steht: Der Runner-Start stellt nach der Neustartregel immer auf
# angehalten (Registerereignis 'neustart'). Ein 'aktiv' VOR dem Start heisst nur, dass ein
# frueheres Fortsetzen ueberschrieben wird -- der Klick muss NACH dem Start kommen.
print(f"2. killswitch im register: {zeile}")
if zeile and zeile[0] == "aktiv":
print(" HINWEIS: steht auf aktiv; der Runner-Start ueberschreibt das mit angehalten, Fortsetzen erst danach")
if zeile and zeile[0] == "gesperrt":
befunde.append("Killswitch ist gesperrt; Fortsetzen allein genuegt dann nicht (Freigeben im Dashboard)")
secrets = Path("config/secrets.toml")
if not secrets.is_file():
print("VORAUSSETZUNG FEHLT: config/secrets.toml")
return 3
werte = tomllib.loads(secrets.read_text(encoding="utf-8"))["alpaca"]
kopf = {"APCA-API-KEY-ID": werte["key_id"], "APCA-API-SECRET-KEY": werte["secret_key"]}
positionen = requests.get(f"{HANDEL_URL}/v2/positions", headers=kopf, timeout=20)
offene = requests.get(f"{HANDEL_URL}/v2/orders", headers=kopf, params={"status": "open", "limit": 100}, timeout=20)
if positionen.status_code != 200 or offene.status_code != 200:
print(f"VORAUSSETZUNG FEHLT: Broker antwortet {positionen.status_code}/{offene.status_code}")
return 3
print(f"3. paper-konto: {len(positionen.json())} position(en), {len(offene.json())} offene order(s)")
for p in positionen.json():
print(f" position {p.get('symbol')} qty {p.get('qty')} side {p.get('side')}")
for o in offene.json():
print(f" order {o.get('side')} {o.get('qty')} {o.get('symbol')} status {o.get('status')}")
if positionen.json() or offene.json():
befunde.append("das Paper-Konto ist nicht glatt")
vorher = datetime.now(UTC)
uhr = requests.get(f"{HANDEL_URL}/v2/clock", headers=kopf, timeout=20)
nachher = datetime.now(UTC)
if uhr.status_code != 200:
print(f"VORAUSSETZUNG FEHLT: /v2/clock antwortet {uhr.status_code}")
return 3
daten = uhr.json()
print(f"4. marktuhr: is_open {daten.get('is_open')}, next_open {daten.get('next_open')}, next_close {daten.get('next_close')}")
datum = uhr.headers.get("Date")
if datum:
server = parsedate_to_datetime(datum)
mitte = vorher + (nachher - vorher) / 2
abstand = (mitte - server).total_seconds()
# Der Date-Kopf hat Sekundenaufloesung; unter zwei Sekunden ist deshalb nichts zu sagen.
print(f"5. eigene uhr gegen den Date-Kopf des Brokers: {abstand:+.1f} s (Aufloesung eine Sekunde)")
if abs(abstand) > 30:
befunde.append(f"die eigene Uhr liegt {abstand:+.0f} s neben der des Brokers")
else:
print("5. kein Date-Kopf in der Antwort; Uhrvergleich entfaellt")
print()
for befund in befunde:
print("BEFUND:", befund)
print(f"{len(befunde)} Befund(e).")
return 2 if befunde else 0
if __name__ == "__main__":
try:
sys.exit(main())
except Exception as ausnahme: # noqa: BLE001 - ein Absturz des Werkzeugs ist selbst ein Befund
print(f"BEFUND: das Werkzeug ist abgebrochen: {ausnahme!r}")
sys.exit(2)