Show both Call and Put legs for OO positions in control.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user