Show risk params box on control cards and modal.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-30 14:58:30 +08:00
parent c5f477b8bc
commit 67f22626e9
4 changed files with 125 additions and 14 deletions
+93 -14
View File
@@ -50,6 +50,95 @@ function hasOpenPosition(position: Record<string, unknown>): boolean {
return OPEN_STATUSES.has(st);
}
function unitLabel(v: unknown): string {
const n = Number(v);
if (!Number.isFinite(n)) return "—";
if (Number.isInteger(n)) return String(n);
return n.toFixed(2).replace(/\.?0+$/, "");
}
function leveragePair(strat: Record<string, unknown>): string {
const lev = Number(strat.leverage);
const opt = Number(strat.min_option_leverage);
const a = Number.isFinite(lev) ? `${Math.round(lev)}x` : "—";
const b = Number.isFinite(opt) ? `${Math.round(opt)}x` : "—";
return `${a}/${b}`;
}
type RiskLines = {
riskBased: boolean;
sizing: string;
lossPct: string | null;
exit: string;
openRatio: string | null;
leverage: string;
};
function riskLines(strat: Record<string, unknown>): RiskLines {
const riskBased =
strat.sizing_mode === "risk_based" || strat.risk_based === true;
const exitMode = String(strat.exit_mode || "fixed_usdt");
let exit: string;
if (riskBased) {
exit = `基数${unitLabel(strat.risk_exit_unit ?? 15)}`;
} else if (exitMode === "premium_multiple") {
exit = `权利金×${fmt(strat.premium_exit_multiple ?? 1, 2)}`;
} else {
exit = `固定 ${fmt(strat.net_profit_target ?? strat.exit_target_usdt, 2)}U`;
}
const lossN = Number(strat.risk_loss_pct);
const lossPct =
riskBased && Number.isFinite(lossN)
? `${fmt(lossN, lossN % 1 === 0 ? 0 : 2)}%`
: null;
const openRatio = riskBased
? `${unitLabel(strat.risk_perp_unit ?? 1)}:${unitLabel(strat.risk_option_unit ?? 2)}`
: null;
return {
riskBased,
sizing: riskBased ? "以损定仓" : "手动仓位",
lossPct,
exit,
openRatio,
leverage: leveragePair(strat),
};
}
function RiskParamsBox({ strat }: { strat: Record<string, unknown> }) {
const r = riskLines(strat);
return (
<div className="risk-box">
<div className="risk-box-title"></div>
<dl className="kv risk-kv">
<div>
<dt></dt>
<dd>{r.sizing}</dd>
</div>
{r.lossPct ? (
<div>
<dt></dt>
<dd>{r.lossPct}</dd>
</div>
) : null}
<div>
<dt></dt>
<dd>{r.exit}</dd>
</div>
{r.openRatio ? (
<div>
<dt></dt>
<dd>{r.openRatio}</dd>
</div>
) : null}
<div>
<dt></dt>
<dd>{r.leverage}</dd>
</div>
</dl>
</div>
);
}
/** 页面级快照:整页刷新前保留上次卡片,避免空白等待 */
let cachedNodes: NodeCard[] = [];
let cachedSseSec = 1;
@@ -374,6 +463,7 @@ export default function MonitorPage() {
</dd>
</div>
</dl>
<RiskParamsBox strat={s.strat} />
{n.fleet_error ? <div className="err soft">{n.fleet_error}</div> : null}
{n.error ? <div className="err soft">{n.error}</div> : null}
<div className="node-actions" onClick={(e) => e.stopPropagation()}>
@@ -447,6 +537,7 @@ export default function MonitorPage() {
<section className="modal-section">
<h4></h4>
<RiskParamsBox strat={detail.strat} />
<dl className="kv detail-kv">
<div>
<dt></dt>
@@ -471,11 +562,8 @@ export default function MonitorPage() {
</dd>
</div>
<div>
<dt> / </dt>
<dd>
{fmt(detail.strat.leverage, 1)}x /{" "}
{String(detail.strat.perp_margin_mode || "—")}
</dd>
<dt></dt>
<dd>{String(detail.strat.perp_margin_mode || "—")}</dd>
</div>
<div>
<dt> /</dt>
@@ -488,15 +576,6 @@ export default function MonitorPage() {
<dt></dt>
<dd>{fmt(detail.strat.exit_target_usdt, 2)} USDT</dd>
</div>
<div>
<dt></dt>
<dd>
{String(detail.strat.sizing_mode || "—")}
{detail.strat.risk_last_k != null
? ` · k=${fmt(detail.strat.risk_last_k, 2)}`
: ""}
</dd>
</div>
<div>
<dt></dt>
<dd>{fmt(detail.index_px, 2)}</dd>