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