feat(hub): show exchange mark price on monitor positions

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-03 22:25:23 +08:00
parent 5b6babd699
commit c1fda1e7d5
4 changed files with 72 additions and 2 deletions
+24
View File
@@ -408,6 +408,24 @@ def _position_entry_price(p: dict[str, Any]) -> float | None:
return None
def _position_mark_price(p: dict[str, Any]) -> float | None:
"""四所 ccxt 持仓统一解析标记价(用于强平/浮盈计算)。"""
info = p.get("info") or {}
if not isinstance(info, dict):
info = {}
for key in (
p.get("markPrice"),
p.get("mark_price"),
info.get("markPx"),
info.get("mark_price"),
info.get("markPrice"),
):
px = _finite_or_none(key)
if px is not None and px > 0:
return px
return None
def _extract_usdt_total(balance: dict[str, Any]) -> float | None:
"""从 ccxt balance 结构中尽量取出 USDT 总额(与 crypto_monitor_binance 一致)。"""
usdt_info = balance.get("USDT") or {}
@@ -583,6 +601,10 @@ def _status_inner(x_control_token: str | None) -> Any:
notional_f = None
entry_f = _position_entry_price(p)
_, entry_fmt, price_tick = _position_price_fmt(ex, sym, entry_f)
mark_f = _position_mark_price(p)
_, mark_fmt, mark_tick = _position_price_fmt(ex, sym, mark_f)
if price_tick is None and mark_tick is not None:
price_tick = mark_tick
positions_out.append(
{
"symbol": sym,
@@ -593,6 +615,8 @@ def _status_inner(x_control_token: str | None) -> Any:
"unrealized_pnl": _finite_or_none(upnl_f),
"entry_price": entry_f,
"entry_price_fmt": entry_fmt,
"mark_price": mark_f,
"mark_price_fmt": mark_fmt,
"price_tick": _finite_or_none(price_tick) if price_tick is not None else None,
}
)
+15 -1
View File
@@ -261,6 +261,18 @@
return fmtSymbolPrice(positionEntryPrice(pos), pos && pos.symbol, tickMap);
}
function positionMarkPrice(pos) {
if (!pos) return null;
const n = Number(pos.mark_price);
if (!Number.isFinite(n) || n <= 0) return null;
return n;
}
function fmtMarkPrice(pos, tickMap) {
if (pos && pos.mark_price_fmt) return String(pos.mark_price_fmt);
return fmtSymbolPrice(positionMarkPrice(pos), pos && pos.symbol, tickMap);
}
function pnlCls(v) {
const n = Number(v);
if (!Number.isFinite(n) || n === 0) return "";
@@ -1301,6 +1313,7 @@
<div class="pos-meta">${meta.map((m) => `<span class="pos-meta-item">${m}</span>`).join("")}</div>
<div class="pos-grid">
<div class="pos-cell"><span class="pos-label">开仓价</span><span class="pos-value">${fmtEntryPrice(pos, tickMap)}</span></div>
<div class="pos-cell"><span class="pos-label">标记价</span><span class="pos-value">${fmtMarkPrice(pos, tickMap)}</span></div>
<div class="pos-cell"><span class="pos-label">止损</span><span class="pos-value">${sl != null && sl !== "" ? fmtSymbolPrice(sl, symbol, tickMap) : "—"}</span></div>
<div class="pos-cell"><span class="pos-label">止盈</span><span class="pos-value">${tpMonitored ? "程序监控" : tp != null && tp !== "" ? fmtSymbolPrice(tp, symbol, tickMap) : "—"}</span></div>
<div class="pos-cell"><span class="pos-label">盈亏比</span><span class="pos-value">${tpMonitored ? "—" : rr != null ? fmt(rr, 2) + ":1" : "-:1"}</span></div>
@@ -1396,11 +1409,12 @@
const symBeBadge = beSecured ? ` ${breakevenBadgeHtml()}` : "";
return `<div class="pos-block">
<div class="table-scroll">
<table class="data-table"><thead><tr><th>合约</th><th>方向</th><th>开仓价</th><th>张数</th><th>浮盈</th><th>操作</th></tr></thead><tbody>
<table class="data-table"><thead><tr><th>合约</th><th>方向</th><th>开仓价</th><th>标记价</th><th>张数</th><th>浮盈</th><th>操作</th></tr></thead><tbody>
<tr>
<td class="td-symbol"><button type="button" class="btn-open-market sym-link" ${mktAttrs} title="打开行情区(含入场/止盈止损)">${esc(x.symbol)}</button>${symBeBadge}</td>
<td class="${sideDirCls(x.side)}">${renderDirectionHtml(x.side)}</td>
<td class="td-entry">${fmtEntryPrice(x, tickMap)}</td>
<td>${fmtMarkPrice(x, tickMap)}</td>
<td>${fmt(x.contracts, 4)}</td>
<td class="${pnlCls(x.unrealized_pnl)}">${fmt(x.unrealized_pnl, 2)}</td>
<td class="td-actions">
+1 -1
View File
@@ -245,6 +245,6 @@
<div id="toast"></div>
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
<script src="/assets/chart.js?v=20260528-hub-no-30m"></script>
<script src="/assets/app.js?v=20260603-hub-board-sse"></script>
<script src="/assets/app.js?v=20260603-hub-mark-price"></script>
</body>
</html>