Trade-Leiste: Laufzeit-Uhr des offenen Trades (v=118)

- trader: open_time (echte Epoch, Broker-Offset korrigiert) aus position.time,
  im Snapshot; nur 1x je Position berechnet, beim Close auf 0.
- _broker_offset_s(sym=None): Symbol explizit uebergebbar -- beim Adoptieren ist
  self.symbol noch nicht gesetzt (war Offset 0 -> Zeit 3h in der Zukunft);
  zusaetzlich Selbstheilung bei Zukunftswerten.
- app.js/index.html/style.css: Feld #tb-dur + Titel-Uhr #tb-runtime, eigener
  1-s-Ticker (flüssig zwischen Snapshots), ab 2h bernstein (Time-Stop-Naehe).
- Verifiziert: Live-Trade 48898382 open_time == Log-Zeile 13:22:48.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-07-30 13:40:41 +02:00
co-authored by Claude Opus 4.8
parent e9653a842a
commit 919ed7c3ee
5 changed files with 71 additions and 7 deletions
+20 -2
View File
@@ -55,6 +55,7 @@ class TradeManager:
self.ticket = self.order_type = None
self.entry_price = self.lots = self.pnl = self.cur_price = 0.0
self.sl = self.tp = self.margin = 0.0
self.open_time = 0.0 # Öffnungszeit als ECHTE Epoch (Broker-Offset korrigiert)
self.symbol = None
self.last_error = ""
self._lock = threading.Lock()
@@ -241,6 +242,7 @@ class TradeManager:
self.ticket = None; self.order_type = None
self.entry_price = 0.0; self.lots = 0.0
self.pnl = 0.0; self.cur_price = 0.0; self.sl = self.tp = self.margin = 0.0
self.open_time = 0.0
self._swap = 0.0; self._tick_size = None; self._tick_value = None
return ""
return "Close: " + _retcode_msg(res)
@@ -366,12 +368,14 @@ class TradeManager:
own = [p for p in all_pos if getattr(p, "magic", 0) == MAGIC]
pick = own[0] if own else all_pos[0]
pos_sym = getattr(pick, "symbol", sym)
_boff = self._broker_offset_s(pos_sym)
with self._lock:
self.ticket = pick.ticket; self.order_type = pick.type
self.entry_price = pick.price_open; self.symbol = pos_sym
self.lots = pick.volume; self.pnl = pick.profit
self.sl = float(getattr(pick, "sl", 0.0) or 0.0)
self.tp = float(getattr(pick, "tp", 0.0) or 0.0)
self.open_time = float(getattr(pick, "time", 0) or 0) - _boff
# Adoptierter Trade ohne SL → Schutz-SL nachrüsten (Lock gehalten)
if not self.sl:
psl, _ptp, _ps = self._calc_sl_tp(pos_sym, pick.type,
@@ -438,6 +442,7 @@ class TradeManager:
self.ticket = None; self.order_type = None
self.entry_price = 0.0; self.pnl = 0.0; self.lots = 0.0
self.cur_price = 0.0; self.sl = self.tp = self.margin = 0.0
self.open_time = 0.0
self._swap = 0.0; self._commission = 0.0
self._tick_size = None; self._tick_value = None
return
@@ -456,6 +461,13 @@ class TradeManager:
margin = float(_m) if _m else 0.0
except Exception:
margin = 0.0
# Öffnungszeit nur EINMAL je Position berechnen (spart den Offset-Tick-Call je
# 1-s-Refresh) — ändert sich nicht, solange die Position offen ist.
_open_time = None
# Neu berechnen, wenn noch unbekannt ODER offensichtlich falsch (in der
# Zukunft = Offset war beim Adoptieren nicht ermittelbar → Selbstheilung).
if not self.open_time or self.open_time > now + 60:
_open_time = float(getattr(p, "time", 0) or 0) - self._broker_offset_s(sym)
with self._lock:
self.lots = p.volume
self._swap = swap
@@ -464,6 +476,8 @@ class TradeManager:
self.sl = float(getattr(p, "sl", 0.0) or 0.0)
self.tp = float(getattr(p, "tp", 0.0) or 0.0)
self.margin = margin
if _open_time is not None:
self.open_time = _open_time
self.cur_price = (tick.bid if p.type == mt5.ORDER_TYPE_BUY
else tick.ask) if tick else p.price_current
if si:
@@ -483,7 +497,7 @@ class TradeManager:
diff = (cur - ep) if otype == mt5.ORDER_TYPE_BUY else (ep - cur)
return diff / ts * tv * lots + swap
def _broker_offset_s(self) -> int:
def _broker_offset_s(self, sym: str | None = None) -> int:
"""
Broker-Serverzeit minus UTC in Sekunden, auf 30 min gerundet
(z.B. UTC+3 → 10800). MT5 liefert deal.time/tick.time in
@@ -494,7 +508,10 @@ class TradeManager:
sonst 0 (keine Korrektur).
"""
try:
sym = self.symbol
# `sym` explizit übergebbar: beim Adoptieren einer Position ist
# `self.symbol` noch nicht gesetzt → sonst kein Tick → Offset 0 und
# die Öffnungszeit bliebe rohe Broker-Zeit (3 h in der Zukunft).
sym = sym or self.symbol
tick = mt5.symbol_info_tick(sym) if sym else None
if tick and tick.time:
off = round((tick.time - time.time()) / 1800) * 1800
@@ -706,4 +723,5 @@ class TradeManager:
entry_price=self.entry_price, lots=self.lots,
pnl=self.pnl, cur_price=self.cur_price,
sl=self.sl, tp=self.tp, margin=self.margin,
open_time=(self.open_time if self.ticket else 0.0),
)