pytest-Suite: 13 Tests in ~1 s, mutations-geprueft
Im Projekt sind ueber Monate dutzende Szenario-Tests entstanden ("mit 10
Szenarien getestet", "mit 18 synthetischen Szenarien") — alle als
Wegwerf-Skripte, keiner lief je ein zweites Mal. Bei einem System, das man vor
jeder Aenderung neu starten und live verifizieren muss, ist das der teuerste
Teil der Schleife.
Erfasst sind die beiden juengsten Pfade:
· _check_pending_fill (6 Faelle) — u.a. "fremde Position NICHT taggen" und
"derselbe Fill zaehlt nur einmal"
· Selbst-Kalibrierung (7 Faelle) — Episoden-Dedup, WARTEN re-armt, Auswertung
in beide Richtungen inkl. Broker-Offset, fehlende Bar bleibt offen,
Aggregation
Drei harte Regeln in tests/conftest.py: keine Live-DB (frische Temp-Datei je
Test), kein MT5 / kein laufender Server (gestubbt), kein Netz. Das Schema legen
die ECHTEN Konstruktoren an — HistoryLogger UND CandleLogger, denn candles_m1
gehoert nicht zum History-Schema. Ein handgebautes Test-Schema wuerde
irgendwann vom Produktivstand abweichen.
Warum Temp-DB statt Kopie der Live-DB: am 06.08. haben echte candles_m1-Zeilen
in einem vermeintlich leeren Fenster einen Test verfaelscht — zweimal sah es
nach einem Code-Fehler aus, es waren Testfehler.
MUTATIONS-GEPRUEFT, weil eine Suite die immer gruen ist wertlos waere: eine
absichtlich eingebaute "fremde Position wird doch getaggt"-Regression wird
gefangen. Nebenbefund: der Fill-Dedup ist DOPPELT gesichert (_pending_tagged
und der pop aus _pending_tickets) — nur eines zu entfernen faellt nicht auf,
beides zusammen bricht sofort zwei Tests. Gewollte Redundanz, kein
ungetesteter Zweig.
tests_rec_outcomes.py (Wegwerf-Fassung von gestern) und die verwaiste
test_pbreak.db entfernt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
79f3e8efe1
commit
9887e3d5da
@@ -0,0 +1,74 @@
|
||||
"""`engine._check_pending_fill` — ordnet eine gefüllte Stop-Order dem Bot zu.
|
||||
|
||||
HINTERGRUND (2026-08-06): eine ruhende Stop-Order füllt IM BROKER, nicht über
|
||||
`engine._open`. `trader._refresh_locked` adoptiert die Position dann per
|
||||
magic-match und loggt sie OHNE `setup` — vier reale Squeeze-Trades waren dadurch
|
||||
von manuellen nicht unterscheidbar, und B4-Monitor, `squeeze_b5` sowie
|
||||
`squeeze_entry_gap` zählten **null**. Deployment-Drift Fall 8: nicht die Strategie
|
||||
driftete, sondern ihre Beobachtbarkeit.
|
||||
|
||||
Die Zuordnung ist eindeutig, weil bei einem Pending-Fill die POSITIONS-Nummer
|
||||
identisch mit der ORDER-Nummer ist (an allen vier Broker-Datensätzen verifiziert).
|
||||
"""
|
||||
|
||||
|
||||
def test_squeeze_fill_wird_getaggt(engine):
|
||||
engine._pending_tickets = {49483981: "squeeze"}
|
||||
engine.trader.snap = {"ticket": 49483981, "order_type": 1,
|
||||
"entry_price": 75.127}
|
||||
engine._check_pending_fill()
|
||||
|
||||
assert engine._squeeze_entry_count == 1
|
||||
assert engine._bot_open_ticket == 49483981
|
||||
assert engine._bot_open_source == "auto_squeeze"
|
||||
assert 49483981 not in engine._pending_tickets # Merker verbraucht
|
||||
|
||||
|
||||
def test_signal_fill_long(engine):
|
||||
engine._pending_tickets = {777: "signal"}
|
||||
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"
|
||||
|
||||
|
||||
def test_fremde_position_wird_nicht_getaggt(engine):
|
||||
"""Der gefährliche Fall: ein manueller Trade darf NICHT als Bot-Trade gelten —
|
||||
sonst verfälscht er genau die Monitore, die der Fix reparieren soll."""
|
||||
engine._pending_tickets = {111: "squeeze"}
|
||||
engine.trader.snap = {"ticket": 999, "order_type": 0, "entry_price": 75.0}
|
||||
engine._check_pending_fill()
|
||||
|
||||
assert engine._squeeze_entry_count == 0
|
||||
assert engine._bot_open_ticket is None
|
||||
assert engine._pending_tickets == {111: "squeeze"} # bleibt liegen
|
||||
|
||||
|
||||
def test_derselbe_fill_zaehlt_nur_einmal(engine):
|
||||
"""Der `_pos_loop` läuft im Sekundentakt — ohne Dedup zählte ein Trade
|
||||
hundertfach und der B4-Monitor wäre wertlos."""
|
||||
engine._pending_tickets = {222: "squeeze"}
|
||||
engine.trader.snap = {"ticket": 222, "order_type": 1, "entry_price": 74.9}
|
||||
for _ in range(5):
|
||||
engine._check_pending_fill()
|
||||
|
||||
assert engine._squeeze_entry_count == 1
|
||||
|
||||
|
||||
def test_flat_ist_ein_no_op(engine):
|
||||
engine._pending_tickets = {333: "squeeze"}
|
||||
engine.trader.snap = {"ticket": None}
|
||||
engine._check_pending_fill()
|
||||
|
||||
assert engine._squeeze_entry_count == 0
|
||||
assert engine._pending_tickets == {333: "squeeze"}
|
||||
|
||||
|
||||
def test_ohne_pending_merker_sofort_raus(engine):
|
||||
"""Der häufigste Fall — es darf keine DB-Arbeit anfallen."""
|
||||
engine.trader.snap = {"ticket": 444, "order_type": 0}
|
||||
engine._check_pending_fill()
|
||||
|
||||
assert engine._bot_open_ticket is None
|
||||
Reference in New Issue
Block a user