"""Fehlbuchung beim Schliessen: darf nicht entstehen — und muss korrigierbar sein. ANLASS (2026-08-12, User: „ueberpruefe ob Tag P/L richtig berechnet wird"). Ergebnis: die FORMEL war richtig, die DATENBASIS nicht — Anzeige +14,26 EUR gegen −74,08 EUR realisiert beim Broker. ⚠⚠ URSACHE, im Log im Sekundentakt sichtbar (#49926460): 20:04:10 SELL 1.64L eroeffnet 20:04:10 „Position extern geschlossen" <- 0 Sekunden spaeter 20:04:12 „kein OUT-Deal in 1 Deals" 20:04:12 Fallback-PnL 0,00 gebucht 20:11:42 der ECHTE Close — lief ins Leere Drei Defekte griffen ineinander: (1) `positions_get` sieht die frische Position einen Tick lang nicht, (2) `_log_external_close` bucht TROTZ „kein OUT-Deal" einen Close, (3) `log_trade_close` fasst nur `exit_time IS NULL` an → die Fehlbuchung blockiert den echten Close dauerhaft. Bei #49852362 kostete das **+5,87 statt −95,38 EUR** und buchte den Trade auf den FALSCHEN TAG. """ from __future__ import annotations import time import pytest # Zeitstempel MUESSEN nach der Eroeffnung liegen: `log_trade_close` schiebt # ein exit VOR dem entry defensiv auf `jetzt` (sonst waere die Haltedauer # negativ). Mit 1000/2000 misst man diese Defensive, nicht den Fix. _T1 = int(time.time()) + 10 _T2 = int(time.time()) + 20 def test_fehlbuchung_wird_von_echtem_close_korrigiert(history): """Defekt (3): eine `unknown`-Zeile darf der Wahrheit nicht im Weg stehen.""" history.log_trade_open(ticket=4711, symbol="SpotCrude", direction="SELL", lots=1.64, entry_price=83.162) # Fehlbuchung, wie der Fallback sie schrieb history.log_trade_close(ticket=4711, exit_price=0.0, pnl=0.0, closed_by="unknown", exit_ts=_T1) # der ECHTE Close mit Deal-Daten history.log_trade_close(ticket=4711, exit_price=83.056, pnl=15.08, closed_by="manual", exit_ts=_T2) t = history.trade_by_ticket(4711) assert t["pnl"] == 15.08, "der echte Close muss die Fehlbuchung ersetzen" assert t["closed_by"] == "manual" assert t["exit_price"] == 83.056 assert t["exit_time"] == _T2, "auch der TAG muss korrigiert werden" def test_gute_zeile_wird_NICHT_ueberschrieben(history): """⚠ Die Gegenprobe — sonst waere der Fix schlimmer als der Fehler. Ein sauber gebuchter Close ist endgueltig.""" history.log_trade_open(ticket=4712, symbol="SpotCrude", direction="BUY", lots=1.0, entry_price=80.0) history.log_trade_close(ticket=4712, exit_price=81.0, pnl=100.0, closed_by="sl", exit_ts=_T1) history.log_trade_close(ticket=4712, exit_price=99.9, pnl=-999.0, closed_by="manual", exit_ts=_T2) t = history.trade_by_ticket(4712) assert t["pnl"] == 100.0 and t["closed_by"] == "sl" and t["exit_time"] == _T1 def test_unknown_ersetzt_nicht_unknown(history): """Eine zweite Fehlbuchung darf die erste nicht ueberschreiben — sonst wandert der Zeitstempel bei jedem Tick weiter und der Trade landet irgendwann auf einem beliebigen Tag.""" history.log_trade_open(ticket=4713, symbol="SpotCrude", direction="BUY", lots=1.0, entry_price=80.0) history.log_trade_close(ticket=4713, exit_price=0.0, pnl=0.0, closed_by="unknown", exit_ts=_T1) history.log_trade_close(ticket=4713, exit_price=0.0, pnl=5.0, closed_by="unknown", exit_ts=_T2) t = history.trade_by_ticket(4713) assert t["exit_time"] == _T1 and t["pnl"] == 0.0 def test_offener_trade_bleibt_offen_ohne_close(history): """Grundinvariante: ohne Close-Buchung keine exit_time — sonst zaehlt ein laufender Trade in den Tages-P&L.""" history.log_trade_open(ticket=4714, symbol="SpotCrude", direction="BUY", lots=1.0, entry_price=80.0) t = history.trade_by_ticket(4714) assert t["exit_time"] is None and t["pnl"] is None if __name__ == "__main__": raise SystemExit(pytest.main([__file__, "-v"]))