Show both Call and Put legs for OO positions in control.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-07 17:40:45 +08:00
parent 3b5a8c1ff9
commit bd7640e36a
2 changed files with 146 additions and 68 deletions
+30 -2
View File
@@ -169,6 +169,12 @@ async def fleet_status(_tok: Annotated[str, Depends(require_fleet_token)]) -> di
except Exception: except Exception:
st = {} st = {}
pos = st.get("position") if isinstance(st.get("position"), dict) else {} pos = st.get("position") if isinstance(st.get("position"), dict) else {}
hedge_mode = str(
pos.get("hedge_mode")
or st.get("hedge_mode")
or ""
).strip().lower()
is_oo = hedge_mode == "option_option" or bool(pos.get("option2_inst_id"))
legs: list[dict] = [] legs: list[dict] = []
if pos.get("has_position") or str(pos.get("status") or "") in ( if pos.get("has_position") or str(pos.get("status") or "") in (
"open", "open",
@@ -176,7 +182,8 @@ async def fleet_status(_tok: Annotated[str, Depends(require_fleet_token)]) -> di
"option_closed_perp_pending", "option_closed_perp_pending",
"opening", "opening",
): ):
if pos.get("perp_side") or pos.get("perp_inst_id"): # 期期无永续腿;有 perp 才展示
if not is_oo and (pos.get("perp_side") or pos.get("perp_inst_id")):
legs.append( legs.append(
{ {
"kind": "perp", "kind": "perp",
@@ -194,7 +201,7 @@ async def fleet_status(_tok: Annotated[str, Depends(require_fleet_token)]) -> di
legs.append( legs.append(
{ {
"kind": "option", "kind": "option",
"side": pos.get("option_side"), "side": pos.get("option_side") or ("call" if is_oo else None),
"inst_id": pos.get("option_inst_id"), "inst_id": pos.get("option_inst_id"),
"qty": pos.get("option_qty_eth"), "qty": pos.get("option_qty_eth"),
"avg_px": pos.get("option_entry_px"), "avg_px": pos.get("option_entry_px"),
@@ -202,6 +209,22 @@ async def fleet_status(_tok: Annotated[str, Depends(require_fleet_token)]) -> di
"upl": pos.get("option_upl"), "upl": pos.get("option_upl"),
"margin": None, "margin": None,
"premium": pos.get("initial_premium"), "premium": pos.get("initial_premium"),
"strike": pos.get("strike"),
}
)
if pos.get("option2_inst_id") or pos.get("option2_side"):
legs.append(
{
"kind": "option",
"side": pos.get("option2_side") or "put",
"inst_id": pos.get("option2_inst_id"),
"qty": pos.get("option2_qty_eth"),
"avg_px": pos.get("option2_entry_px"),
"mark_px": pos.get("option2_mark_px"),
"upl": pos.get("option2_upl"),
"margin": None,
"premium": pos.get("initial_premium2"),
"strike": pos.get("strike2"),
} }
) )
@@ -292,6 +315,7 @@ async def fleet_status(_tok: Annotated[str, Depends(require_fleet_token)]) -> di
if ( if (
hm := str( hm := str(
st.get("hedge_mode") st.get("hedge_mode")
or ("option_option" if is_oo else None)
or db.get_setting("hedge_mode", settings.hedge_mode) or db.get_setting("hedge_mode", settings.hedge_mode)
or settings.hedge_mode or settings.hedge_mode
or "perp_option" or "perp_option"
@@ -306,15 +330,19 @@ async def fleet_status(_tok: Annotated[str, Depends(require_fleet_token)]) -> di
"position": { "position": {
"status": pos.get("status") or ("open" if pos.get("has_position") else "flat"), "status": pos.get("status") or ("open" if pos.get("has_position") else "flat"),
"has_position": bool(pos.get("has_position")), "has_position": bool(pos.get("has_position")),
"hedge_mode": "option_option" if is_oo else "perp_option",
"group_id": pos.get("group_id"), "group_id": pos.get("group_id"),
"open_at_ms": pos.get("open_at_ms"), "open_at_ms": pos.get("open_at_ms"),
"initial_premium": pos.get("initial_premium"), "initial_premium": pos.get("initial_premium"),
"initial_premium2": pos.get("initial_premium2"),
"perp_margin": pos.get("perp_margin"), "perp_margin": pos.get("perp_margin"),
"exit_target_usdt": pos.get("exit_target_usdt"), "exit_target_usdt": pos.get("exit_target_usdt"),
"net_pnl": pos.get("net_pnl"), "net_pnl": pos.get("net_pnl"),
"perp_upl": pos.get("perp_upl"), "perp_upl": pos.get("perp_upl"),
"option_upl": pos.get("option_upl"), "option_upl": pos.get("option_upl"),
"option2_upl": pos.get("option2_upl"),
"strike": pos.get("strike"), "strike": pos.get("strike"),
"strike2": pos.get("strike2"),
"expiry_ymd": pos.get("expiry_ymd"), "expiry_ymd": pos.get("expiry_ymd"),
"legs": legs, "legs": legs,
}, },
+116 -66
View File
@@ -1196,40 +1196,74 @@ export default function MonitorPage() {
<section className="modal-section"> <section className="modal-section">
<h4></h4> <h4></h4>
<p className="meta"> {(() => {
{String(detail.position.status || "flat")} const pos = detail.position;
{detail.position.net_pnl != null ? ( const isOo =
<> String(pos.hedge_mode || detail.strat.hedge_mode || "")
{" · 净浮盈 "} .toLowerCase() === "option_option" ||
<span className={pnlClass(detail.position.net_pnl)}> detailLegs.some(
{fmt(detail.position.net_pnl, 2)} USDT (lg) =>
</span> String(lg.side || "").toLowerCase() === "put" &&
</> String(lg.kind || "") === "option",
) : null} );
{detail.position.perp_margin != null ? ( const prem1 = Number(pos.initial_premium);
<> const prem2 = Number(pos.initial_premium2);
{" · 保证金 "} const premTotal =
<span className="mono"> (Number.isFinite(prem1) ? prem1 : 0) +
{fmt(detail.position.perp_margin, 2)} U (Number.isFinite(prem2) ? prem2 : 0);
</span> const strikeLabel = isOo
</> ? `C@${
) : null} pos.strike != null ? Math.round(Number(pos.strike)) : "—"
{detail.position.initial_premium != null ? ( } / P@${
<> pos.strike2 != null
{" · 权利金 "} ? Math.round(Number(pos.strike2))
<span className="mono">
{fmt(detail.position.initial_premium, 2)} U
</span>
</>
) : null}
{detail.position.expiry_ymd
? ` · 到期 ${String(detail.position.expiry_ymd)} @ ${
detail.position.strike != null
? Math.round(Number(detail.position.strike))
: "—" : "—"
}` }`
: ""} : pos.strike != null
</p> ? String(Math.round(Number(pos.strike)))
: "—";
return (
<p className="meta">
{String(pos.status || "flat")}
{isOo ? (
<>
{" · "}
<span className="mode-hedge-oo"></span>
</>
) : null}
{pos.net_pnl != null ? (
<>
{" · 净浮盈 "}
<span className={pnlClass(pos.net_pnl)}>
{fmt(pos.net_pnl, 2)} USDT
</span>
</>
) : null}
{pos.perp_margin != null && !isOo ? (
<>
{" · 保证金 "}
<span className="mono">
{fmt(pos.perp_margin, 2)} U
</span>
</>
) : null}
{premTotal > 0 || pos.initial_premium != null ? (
<>
{" · 权利金 "}
<span className="mono">
{isOo && Number.isFinite(prem2)
? `${fmt(prem1, 2)}+${fmt(prem2, 2)}=${fmt(premTotal, 2)}`
: fmt(pos.initial_premium, 2)}{" "}
U
</span>
</>
) : null}
{pos.expiry_ymd
? ` · 到期 ${String(pos.expiry_ymd)} ${strikeLabel}`
: ""}
</p>
);
})()}
{detailLegs.length ? ( {detailLegs.length ? (
<div className="legs-table-wrap"> <div className="legs-table-wrap">
<table className="legs-table"> <table className="legs-table">
@@ -1247,40 +1281,56 @@ export default function MonitorPage() {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{detailLegs.map((lg, i) => ( {detailLegs.map((lg, i) => {
<tr key={`${lg.inst_id || i}`}> const kind = String(lg.kind || "");
<td>{String(lg.kind || "")}</td> const side = String(lg.side || "").toLowerCase();
<td>{String(lg.side || "—")}</td> const legLabel =
<td className="mono">{String(lg.inst_id || "—")}</td> kind === "perp"
<td className="mono">{fmt(lg.qty, 2)}</td> ? "永续"
<td className="mono"> : side === "call"
{fmtExPx(lg.kind, lg.avg_px)} ? "Call"
</td> : side === "put"
<td className="mono"> ? "Put"
{fmtExPx(lg.kind, lg.mark_px)} : kind || "—";
</td> return (
<td className="mono"> <tr key={`${lg.inst_id || i}`}>
{lg.kind === "perp" <td>
? fmt( {side === "put" ? (
lg.margin ?? detail.position.perp_margin, <span className="mode-hedge-oo">{legLabel}</span>
2, ) : (
) legLabel
: "—"} )}
</td> </td>
<td className="mono"> <td>{String(lg.side || "—")}</td>
{lg.kind === "option" <td className="mono">
? fmt( {String(lg.inst_id || "—")}
lg.premium ?? </td>
detail.position.initial_premium, <td className="mono">{fmt(lg.qty, 2)}</td>
2, <td className="mono">
) {fmtExPx(lg.kind, lg.avg_px)}
: "—"} </td>
</td> <td className="mono">
<td className={`mono ${pnlClass(lg.upl)}`}> {fmtExPx(lg.kind, lg.mark_px)}
{fmt(lg.upl, 2)} </td>
</td> <td className="mono">
</tr> {kind === "perp"
))} ? fmt(
lg.margin ?? detail.position.perp_margin,
2,
)
: "—"}
</td>
<td className="mono">
{kind === "option" || kind === "option2"
? fmt(lg.premium, 2)
: "—"}
</td>
<td className={`mono ${pnlClass(lg.upl)}`}>
{fmt(lg.upl, 2)}
</td>
</tr>
);
})}
</tbody> </tbody>
</table> </table>
</div> </div>