Hide perp-option settings when option-option hedge is selected.

Show OO selection and reward-ratio fields in their tabs instead.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-07 16:21:36 +08:00
parent 8d3c181246
commit cd88fa5d83
+165 -76
View File
@@ -368,10 +368,12 @@ export default function SettingsPage() {
setStratOk(""); setStratOk("");
setErr(""); setErr("");
const isLive = mode === "LIVE"; const isLive = mode === "LIVE";
const oo = hedgeMode === "option_option";
try { try {
const body: Record<string, unknown> = { const body: Record<string, unknown> = {
fee_rate: fee, fee_rate: fee,
exit_mode: sizingMode === "risk_based" ? "fixed_usdt" : exitMode, exit_mode:
oo || sizingMode === "risk_based" ? "fixed_usdt" : exitMode,
premium_exit_multiple: premMult, premium_exit_multiple: premMult,
rest_seconds: rest, rest_seconds: rest,
live_order_interval_sec: orderInterval, live_order_interval_sec: orderInterval,
@@ -381,16 +383,16 @@ export default function SettingsPage() {
perp_margin_mode: perpMarginMode, perp_margin_mode: perpMarginMode,
min_option_hours: minHours, min_option_hours: minHours,
min_option_leverage: minOptLev, min_option_leverage: minOptLev,
atm_open_offset_enabled: atmOffOn, atm_open_offset_enabled: oo ? false : atmOffOn,
max_atm_open_offset: maxAtmOff, max_atm_open_offset: maxAtmOff,
fixed_direction_enabled: fixedDirOn, fixed_direction_enabled: oo ? false : fixedDirOn,
fixed_perp_side: fixedPerpSide, fixed_perp_side: fixedPerpSide,
close_bid_mark_max_pct: closeDevPct, close_bid_mark_max_pct: closeDevPct,
residual_min_premium_pct: residualMinPremPct, residual_min_premium_pct: residualMinPremPct,
show_manual_trade_buttons: showManualTrade, show_manual_trade_buttons: showManualTrade,
sizing_mode: sizingMode, sizing_mode: oo ? "risk_based" : sizingMode,
risk_leverage_basis: riskLeverageBasis, risk_leverage_basis: riskLeverageBasis,
risk_loss_mode: riskLossMode, risk_loss_mode: oo ? "percent" : riskLossMode,
risk_loss_pct: riskLossPct, risk_loss_pct: riskLossPct,
risk_loss_usdt: riskLossUsdt, risk_loss_usdt: riskLossUsdt,
risk_capital_source: riskCapitalSource, risk_capital_source: riskCapitalSource,
@@ -399,8 +401,8 @@ export default function SettingsPage() {
risk_option_unit: riskOptUnit, risk_option_unit: riskOptUnit,
risk_exit_unit: riskExitUnit, risk_exit_unit: riskExitUnit,
martingale_enabled: martingale_enabled:
sizingMode === "risk_based" && (oo || sizingMode === "risk_based") &&
riskLossMode === "percent" && (oo || riskLossMode === "percent") &&
riskLossPct <= 3 && riskLossPct <= 3 &&
martingaleOn, martingaleOn,
martingale_start_after_loss_days: martingaleStartAfter, martingale_start_after_loss_days: martingaleStartAfter,
@@ -543,6 +545,8 @@ export default function SettingsPage() {
} }
} }
const isOo = hedgeMode === "option_option";
return ( return (
<div className="settings-page"> <div className="settings-page">
<h2 style={{ marginTop: 0 }}>系统设置</h2> <h2 style={{ marginTop: 0 }}>系统设置</h2>
@@ -676,6 +680,8 @@ export default function SettingsPage() {
/> />
</div> </div>
) : null} ) : null}
{!isOo ? (
<>
<div className="field"> <div className="field">
<label htmlFor="lev">永续杠杆倍数</label> <label htmlFor="lev">永续杠杆倍数</label>
<input <input
@@ -696,7 +702,9 @@ export default function SettingsPage() {
value={perpMarginMode} value={perpMarginMode}
onChange={(e) => onChange={(e) =>
setPerpMarginMode( setPerpMarginMode(
e.target.value === "isolated" ? "isolated" : "cross", e.target.value === "isolated"
? "isolated"
: "cross",
) )
} }
> >
@@ -704,6 +712,8 @@ export default function SettingsPage() {
<option value="isolated">逐仓</option> <option value="isolated">逐仓</option>
</select> </select>
</div> </div>
</>
) : null}
<div className="field"> <div className="field">
<label htmlFor="hedgeMode">对冲模式</label> <label htmlFor="hedgeMode">对冲模式</label>
<select <select
@@ -730,60 +740,7 @@ export default function SettingsPage() {
二选一。期期:近 N 小时振幅高低匹配虚值 Call/Put;达标只平盈利腿。 二选一。期期:近 N 小时振幅高低匹配虚值 Call/Put;达标只平盈利腿。
</p> </p>
</div> </div>
{hedgeMode === "option_option" ? ( {isOo ? (
<>
<div className="field">
<label htmlFor="ooAmp">振幅最小(%)</label>
<input
id="ooAmp"
className="mono"
type="number"
step="0.1"
min="0.1"
value={ooAmpPct}
onChange={(e) => setOoAmpPct(Number(e.target.value))}
/>
</div>
<div className="field">
<label htmlFor="ooAmpH">振幅回看(小时)</label>
<input
id="ooAmpH"
className="mono"
type="number"
step="1"
min="1"
value={ooAmpHours}
onChange={(e) =>
setOoAmpHours(Number(e.target.value))
}
/>
</div>
<div className="field">
<label htmlFor="ooMinH">最短剩余到期(小时)</label>
<input
id="ooMinH"
className="mono"
type="number"
step="1"
min="1"
value={ooMinHours}
onChange={(e) =>
setOoMinHours(Number(e.target.value))
}
/>
</div>
<div className="field">
<label htmlFor="ooLev">单腿最低杠杆</label>
<input
id="ooLev"
className="mono"
type="number"
step="1"
min="1"
value={ooMinLev}
onChange={(e) => setOoMinLev(Number(e.target.value))}
/>
</div>
<div className="field"> <div className="field">
<label htmlFor="ooRR">盈亏比</label> <label htmlFor="ooRR">盈亏比</label>
<input <input
@@ -799,18 +756,17 @@ export default function SettingsPage() {
/> />
<p className="hint" style={{ margin: "0.35rem 0 0" }}> <p className="hint" style={{ margin: "0.35rem 0 0" }}>
目标盈利 = 以损预算 × 盈亏比(例预算 100U、比 2 → 目标 目标盈利 = 以损预算 × 盈亏比(例预算 100U、比 2 → 目标
200U)。两腿 1:1 平分预算。 200U)。两腿 1:1 平分预算。振幅/选约参数见「选约」页。
</p> </p>
</div> </div>
</>
) : null} ) : null}
{!isOo ? (
<div className="field"> <div className="field">
<label htmlFor="sizingMode">定仓模式</label> <label htmlFor="sizingMode">定仓模式</label>
<select <select
id="sizingMode" id="sizingMode"
className="mono" className="mono"
value={sizingMode} value={sizingMode}
disabled={hedgeMode === "option_option"}
onChange={(e) => onChange={(e) =>
setSizingMode( setSizingMode(
e.target.value === "risk_based" e.target.value === "risk_based"
@@ -820,13 +776,26 @@ export default function SettingsPage() {
} }
> >
<option value="manual">手动仓位(填 ETH 名义)</option> <option value="manual">手动仓位(填 ETH 名义)</option>
<option value="risk_based">以损定仓(按亏损预算反推)</option> <option value="risk_based">
以损定仓(按亏损预算反推)
</option>
</select> </select>
</div> </div>
{sizingMode === "risk_based" ? ( ) : (
<>
<div className="field"> <div className="field">
<label htmlFor="riskLevBasis">节奏杠杆(定仓口径)</label> <label>定仓模式</label>
<div className="mono" style={{ fontSize: 13 }}>
以损定仓(期期固定 · Call/Put 1:1)
</div>
</div>
)}
{sizingMode === "risk_based" || isOo ? (
<>
{!isOo ? (
<div className="field">
<label htmlFor="riskLevBasis">
节奏杠杆(定仓口径)
</label>
<select <select
id="riskLevBasis" id="riskLevBasis"
className="mono" className="mono"
@@ -845,10 +814,13 @@ export default function SettingsPage() {
<option value="actual">实际杠杆(盘口卖一)</option> <option value="actual">实际杠杆(盘口卖一)</option>
</select> </select>
<p className="hint" style={{ margin: "0.35rem 0 0" }}> <p className="hint" style={{ margin: "0.35rem 0 0" }}>
选约杠杆:按「指数÷期权杠杆≥」估权利金算 k,权利金再便宜也不放大仓位。 选约杠杆:按「指数÷期权杠杆≥」估权利金算
k,权利金再便宜也不放大仓位。
实际杠杆:按盘口卖一定仓,便宜时 k 会变大。 实际杠杆:按盘口卖一定仓,便宜时 k 会变大。
</p> </p>
</div> </div>
) : null}
{!isOo ? (
<div className="field"> <div className="field">
<label htmlFor="riskLossMode">亏损约束</label> <label htmlFor="riskLossMode">亏损约束</label>
<select <select
@@ -864,11 +836,14 @@ export default function SettingsPage() {
if (mode !== "percent") setMartingaleOn(false); if (mode !== "percent") setMartingaleOn(false);
}} }}
> >
<option value="percent">亏损幅度(占本金 %)</option> <option value="percent">
亏损幅度(占本金 %)
</option>
<option value="absolute">亏损值(USDT)</option> <option value="absolute">亏损值(USDT)</option>
</select> </select>
</div> </div>
{riskLossMode === "percent" ? ( ) : null}
{isOo || riskLossMode === "percent" ? (
<> <>
<div className="field"> <div className="field">
<label htmlFor="riskCapSrc">本金来源</label> <label htmlFor="riskCapSrc">本金来源</label>
@@ -1072,10 +1047,18 @@ export default function SettingsPage() {
: mg != null && mg.enabled === true : mg != null && mg.enabled === true
? ` · 倍投待命(连亏${Number(mg.loss_days) || 0}天)` ? ` · 倍投待命(连亏${Number(mg.loss_days) || 0}天)`
: ""; : "";
return `k=${pk ?? "—"} · 预算=${budS}U · 估亏=${mxS}U · 永续=${perp ?? "—"} · 期权=${opt ?? "—"} · 出场=${exit ?? "—"}${basisS}${mgS}`; return isOo
? `预算=${budS}U · 估亏=${mxS}U · 出场=预算×${ooRewardRatio}${
riskPreview.option_qty_eth != null
? ` · 单腿≈${riskPreview.option_qty_eth}ETH`
: ""
}${mgS}`
: `k=${pk ?? "—"} · 预算=${budS}U · 估亏=${mxS}U · 永续=${perp ?? "—"} · 期权=${opt ?? "—"} · 出场=${exit ?? "—"}${basisS}${mgS}`;
})()} })()}
</div> </div>
</div> </div>
{!isOo ? (
<>
<div className="field"> <div className="field">
<label htmlFor="riskPerpU">永续比例(k=1)</label> <label htmlFor="riskPerpU">永续比例(k=1)</label>
<input <input
@@ -1174,6 +1157,10 @@ export default function SettingsPage() {
</div> </div>
</> </>
) : null} ) : null}
</>
) : null}
{!isOo ? (
<>
<div className="field"> <div className="field">
<label htmlFor="perp"> <label htmlFor="perp">
永续 ETH 数量 永续 ETH 数量
@@ -1220,6 +1207,8 @@ export default function SettingsPage() {
onChange={(e) => setOptQty(Number(e.target.value))} onChange={(e) => setOptQty(Number(e.target.value))}
/> />
</div> </div>
</>
) : null}
</div> </div>
</section> </section>
) : null} ) : null}
@@ -1228,6 +1217,66 @@ export default function SettingsPage() {
<section className="settings-section"> <section className="settings-section">
<h3>选约条件</h3> <h3>选约条件</h3>
<div className="settings-fields"> <div className="settings-fields">
{isOo ? (
<>
<div className="field">
<label htmlFor="ooAmp">振幅最小(%)</label>
<input
id="ooAmp"
className="mono"
type="number"
step="0.1"
min="0.1"
value={ooAmpPct}
onChange={(e) => setOoAmpPct(Number(e.target.value))}
/>
</div>
<div className="field">
<label htmlFor="ooAmpH">振幅回看(小时)</label>
<input
id="ooAmpH"
className="mono"
type="number"
step="1"
min="1"
value={ooAmpHours}
onChange={(e) =>
setOoAmpHours(Number(e.target.value))
}
/>
</div>
<div className="field">
<label htmlFor="ooMinH">最短剩余到期(小时)</label>
<input
id="ooMinH"
className="mono"
type="number"
step="1"
min="1"
value={ooMinHours}
onChange={(e) =>
setOoMinHours(Number(e.target.value))
}
/>
</div>
<div className="field">
<label htmlFor="ooLev">单腿最低杠杆</label>
<input
id="ooLev"
className="mono"
type="number"
step="1"
min="1"
value={ooMinLev}
onChange={(e) => setOoMinLev(Number(e.target.value))}
/>
<p className="hint" style={{ margin: "0.35rem 0 0" }}>
期期:同到期虚值 Call 贴高点、Put 贴低点;不使用 ATM/固定方向。
</p>
</div>
</>
) : (
<>
<div className="field"> <div className="field">
<label htmlFor="hours">期权最小剩余小时</label> <label htmlFor="hours">期权最小剩余小时</label>
<input <input
@@ -1309,6 +1358,8 @@ export default function SettingsPage() {
onChange={(e) => setMaxAtmOff(Number(e.target.value))} onChange={(e) => setMaxAtmOff(Number(e.target.value))}
/> />
</div> </div>
</>
)}
</div> </div>
</section> </section>
) : null} ) : null}
@@ -1317,6 +1368,26 @@ export default function SettingsPage() {
<section className="settings-section"> <section className="settings-section">
<h3>出场与平仓</h3> <h3>出场与平仓</h3>
<div className="settings-fields"> <div className="settings-fields">
{isOo ? (
<div className="field">
<label htmlFor="ooRRExit">盈亏比(出场)</label>
<input
id="ooRRExit"
className="mono"
type="number"
step="0.1"
min="0.5"
value={ooRewardRatio}
onChange={(e) =>
setOoRewardRatio(Number(e.target.value))
}
/>
<p className="hint" style={{ margin: "0.35rem 0 0" }}>
净盈 ≥ 预算×盈亏比时只平盈利腿;亏损腿进残留,按下方回收比例或到期结算。
</p>
</div>
) : (
<>
<div className="field"> <div className="field">
<label htmlFor="exitMode">出场规则</label> <label htmlFor="exitMode">出场规则</label>
<select <select
@@ -1387,6 +1458,8 @@ export default function SettingsPage() {
onChange={(e) => setPremMult(Number(e.target.value))} onChange={(e) => setPremMult(Number(e.target.value))}
/> />
</div> </div>
)}
</>
)} )}
<div className="field"> <div className="field">
<label htmlFor="closeDev">平仓买一/标记最大偏差(%)</label> <label htmlFor="closeDev">平仓买一/标记最大偏差(%)</label>
@@ -1402,7 +1475,9 @@ export default function SettingsPage() {
</div> </div>
<div className="field"> <div className="field">
<label htmlFor="residualPrem"> <label htmlFor="residualPrem">
残留期权回收:当前权利金 / 初始权利金 ≥(%) {isOo
? "亏损腿残留回收:当前权利金 / 初始权利金 ≥(%)"
: "残留期权回收:当前权利金 / 初始权利金 ≥(%)"}
</label> </label>
<input <input
id="residualPrem" id="residualPrem"
@@ -1532,13 +1607,25 @@ export default function SettingsPage() {
10000。 10000。
</li> </li>
) : null} ) : null}
{isOo ? (
<li>
期期对冲:强制以损定仓 + 亏损幅度%;两腿 1:1 平分预算;出场用盈亏比,不使用永续名义/比例/k。
</li>
) : (
<li> <li>
永续保证金模式:仅作用于永续(全仓/逐仓,默认全仓)。OKX 永续保证金模式:仅作用于永续(全仓/逐仓,默认全仓)。OKX
期权始终为现金/逐仓(cash),不受此选项影响。 期权始终为现金/逐仓(cash),不受此选项影响。
</li> </li>
)}
</> </>
) : null} ) : null}
{stratSub === "select" ? ( {stratSub === "select" ? (
isOo ? (
<li>
期期选约:回看窗内真实高低点振幅达标后,同到期虚值 Call
贴高、Put 贴低;剩余到期与单腿杠杆门限见本页。
</li>
) : (
<> <>
<li> <li>
固定方向:默认关。开启后固定永续多→买 Put,或永续空→买 固定方向:默认关。开启后固定永续多→买 Put,或永续空→买
@@ -1552,6 +1639,7 @@ export default function SettingsPage() {
开仓 ATM 最大偏差:仅开关开启且未开固定方向时生效。币安行权价较粗时可能长时间无开仓机会。 开仓 ATM 最大偏差:仅开关开启且未开固定方向时生效。币安行权价较粗时可能长时间无开仓机会。
</li> </li>
</> </>
)
) : null} ) : null}
{stratSub === "exit" ? ( {stratSub === "exit" ? (
<> <>
@@ -1560,8 +1648,9 @@ export default function SettingsPage() {
流动性闸门;LIVE 以交易所能否成交为准。 流动性闸门;LIVE 以交易所能否成交为准。
</li> </li>
<li> <li>
残留期权回收比例:只平永续后,当买一权利金回升到初始权利金的该比例及以上时,按最新买一 {isOo
IOC 限价卖掉归档期权(不扫市价);默认 20%。未达标或未完全成交则等到下次巡检或到期结算。 ? "残留回收:达标只平盈利腿后,亏损腿权利金回升到初始比例及以上时可尝试平仓;否则到期结算。"
: "残留期权回收比例:只平永续后,当买一权利金回升到初始权利金的该比例及以上时,按最新买一 IOC 限价卖掉归档期权(不扫市价);默认 20%。未达标或未完全成交则等到下次巡检或到期结算。"}
</li> </li>
</> </>
) : null} ) : null}