User-Entscheidung. Der Pfad war seit 01.08. aus und auf BEIDEN Ausfuehrungs-Achsen gemessen negativ: Markt-Einstieg in allen 8 Feldern, ruhende Order mit Touch-Fill -0,392/-0,338 in allen 6 und damit sogar schlechter als Markt. Die +0,35/+0,32 aus backtest_auto_signal_v3.py galten in Variante B (Fill beim Close-Bruch) und waren Look-ahead - Aufschlag 0,77-0,85 R. Ursache strukturell: das Bestaetigungs-Level _pend WANDERT. Nebenbei entfaellt der offene Churn-Defekt (4x setzen/stornieren in 76 s bei identischem Level, gefunden 06.08., nie behoben). Entfernt: _check_auto_signal, set_auto_signal, der Signal-Zweig in _pending_ziel, /api/autosignal, Button + Handler, vier Snapshot-Felder, die auto_signal*/signal_pending_entry-Leser. Geblieben: AUTOSIG_*-Trades in der DB, die Backtests, wave_rec.breakout (speist block_reason) und _confirm_breakout selbst. Sicherung: .removed_backup/auto_signal_2026-08-12.py + Git. Das Risiko war nicht SIG, sondern der GETEILTE Pending-Manager (zwei getrennte Manager wuerden sich gegenseitig stornieren). Deshalb wurde das Squeeze-Verhalten VOR dem Eingriff in tests/test_pending_ziel.py festgenagelt - 7 Tests, vorher gruen, laufen unveraendert weiter. Zwei Folgefehler fing die Pruefkette sofort: ruff F821 ein verwaistes _t, py_compile ein dangling if. Zwei Tests wurden mitgezogen statt geloescht - test_signal_fill_long ist jetzt umgedreht (unbekannte Quelle darf NICHT als Squeeze durchgehen). Live verifiziert: alle vier Snapshot-Felder weg, Endpunkt tot, auto_squeeze/squeeze_pending unveraendert, 67 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
106 lines
4.0 KiB
Python
106 lines
4.0 KiB
Python
"""Der Systemzustand im Tagesreport — sagt er die Wahrheit, und faellt er nie aus?
|
|
|
|
ANLASS (2026-08-11). Das Projekt hat sieben Waechter, und sie funktionieren. Was
|
|
fehlte, war der letzte Meter: die Marker-Datei `cfg_auto_sr_close` traegt den
|
|
Zeitstempel **06.08. 18:10** und wurde danach NIE wieder gemeldet — fuenf Tage
|
|
lang stand ein Exit aus, der am 11.08. mit **+350,81 EUR ueber 43 Trades**
|
|
gemessen wurde. Ursache ist ein Kategorienfehler: die Marker-Logik behandelt
|
|
einen DAUERZUSTAND wie ein EINMAL-EREIGNIS.
|
|
|
|
Deshalb die Trennung: Popup = „etwas Neues", Tagesreport = „so steht es gerade".
|
|
|
|
⚠ Die wichtigste Eigenschaft dieses Blocks ist, dass er NIE den Report sprengt.
|
|
Ein Report, der wegen der Statuszeile gar nicht ankommt, waere schlimmer als
|
|
gar keine Statuszeile — deshalb prueft der Test vor allem die Ausfallpfade.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from core.engine import TradingEngine
|
|
from core.history import HistoryLogger
|
|
|
|
|
|
class _Hist:
|
|
def __init__(self, puls):
|
|
self._puls = puls
|
|
|
|
def telemetrie_puls(self):
|
|
if isinstance(self._puls, Exception):
|
|
raise self._puls
|
|
return self._puls
|
|
|
|
|
|
class _Stub:
|
|
_system_status = TradingEngine._system_status
|
|
|
|
def __init__(self, *, brk=False, src=True, flip=False, puls=None):
|
|
self._auto_squeeze = brk
|
|
self._auto_sr_close = src
|
|
self._flip_close = flip
|
|
self.history = _Hist(puls if puls is not None else [])
|
|
|
|
|
|
def _text(z):
|
|
return "\n".join(z)
|
|
|
|
|
|
def test_automatik_zeile_nennt_alle_schalter():
|
|
"""⚠ Seit dem 2026-08-12 sind es DREI — der Auto-Signal-Pfad (SIG) ist
|
|
entfernt. Der Test zaehlt sie bewusst einzeln auf: verschwindet einer
|
|
stillschweigend aus der Zeile, faellt es hier auf und nicht erst, wenn
|
|
monatelang niemand merkt, dass ein Schalter aus ist (real geschehen:
|
|
`auto_sr_close` stand 5 Tage aus, gemessener Wert +350,81 EUR)."""
|
|
z = _text(_Stub(brk=True, src=True, flip=False)._system_status())
|
|
assert "BRK AN" in z and "S/R-Close AN" in z and "Flip aus" in z
|
|
assert "SIG" not in z, "der entfernte Pfad darf nicht wieder auftauchen"
|
|
|
|
|
|
def test_toter_logger_wird_gemeldet():
|
|
"""Der reale Fund: rec_outcomes stand 5 Tage bei 0 Zeilen."""
|
|
z = _text(_Stub(puls=["rec_outcomes: 0 Zeilen — Logger hat NIE geschrieben"])
|
|
._system_status())
|
|
assert "⚠ Puls:" in z and "rec_outcomes" in z
|
|
|
|
|
|
def test_stiller_puls_wird_ausdruecklich_bestaetigt():
|
|
"""⚠ Kein Befund muss SICHTBAR sein — eine fehlende Zeile liest sich sonst
|
|
wie „nicht geprueft"."""
|
|
z = _text(_Stub(puls=[])._system_status())
|
|
assert "alle Telemetrie-Tabellen schreiben" in z
|
|
|
|
|
|
def test_puls_fehler_wird_gemeldet_nicht_verschluckt():
|
|
"""⚠ Genau die Fehlerklasse, die rec_outcomes 5 Tage verbarg: ein stiller
|
|
Fallback saehe aus wie „alles gut"."""
|
|
z = _text(_Stub(puls=RuntimeError("DB weg"))._system_status())
|
|
assert "⚠ Puls: Prüfung fehlgeschlagen" in z and "DB weg" in z
|
|
|
|
|
|
def test_ohne_history_kein_absturz():
|
|
s = _Stub()
|
|
s.history = None
|
|
z = _text(s._system_status())
|
|
assert "Automatik:" in z # der Rest laeuft weiter
|
|
|
|
|
|
def test_liefert_immer_zeilen_und_wirft_nie():
|
|
"""Die Kern-Invariante: der Tagesreport darf an dieser Zeile nicht scheitern."""
|
|
for kw in ({}, {"brk": True}, {"puls": RuntimeError("x")}):
|
|
z = _Stub(**kw)._system_status()
|
|
assert isinstance(z, list) and z, "muss immer mindestens eine Zeile liefern"
|
|
assert all(isinstance(x, str) for x in z)
|
|
|
|
|
|
def test_puls_tabellenliste_ist_vollstaendig():
|
|
"""Jede Telemetrie-Tabelle, die das Projekt schreibt, muss ueberwacht sein —
|
|
sonst entsteht genau die Luecke wieder, die rec_outcomes hatte."""
|
|
ueberwacht = {t for t, *_ in HistoryLogger.PULS}
|
|
for t in ("recommendations", "candles_m1", "verdict_votes", "m15_states",
|
|
"cone_checks", "pbreak_predictions", "rec_outcomes"):
|
|
assert t in ueberwacht, f"{t} wird nicht ueberwacht"
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(pytest.main([__file__, "-v"]))
|