Systemzustand in den Tagesreport - der fehlende letzte Meter

Selbstaendige Empfehlung nach der Gesamtpruefung, vom User beauftragt.

Befund: die Erkennung funktioniert, die Zustellung nicht. Sieben
Waechter, jeder nach einem eigenen Vorfall gebaut, jeder funktioniert -
und trotzdem blieben diese Woche sechs Zustaende tagelang unbemerkt,
vier davon waren bereits erkannt.

Belegt an den Marker-Dateien: cfg_auto_sr_close traegt 06.08. 18:10,
cfg_auto_squeeze sogar 02.08. Der Config-Waechter meldet je Schluessel
genau einmal und schweigt danach fuer immer. auto_sr_close war damit
fuenf Tage aus - gemessen +350,81 EUR ueber 43 Trades. Ursache ist ein
Kategorienfehler: die Marker-Logik behandelt einen Dauerzustand wie ein
Einmal-Ereignis.

Gebaut: engine._system_status() als Block im Tagesreport (07:30,
Telegram + E-Mail) - der einzige Kanal, der nachweislich jeden Tag
ankommt. Saubere Trennung: Popup = "etwas Neues", Report = "so steht
es gerade". Reine Anzeige.

- telemetrie_puls() liegt in core/history.py, beide Nutzer rufen
  dieselbe Funktion (Skripte importieren core, nie umgekehrt)
- measurement_reminder wird lazy und gekapselt importiert; ein
  Fehlschlag wird sichtbar gemeldet statt still verschluckt
- tests/test_system_status.py: 7 Tests, Schwerpunkt Ausfallpfade -
  der Report darf an dieser Zeile nie scheitern

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-11 22:43:24 +02:00
co-authored by Claude Opus 5
parent dca3c11bbd
commit b18b594639
5 changed files with 297 additions and 38 deletions
+52
View File
@@ -407,6 +407,58 @@ class HistoryLogger:
# alert „🔔 CLOSE-Signal") sowie bei Notfall-/Gewinn-Auto-Close. Die terse
# „Sym +X"-Bestätigung bei jedem Close ist entfernt.
# ── TELEMETRIE-PULS ──────────────────────────────────────────────────
# ⚠⚠ ANLASS (2026-08-11): `rec_outcomes` stand seit dem Einbau am 06.08. bei
# 0 Zeilen — ein falsches dict liess die Schreibbedingung nie wahr werden.
# KEIN NameError (der Linter sieht nichts), und das umgebende `except`
# loggt auf DEBUG. Fuenf Tage still tot; aufgefallen nur, weil die Tabelle
# neben ihren Schwestern mit 0 Zeilen stand.
# ⚠ Warum hier und nicht im Reminder-Skript: der Puls ist eine DB-Frage, und
# BEIDE Nutzer (Reminder-Popup + Tagesreport) brauchen ihn. Zwei Kopien
# waeren genau die Divergenz, die im Projekt dreimal zugeschlagen hat.
# Richtungsregel: Skripte duerfen `core` importieren, nicht umgekehrt.
PULS = (
# (Tabelle, Zeitspalte, erwartete Kadenz in Minuten, wozu)
("recommendations", "timestamp", 1, "Empfehlungs-Log"),
("candles_m1", "time", 1, "M1-Kerzen-Sammlung"),
("verdict_votes", "ts", 1, "Modul-Stimmen"),
("m15_states", "ts", 60, "M15-Karte Selbstmessung"),
("cone_checks", "ts", 60, "Kegel-Kalibrierung (ACI)"),
("pbreak_predictions", "ts", 60, "P(break)-Prognose-Tracking"),
("rec_outcomes", "ts", 180, "Selbst-Kalibrierung Empfehlung"),
)
def telemetrie_puls(self) -> list[str]:
"""Welche Telemetrie-Tabelle schreibt nicht mehr? → Klartext-Zeilen.
⚠ Toleranz bewusst grosszuegig (Faktor 20 der Kadenz): ein ruhiger Markt
oder ein Wochenende darf keinen Fehlalarm ausloesen — ein Waechter, der
oft warnt, wird ignoriert (Lehre vom 02.08.).
"""
out: list[str] = []
try:
jetzt = time.time()
with self._lock, self._connect() as conn:
for tab, sp, kadenz, wozu in self.PULS:
try:
n = conn.execute(f"SELECT COUNT(*) FROM {tab}").fetchone()[0]
letzt = conn.execute(f"SELECT MAX({sp}) FROM {tab}").fetchone()[0]
except Exception:
continue # Tabelle gibt es (noch) nicht
if not n:
out.append(f"{tab}: 0 Zeilen — Logger hat NIE geschrieben ({wozu})")
continue
# ⚠ `candles_m1.time` ist ROHE Brokerzeit (UTC+3) — ohne den
# Abzug meldet der Puls dort dauerhaft 3 h Rueckstand.
ts = letzt - 10800 if tab == "candles_m1" else letzt
alter_min = (jetzt - ts) / 60.0
if alter_min > kadenz * 20:
out.append(f"{tab}: letzte Zeile vor {alter_min/60:.1f} h "
f"(erwartet ~alle {kadenz} min) — {wozu}")
except Exception:
return out # fail-open wie der Rest der Schicht
return out
def trade_by_ticket(self, ticket: int) -> dict | None:
"""Einen Trade komplett lesen — für die Close-Benachrichtigung (2026-08-11).