Netto-Rechnung aus den ECHTEN Steuer-Buchungen statt geschaetzt
Der seit 05.08. dokumentierte Defekt ist behoben. _add_net rechnete net_pnl = total_pnl - gross_win * 26,375 % und unterstellte, die Quellensteuer sei endgueltig verloren. Sie wird aber taeglich per "Tax settlement" erstattet - real zu 99 %. all: vorher net_pnl -2.449,95 EUR, jetzt +181,00 (total +209,93, echte Steuerlast +28,93). Verzerrung rund 2.630 EUR - sie machte aus einem profitablen Konto ein verlustreiches. Die Schaetzung der EINBEHALTENEN Summe war fast exakt; falsch war allein die Annahme, sie bleibe weg. trader.steuer_buchungen() liest die WHT-/Tax-Deals direkt (position_id == 0, sauber von Trade-Deals getrennt), unter mt5_lock, 5 min gecacht. Fail-safe: ohne MT5 Rueckfall auf die Schaetzung, das Feld wht_quelle sagt welche Zahl drinsteht. Kurze Zeitraeume bleiben verzerrt, in BEIDE Richtungen - die Erstattung kommt am Folgetag. Bei "today" ist wht sogar negativ, weil die Erstattung von gestern heute einging. Ehrlich benannt, nicht kaschiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d8839ef7c5
commit
5819a9df21
@@ -826,6 +826,65 @@ class TradeManager:
|
||||
except Exception as e:
|
||||
log_hist.error(f"_log_external_close: {e}")
|
||||
|
||||
# ── ECHTE Steuer-Buchungen statt Schaetzung ──────────────────────────────
|
||||
_STEUER_TTL_S = 300 # MT5-Abfrage gehoert nicht in jeden Aufruf
|
||||
|
||||
def steuer_buchungen(self, von_ts: int, bis_ts: int) -> dict | None:
|
||||
"""Tatsaechlich einbehaltene und erstattete Quellensteuer im Zeitfenster.
|
||||
|
||||
⚠⚠ WARUM DAS DIE SCHAETZUNG ERSETZT (Befund 05.08., nachgemessen 12.08.):
|
||||
`server._add_net` rechnete `net_pnl = total_pnl − gross_win × 26,375 %` und
|
||||
unterstellte damit, die WHT auf JEDEN Gewinn-Trade sei endgueltig verloren.
|
||||
Sie wird aber **taeglich per `Tax settlement` erstattet**. Gemessen ueber
|
||||
90 Tage: einbehalten −2.625,89 €, erstattet +2.608,83 € → verblieben
|
||||
**−17,06 €**. Die Schaetzung sagte −2.619,93 € und machte damit aus
|
||||
**+153 € rund −2.450 €**.
|
||||
✅ Die Schaetzung der EINBEHALTENEN Summe ist fast exakt (2.620 gegen
|
||||
2.626) — falsch ist allein die Annahme, sie bleibe weg.
|
||||
|
||||
Rueckgabe `{wht, tax, netto, n}` in Kontowaehrung (wht negativ, tax
|
||||
positiv, netto = Summe) oder `None`, wenn MT5 nicht antwortet.
|
||||
⚠ Die Buchungen haben `position_id == 0` und sind damit von den Trade-Deals
|
||||
sauber getrennt — kein Doppelzaehlen mit `total_pnl`.
|
||||
⚠ ZEITFALLE: `history_deals_get` filtert nach BROKER-Wallclock. Deshalb
|
||||
Epochs mit Offset uebergeben und beim Lesen wieder abziehen — mit
|
||||
`datetime`-Objekten verschiebt sich das Fenster (real zweimal passiert).
|
||||
⚠ Gecacht (`_STEUER_TTL_S`), weil die Abfrage ueber lange Zeitraeume
|
||||
Sekunden braucht.
|
||||
"""
|
||||
schluessel = (int(von_ts), int(bis_ts))
|
||||
cache = getattr(self, "_steuer_cache", None)
|
||||
if cache and cache[0] == schluessel and time.time() - cache[1] < self._STEUER_TTL_S:
|
||||
return cache[2]
|
||||
off = self._broker_offset_s()
|
||||
try:
|
||||
with mt5_lock(timeout=8) as got:
|
||||
if not got:
|
||||
return None
|
||||
deals = mt5.history_deals_get(int(von_ts) + off, int(bis_ts) + off + 300)
|
||||
except Exception as e:
|
||||
log_hist.warning(f"steuer_buchungen: {e}")
|
||||
return None
|
||||
if deals is None:
|
||||
return None
|
||||
wht = tax = 0.0
|
||||
n = 0
|
||||
for d in deals:
|
||||
k = (getattr(d, "comment", "") or "")
|
||||
if getattr(d, "position_id", 0):
|
||||
continue # Trade-Deal, nicht Steuer
|
||||
ts = int(getattr(d, "time", 0) or 0) - off
|
||||
if not (von_ts <= ts <= bis_ts):
|
||||
continue
|
||||
if "WHT" in k:
|
||||
wht += float(d.profit); n += 1
|
||||
elif "Tax" in k:
|
||||
tax += float(d.profit); n += 1
|
||||
res = {"wht": round(wht, 2), "tax": round(tax, 2),
|
||||
"netto": round(wht + tax, 2), "n": n}
|
||||
self._steuer_cache = (schluessel, time.time(), res)
|
||||
return res
|
||||
|
||||
def reconcile_open_trades(self, lookback_hours: int = 168):
|
||||
if not self.history:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user