diff --git a/install_trading.py b/install_trading.py index 2e350d5..827b07d 100644 --- a/install_trading.py +++ b/install_trading.py @@ -430,9 +430,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se trailing_be = int(existing.get("trailing_be") or 0) open_time_val = existing.get("open_time") or now_s if ctp_open_time: - prev = (open_time_val or "")[:19] - if not prev or ctp_open_time < prev: - open_time_val = ctp_open_time + open_time_val = ctp_open_time conn.execute( """UPDATE trade_order_monitors SET symbol=?, symbol_name=?, market_code=?, lots=?, entry_price=?, @@ -495,12 +493,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se "SELECT open_time FROM trade_order_monitors WHERE id=?", (mid,), ).fetchone() db_open = (row["open_time"] or "").strip() if row else "" - open_time_val = db_open or ctp_open - if ctp_open and db_open: - if ctp_open < db_open[:19]: - open_time_val = ctp_open - elif ctp_open: - open_time_val = ctp_open + open_time_val = ctp_open or db_open execute_retry( conn, """UPDATE trade_order_monitors SET lots=?, entry_price=?, @@ -552,7 +545,12 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se sl = float(mon["stop_loss"]) if mon and mon.get("stop_loss") is not None else None tp = float(mon["take_profit"]) if mon and mon.get("take_profit") is not None else None ctp_open = (ctp.get("open_time") or "").strip() if ctp else "" - open_time = ctp_open or ((mon.get("open_time") or "") if mon else "") + if ctp and ctp_open: + open_time = ctp_open + open_time_source = "ctp" + else: + open_time = ((mon.get("open_time") or "") if mon else "") + open_time_source = "local" if open_time else "" holding = _holding_duration(open_time, now_iso) if open_time else "" mark = None @@ -630,6 +628,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se "stop_loss": sl, "take_profit": tp, "open_time": open_time or None, + "open_time_source": open_time_source or None, "holding_duration": holding or None, "mark_price": mark, "current_price": mark, @@ -637,6 +636,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se "margin_source": margin_source, "position_pct": position_pct, "risk_amount": pos_metrics.get("risk_amount") if sl is not None else None, + "reward_amount": pos_metrics.get("reward_amount") if tp is not None else None, "risk_pct": pos_metrics.get("risk_pct") if sl is not None else None, "rr_ratio": pos_metrics.get("rr_ratio") if sl is not None and tp is not None else None, "float_pnl": float_pnl, diff --git a/static/js/trade.js b/static/js/trade.js index 315c178..ec391af 100644 --- a/static/js/trade.js +++ b/static/js/trade.js @@ -683,14 +683,17 @@ var metaLine = '来源 ' + (row.source_label || 'CTP') + '' + (row.rr_ratio != null ? ' · 盈亏比 ' + row.rr_ratio + ':1' : '') + - ' · 止损 ' + (row.stop_loss != null ? fmtNum(row.stop_loss) : '--') + '' + - ' · 止盈 ' + (row.take_profit != null ? fmtNum(row.take_profit) : '--') + '' + + ' · 止损金额 ' + + (row.risk_amount != null ? fmtNum(row.risk_amount) + ' 元' : '--') + '' + + ' · 盈利金额 ' + + (row.reward_amount != null ? fmtNum(row.reward_amount) + ' 元' : '--') + '' + ' · ' + slTpStatusHtml(row) + ' · 移动保本 ' + trailingStatusHtml(row) + (slTpBtn ? ' · ' + slTpBtn : '') + (row.sync_pending ? ' · 同步柜台中…' : ''); var feeLabel = row.fee_source === 'ctp' ? '手续费(柜台)' : '手续费'; var marginLabel = row.margin_source === 'ctp' ? '占用保证金(柜台)' : '占用保证金'; + var openLabel = row.open_time_source === 'ctp' ? '开仓(柜台)' : '开仓'; return ( '
' + '
' + row.symbol + ' ' + dirBadge + '
' + @@ -698,13 +701,14 @@ actionBtns + '
' + '
' + metaLine + '
' + '
' + - '
' + row.lots + ' 手 @ ' + fmtNum(row.entry_price) + '
' + + '
' + row.lots + ' 手
' + + '
' + fmtNum(row.entry_price) + '
' + '
' + (row.current_price != null ? fmtNum(row.current_price) : '--') + '
' + '
' + (row.margin != null ? fmtNum(row.margin) + ' 元' : '--') + '
' + '
' + (row.position_pct != null ? fmtNum(row.position_pct) + '%' : '--') + '
' + '
' + pnlText + '
' + '
' + (row.est_fee != null ? fmtNum(row.est_fee) + ' 元' : '--') + '
' + - '
' + (openT || '--') + '
' + + '
' + (openT || '--') + '
' + '
' + (row.holding_duration || '--') + '
' + '
' + buildPendingHtml(row.pending_orders) + '
' diff --git a/vnpy_bridge.py b/vnpy_bridge.py index 2b5c4d6..f2aedf1 100644 --- a/vnpy_bridge.py +++ b/vnpy_bridge.py @@ -732,11 +732,22 @@ class CtpBridge: return (self._position_open_times.get(self._position_margin_key(sym, direction)) or "").strip() @staticmethod + def _parse_ctp_open_datetime(date_raw: str, time_raw: str = "") -> str: + """CTP OpenDate + OpenTime → YYYY-MM-DD HH:MM[:SS]。""" + d = (date_raw or "").strip() + if len(d) >= 8 and d[:8].isdigit(): + date_part = f"{d[:4]}-{d[4:6]}-{d[6:8]}" + else: + return "" + t = (time_raw or "").strip().replace(":", "") + if len(t) >= 6 and t[:6].isdigit(): + return f"{date_part} {t[0:2]}:{t[2:4]}:{t[4:6]}" + if len(t) >= 4 and t.isdigit(): + return f"{date_part} {t[0:2]}:{t[2:4]}" + return date_part + def _parse_ctp_open_date(raw: str) -> str: - s = (raw or "").strip() - if len(s) >= 8 and s[:8].isdigit(): - return f"{s[:4]}-{s[4:6]}-{s[6:8]} 09:00:00" - return "" + return CtpBridge._parse_ctp_open_datetime(raw, "") def _install_position_margin_hook(self) -> None: """拦截 CTP 持仓回报,缓存柜台 UseMargin。""" @@ -769,8 +780,12 @@ class CtpBridge: bridge._position_margins[k] = ( bridge._position_margins.get(k, 0.0) + margin ) - open_date = bridge._parse_ctp_open_date( - str(data.get("OpenDate") or data.get("open_date") or "") + open_date = bridge._parse_ctp_open_datetime( + str(data.get("OpenDate") or data.get("open_date") or ""), + str( + data.get("OpenTime") or data.get("open_time") + or data.get("TradeTime") or "" + ), ) if sym and open_date: k = bridge._position_margin_key(sym, d)