Pending-Fill: Bot-Trades wurden nicht als solche gebucht

Auftrag "überprüfe die heutigen autotrades": die DB meldete für den 06.08.
null Bot-Trades, das Log vier gefüllte SQZ-STOP-Orders. Beides stimmte.

Ein Pending-Fill läuft nicht über engine._open — die Position wird von
trader._refresh_locked per magic-match adoptiert, und dieser Pfad loggt nur
Ticket/Symbol/Richtung/Lots/Preis. Es fehlten setup, sl_at_entry und alle
16 ctx_*-Spalten. Damit war genau die Messpipeline, die den Pending-Umbau
vom 05.08. kontrollieren soll, blind für ihn (B4-Monitor, squeeze_b5,
squeeze_entry_gap zählten null) — Deployment-Drift Fall 8, erste Variante,
bei der nicht die Strategie driftet, sondern ihre Beobachtbarkeit.

- history.tag_bot_trade(): UPDATE nur wenn setup IS NULL, stiller Fail-open
- engine._pending_tickets: Ticket -> Quelle, beim Platzieren/Stornieren gepflegt
- engine._check_pending_fill() im _pos_loop vor dem Pending-Manager;
  Zuordnung über Positions-Nr == Order-Nr (an allen 4 Fills verifiziert),
  setzt zusätzlich _bot_open_ticket/_source und die Entry-Zähler

6 Szenarien getestet, darunter die zwei gefährlichen: fremde Position wird
nicht getaggt, derselbe Fill zählt bei Folge-Ticks nur einmal.
Die 4 Trades des 06.08. nachgetragen (Backup .bak-2026-08-06).

ctx_*/sl_at_entry bewusst NICHT nachgetragen: rekonstruiert wären sie später
von gemessenen Werten nicht unterscheidbar.

Dabei gefunden, dokumentiert, NICHT behoben: der Signal-Pfad setzt/storniert
dieselbe Order 4x in 76 s bei identischem Level, weil block_reason zwischen
entry_room und breakout_pending pendelt (Drift Fall 9).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-06 10:55:41 +02:00
co-authored by Claude Opus 5
parent 730219fce8
commit 3c1442109d
3 changed files with 147 additions and 0 deletions
+60
View File
@@ -1882,6 +1882,64 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
entry_gap.py` rechnet DST-korrekt über `zoneinfo` und liefert **+0,275 / +0,686**
(n=34 unverändert). Gültig sind die zweiten Werte; die Schlussfolgerung ändert
sich nicht.
- **⚠⚠ EIN PENDING-FILL LIEF AN DER GESAMTEN BUCHFÜHRUNG VORBEI (Fix 2026-08-06,
User-Auftrag „überprüfe die heutigen autotrades").** Der Befund kam nur zustande,
weil die Frage überhaupt gestellt wurde: die DB meldete für den 06.08. **null
Bot-Trades**, das Log dagegen vier gefüllte `SQZ-STOP`-Orders. Beides stimmte.
**Mechanismus:** eine ruhende Stop-Order füllt **im Broker**, nicht über
`engine._open`. Die Position wird erst später von `trader._refresh_locked` per
magic-match **adoptiert** — und dieser Pfad ruft `history.log_trade_open()` nur mit
Ticket/Symbol/Richtung/Lots/Preis auf. Es fehlten damit **alle** Felder, die der
Market-Pfad setzt: `setup`, `sl_at_entry` und die **16 `ctx_*`-Spalten** der
Telemetrie-Stufe 1. Vier reale Trades waren so von manuellen **nicht
unterscheidbar**.
**Die Tragweite ist größer als „ein NULL-Feld":** genau die Messpipeline, die den
Pending-Umbau vom 05.08. kontrollieren soll, war **blind für ihn** — B4-Monitor,
`squeeze_b5` (Abbruchregel!) und `squeeze_entry_gap` zählten null Trades, und
`sl_at_entry` fehlte ausgerechnet bei den Trades, an denen die M5-SL-Umstellung zu
prüfen war. **Deployment-Drift Fall 8**, und die erste Variante, bei der nicht die
Strategie driftet, sondern **ihre Beobachtbarkeit**. Ein Umbau, der den Bestellweg
ändert, ändert auch, welcher Code die Bücher führt — das gehört beim Umbau geprüft.
**Fix:** `history.tag_bot_trade(ticket, setup)` (UPDATE nur wenn `setup IS NULL`,
stiller Fail-open — `core/history.py` hat bewusst keinen Logger) +
`engine._pending_tickets` (Ticket → Quelle, beim Platzieren gesetzt, beim
Stornieren/Taggen entfernt) + `_check_pending_fill()` im `_pos_loop` VOR dem
Pending-Manager. Die Zuordnung ist eindeutig, weil bei einem Pending-Fill die
**Positions-Nummer identisch mit der Order-Nummer** ist (an allen vier
Broker-Datensätzen des 06.08. verifiziert) — kein Zeitfenster-Raten. Der Fix setzt
zusätzlich `_bot_open_ticket`/`_bot_open_source` und die Entry-Zähler, damit
15-Min-Regel, B4-Monitor und Snapshot wieder dieselbe Wahrheit sehen.
Mit **6 Szenarien getestet**, darunter die beiden gefährlichen: fremde/manuelle
Position wird NICHT getaggt, und derselbe Fill zählt bei wiederholten Ticks nur
**einmal**. Die vier Trades des 06.08. wurden nachgetragen (DB-Backup
`oil_widget_history.db.bak-2026-08-06`).
**Bewusst NICHT nachgetragen: `ctx_*` und `sl_at_entry`.** Die stehen für den
Zustand **im Moment des Fills** und lassen sich nachträglich nur rekonstruieren,
nicht messen — eine rekonstruierte Zahl in einer Telemetrie-Spalte wäre später von
einer gemessenen nicht mehr unterscheidbar. Ab jetzt sind sie für Pending-Fills
weiterhin leer; wer sie braucht, muss den Kontext beim **Platzieren** der Order
einfrieren (offen, nicht gebaut).
- **⚠ OFFEN: Pending-Churn im SIGNAL-Pfad (gefunden 06.08., nicht behoben).** Am
06.08. wurde dieselbe SELL_STOP viermal in 76 s gesetzt und storniert — **bei
identischem Level 74,981**, es wandert also nichts. Die Ursache steht in
`recommendations.block_reason`: der Grund **pendelt zwischen `entry_room` und
`breakout_pending`**. Sobald das Raum-Gate den Kurs kurz stummschaltet, liefert
`_pending_ziel()` `{}` und der Manager storniert; eine Sekunde später ist das Gate
wieder offen und die Order wird neu gelegt.
**Das ist wieder Deployment-Drift, nicht nur Log-Lärm:** `backtest_auto_signal_v3.py`
hat den Pending-Vorteil (ØR +0,35/+0,32 gegen 0,09/0,11 am Markt) unter der
Annahme gemessen, dass die Order **durchgehend am Level liegt**. Flackert sie im
5-s-Takt, geht genau der Ausbruch verloren, der in eine Storno-Lücke fällt — und
das ist der Fall, für den sie da ist.
**Lösungsrichtung (ungebaut):** harte von weichen Storno-Gründen trennen. HART
(sofort stornieren) = Position offen · Startup-Schonfrist · Guard (Nacht/Circuit/
News) · Richtungswechsel · Level wandert über die Toleranz · Feature aus. WEICH
(Order liegen lassen) = kurzzeitiges Stummschalten durch `entry_room`/`min_conf`/
`breakout_pending`. Argument dafür: die Order füllt nur, wenn der Kurs das Level
**bricht** — dann hat er sich vom Gegenlevel wegbewegt, die Raum-Bedingung ist im
Fill-Moment also besser als im Flacker-Moment. Argument dagegen: das ist eine
Aussage über den Einstiegs-Filter und damit **nicht rein infrastrukturell**
vor dem Bau messen (Variante „Order bleibt liegen" gegen „Order flackert").
- **Auto-Squeeze-Entry = LIVE AUTONOM (User-Vorgabe 2026-07-16, `[trading]
auto_squeeze=true`, Default FALSE):** der Bot eröffnet **selbständig eine echte
Order**, sobald der **gemessen-validierte** Squeeze-Ausbruch feuert (`wave.squeeze.
@@ -3374,6 +3432,8 @@ Bedingungen BETRIEBEN als VALIDIERT. An EINEM Tag wurden drei Fälle gefunden:
| 7 | Auto-Squeeze-**Einstiegspreis** | Fill **AM Ausbruchs-Level** (`backtest_breakout_squeeze.py`) | **MARKET**-Order, live median **+0,275×ATR** über dem Level (34 echte Entries) | ØR **+0,456 → 0,158** (H1) bzw. **+0,611 → 0,006** (H2); WR 56 → 37 % |
| 6 | `auto_squeeze_skip_night` | Nacht-Kostenfalle am **Wellensignal** (alle Nacht-Bars: 0,333×ATR) | **Squeeze**-Ausbrüche, die nur bei anziehendem ATR feuern (0,183×ATR = Tag-Niveau) | Gate kostet ΣR **42 (H1) / 243 (H2)**; geblockte Ausbrüche sind in beiden Hälften positiv |
| 5 | `backtest_auto_signal.py` | **ohne Winkel** (`_build` ohne `angle=` → Default 90 → `ad=0`) | Winkel steuert einen **Signalzweig** (Reversal) UND **±15 Konfidenzpunkte** | Urteil vom 30.07. beschrieb weder das alte noch das neue System; der Winkelterm allein hebt H1 bei conf 75 von **0,123 auf 0,047** |
| **8** | **Buchführung der Bot-Trades** (nicht die Strategie, ihre **Beobachtbarkeit**) | Einstieg läuft über `engine._open` → schreibt `setup` + `sl_at_entry` + 16 `ctx_*` | **Pending-Fill** füllt im Broker, `trader._refresh_locked` adoptiert und loggt nur 5 Felder | 4 reale Squeeze-Trades am 06.08. als `setup=NULL` = **von manuellen ununterscheidbar**; B4-Monitor, `squeeze_b5` (Abbruchregel!) und `squeeze_entry_gap` zählten **null** |
| **9** | **Pending-Order liegt am Level** (offen) | `backtest_auto_signal_v3.py`: Order liegt **durchgehend** (ØR +0,35/+0,32) | `entry_room``breakout_pending` flackern → 4× setzen/stornieren in 76 s bei identischem Level | Ausbruch in einer Storno-Lücke = Entry ganz verloren; Ausmaß **ungemessen** |
Keiner wäre durch MEHR Backtesting gefunden worden — die Backtests waren korrekt.
**Ursachen (strukturell):** (a) Backtests sind NACHBAUTEN, keine Nutzer des Live-Codes