Add option-option hedge mode with SIM/LIVE parity.

Mutual hedge_mode, amplitude OTM selection, 1:1 risk sizing, win-leg/full close, dual audits and docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-07 16:01:16 +08:00
parent 15fe2f72dc
commit ec244c63c6
22 changed files with 2640 additions and 83 deletions
+123
View File
@@ -108,6 +108,14 @@ export default function SettingsPage() {
const [martingaleOn, setMartingaleOn] = useState(false);
const [martingaleStartAfter, setMartingaleStartAfter] = useState(2);
const [martingaleMaxDoubles, setMartingaleMaxDoubles] = useState(3);
const [hedgeMode, setHedgeMode] = useState<"perp_option" | "option_option">(
"perp_option",
);
const [ooAmpPct, setOoAmpPct] = useState(1.5);
const [ooAmpHours, setOoAmpHours] = useState(12);
const [ooMinHours, setOoMinHours] = useState(24);
const [ooMinLev, setOoMinLev] = useState(200);
const [ooRewardRatio, setOoRewardRatio] = useState(2);
const [riskPreview, setRiskPreview] = useState<Record<string, unknown> | null>(
null,
);
@@ -221,6 +229,14 @@ export default function SettingsPage() {
setMartingaleOn(s.martingale_enabled === true);
setMartingaleStartAfter(s.martingale_start_after_loss_days ?? 2);
setMartingaleMaxDoubles(s.martingale_max_doubles ?? 3);
setHedgeMode(
s.hedge_mode === "option_option" ? "option_option" : "perp_option",
);
setOoAmpPct(s.oo_amplitude_pct ?? 1.5);
setOoAmpHours(s.oo_amplitude_hours ?? 12);
setOoMinHours(s.oo_min_option_hours ?? 24);
setOoMinLev(s.oo_min_leverage ?? 200);
setOoRewardRatio(s.oo_reward_ratio ?? 2);
setRiskPreview(
s.risk_sizing_preview && typeof s.risk_sizing_preview === "object"
? s.risk_sizing_preview
@@ -389,6 +405,12 @@ export default function SettingsPage() {
martingaleOn,
martingale_start_after_loss_days: martingaleStartAfter,
martingale_max_doubles: martingaleMaxDoubles,
hedge_mode: hedgeMode,
oo_amplitude_pct: ooAmpPct,
oo_amplitude_hours: ooAmpHours,
oo_min_option_hours: ooMinHours,
oo_min_leverage: ooMinLev,
oo_reward_ratio: ooRewardRatio,
exchange,
};
// 以损定仓不提交手填名义/出场,避免禁用输入框脏值导致 422
@@ -682,12 +704,113 @@ export default function SettingsPage() {
<option value="isolated"></option>
</select>
</div>
<div className="field">
<label htmlFor="hedgeMode"></label>
<select
id="hedgeMode"
className="mono"
value={hedgeMode}
onChange={(e) => {
const v =
e.target.value === "option_option"
? "option_option"
: "perp_option";
setHedgeMode(v);
if (v === "option_option") {
setSizingMode("risk_based");
setRiskLossMode("percent");
setFixedDirOn(false);
}
}}
>
<option value="perp_option">+</option>
<option value="option_option"></option>
</select>
<p className="hint" style={{ margin: "0.35rem 0 0" }}>
N Call/Put
</p>
</div>
{hedgeMode === "option_option" ? (
<>
<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">
<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}
<div className="field">
<label htmlFor="sizingMode"></label>
<select
id="sizingMode"
className="mono"
value={sizingMode}
disabled={hedgeMode === "option_option"}
onChange={(e) =>
setSizingMode(
e.target.value === "risk_based"