Files
AH-Oil-Trader/tests/test_pending_ziel.py
Axel HocksandClaude Opus 5 bf7e382818 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>
2026-08-12 18:47:28 +02:00

109 lines
3.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""`_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"]))