Auto-Signal-Entry (SIG) komplett entfernt inkl. Button (v=166)

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>
This commit is contained in:
Axel Hocks
2026-08-12 18:47:28 +02:00
co-authored by Claude Opus 5
parent b85dd010ab
commit bf7e382818
9 changed files with 269 additions and 205 deletions
+14 -4
View File
@@ -24,14 +24,24 @@ def test_squeeze_fill_wird_getaggt(engine):
assert 49483981 not in engine._pending_tickets # Merker verbraucht
def test_signal_fill_long(engine):
engine._pending_tickets = {777: "signal"}
def test_unbekannte_quelle_wird_NICHT_getaggt(engine):
"""⚠ Ersetzt `test_signal_fill_long` (der Auto-Signal-Pfad ist am
2026-08-12 entfernt). Der Test wurde NICHT geloescht, sondern umgedreht:
seit es nur noch den Squeeze gibt, darf ein Fill mit einer FREMDEN Quelle
nicht stillschweigend als Squeeze durchgehen — das waere genau
Deployment-Drift Fall 8 (vier reale Trades landeten als `setup=NULL` und
waren von manuellen nicht unterscheidbar), nur mit umgekehrtem Vorzeichen:
ein Fremd-Trade wuerde die B4-/B5-Monitore verfaelschen."""
engine._pending_tickets = {777: "signal"} # Altbestand / unbekannt
engine.trader.snap = {"ticket": 777, "order_type": 0, "entry_price": 75.2}
engine._check_pending_fill()
assert engine._signal_entry_count == 1
assert engine._squeeze_entry_count == 0
assert engine._bot_open_source == "auto_signal"
assert engine._bot_open_source is None
# ⚠ Der Merker bleibt STEHEN — nur ein sauber zugeordneter Fill verbraucht
# ihn. So wird derselbe Fall beim naechsten Tick erneut gemeldet, statt
# still zu verschwinden.
assert 777 in engine._pending_tickets
def test_fremde_position_wird_nicht_getaggt(engine):
+108
View File
@@ -0,0 +1,108 @@
"""`_pending_ziel`: das SQUEEZE-Verhalten darf sich beim Entfernen von SIG
NICHT aendern.
ANLASS (2026-08-12): der Auto-Signal-Pfad wird entfernt — gemessen negativ auf
BEIDEN Ausfuehrungs-Achsen (Markt: alle 8 Felder; ruhende Order mit Touch-Fill:
alle 6 Felder, sogar schlechter als der Markt-Einstieg). Grund strukturell:
`_pend` WANDERT, und „ein Level ist nur dann ein Ausfuehrungs-Vorteil, wenn es
STILLSTEHT".
⚠⚠ DAS RISIKO IST NICHT SIG, SONDERN DER GETEILTE MANAGER. Squeeze und Signal
teilen sich `_pending_ziel` — bewusst, weil zwei getrennte Manager ueber
`trader.pending_orders()` die Orders des jeweils anderen sehen und sich
**gegenseitig stornieren** wuerden (der Bug waere sporadisch und im Log kaum
erkennbar). Dieser Test nagelt den Squeeze-Zweig fest, BEVOR der Signal-Zweig
faellt, und laeuft danach unveraendert weiter.
"""
from __future__ import annotations
import MetaTrader5 as mt5
import pytest
from core.engine import TradingEngine
_SQ_K = 0.1 # `_SQ_K_ENG` — Ausbruchspuffer in ×ATR
class _Wave:
def __init__(self, snap):
self._snap = snap
def snapshot(self):
return self._snap
class _Stub:
"""Minimal-Engine — nur was `_pending_ziel` liest."""
_pending_ziel = TradingEngine._pending_ziel
def __init__(self, *, sq_state="armed", hi=80.5, lo=80.0, atr=0.2,
sq_pending=True, guard=None):
self._squeeze_pending = sq_pending
self._guard = guard
self.wave = _Wave({
"pb_feats": {"atr": atr},
"squeeze": {"state": sq_state, "box_hi": hi, "box_lo": lo},
})
# SIG-Felder bleiben gesetzt, damit der Test VOR und NACH dem Entfernen
# laeuft: existiert der Signal-Zweig nicht mehr, werden sie ignoriert.
self._signal_pending = False
self._auto_signal_min_conf = 75
def _auto_guard(self, quelle):
return self._guard
def test_armed_legt_beide_box_grenzen():
"""Der Kern: solange der Squeeze `armed` ist, liegen BEIDE Stop-Orders —
je einen Ausbruchspuffer ausserhalb der Box."""
ziel, quelle = _Stub(hi=80.5, lo=80.0, atr=0.2)._pending_ziel()
assert quelle == "squeeze"
assert ziel == {mt5.ORDER_TYPE_BUY_STOP: round(80.5 + _SQ_K * 0.2, 3),
mt5.ORDER_TYPE_SELL_STOP: round(80.0 - _SQ_K * 0.2, 3)}
def test_nicht_armed_legt_nichts():
for st in ("active", None):
ziel, quelle = _Stub(sq_state=st)._pending_ziel()
assert ziel == {} and quelle == ""
def test_guard_storniert():
"""Nacht-Fenster / News / Circuit-Breaker: der Guard ist die EINE Quelle fuer
beide Wege — schlaegt er zu, darf nichts liegen."""
ziel, quelle = _Stub(guard="nacht")._pending_ziel()
assert ziel == {} and quelle == ""
def test_feature_aus_legt_nichts():
ziel, quelle = _Stub(sq_pending=False)._pending_ziel()
assert ziel == {} and quelle == ""
def test_ohne_atr_kein_absturz():
s = _Stub()
s.wave = _Wave({"pb_feats": {"atr": 0.0}, "squeeze": {"state": "armed",
"box_hi": 80.5, "box_lo": 80.0}})
ziel, quelle = s._pending_ziel()
assert ziel == {} and quelle == ""
def test_fehlende_box_legt_nichts():
for hi, lo in ((None, 80.0), (80.5, None), (None, None)):
ziel, quelle = _Stub(hi=hi, lo=lo)._pending_ziel()
assert ziel == {} and quelle == ""
def test_preise_haengen_am_atr():
"""Der Puffer skaliert mit dem ATR — sonst waere er in ruhigen Phasen zu weit
und in schnellen zu eng."""
a, _ = _Stub(atr=0.1)._pending_ziel()
b, _ = _Stub(atr=0.4)._pending_ziel()
assert a[mt5.ORDER_TYPE_BUY_STOP] < b[mt5.ORDER_TYPE_BUY_STOP]
assert a[mt5.ORDER_TYPE_SELL_STOP] > b[mt5.ORDER_TYPE_SELL_STOP]
if __name__ == "__main__":
raise SystemExit(pytest.main([__file__, "-v"]))
+10 -5
View File
@@ -34,9 +34,8 @@ class _Hist:
class _Stub:
_system_status = TradingEngine._system_status
def __init__(self, *, brk=False, sig=False, src=True, flip=False, puls=None):
def __init__(self, *, brk=False, src=True, flip=False, puls=None):
self._auto_squeeze = brk
self._auto_signal = sig
self._auto_sr_close = src
self._flip_close = flip
self.history = _Hist(puls if puls is not None else [])
@@ -46,9 +45,15 @@ def _text(z):
return "\n".join(z)
def test_automatik_zeile_nennt_alle_vier_schalter():
z = _text(_Stub(brk=True, sig=False, src=True, flip=False)._system_status())
assert "BRK AN" in z and "SIG aus" in z and "S/R-Close AN" in z and "Flip aus" in 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():