Extend semi-auto with ITM/ATM/OTM, offset, and 1:4 sizing.
Moneyness and perp:option units are plan-only; OTM enforces leverage >=180. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -277,6 +277,10 @@ export type PlanState = {
|
||||
semi_perp_exit_unit?: number;
|
||||
semi_min_option_hours?: number;
|
||||
semi_min_option_leverage?: number;
|
||||
semi_moneyness?: "itm" | "atm" | "otm";
|
||||
semi_otm_max_offset?: number;
|
||||
semi_perp_unit?: number;
|
||||
semi_option_unit?: number;
|
||||
semi_net_exit_target?: number;
|
||||
can_open: boolean;
|
||||
open_capacity?: {
|
||||
@@ -421,6 +425,10 @@ export type StrategySettings = {
|
||||
semi_perp_exit_unit?: number;
|
||||
semi_min_option_hours?: number;
|
||||
semi_min_option_leverage?: number;
|
||||
semi_moneyness?: "itm" | "atm" | "otm";
|
||||
semi_otm_max_offset?: number;
|
||||
semi_perp_unit?: number;
|
||||
semi_option_unit?: number;
|
||||
close_bid_mark_max_pct?: number;
|
||||
residual_min_premium_pct?: number;
|
||||
residual_close_check_sec?: number;
|
||||
|
||||
+101
-18
@@ -116,9 +116,25 @@ export default function PlanPage() {
|
||||
const [semiMove, setSemiMove] = useState(50);
|
||||
const [semiExitU, setSemiExitU] = useState(5);
|
||||
const [semiMinH, setSemiMinH] = useState(30);
|
||||
const [semiMinLev, setSemiMinLev] = useState(100);
|
||||
const [semiMinLev, setSemiMinLev] = useState(200);
|
||||
const [semiMny, setSemiMny] = useState<"itm" | "atm" | "otm">("otm");
|
||||
const [semiOtmOff, setSemiOtmOff] = useState(25);
|
||||
const [semiPerpU, setSemiPerpU] = useState(1);
|
||||
const [semiOptU, setSemiOptU] = useState(4);
|
||||
const [semiDirty, setSemiDirty] = useState(false);
|
||||
|
||||
const semiParamsBody = () => ({
|
||||
semi_view_side: semiView,
|
||||
semi_option_move_points: semiMove,
|
||||
semi_perp_exit_unit: semiExitU,
|
||||
semi_min_option_hours: semiMinH,
|
||||
semi_min_option_leverage: semiMinLev,
|
||||
semi_moneyness: semiMny,
|
||||
semi_otm_max_offset: semiOtmOff,
|
||||
semi_perp_unit: semiPerpU,
|
||||
semi_option_unit: semiOptU,
|
||||
});
|
||||
|
||||
async function refresh() {
|
||||
try {
|
||||
const [m, p] = await Promise.all([
|
||||
@@ -132,7 +148,12 @@ export default function PlanPage() {
|
||||
setSemiMove(Number(p.semi_option_move_points ?? 50));
|
||||
setSemiExitU(Number(p.semi_perp_exit_unit ?? 5));
|
||||
setSemiMinH(Number(p.semi_min_option_hours ?? 30));
|
||||
setSemiMinLev(Number(p.semi_min_option_leverage ?? 100));
|
||||
setSemiMinLev(Number(p.semi_min_option_leverage ?? 200));
|
||||
const m = p.semi_moneyness;
|
||||
setSemiMny(m === "itm" || m === "atm" || m === "otm" ? m : "otm");
|
||||
setSemiOtmOff(Number(p.semi_otm_max_offset ?? 25));
|
||||
setSemiPerpU(Number(p.semi_perp_unit ?? 1));
|
||||
setSemiOptU(Number(p.semi_option_unit ?? 4));
|
||||
}
|
||||
setErr("");
|
||||
} catch (e) {
|
||||
@@ -146,13 +167,7 @@ export default function PlanPage() {
|
||||
try {
|
||||
const p = await apiFetch<PlanState>("/api/plan/semi/params", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({
|
||||
semi_view_side: semiView,
|
||||
semi_option_move_points: semiMove,
|
||||
semi_perp_exit_unit: semiExitU,
|
||||
semi_min_option_hours: semiMinH,
|
||||
semi_min_option_leverage: semiMinLev,
|
||||
}),
|
||||
body: JSON.stringify(semiParamsBody()),
|
||||
});
|
||||
setPlan(p);
|
||||
setSemiDirty(false);
|
||||
@@ -170,13 +185,7 @@ export default function PlanPage() {
|
||||
if (semiDirty) {
|
||||
await apiFetch<PlanState>("/api/plan/semi/params", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({
|
||||
semi_view_side: semiView,
|
||||
semi_option_move_points: semiMove,
|
||||
semi_perp_exit_unit: semiExitU,
|
||||
semi_min_option_hours: semiMinH,
|
||||
semi_min_option_leverage: semiMinLev,
|
||||
}),
|
||||
body: JSON.stringify(semiParamsBody()),
|
||||
});
|
||||
setSemiDirty(false);
|
||||
}
|
||||
@@ -568,7 +577,7 @@ export default function PlanPage() {
|
||||
<section className="card plan-semi-card" aria-label="半自动本单">
|
||||
<h3 className="plan-panel-title">半自动 · 本单</h3>
|
||||
<p className="meta" style={{ marginTop: 0 }}>
|
||||
人工定方向与目标 → 授权后机器盯选约/开平 → 平完停,再授权下一单。选约:≥剩余时长与杠杆、实值或平值。顺向:开仓指数±点数且组合净利>0;逆向兑现:净利≥基数×k。平仓先期权后永续。
|
||||
人工定方向/行权类型/配比 → 授权后机器盯开平 → 平完停。虚值杠杆门≥180(默认200)。顺向:开仓指数±点数且净利>0;逆向:净利≥基数×k。
|
||||
</p>
|
||||
<div className="settings-fields" style={{ marginTop: 8 }}>
|
||||
<div className="field">
|
||||
@@ -587,6 +596,76 @@ export default function PlanPage() {
|
||||
<option value="short">空 · 买 Put + 永续多</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="semiMny">行权类型</label>
|
||||
<select
|
||||
id="semiMny"
|
||||
className="mono"
|
||||
disabled={open || !!busy || !!plan.semi_armed}
|
||||
value={semiMny}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value;
|
||||
setSemiMny(
|
||||
v === "itm" || v === "atm" || v === "otm" ? v : "otm",
|
||||
);
|
||||
setSemiDirty(true);
|
||||
}}
|
||||
>
|
||||
<option value="itm">实值(含平值)</option>
|
||||
<option value="atm">平值</option>
|
||||
<option value="otm">虚值</option>
|
||||
</select>
|
||||
</div>
|
||||
{semiMny === "otm" ? (
|
||||
<div className="field">
|
||||
<label htmlFor="semiOtmOff">虚值最大偏离(点)</label>
|
||||
<input
|
||||
id="semiOtmOff"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
disabled={open || !!busy || !!plan.semi_armed}
|
||||
value={semiOtmOff}
|
||||
onChange={(e) => {
|
||||
setSemiOtmOff(Number(e.target.value));
|
||||
setSemiDirty(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="field">
|
||||
<label htmlFor="semiPerpU">永续名义单位</label>
|
||||
<input
|
||||
id="semiPerpU"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.1"
|
||||
min="0.01"
|
||||
disabled={open || !!busy || !!plan.semi_armed}
|
||||
value={semiPerpU}
|
||||
onChange={(e) => {
|
||||
setSemiPerpU(Number(e.target.value));
|
||||
setSemiDirty(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="semiOptU">期权名义单位(配比)</label>
|
||||
<input
|
||||
id="semiOptU"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="0.1"
|
||||
min="0.01"
|
||||
disabled={open || !!busy || !!plan.semi_armed}
|
||||
value={semiOptU}
|
||||
onChange={(e) => {
|
||||
setSemiOptU(Number(e.target.value));
|
||||
setSemiDirty(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="semiMove">期权出场 · 波动点数</label>
|
||||
<input
|
||||
@@ -661,7 +740,11 @@ export default function PlanPage() {
|
||||
const n = Number(idx);
|
||||
const tgt =
|
||||
semiView === "long" ? n + Number(semiMove) : n - Number(semiMove);
|
||||
return `指数 ${fmtExPx("index", n)} → 到点 ${fmtExPx("index", tgt)} · 净利目标≈${fmt(plan.semi_net_exit_target ?? semiExitU, 2)}U · ${plan.semi_armed ? "已授权盯开" : "未授权"}`;
|
||||
const mnyZh =
|
||||
semiMny === "itm" ? "实值" : semiMny === "atm" ? "平值" : "虚值";
|
||||
return `指数 ${fmtExPx("index", n)} → 到点 ${fmtExPx("index", tgt)} · ${mnyZh}${
|
||||
semiMny === "otm" ? `≤${fmt(semiOtmOff, 0)}点` : ""
|
||||
} · 配比 ${fmt(semiPerpU, 2)}:${fmt(semiOptU, 2)} · 净利目标≈${fmt(plan.semi_net_exit_target ?? semiExitU, 2)}U · ${plan.semi_armed ? "已授权盯开" : "未授权"}`;
|
||||
})()}
|
||||
</div>
|
||||
<div className="plan-actions" style={{ marginTop: 10, paddingTop: 0 }}>
|
||||
|
||||
Reference in New Issue
Block a user