Hide perp signal direction in option-option mode.

Plan panel shows Call+Put structure and OO selection/exit labels instead.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-07 16:18:40 +08:00
parent 42e56d940c
commit 8d3c181246
3 changed files with 142 additions and 51 deletions
+30
View File
@@ -261,6 +261,36 @@ class StrategyEngine:
in ("perp_option", "option_option") in ("perp_option", "option_option")
else "perp_option" else "perp_option"
), ),
"oo_amplitude_pct": float(
self.ledger.get_setting_float(
"oo_amplitude_pct", s.oo_amplitude_pct
)
or s.oo_amplitude_pct
),
"oo_amplitude_hours": float(
self.ledger.get_setting_float(
"oo_amplitude_hours", s.oo_amplitude_hours
)
or s.oo_amplitude_hours
),
"oo_min_option_hours": float(
self.ledger.get_setting_float(
"oo_min_option_hours", s.oo_min_option_hours
)
or s.oo_min_option_hours
),
"oo_min_leverage": float(
self.ledger.get_setting_float(
"oo_min_leverage", s.oo_min_leverage
)
or s.oo_min_leverage
),
"oo_reward_ratio": float(
self.ledger.get_setting_float(
"oo_reward_ratio", s.oo_reward_ratio
)
or s.oo_reward_ratio
),
"risk_perp_unit": risk_perp_unit, "risk_perp_unit": risk_perp_unit,
"risk_option_unit": risk_option_unit, "risk_option_unit": risk_option_unit,
"risk_exit_unit": risk_exit_unit, "risk_exit_unit": risk_exit_unit,
+5
View File
@@ -348,6 +348,11 @@ export type PlanState = {
risk_effective_loss_pct?: number; risk_effective_loss_pct?: number;
risk_loss_pct?: number; risk_loss_pct?: number;
hedge_mode?: "perp_option" | "option_option"; hedge_mode?: "perp_option" | "option_option";
oo_amplitude_pct?: number;
oo_amplitude_hours?: number;
oo_min_option_hours?: number;
oo_min_leverage?: number;
oo_reward_ratio?: number;
sizing_mode?: "manual" | "risk_based"; sizing_mode?: "manual" | "risk_based";
risk_based?: boolean; risk_based?: boolean;
ledger: { equity: number; available: number; reserved: number }; ledger: { equity: number; available: number; reserved: number };
+107 -51
View File
@@ -185,6 +185,10 @@ export default function PlanPage() {
); );
const pos = plan?.position; const pos = plan?.position;
const isOo =
plan?.hedge_mode === "option_option" ||
pos?.hedge_mode === "option_option" ||
!!pos?.option2_inst_id;
const open = !!pos?.has_position; const open = !!pos?.has_position;
const exitMode = plan?.exit_mode ?? "fixed_usdt"; const exitMode = plan?.exit_mode ?? "fixed_usdt";
const riskBased = const riskBased =
@@ -202,20 +206,28 @@ export default function PlanPage() {
: null; : null;
const netPnl = pos?.net_pnl ?? 0; const netPnl = pos?.net_pnl ?? 0;
const movePct = pos?.move_pct ?? 0; const movePct = pos?.move_pct ?? 0;
const exitRuleLabel = const ooRatio = Number(plan?.oo_reward_ratio ?? 2);
exitMode === "premium_multiple" const exitRuleLabel = isOo
? exitTarget != null
? `${riskLocked ? "锁定 " : ""}净盈≥${fmt(exitTarget)} U(预算×${fmt(ooRatio, 1)}`
: `净盈≥预算×${fmt(ooRatio, 1)}(达标只平盈利腿)`
: exitMode === "premium_multiple"
? `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}` ? `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}`
: riskBased : riskBased
? exitTarget != null ? exitTarget != null
? `${riskLocked ? "锁定 " : "固定 "}${fmt(exitTarget)} U(基数${fmt(plan?.risk_exit_unit ?? 15)}` ? `${riskLocked ? "锁定 " : "固定 "}${fmt(exitTarget)} U(基数${fmt(plan?.risk_exit_unit ?? 15)}`
: `待估算(基数${fmt(plan?.risk_exit_unit ?? 15)}` : `待估算(基数${fmt(plan?.risk_exit_unit ?? 15)}`
: `固定 ${fmt(plan?.net_profit_target ?? 15)} U`; : `固定 ${fmt(plan?.net_profit_target ?? 15)} U`;
const riskRatioLabel = `比例${Number(plan?.risk_perp_unit ?? 1)}:${Number(plan?.risk_option_unit ?? 2)}`; const riskRatioLabel = isOo
? "Call:Put 1:1"
: `比例${Number(plan?.risk_perp_unit ?? 1)}:${Number(plan?.risk_option_unit ?? 2)}`;
const sizingModeLabel = riskBased const sizingModeLabel = riskBased
? [ ? [
plan?.hedge_mode === "option_option" ? "期期对冲" : "以损定仓", isOo ? "期期对冲" : "以损定仓",
riskRatioLabel, riskRatioLabel,
plan?.risk_last_k != null ? `k=${fmt(plan.risk_last_k, 1)}` : null, !isOo && plan?.risk_last_k != null
? `k=${fmt(plan.risk_last_k, 1)}`
: null,
riskLocked riskLocked
? exitTarget != null ? exitTarget != null
? `目标锁定${fmt(exitTarget, 2)}U` ? `目标锁定${fmt(exitTarget, 2)}U`
@@ -236,7 +248,7 @@ export default function PlanPage() {
] ]
.filter(Boolean) .filter(Boolean)
.join(" ") .join(" ")
: plan?.hedge_mode === "option_option" : isOo
? "期期对冲" ? "期期对冲"
: "手动仓位"; : "手动仓位";
const phaseLabel = PHASE_ZH[plan?.phase || ""] || plan?.phase || "—"; const phaseLabel = PHASE_ZH[plan?.phase || ""] || plan?.phase || "—";
@@ -247,8 +259,18 @@ export default function PlanPage() {
: plan?.atm_open_offset_enabled : plan?.atm_open_offset_enabled
? `ATM偏差≤${fmt(plan?.max_atm_open_offset ?? 3, 0)}` ? `ATM偏差≤${fmt(plan?.max_atm_open_offset ?? 3, 0)}`
: "ATM偏差关"; : "ATM偏差关";
const exitDetail = const ooSelectLabel = [
exitMode === "premium_multiple" `振幅≥${fmt(plan?.oo_amplitude_pct ?? 1.5, 1)}%/${fmt(plan?.oo_amplitude_hours ?? 12, 0)}h`,
"虚值Call@高·Put@低",
`剩余≥${fmt(plan?.oo_min_option_hours ?? 24, 0)}h`,
`杠杆≥${fmt(plan?.oo_min_leverage ?? 200, 0)}x`,
displayOptQty != null ? `单腿${fmt(displayOptQty, 1)}ETH` : null,
]
.filter(Boolean)
.join(" · ");
const exitDetail = isOo
? `净盈利≥预算×${fmt(ooRatio, 1)} → 只平盈利腿,亏损腿残留`
: exitMode === "premium_multiple"
? `净盈利≥初始权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}` ? `净盈利≥初始权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}`
: `净盈利≥${fmt(plan?.net_profit_target ?? 15)} U`; : `净盈利≥${fmt(plan?.net_profit_target ?? 15)} U`;
const expiryMs = const expiryMs =
@@ -328,26 +350,40 @@ export default function PlanPage() {
</p> </p>
<p> <p>
<span className="plan-rules-k"></span> <span className="plan-rules-k"></span>
{" "} {isOo ? (
{displayPerpQty != null <>
? `${fmt(displayPerpQty, 2)} ETH` {ooSelectLabel}
: "—"}{" "} {plan?.skip_weekends ? " · 周末跳过开仓" : ""}
/ {fmt(plan?.leverage, 0)}x {plan?.one_expiry_per_day !== false
· {" "} ? " · 每个到期只开一次"
{displayOptQty != null : ""}
? `${fmt(displayOptQty, 2)} ETH` </>
: "—"}{" "} ) : (
· <>
{fmt(plan?.min_option_hours, 0)}h · {" "}
{fmt(plan?.min_option_leverage, 0)}x · {atmRule} {displayPerpQty != null
{plan?.skip_weekends ? " · 周末跳过开仓" : ""} ? `${fmt(displayPerpQty, 2)} ETH`
{plan?.one_expiry_per_day !== false : "—"}{" "}
? " · 每个到期只开一次" / {fmt(plan?.leverage, 0)}x
: ""} · {" "}
{displayOptQty != null
? `${fmt(displayOptQty, 2)} ETH`
: "—"}{" "}
·
{fmt(plan?.min_option_hours, 0)}h ·
{fmt(plan?.min_option_leverage, 0)}x · {atmRule}
{plan?.skip_weekends ? " · 周末跳过开仓" : ""}
{plan?.one_expiry_per_day !== false
? " · 每个到期只开一次"
: ""}
</>
)}
</p> </p>
<p> <p>
<span className="plan-rules-k"></span> <span className="plan-rules-k"></span>
{exitDetail} {isOo
? `${exitDetail};否则到期结算`
: `${exitDetail} → 双腿全平;期权远虚则只平永续、期权到期结算;未达标拖到期`}
</p> </p>
<p> <p>
<span className="plan-rules-k"></span> <span className="plan-rules-k"></span>
@@ -460,24 +496,30 @@ export default function PlanPage() {
<div className="kv"> <div className="kv">
<span></span> <span></span>
<span className="mono"> <span className="mono">
{isOo ? (
{displayPerpQty != null ooSelectLabel
? `${fmt(displayPerpQty, 2)}ETH` ) : (
: "—"} <>
/
{displayOptQty != null {displayPerpQty != null
? `${fmt(displayOptQty, 2)}ETH` ? `${fmt(displayPerpQty, 2)}ETH`
: "—"}{" "} : "—"}
· /
{fmt(plan?.min_option_hours, 0)}h · {displayOptQty != null
{fmt(plan?.min_option_leverage, 0)}x ? `${fmt(displayOptQty, 2)}ETH`
{plan?.fixed_direction_enabled : "—"}{" "}
? plan?.fixed_perp_side === "short" ·
? " · 固定空+Call(实/平)" {fmt(plan?.min_option_hours, 0)}h ·
: " · 固定多+Put(实/平)" {fmt(plan?.min_option_leverage, 0)}x
: plan?.atm_open_offset_enabled {plan?.fixed_direction_enabled
? ` · ATM偏差≤${fmt(plan?.max_atm_open_offset ?? 3, 0)}` ? plan?.fixed_perp_side === "short"
: " · ATM偏差关"} ? " · 固定空+Call(实/平)"
: " · 固定多+Put(实/平)"
: plan?.atm_open_offset_enabled
? ` · ATM偏差≤${fmt(plan?.max_atm_open_offset ?? 3, 0)}`
: " · ATM偏差关"}
</>
)}
</span> </span>
</div> </div>
<div className="kv"> <div className="kv">
@@ -525,10 +567,20 @@ export default function PlanPage() {
</span> </span>
</span> </span>
</div> </div>
<div className="kv"> {isOo ? (
<span></span> <div className="kv">
<span className="mono">{biasTag}</span> <span></span>
</div> <span className="mono">
<span className="tag"> Call + Put</span>
<span className="meta"> · </span>
</span>
</div>
) : (
<div className="kv">
<span></span>
<span className="mono">{biasTag}</span>
</div>
)}
<div className="kv"> <div className="kv">
<span></span> <span></span>
<span className="mono">{exitRuleLabel}</span> <span className="mono">{exitRuleLabel}</span>
@@ -540,7 +592,7 @@ export default function PlanPage() {
{open ? fmt(netPnl) : "—"} {open ? fmt(netPnl) : "—"}
</span> </span>
{" / "} {" / "}
{open || exitMode === "fixed_usdt" {open || exitMode === "fixed_usdt" || isOo
? exitTarget != null ? exitTarget != null
? fmt(exitTarget) ? fmt(exitTarget)
: "—" : "—"
@@ -548,11 +600,15 @@ export default function PlanPage() {
</span> </span>
</div> </div>
<div className="kv"> <div className="kv">
<span>/</span> <span>{isOo ? "Call/Put 浮盈" : "永续/期权浮盈"}</span>
<span className="mono"> <span className="mono">
<span className={pnlClass(pos?.perp_upl)}>{fmt(pos?.perp_upl)}</span> <span className={pnlClass(pos?.perp_upl)}>
{fmt(isOo ? pos?.option_upl : pos?.perp_upl)}
</span>
{" / "} {" / "}
<span className={pnlClass(pos?.option_upl)}>{fmt(pos?.option_upl)}</span> <span className={pnlClass(isOo ? pos?.option2_upl : pos?.option_upl)}>
{fmt(isOo ? pos?.option2_upl : pos?.option_upl)}
</span>
</span> </span>
</div> </div>
{plan?.mode === "LIVE" && open ? ( {plan?.mode === "LIVE" && open ? (