Tag P/L war falsch: Fehlbuchungen beim Schliessen behoben + repariert

User-Frage "ueberpruefe ob Tag P/L richtig berechnet wird". Die Formel
(realisiert + offen) war bitgenau richtig - die Datenbasis nicht:
Anzeige +14,26 EUR gegen -74,08 EUR realisiert beim Broker.

Ursache, im Log im Sekundentakt (#49926460): 20:04:10 eroeffnet ->
20:04:10 "extern geschlossen" -> "kein OUT-Deal in 1 Deals" ->
Fallback bucht 0,00 -> 20:11:42 der echte Close lief ins Leere.
Drei Defekte: (1) positions_get sieht die frische Position einen Tick
lang nicht, (2) _log_external_close buchte TROTZ "kein OUT-Deal" einen
Close - dabei ist genau das der Beweis, dass sie noch offen ist,
(3) log_trade_close fasste nur exit_time IS NULL an, die Fehlbuchung
blockierte den echten Close dauerhaft. Bei #49852362 kostete das
+5,87 statt -95,38 EUR und den falschen Tag.

Behoben: (1) Abbruch statt Fallback bei "kein OUT-Deal"; (2) eine
erkennbare Fehlbuchung (closed_by='unknown') darf von einem echten
Close korrigiert werden - eng gefasst, gute Zeilen bleiben unberuehrt.
4 Tests inkl. Gegenprobe.

Altlast: tools/repair_closes.py (Trockenlauf Standard, Backup
automatisch). 18 Zeilen ueber 60 Tage korrigiert. Heute von +7,63 auf
-72,67 (Restfehler 1,41). Gesamt-P&L von -282,78 auf +169,98.

Zeitfalle zweimal getroffen: history_deals_get filtert nach
Broker-Wallclock, und fromtimestamp(d.time, BROKER) rendert 3 h zu
spaet. Aufgefallen nur, weil eine Deal-Zeit 23:11 lautete, das Log
aber 20:11:42 sagte.

Statistik-Modul separat geprueft: Arithmetik in allen drei Zeitraeumen
bitgenau korrekt (Abweichung 0,00). Der Netto-Defekt vom 05.08. besteht
weiter und ist groesser als damals: angezeigt -2.449,95 EUR, real
verblieben -17,06 (99 % der Steuer werden erstattet). Nicht gebaut -
die Loesung ist dokumentiert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-12 20:29:22 +02:00
co-authored by Claude Opus 5
parent 19ee379f20
commit d8839ef7c5
5 changed files with 369 additions and 2 deletions
+26 -1
View File
@@ -779,9 +779,34 @@ class TradeManager:
f"Externer Close: T={ticket} {closed_by} @ "
f"{close_deal.price:.3f} pnl={total_profit:.2f}")
return
log_hist.warning(f"T={ticket}: kein OUT-Deal in {len(own)} Deals")
# ⚠⚠ HIER LAG DER FEHLER (behoben 2026-08-12). Vorher fiel dieser
# Zweig in den Fallback durch und buchte den zuletzt bekannten
# SCHWEBE-Gewinn als realisierten Close.
# Das ist genau falsch herum: wir haben die Deals der Position
# GEFUNDEN und festgestellt, dass **kein OUT-Deal existiert**.
# Das ist positive Evidenz, dass die Position NOCH OFFEN ist —
# typischerweise ein transient leeres `positions_get` direkt
# nach dem Eröffnen.
# Real am 12.08.: #49926460 wurde 0 Sekunden nach dem Öffnen als
# „extern geschlossen" mit pnl 0,00 und exit_price 0,0 gebucht;
# der ECHTE Close 7 min später lief ins Leere, weil
# `log_trade_close` nur Zeilen mit `exit_time IS NULL` anfasst.
# Bei #49852362 kostete dasselbe **+5,87 statt 95,38 €** und
# buchte den Trade auf den FALSCHEN TAG.
# ➜ Nicht schreiben. Der nächste Tick prüft erneut; bleibt die
# Position wirklich weg, liefert der Broker dann einen
# OUT-Deal. `reconcile_open_trades` ist das zweite Netz.
log_hist.warning(
f"T={ticket}: kein OUT-Deal in {len(own)} Deals — Position gilt "
f"als WEITER OFFEN, kein Close gebucht (Fallback uebersprungen)")
return
# ── Methode 3: Fallback — letzter bekannter PnL ───────────────────
# ⚠ Nur noch fuer den WIRKLICH blinden Fall: es liess sich KEIN
# einziger Deal der Position abrufen (MT5-Aussetzer, Broker setzt
# `position_id` nicht). Dann ist „offen oder zu" nicht entscheidbar,
# und eine Zeile mit `closed_by='unknown'` ist besser als ein Trade,
# der ewig offen in der DB haengt.
if fallback_pnl is not None:
self.history.log_trade_close(
ticket=ticket,