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
@@ -55,11 +55,38 @@ Zeile in der Tagesnotiz (benannte Luecke; der Totmann in Home Assistant, Paket Z
|
||||
Nicht gesehen: die Guete der Kurse (steht im Bericht), Teilausfuehrungen einzeln, alles vor dem
|
||||
Handelstag. Betraege gibt das Werkzeug nie aus; fremde Zeilen laufen durch maskiere().
|
||||
|
||||
Seit Paket IB2 prueft --teil ibkr statt des Alpaca-Runners die IBKR-Instanz in ihrem Betriebsordner
|
||||
(--ibkr-ordner, Vorgabe IBKR_ORDNER), mit eigener Ausgabe und eigener Meldung (Titel "IBKR ..."):
|
||||
I1. Handelstag? wie Frage 1, derselbe Kalender von Alpaca.
|
||||
I2. Lief die Instanz? Protokolle <tag>T*-paper.jsonl unter artifacts/paper des Betriebsordners.
|
||||
Keines und trading-paper-ibkr.timer nicht active -> nur der Hinweis
|
||||
"IBKR-Instanz heute nicht gestartet", kein Befund, der Rest entfaellt
|
||||
(No-Go-Tag). Keines bei aktivem oder unlesbarem Zeitgeber -> Befund.
|
||||
Der Zeitgeber wird zum Zeitpunkt der Kontrolle gelesen, nicht fuer
|
||||
den geprueften Tag -- beim Nachholen eine benannte Luecke.
|
||||
I3. Ist der Ordner die Instanz? data/paper/broker.txt sagt ibkr, sonst Befund.
|
||||
I4. Killswitch der Instanz? killswitch_zustand in data/registry.db des Betriebsordners (mode=ro);
|
||||
gesperrt -> Befund mit Stufe und Ausloeser des juengsten Ereignisses.
|
||||
I5. Ende je Lauf, Handel? wie Fragen 3 und 6 (pruefe_laeufe); dazu die Schlussposition jedes
|
||||
Laufs aus seiner Zeile ende (pruefe_schlusspositionen), unabhaengig
|
||||
vom Journal: Position != 0 -> Befund.
|
||||
I6. Journal? wie Frage 3 fuer trading-paper-ibkr.service; jede Zeile mit einem
|
||||
Muster aus IBKR_MUSTER -> Befund.
|
||||
I7. Konto am Gateway glatt? scripts/ibkr_pruefen.py lesen im Betriebsordner mit IBKR_PY (Client-ID
|
||||
12, lesend), nur werktags ausserhalb 09:00 bis 16:15 New Yorker Zeit
|
||||
und am Wochenende, sonst ein Hinweis. Weiter gehen nur die Zahlen aus
|
||||
"Positionen: n" und "Offene Orders: n" und die BEFUND-Zeilen, maskiert,
|
||||
Kontonummern als [konto]. n != 0, eine fehlende Zeile oder Exit != 0 ->
|
||||
Befund.
|
||||
Nicht gesehen im Teil ibkr: ein Tagesbericht (die Instanz hat keine Tagesauswertung),
|
||||
handelstag_auswertung.py und der Dienst ibkr-gateway.service.
|
||||
|
||||
Ein Fangnetz, kein Nachweis ueber den Handelstag.
|
||||
Aufruf (WSL, Venv): python abendkontrolle_wsl.py --tag JJJJ-MM-TT [--ohne-meldung]
|
||||
Aufruf (WSL, Venv): python abendkontrolle_wsl.py --tag JJJJ-MM-TT [--ohne-meldung] [--teil ibkr]
|
||||
Nur eine Meldung schicken: python abendkontrolle_wsl.py --meldung "<titel>" "<text>"
|
||||
Fuer Angriffsfaelle ohne Netz und ohne systemd: --projekt, --kalender, --journal, --dienste,
|
||||
--ohne-broker, --ohne-kerzen.
|
||||
--ohne-broker, --ohne-kerzen; im Teil ibkr dazu --ibkr-ordner, --ibkr-konto (JSON mit exit und
|
||||
text statt des Aufrufs von ibkr_pruefen.py) und --jetzt (ISO-Zeitpunkt mit Versatz).
|
||||
Ausgabe: genau eine JSON-Zeile auf stdout. Exit 0 gelaufen (Befunde stehen in der Ausgabe),
|
||||
2 abgebrochen (die bis dahin gesammelten Befunde stehen trotzdem in der Ausgabe).
|
||||
"""
|
||||
@@ -73,8 +100,10 @@ import subprocess
|
||||
import sys
|
||||
import time
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
PROJEKT = "/mnt/d/Claude/Code Projekte/active - Trading Plattform"
|
||||
GEHEIMNISSE = "/home/claude/.config/trading-plattform/secrets.toml"
|
||||
@@ -87,6 +116,30 @@ DIENSTE_NOCHMAL_S = 30
|
||||
KILLSWITCH_ZUSTAENDE = ("aktiv", "angehalten", "gesperrt")
|
||||
AUSWERTUNG_TIMEOUT_S = 900
|
||||
|
||||
# Seit Paket IB2: die IBKR-Instanz im eigenen Betriebsordner (Unit trading-paper-ibkr.service).
|
||||
IBKR_ORDNER = "/mnt/d/Claude/.arbeitsbaeume/trading-ibkr-betrieb"
|
||||
IBKR_PY = "/home/claude/venvs/trading-ibkr/bin/python"
|
||||
IBKR_DIENST = "trading-paper-ibkr.service"
|
||||
IBKR_ZEITGEBER = "trading-paper-ibkr.timer"
|
||||
IBKR_MARKE = Path("data") / "paper" / "broker.txt"
|
||||
IBKR_TITEL = "IBKR "
|
||||
IBKR_LESEN_TIMEOUT_S = 180
|
||||
# Journalzeilen, die ohne Ausnahme ein Befund sind: ein Traceback, und der Satz, mit dem
|
||||
# scripts/paper.py abbricht, wenn Instanzmarke und Konfiguration nicht zusammenpassen.
|
||||
IBKR_MUSTER = ("Traceback (most recent call last)", "Nichts wird gebaut")
|
||||
# Das Konto wird am Gateway nur ausserhalb dieses Fensters gelesen (New Yorker Zeit, werktags):
|
||||
# Waehrend der Handelszeit gehoerten Positionen und Orders schon zum naechsten Handelstag.
|
||||
NEW_YORK = ZoneInfo("America/New_York")
|
||||
IBKR_SPERRE_VON = (9, 0)
|
||||
IBKR_SPERRE_BIS = (16, 15)
|
||||
IBKR_POSITIONEN = re.compile(r"^Positionen: (\d+)[ \t\r]*$", re.MULTILINE)
|
||||
IBKR_ORDERS = re.compile(r"^Offene Orders: (\d+)[ \t\r]*$", re.MULTILINE)
|
||||
# IBKR-Kontonummern: Buchstaben vorn, dann Ziffern. Das Paper-Konto hat mehr als "DU" vorn;
|
||||
# die erste Fassung (nur DU/U) traf die echte Nummer nicht (Pruefung IB2, 2026-09-25). Dieselbe
|
||||
# Form wie im Projekt (trading/broker/ibkr.py). Nie in Notiz oder Meldung.
|
||||
KONTONUMMER = re.compile(r"\b[A-Z]{1,4}[0-9]{5,}\b")
|
||||
NICHT_GESTARTET = "IBKR-Instanz heute nicht gestartet"
|
||||
|
||||
ENDE = re.compile(
|
||||
r"Ende: (?P<grund>[^,]+), Position (?P<position>[^,]+), Orders (?P<orders>[^,]+), "
|
||||
r"Killswitch (?P<killswitch>[^,]+), Exit (?P<exit>-?\d+)"
|
||||
@@ -238,12 +291,12 @@ def pruefe_laeufe(laeufe: list[dict], k: Kontrolle) -> None:
|
||||
|
||||
# --- 3 Journal -------------------------------------------------------------------------------------
|
||||
|
||||
def lies_journal(tag: str, journal_datei: str | None) -> str:
|
||||
def lies_journal(tag: str, journal_datei: str | None, dienst: str = "trading-paper.service") -> str:
|
||||
if journal_datei is not None:
|
||||
return Path(journal_datei).read_text(encoding="utf-8", errors="replace")
|
||||
morgen = (date.fromisoformat(tag) + timedelta(days=1)).isoformat()
|
||||
lauf = subprocess.run(
|
||||
["journalctl", "-u", "trading-paper.service", "--since", f"{tag} 00:00", "--until", f"{morgen} 06:00",
|
||||
["journalctl", "-u", dienst, "--since", f"{tag} 00:00", "--until", f"{morgen} 06:00",
|
||||
"-o", "cat", "--no-pager"],
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=60,
|
||||
)
|
||||
@@ -252,7 +305,7 @@ def lies_journal(tag: str, journal_datei: str | None) -> str:
|
||||
return lauf.stdout
|
||||
|
||||
|
||||
def pruefe_journal(text: str, k: Kontrolle) -> dict | None:
|
||||
def pruefe_journal(text: str, k: Kontrolle, dienst: str = "trading-paper.service") -> dict | None:
|
||||
alle = [{schluessel: wert.strip() for schluessel, wert in m.groupdict().items()} for m in ENDE.finditer(text)]
|
||||
# Ein Lauf ausserhalb der Handelszeit (Neustart nach Boersenschluss) endet mit wartezeit und Exit 3.
|
||||
enden = [satz for satz in alle if satz["grund"] != "wartezeit"]
|
||||
@@ -260,9 +313,9 @@ def pruefe_journal(text: str, k: Kontrolle) -> dict | None:
|
||||
if len(alle) > len(enden):
|
||||
k.hinweis(f"{len(alle) - len(enden)} Lauf/Laeufe ausserhalb der Handelszeit im Journal (Ende wartezeit)")
|
||||
if "Failed with result" in text:
|
||||
k.befund("Journal: trading-paper.service endete mit 'Failed with result'")
|
||||
k.befund(f"Journal: {dienst} endete mit 'Failed with result'")
|
||||
if not enden:
|
||||
k.befund("Journal: keine Zeile 'Ende: …, Exit n' eines Handelslaufs von trading-paper.service an diesem Tag")
|
||||
k.befund(f"Journal: keine Zeile 'Ende: …, Exit n' eines Handelslaufs von {dienst} an diesem Tag")
|
||||
return None
|
||||
# Jeder Lauf des Tages zaehlt: Ein Runner, der mittags mit einer Stoerung endete und neu gestartet
|
||||
# wurde, ist auch dann ein Befund, wenn der zweite sauber schliesst.
|
||||
@@ -354,6 +407,178 @@ def pruefe_killswitch(projekt: Path, k: Kontrolle) -> None:
|
||||
k.befund(f"Killswitch in unbekanntem Zustand {zeile[0]!r}")
|
||||
|
||||
|
||||
# --- IBKR-Instanz (seit Paket IB2) -----------------------------------------------------------------
|
||||
|
||||
def pruefe_marke_ibkr(ordner: Path, k: Kontrolle) -> None:
|
||||
datei = ordner / IBKR_MARKE
|
||||
marke = datei.read_text(encoding="utf-8").strip() if datei.is_file() else None
|
||||
k.fakten["marke"] = marke
|
||||
if marke != "ibkr":
|
||||
k.befund(
|
||||
f"Instanzmarke {IBKR_MARKE.as_posix()} sagt {marke!r} statt 'ibkr': "
|
||||
"Das ist nicht der Betriebsordner der IBKR-Instanz"
|
||||
)
|
||||
|
||||
|
||||
def pruefe_register_ibkr(ordner: Path, k: Kontrolle) -> None:
|
||||
"""Zustand und juengstes Ereignis aus dem Register der Instanz, nur lesend (mode=ro)."""
|
||||
register = ordner / "data" / "registry.db"
|
||||
if not register.is_file():
|
||||
k.befund("IBKR-Register data/registry.db fehlt im Betriebsordner")
|
||||
return
|
||||
with sqlite3.connect(f"file:{register}?mode=ro", uri=True, timeout=10) as verbindung:
|
||||
stand = verbindung.execute(
|
||||
"SELECT zustand, seit, ausloeser FROM killswitch_zustand WHERE id = 1"
|
||||
).fetchone()
|
||||
letztes = verbindung.execute(
|
||||
"SELECT stufe, ausloeser FROM killswitch_ereignisse ORDER BY id DESC LIMIT 1"
|
||||
).fetchone()
|
||||
if stand is None:
|
||||
k.befund("IBKR-Register: kein Killswitch-Zustand")
|
||||
return
|
||||
zustand = stand[0]
|
||||
k.fakten["killswitch"] = {"zustand": zustand, "seit": stand[1], "ausloeser": stand[2]}
|
||||
if zustand == "gesperrt":
|
||||
grund = "ohne Ereignis im Register" if letztes is None else f"letztes Ereignis {letztes[0]} von {letztes[1]}"
|
||||
k.befund(f"IBKR-Killswitch steht auf gesperrt ({grund}): Ohne Freigeben an der Konsole handelt die Instanz nicht")
|
||||
elif zustand not in KILLSWITCH_ZUSTAENDE:
|
||||
k.befund(f"IBKR-Killswitch in unbekanntem Zustand {zustand!r}")
|
||||
|
||||
|
||||
def pruefe_schlusspositionen(laeufe: list[dict], k: Kontrolle) -> None:
|
||||
"""Die Schlussposition jedes Laufs aus seiner Zeile ende, unabhaengig vom Journal.
|
||||
|
||||
pruefe_laeufe bewertet die Position nicht, und pruefe_journal nur die des letzten Laufs;
|
||||
der Alpaca-Teil faengt sie ueber handelstag_auswertung.py, das der Teil ibkr nicht faehrt.
|
||||
Ebenso einen Lauf ohne Zeile ende: pruefe_laeufe ueberspringt ihn, weil im Alpaca-Teil die
|
||||
Auswertung ihn meldet (Nachkritik IB2).
|
||||
"""
|
||||
for lauf in laeufe:
|
||||
ende = lauf["ende"]
|
||||
if ende is None:
|
||||
# Im Alpaca-Teil meldet das handelstag_auswertung.py; hier sonst niemand.
|
||||
k.befund(f"{lauf['datei']}: Lauf ohne Zeile ende im Protokoll")
|
||||
elif ende.get("position") != 0:
|
||||
k.befund(f"{lauf['datei']}: Lauf endete laut Protokoll mit Position {ende.get('position')}")
|
||||
|
||||
|
||||
def lies_zeitgeber_ibkr(dienste_datei: str | None) -> str:
|
||||
"""Der Zustand von trading-paper-ibkr.timer (active, inactive, ...); aus einer Datei in den Angriffsfaellen.
|
||||
|
||||
Gelesen wird der Zustand jetzt, nicht der am geprueften Tag: Beim Nachholen kann er sich
|
||||
inzwischen geaendert haben (benannte Luecke).
|
||||
"""
|
||||
if dienste_datei is not None:
|
||||
zustaende = json.loads(Path(dienste_datei).read_text(encoding="utf-8"))
|
||||
if not isinstance(zustaende, dict):
|
||||
raise ValueError("Dienste-Datei ist kein Objekt")
|
||||
return str(zustaende.get(IBKR_ZEITGEBER, "fehlt"))
|
||||
lauf = subprocess.run(["systemctl", "is-active", IBKR_ZEITGEBER], capture_output=True, text=True, timeout=30)
|
||||
return lauf.stdout.strip() or f"exit {lauf.returncode}"
|
||||
|
||||
|
||||
def lesen_erlaubt(jetzt: datetime) -> bool:
|
||||
"""Ob der Stand am Gateway jetzt fuer den Tagesschluss spricht: Wochenende, oder werktags
|
||||
ausserhalb IBKR_SPERRE_VON bis IBKR_SPERRE_BIS New Yorker Zeit."""
|
||||
ny = jetzt.astimezone(NEW_YORK)
|
||||
if ny.weekday() >= 5:
|
||||
return True
|
||||
return not IBKR_SPERRE_VON <= (ny.hour, ny.minute) < IBKR_SPERRE_BIS
|
||||
|
||||
|
||||
def lies_konto_ibkr(ordner: Path, konto_datei: str | None) -> tuple[int, str]:
|
||||
"""Exit und Ausgabe (stdout und stderr) von scripts/ibkr_pruefen.py lesen, oder aus einer Datei."""
|
||||
if konto_datei is not None:
|
||||
daten = json.loads(Path(konto_datei).read_text(encoding="utf-8"))
|
||||
return int(daten["exit"]), str(daten["text"])
|
||||
lauf = subprocess.run(
|
||||
[IBKR_PY, "scripts/ibkr_pruefen.py", "lesen"], cwd=ordner, capture_output=True, text=True,
|
||||
encoding="utf-8", errors="replace", timeout=IBKR_LESEN_TIMEOUT_S,
|
||||
env={**os.environ, "PYTHONIOENCODING": "utf-8"},
|
||||
)
|
||||
return lauf.returncode, lauf.stdout + "\n" + lauf.stderr
|
||||
|
||||
|
||||
def ohne_kontonummer(text: str) -> str:
|
||||
# Erst die Kontonummer, dann die Maske: Sonst machte die Maske aus "Konto DU..." ein
|
||||
# "Konto DU[x]", und die Kontonummer-Regel faende nichts mehr.
|
||||
return maskiere(KONTONUMMER.sub("[konto]", text))
|
||||
|
||||
|
||||
def pruefe_konto_ibkr(code: int, text: str, k: Kontrolle) -> None:
|
||||
"""Positionen und offene Orders am Gateway; aus der Ausgabe gehen nur Zahlen und Befunde weiter."""
|
||||
treffer_positionen = IBKR_POSITIONEN.search(text)
|
||||
treffer_orders = IBKR_ORDERS.search(text)
|
||||
positionen = int(treffer_positionen.group(1)) if treffer_positionen else None
|
||||
orders = int(treffer_orders.group(1)) if treffer_orders else None
|
||||
k.fakten["konto"] = {"positionen": positionen, "orders": orders, "exit": code}
|
||||
for zeile in text.splitlines():
|
||||
if zeile.strip().startswith(("BEFUND", "Voraussetzung fehlt")):
|
||||
k.befund(f"ibkr_pruefen.py lesen: {ohne_kontonummer(zeile.strip())[:200]}")
|
||||
if code != 0:
|
||||
k.befund(f"ibkr_pruefen.py lesen endete mit Exit {code}")
|
||||
if positionen is None or orders is None:
|
||||
k.befund("Konto am Gateway nicht gelesen: keine Zeile 'Positionen: n' oder 'Offene Orders: n'")
|
||||
return
|
||||
if positionen != 0:
|
||||
k.befund(f"Beim IBKR-Gateway stehen {positionen} Position(en)")
|
||||
if orders != 0:
|
||||
k.befund(f"Beim IBKR-Gateway stehen {orders} offene Order(s)")
|
||||
|
||||
|
||||
def kontrolliere_ibkr(argumente: argparse.Namespace, k: Kontrolle) -> None:
|
||||
ordner = Path(argumente.ibkr_ordner)
|
||||
tag = argumente.tag
|
||||
date.fromisoformat(tag) # ein falsches Datum ist ein Bedienfehler und bricht ab
|
||||
k.fakten["ordner"] = str(ordner)
|
||||
if not ordner.is_dir():
|
||||
k.befund(f"Betriebsordner der IBKR-Instanz fehlt: {ordner}")
|
||||
return
|
||||
liste = k.schritt("Kalender, und damit Lauf, Ende und Konto,", lies_kalender, tag, argumente.kalender,
|
||||
Path(argumente.projekt))
|
||||
handelstag = None if liste is None else ist_handelstag(tag, liste)
|
||||
k.fakten["handelstag"] = handelstag
|
||||
if handelstag is None:
|
||||
if liste is not None:
|
||||
k.befund("Kalender in unerwarteter Form; Lauf, Ende und Konto der IBKR-Instanz ungeprueft")
|
||||
return
|
||||
dateien = protokolle(ordner, tag)
|
||||
if not handelstag:
|
||||
if dateien:
|
||||
k.hinweis(f"kein Handelstag, aber {len(dateien)} Protokoll(e) der IBKR-Instanz")
|
||||
return
|
||||
zeitgeber = k.schritt("Zeitgeber der IBKR-Instanz", lies_zeitgeber_ibkr, argumente.dienste)
|
||||
k.fakten["zeitgeber"] = zeitgeber
|
||||
# No-Go-Tag: Pascal hat den Zeitgeber abgeschaltet, und die Instanz lief nicht. Leise.
|
||||
if not dateien and zeitgeber is not None and zeitgeber != "active":
|
||||
k.fakten["nicht_gestartet"] = True
|
||||
k.hinweis(f"{NICHT_GESTARTET} ({IBKR_ZEITGEBER}: {zeitgeber})")
|
||||
return
|
||||
k.schritt("Instanzmarke", pruefe_marke_ibkr, ordner, k)
|
||||
k.schritt("IBKR-Killswitch", pruefe_register_ibkr, ordner, k)
|
||||
if not dateien:
|
||||
k.befund(f"kein Protokoll der IBKR-Instanz an einem Handelstag, obwohl {IBKR_ZEITGEBER} "
|
||||
f"{zeitgeber} ist -- die Instanz lief nicht")
|
||||
laeufe = [lauf for lauf in (k.schritt(f"Protokoll {pfad.name}", lies_protokoll, pfad, k) for pfad in dateien) if lauf]
|
||||
k.fakten["laeufe"] = laeufe
|
||||
pruefe_laeufe(laeufe, k)
|
||||
pruefe_schlusspositionen(laeufe, k)
|
||||
text = k.schritt("Journal", lies_journal, tag, argumente.journal, IBKR_DIENST)
|
||||
if text is not None:
|
||||
k.fakten["ende"] = pruefe_journal(text, k, IBKR_DIENST)
|
||||
for muster in IBKR_MUSTER:
|
||||
if muster in text:
|
||||
k.befund(f"Journal: {IBKR_DIENST} zeigt '{muster}'")
|
||||
jetzt = datetime.fromisoformat(argumente.jetzt) if argumente.jetzt else datetime.now(NEW_YORK)
|
||||
if not lesen_erlaubt(jetzt):
|
||||
k.hinweis(f"Konto am IBKR-Gateway nicht gelesen: {jetzt.astimezone(NEW_YORK):%H:%M} New Yorker Zeit "
|
||||
"liegt im Handelsfenster")
|
||||
return
|
||||
ergebnis = k.schritt("Konto am IBKR-Gateway", lies_konto_ibkr, ordner, argumente.ibkr_konto)
|
||||
if ergebnis is not None:
|
||||
pruefe_konto_ibkr(ergebnis[0], ergebnis[1], k)
|
||||
|
||||
|
||||
# --- Meldung ---------------------------------------------------------------------------------------
|
||||
|
||||
def melde(titel: str, text: str, projekt: Path) -> str:
|
||||
@@ -439,6 +664,12 @@ def parse_args(argv=None) -> argparse.Namespace:
|
||||
zerleger.add_argument("--ohne-broker", action="store_true")
|
||||
zerleger.add_argument("--ohne-kerzen", action="store_true")
|
||||
zerleger.add_argument("--ohne-meldung", action="store_true")
|
||||
# Seit Paket IB2: der Teil ibkr prueft die IBKR-Instanz statt des Alpaca-Runners.
|
||||
zerleger.add_argument("--teil", choices=("alpaca", "ibkr"), default="alpaca")
|
||||
zerleger.add_argument("--ibkr-ordner", default=IBKR_ORDNER)
|
||||
# Nur fuer die Angriffsfaelle: Exit und Ausgabe von ibkr_pruefen.py als JSON-Datei, und die Uhr.
|
||||
zerleger.add_argument("--ibkr-konto")
|
||||
zerleger.add_argument("--jetzt", help="ISO-Zeitpunkt mit Versatz statt der Uhr")
|
||||
argumente = zerleger.parse_args(argv)
|
||||
if bool(argumente.tag) == bool(argumente.meldung):
|
||||
zerleger.error("genau eines von --tag und --meldung")
|
||||
@@ -454,18 +685,22 @@ def main(argv=None) -> int:
|
||||
|
||||
k = Kontrolle()
|
||||
code = 0
|
||||
ibkr = argumente.teil == "ibkr"
|
||||
try:
|
||||
kontrolliere(argumente, k)
|
||||
if ibkr:
|
||||
kontrolliere_ibkr(argumente, k)
|
||||
else:
|
||||
kontrolliere(argumente, k)
|
||||
except Exception as ausnahme:
|
||||
k.befund(f"Abendkontrolle abgebrochen: {type(ausnahme).__name__}: {ausnahme}")
|
||||
code = 2
|
||||
if k.befunde and not argumente.ohne_meldung:
|
||||
text = "\n".join(f"- {befund}" for befund in k.befunde[:12])
|
||||
k.fakten["meldung"] = melde(
|
||||
f"Abendkontrolle {argumente.tag}: {len(k.befunde)} Befund(e)", text, Path(argumente.projekt)
|
||||
)
|
||||
titel = f"Abendkontrolle {argumente.tag}: {len(k.befunde)} Befund(e)"
|
||||
k.fakten["meldung"] = melde(IBKR_TITEL + titel if ibkr else titel, text, Path(argumente.projekt))
|
||||
print(json.dumps(
|
||||
{"tag": argumente.tag, "befunde": k.befunde, "hinweise": k.hinweise, "fakten": k.fakten, "exit": code},
|
||||
{"tag": argumente.tag, "teil": argumente.teil, "befunde": k.befunde, "hinweise": k.hinweise,
|
||||
"fakten": k.fakten, "exit": code},
|
||||
ensure_ascii=False, default=str,
|
||||
))
|
||||
return code
|
||||
|
||||
Reference in new issue
Block a user