Show pure-option source and net PnL on dashboard.
Restore 纯期权 label for non-hedge option rows and use close-preview net PnL instead of exchange upl. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1057,7 +1057,7 @@ def resolve_position_monitor_source(pos: dict, hub_mon: Optional[dict]) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _options_source_label(p: dict) -> str:
|
def _options_source_label(p: dict) -> str:
|
||||||
"""看板期权来源:仅对冲标期期/永期;纯期权或对不上监控显示 —."""
|
"""看板期权来源:期期/永期对冲,其余为纯期权."""
|
||||||
source = str(p.get("source") or "").strip()
|
source = str(p.get("source") or "").strip()
|
||||||
label = str(p.get("source_label") or "").strip()
|
label = str(p.get("source_label") or "").strip()
|
||||||
if source == "perp_options" or label == "永期对冲":
|
if source == "perp_options" or label == "永期对冲":
|
||||||
@@ -1067,7 +1067,9 @@ def _options_source_label(p: dict) -> str:
|
|||||||
hedge = p.get("hedge_plan_target") if isinstance(p.get("hedge_plan_target"), dict) else None
|
hedge = p.get("hedge_plan_target") if isinstance(p.get("hedge_plan_target"), dict) else None
|
||||||
if hedge:
|
if hedge:
|
||||||
return _hedge_source_label(hedge)
|
return _hedge_source_label(hedge)
|
||||||
return "—"
|
if label == "纯期权" or source in ("", "option"):
|
||||||
|
return "纯期权"
|
||||||
|
return label or "纯期权"
|
||||||
|
|
||||||
|
|
||||||
def _options_target_monitor_text(p: dict) -> str:
|
def _options_target_monitor_text(p: dict) -> str:
|
||||||
@@ -1115,19 +1117,27 @@ def format_dashboard_account_detail(ac: dict) -> dict[str, Any]:
|
|||||||
row = dict(p)
|
row = dict(p)
|
||||||
row["source_label"] = _options_source_label(p)
|
row["source_label"] = _options_source_label(p)
|
||||||
row["target_monitor_text"] = _options_target_monitor_text(p)
|
row["target_monitor_text"] = _options_target_monitor_text(p)
|
||||||
|
try:
|
||||||
|
from lib.options.options_positions_lib import net_pnl_from_display_row
|
||||||
|
|
||||||
|
net = net_pnl_from_display_row(row)
|
||||||
|
except Exception:
|
||||||
|
net = None
|
||||||
|
row["net_pnl"] = round(float(net), 4) if net is not None else None
|
||||||
options_positions.append(row)
|
options_positions.append(row)
|
||||||
inst = row.get("inst_id") or "?"
|
inst = row.get("inst_id") or "?"
|
||||||
opt_type = (row.get("opt_type") or "").upper()
|
opt_type = (row.get("opt_type") or "").upper()
|
||||||
label = "Call" if opt_type == "C" else "Put" if opt_type == "P" else opt_type or "OPT"
|
label = "Call" if opt_type == "C" else "Put" if opt_type == "P" else opt_type or "OPT"
|
||||||
upl = row.get("upl")
|
|
||||||
line: dict[str, Any] = {
|
line: dict[str, Any] = {
|
||||||
"kind": "options",
|
"kind": "options",
|
||||||
"source": row.get("source_label") or "—",
|
"source": row.get("source_label") or "纯期权",
|
||||||
"text": f"期权 {inst} {label}",
|
"text": f"期权 {inst} {label}",
|
||||||
}
|
}
|
||||||
if upl is not None:
|
if row.get("net_pnl") is not None:
|
||||||
|
line["pnl"] = row["net_pnl"]
|
||||||
|
elif row.get("upl") is not None:
|
||||||
try:
|
try:
|
||||||
line["pnl"] = round(float(upl), 4)
|
line["pnl"] = round(float(row["upl"]), 4)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
pass
|
pass
|
||||||
position_lines.append(line)
|
position_lines.append(line)
|
||||||
|
|||||||
@@ -147,6 +147,7 @@
|
|||||||
function sourceBadgeClass(source) {
|
function sourceBadgeClass(source) {
|
||||||
const s = String(source || "");
|
const s = String(source || "");
|
||||||
if (s.indexOf("对冲") >= 0) return "is-hedge";
|
if (s.indexOf("对冲") >= 0) return "is-hedge";
|
||||||
|
if (s.indexOf("纯期权") >= 0 || s === "期权") return "is-opt";
|
||||||
if (s.indexOf("顺势") >= 0) return "is-roll";
|
if (s.indexOf("顺势") >= 0) return "is-roll";
|
||||||
if (s.indexOf("趋势") >= 0) return "is-trend";
|
if (s.indexOf("趋势") >= 0) return "is-trend";
|
||||||
if (s.indexOf("关键位") >= 0) return "is-key";
|
if (s.indexOf("关键位") >= 0) return "is-key";
|
||||||
@@ -187,6 +188,20 @@
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function optionsNetPnl(p) {
|
||||||
|
if (!p || typeof p !== "object") return null;
|
||||||
|
if (p.net_pnl != null && Number.isFinite(Number(p.net_pnl))) return Number(p.net_pnl);
|
||||||
|
const preview = p.close_preview || {};
|
||||||
|
if (preview.estimated_pnl != null && Number.isFinite(Number(preview.estimated_pnl))) {
|
||||||
|
return Number(preview.estimated_pnl);
|
||||||
|
}
|
||||||
|
if (preview.total_received != null && p.premium_paid != null) {
|
||||||
|
const n = Number(preview.total_received) - Number(p.premium_paid);
|
||||||
|
return Number.isFinite(n) ? n : null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function renderDashboardOptionsTable(positions) {
|
function renderDashboardOptionsTable(positions) {
|
||||||
const pos = Array.isArray(positions) ? positions : [];
|
const pos = Array.isArray(positions) ? positions : [];
|
||||||
if (!pos.length) return "";
|
if (!pos.length) return "";
|
||||||
@@ -198,9 +213,10 @@
|
|||||||
: (p.opt_type || "").toUpperCase() === "P"
|
: (p.opt_type || "").toUpperCase() === "P"
|
||||||
? "Put"
|
? "Put"
|
||||||
: p.opt_type || "—";
|
: p.opt_type || "—";
|
||||||
const source = String(p.source_label || p.source || "—");
|
const source = String(p.source_label || p.source || "纯期权");
|
||||||
const target = String(p.target_monitor_text || "—");
|
const target = String(p.target_monitor_text || "—");
|
||||||
const targetCls = target && target !== "—" ? "dash-target-monitor is-on" : "dash-target-monitor";
|
const targetCls = target && target !== "—" ? "dash-target-monitor is-on" : "dash-target-monitor";
|
||||||
|
const net = optionsNetPnl(p);
|
||||||
return `<tr>
|
return `<tr>
|
||||||
<td><span class="dash-pos-source ${sourceBadgeClass(source)}">${esc(source)}</span></td>
|
<td><span class="dash-pos-source ${sourceBadgeClass(source)}">${esc(source)}</span></td>
|
||||||
<td title="${esc(p.inst_id || "")}">${esc(shortDashInst(p.inst_id))}</td>
|
<td title="${esc(p.inst_id || "")}">${esc(shortDashInst(p.inst_id))}</td>
|
||||||
@@ -208,7 +224,7 @@
|
|||||||
<td>${dashOptionsExpiryCd(p.exp_time_ms != null ? p.exp_time_ms : p.exp_time)}</td>
|
<td>${dashOptionsExpiryCd(p.exp_time_ms != null ? p.exp_time_ms : p.exp_time)}</td>
|
||||||
<td>${p.idx_px != null ? fmt(p.idx_px, 0) : "—"}</td>
|
<td>${p.idx_px != null ? fmt(p.idx_px, 0) : "—"}</td>
|
||||||
<td><span class="${targetCls}">${esc(target)}</span></td>
|
<td><span class="${targetCls}">${esc(target)}</span></td>
|
||||||
<td class="${pnlClass(p.upl)}">${p.upl != null ? pnlSigned(p.upl, 2) : "—"}</td>
|
<td class="${pnlClass(net)}">${net != null ? pnlSigned(net, 2) : "—"}</td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
@@ -217,7 +233,7 @@
|
|||||||
<div class="dash-table-wrap dash-options-table-wrap">
|
<div class="dash-table-wrap dash-options-table-wrap">
|
||||||
<table class="dash-table dash-options-table">
|
<table class="dash-table dash-options-table">
|
||||||
<thead><tr>
|
<thead><tr>
|
||||||
<th>来源</th><th>合约</th><th>类型</th><th>到期倒计时</th><th>指数</th><th>目标监控</th><th>浮盈</th>
|
<th>来源</th><th>合约</th><th>类型</th><th>到期倒计时</th><th>指数</th><th>目标监控</th><th>净盈亏</th>
|
||||||
</tr></thead>
|
</tr></thead>
|
||||||
<tbody>${rows}</tbody>
|
<tbody>${rows}</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<link rel="stylesheet" href="/assets/trade_stats_calendar.css?v=4" />
|
<link rel="stylesheet" href="/assets/trade_stats_calendar.css?v=4" />
|
||||||
<link rel="stylesheet" href="/assets/account_risk_badge.css?v=4" />
|
<link rel="stylesheet" href="/assets/account_risk_badge.css?v=4" />
|
||||||
<script src="/assets/account_risk_badge.js?v=4"></script>
|
<script src="/assets/account_risk_badge.js?v=4"></script>
|
||||||
<link rel="stylesheet" href="/assets/dashboard.css?v=20260717-dash-source" />
|
<link rel="stylesheet" href="/assets/dashboard.css?v=20260717-dash-opt-net" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app-bg" aria-hidden="true"></div>
|
<div class="app-bg" aria-hidden="true"></div>
|
||||||
@@ -1373,7 +1373,7 @@
|
|||||||
<script src="/assets/archive.js?v=20260717-archive-cal-chart"></script>
|
<script src="/assets/archive.js?v=20260717-archive-cal-chart"></script>
|
||||||
<script src="/assets/quotes.js?v=20260717-quotes-feed"></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/funds.js?v=20260717-funds-scroll-fix"></script>
|
||||||
<script src="/assets/dashboard.js?v=20260717-dash-source"></script>
|
<script src="/assets/dashboard.js?v=20260717-dash-opt-net"></script>
|
||||||
<script src="/assets/strategy.js?v=3"></script>
|
<script src="/assets/strategy.js?v=3"></script>
|
||||||
<script src="/assets/help.js?v=1"></script>
|
<script src="/assets/help.js?v=1"></script>
|
||||||
<script src="/assets/logs.js?v=1"></script>
|
<script src="/assets/logs.js?v=1"></script>
|
||||||
|
|||||||
@@ -51,8 +51,9 @@ class TestDashboardPositionSource(unittest.TestCase):
|
|||||||
"顺势加仓",
|
"顺势加仓",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_options_plain_is_dash(self):
|
def test_options_plain_is_pure(self):
|
||||||
self.assertEqual(_options_source_label({"source": "option", "source_label": "纯期权"}), "—")
|
self.assertEqual(_options_source_label({"source": "option", "source_label": "纯期权"}), "纯期权")
|
||||||
|
self.assertEqual(_options_source_label({"source": "option"}), "纯期权")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
_options_source_label({"source": "options_options", "source_label": "期期对冲"}),
|
_options_source_label({"source": "options_options", "source_label": "期期对冲"}),
|
||||||
"期期对冲",
|
"期期对冲",
|
||||||
|
|||||||
Reference in New Issue
Block a user