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
+269 -180
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,34 +680,40 @@ export default function SettingsPage() {
/> />
</div> </div>
) : null} ) : null}
<div className="field"> {!isOo ? (
<label htmlFor="lev">永续杠杆倍数</label> <>
<input <div className="field">
id="lev" <label htmlFor="lev">永续杠杆倍数</label>
className="mono" <input
type="number" id="lev"
step="1" className="mono"
min="1" type="number"
value={leverage} step="1"
onChange={(e) => setLeverage(Number(e.target.value))} min="1"
/> value={leverage}
</div> onChange={(e) => setLeverage(Number(e.target.value))}
<div className="field"> />
<label htmlFor="perpMm">永续保证金模式</label> </div>
<select <div className="field">
id="perpMm" <label htmlFor="perpMm">永续保证金模式</label>
className="mono" <select
value={perpMarginMode} id="perpMm"
onChange={(e) => className="mono"
setPerpMarginMode( value={perpMarginMode}
e.target.value === "isolated" ? "isolated" : "cross", onChange={(e) =>
) setPerpMarginMode(
} e.target.value === "isolated"
> ? "isolated"
<option value="cross">全仓(默认)</option> : "cross",
<option value="isolated">逐仓</option> )
</select> }
</div> >
<option value="cross">全仓(默认)</option>
<option value="isolated">逐仓</option>
</select>
</div>
</>
) : null}
<div className="field"> <div className="field">
<label htmlFor="hedgeMode">对冲模式</label> <label htmlFor="hedgeMode">对冲模式</label>
<select <select
@@ -730,145 +740,110 @@ export default function SettingsPage() {
二选一。期期:近 N 小时振幅高低匹配虚值 Call/Put;达标只平盈利腿。 二选一。期期:近 N 小时振幅高低匹配虚值 Call/Put;达标只平盈利腿。
</p> </p>
</div> </div>
{hedgeMode === "option_option" ? ( {isOo ? (
<> <div className="field">
<div className="field"> <label htmlFor="ooRR">盈亏比</label>
<label htmlFor="ooAmp">振幅最小(%)</label> <input
<input id="ooRR"
id="ooAmp" className="mono"
className="mono" type="number"
type="number" step="0.1"
step="0.1" min="0.5"
min="0.1" value={ooRewardRatio}
value={ooAmpPct} onChange={(e) =>
onChange={(e) => setOoAmpPct(Number(e.target.value))} setOoRewardRatio(Number(e.target.value))
/> }
</div> />
<div className="field"> <p className="hint" style={{ margin: "0.35rem 0 0" }}>
<label htmlFor="ooAmpH">振幅回看(小时)</label> 目标盈利 = 以损预算 × 盈亏比(例预算 100U、比 2 → 目标
<input 200U)。两腿 1:1 平分预算。振幅/选约参数见「选约」页。
id="ooAmpH" </p>
className="mono" </div>
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">
<label htmlFor="ooRR">盈亏比</label>
<input
id="ooRR"
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" }}>
目标盈利 = 以损预算 × 盈亏比(例预算 100U、比 2 → 目标
200U)。两腿 1:1 平分预算。
</p>
</div>
</>
) : null} ) : null}
<div className="field"> {!isOo ? (
<label htmlFor="sizingMode">定仓模式</label> <div className="field">
<select <label htmlFor="sizingMode">定仓模式</label>
id="sizingMode" <select
className="mono" id="sizingMode"
value={sizingMode} className="mono"
disabled={hedgeMode === "option_option"} value={sizingMode}
onChange={(e) => onChange={(e) =>
setSizingMode( setSizingMode(
e.target.value === "risk_based" e.target.value === "risk_based"
? "risk_based" ? "risk_based"
: "manual", : "manual",
) )
} }
> >
<option value="manual">手动仓位(填 ETH 名义)</option> <option value="manual">手动仓位(填 ETH 名义)</option>
<option value="risk_based">以损定仓(按亏损预算反推)</option> <option value="risk_based">
</select> 以损定仓(按亏损预算反推)
</div> </option>
{sizingMode === "risk_based" ? ( </select>
</div>
) : (
<div className="field">
<label>定仓模式</label>
<div className="mono" style={{ fontSize: 13 }}>
以损定仓(期期固定 · Call/Put 1:1)
</div>
</div>
)}
{sizingMode === "risk_based" || isOo ? (
<> <>
<div className="field"> {!isOo ? (
<label htmlFor="riskLevBasis">节奏杠杆(定仓口径)</label> <div className="field">
<select <label htmlFor="riskLevBasis">
id="riskLevBasis" 节奏杠杆(定仓口径)
className="mono" </label>
value={riskLeverageBasis} <select
onChange={(e) => id="riskLevBasis"
setRiskLeverageBasis( className="mono"
e.target.value === "actual" value={riskLeverageBasis}
? "actual" onChange={(e) =>
: "selection", setRiskLeverageBasis(
) e.target.value === "actual"
} ? "actual"
> : "selection",
<option value="selection"> )
选约杠杆(推荐控节奏) }
</option> >
<option value="actual">实际杠杆(盘口卖一)</option> <option value="selection">
</select> 选约杠杆(推荐控节奏)
<p className="hint" style={{ margin: "0.35rem 0 0" }}> </option>
选约杠杆:按「指数÷期权杠杆≥」估权利金算 k,权利金再便宜也不放大仓位。 <option value="actual">实际杠杆(盘口卖一)</option>
实际杠杆:按盘口卖一定仓,便宜时 k 会变大。 </select>
</p> <p className="hint" style={{ margin: "0.35rem 0 0" }}>
</div> 选约杠杆:按「指数÷期权杠杆≥」估权利金算
<div className="field"> k,权利金再便宜也不放大仓位。
<label htmlFor="riskLossMode">亏损约束</label> 实际杠杆:按盘口卖一定仓,便宜时 k 会变大。
<select </p>
id="riskLossMode" </div>
className="mono" ) : null}
value={riskLossMode} {!isOo ? (
onChange={(e) => { <div className="field">
const mode = <label htmlFor="riskLossMode">亏损约束</label>
e.target.value === "absolute" <select
? "absolute" id="riskLossMode"
: "percent"; className="mono"
setRiskLossMode(mode); value={riskLossMode}
if (mode !== "percent") setMartingaleOn(false); onChange={(e) => {
}} const mode =
> e.target.value === "absolute"
<option value="percent">亏损幅度(占本金 %)</option> ? "absolute"
<option value="absolute">亏损值(USDT)</option> : "percent";
</select> setRiskLossMode(mode);
</div> if (mode !== "percent") setMartingaleOn(false);
{riskLossMode === "percent" ? ( }}
>
<option value="percent">
亏损幅度(占本金 %)
</option>
<option value="absolute">亏损值(USDT)</option>
</select>
</div>
) : 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
@@ -1172,8 +1155,12 @@ 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}
<li> {isOo ? (
永续保证金模式:仅作用于永续(全仓/逐仓,默认全仓)。OKX <li>
期权始终为现金/逐仓(cash),不受此选项影响。 期期对冲:强制以损定仓 + 亏损幅度%;两腿 1:1 平分预算;出场用盈亏比,不使用永续名义/比例/k。
</li> </li>
) : (
<li>
永续保证金模式:仅作用于永续(全仓/逐仓,默认全仓)。OKX
期权始终为现金/逐仓(cash),不受此选项影响。
</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}