Fix dashboard TP profit display; add options ROI column.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-20 12:41:32 +08:00
parent 977d62bddf
commit 0f5fe801e2
3 changed files with 92 additions and 19 deletions
+62 -16
View File
@@ -1060,27 +1060,73 @@ def resolve_position_monitor_source(pos: dict, hub_mon: Optional[dict]) -> str:
def resolve_position_reward_at_tp(pos: dict, hub_mon: Optional[dict]) -> Optional[float]:
"""与监控区「盈利金额」一致:优先匹配下单监控 reward_at_tp_usdt,否则仓位自身字段."""
"""与监控区「盈利金额」一致:有字段用字段,否则按止盈价×张数推算."""
sym = str(pos.get("symbol") or "")
side = str(pos.get("side") or "")
side = str(pos.get("side") or "").lower()
if side in ("buy",):
side = "long"
elif side in ("sell",):
side = "short"
matched: Optional[dict] = None
if isinstance(hub_mon, dict) and hub_mon.get("ok") is not False and sym:
# 与前端 findMonitorOrder 一致:先扫 orders
for o in hub_mon.get("orders") or []:
if isinstance(o, dict) and _monitor_item_matches_position(o, sym, side):
for bucket in ("orders", "rolls", "trends"):
for o in hub_mon.get(bucket) or []:
if not isinstance(o, dict):
continue
o_sym = o.get("exchange_symbol") or o.get("symbol") or ""
if not _symbols_match(sym, str(o_sym)):
continue
o_side = str(o.get("direction") or "").lower()
# 与前端 findMonitorOrder 一致:方向为空也可匹配
if o_side and o_side != side:
continue
matched = o
v = _safe_float(o.get("reward_at_tp_usdt"))
if v is not None:
return v
for r in hub_mon.get("rolls") or []:
if isinstance(r, dict) and _monitor_item_matches_position(r, sym, side):
v = _safe_float(r.get("reward_at_tp_usdt"))
if v is not None:
return v
for t in hub_mon.get("trends") or []:
if isinstance(t, dict) and _monitor_item_matches_position(t, sym, side):
v = _safe_float(t.get("reward_at_tp_usdt"))
if v is not None:
return v
return _safe_float(pos.get("reward_at_tp_usdt"))
break
if matched is not None:
break
v = _safe_float(pos.get("reward_at_tp_usdt"))
if v is not None:
return v
entry = _safe_float(pos.get("entry_price"))
if entry is None and matched is not None:
entry = _safe_float(
matched.get("avg_entry_price")
or matched.get("entry_price")
or matched.get("avg_px")
)
tp = None
if matched is not None:
tp = _safe_float(matched.get("take_profit"))
if tp is None:
tp = _safe_float(matched.get("take_profit_display"))
if tp is None:
tpsl = _resolve_position_tpsl(pos, hub_mon)
tp = tpsl.get("tp")
contracts = pos.get("contracts")
if contracts is None:
contracts = pos.get("size")
if contracts is None and matched is not None:
contracts = matched.get("contracts")
try:
qty = abs(float(contracts)) if contracts is not None else None
except (TypeError, ValueError):
qty = None
cs = _safe_float(pos.get("contract_size"))
if cs is None or cs <= 0:
cs = 1.0
if entry is None or tp is None or not qty:
return None
try:
from lib.strategy.strategy_roll_ui_lib import reward_at_tp_usdt
return reward_at_tp_usdt(side or "long", float(entry), float(tp), float(qty), contract_size=float(cs))
except Exception:
return None
def _options_source_label(p: dict) -> str:
+28 -1
View File
@@ -259,6 +259,20 @@
return "solo:" + ex + ":" + String((p && p.inst_id) || Math.random());
}
function optionsRoiPct(p) {
if (!p || typeof p !== "object") return null;
const preview = p.close_preview || {};
if (preview.estimated_pnl_ratio_pct != null && Number.isFinite(Number(preview.estimated_pnl_ratio_pct))) {
return Number(preview.estimated_pnl_ratio_pct);
}
const net = optionsNetPnl(p);
const paid = Number(p.premium_paid);
if (net != null && Number.isFinite(paid) && paid > 0) {
return (net / paid) * 100;
}
return null;
}
function renderOptionsLegRow(ac, p, groupCls) {
const optType =
(p.opt_type || "").toUpperCase() === "C"
@@ -270,6 +284,7 @@
const target = String(p.target_monitor_text || "—");
const targetCls = target && target !== "—" ? "dash-target-monitor is-on" : "dash-target-monitor";
const net = optionsNetPnl(p);
const roi = optionsRoiPct(p);
return `<tr class="${groupCls || ""}">
<td>${exchangeLinkCell(ac)}</td>
<td>${sourceTypeCell(source)}</td>
@@ -279,6 +294,7 @@
<td>${p.idx_px != null ? fmt(p.idx_px, 0) : "—"}</td>
<td><span class="${targetCls}">${esc(target)}</span></td>
<td class="${pnlClass(net)}">${net != null ? pnlSigned(net, 2) : "—"}</td>
<td class="${pnlClass(roi)}">${roi != null ? esc(Number(roi).toFixed(2)) + "%" : "—"}</td>
</tr>`;
}
@@ -322,14 +338,22 @@
groups.forEach((g) => {
if (g.isHedge && g.items.length >= 1) {
let sum = 0;
let paidSum = 0;
let hasSum = false;
let hasPaid = false;
g.items.forEach(({ p }) => {
const n = optionsNetPnl(p);
if (n != null) {
sum += n;
hasSum = true;
}
const paid = Number(p && p.premium_paid);
if (Number.isFinite(paid) && paid > 0) {
paidSum += paid;
hasPaid = true;
}
});
const groupRoi = hasSum && hasPaid && paidSum > 0 ? (sum / paidSum) * 100 : null;
const title =
(g.source && g.source !== "—" ? g.source : "对冲") +
(g.planId ? " #" + g.planId : "") +
@@ -339,6 +363,9 @@
body += `<tr class="dash-opt-group-head">
<td colspan="7"><span class="dash-opt-group-label">${esc(title)}</span></td>
<td class="${hasSum ? pnlClass(sum) : ""}">${hasSum ? pnlSigned(sum, 2) : "—"}</td>
<td class="${groupRoi != null ? pnlClass(groupRoi) : ""}">${
groupRoi != null ? esc(Number(groupRoi).toFixed(2)) + "%" : "—"
}</td>
</tr>`;
g.items.forEach(({ ac, p }, idx) => {
const cls =
@@ -359,7 +386,7 @@
<div class="dash-table-wrap dash-options-table-wrap">
<table class="dash-table dash-options-table">
<thead><tr>
<th>交易所</th><th>类型</th><th>合约</th><th>Call/Put</th><th>到期倒计时</th><th>指数</th><th>目标监控</th><th>净盈亏</th>
<th>交易所</th><th>类型</th><th>合约</th><th>Call/Put</th><th>到期倒计时</th><th>指数</th><th>目标监控</th><th>净盈亏</th><th>收益率</th>
</tr></thead>
<tbody>${body}</tbody>
</table>
+2 -2
View File
@@ -20,7 +20,7 @@
<link rel="stylesheet" href="/assets/trade_stats_calendar.css?v=4" />
<link rel="stylesheet" href="/assets/account_risk_badge.css?v=4" />
<script src="/assets/account_risk_badge.js?v=4"></script>
<link rel="stylesheet" href="/assets/dashboard.css?v=20260720-dash-hedge-group" />
<link rel="stylesheet" href="/assets/dashboard.css?v=20260720-dash-tp-roi" />
</head>
<body>
<div class="app-bg" aria-hidden="true"></div>
@@ -1385,7 +1385,7 @@
<script src="/assets/archive.js?v=20260717-archive-cal-chart"></script>
<script src="/assets/quotes.js?v=20260717-quotes-feed"></script>
<script src="/assets/funds.js?v=20260717-funds-scroll-fix"></script>
<script src="/assets/dashboard.js?v=20260720-dash-hedge-group"></script>
<script src="/assets/dashboard.js?v=20260720-dash-tp-roi"></script>
<script src="/assets/strategy.js?v=8"></script>
<script src="/assets/help.js?v=1"></script>
<script src="/assets/logs.js?v=1"></script>