Anchor semi exit to strike +/- move points; side-by-side semi cards.
Forward target is K+/-N not spot+/-N; UI shows option target and perp net lock. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -28,8 +28,8 @@ const CLOSE_REASON_ZH: Record<string, string> = {
|
||||
premium_multiple: "权利金倍数达标·双腿全平",
|
||||
target_perp_only: "净盈利达标·只平永续(期权归档)",
|
||||
residual_premium_close: "残留期权·权利金回收中途平",
|
||||
semi_target_points: "半自动·标的到点且组合净利>0·双腿全平",
|
||||
semi_perp_exit: "半自动·净利基数达标·双腿全平",
|
||||
semi_target_points: "半自动·指数到行权价±波动点且净利>0·双腿全平",
|
||||
semi_perp_exit: "半自动·永续净利锁定达标·双腿全平",
|
||||
expiry: "到期结算",
|
||||
emergency: "紧急全平",
|
||||
manual: "手动平仓",
|
||||
|
||||
+68
-38
@@ -226,8 +226,8 @@ function estimateSemiRiskQty(args: {
|
||||
}
|
||||
|
||||
/**
|
||||
* 标的顺向波动 move 点后的组合盈亏粗估(期权用内在价值变化,永续按点位)。
|
||||
* 多=Call+永续空;空=Put+永续多。不含权利金时间价值/IV。
|
||||
* 指数走到「行权价 ± 波动点」时的组合盈亏粗估。
|
||||
* 多=Call 目标 K+N;空=Put 目标 K−N。不含 IV/时间价值。
|
||||
*/
|
||||
function estimateSemiMoveProfit(args: {
|
||||
view: "long" | "short";
|
||||
@@ -263,16 +263,17 @@ function estimateSemiMoveProfit(args: {
|
||||
if (!(S > 0) || !(K > 0) || !(move > 0) || !(oq > 0) || !(pq > 0)) {
|
||||
return { ok: false, detail: "缺指数/行权价/数量" };
|
||||
}
|
||||
const tgt = view === "long" ? S + move : S - move;
|
||||
const tgt = view === "long" ? K + move : K - move;
|
||||
const dSpot = tgt - S;
|
||||
const callIntr = (px: number) => Math.max(0, px - K);
|
||||
const putIntr = (px: number) => Math.max(0, K - px);
|
||||
const dIntr =
|
||||
view === "long"
|
||||
? callIntr(tgt) - callIntr(S)
|
||||
: putIntr(tgt) - putIntr(S);
|
||||
// 多:空永续随涨亏;空:多永续随跌亏 → 均为 -move×qty
|
||||
const optionPnl = dIntr * oq;
|
||||
const perpPnl = -move * pq;
|
||||
// 多:空永续;空:多永续 → 指数变化 dSpot 时永续盈亏 = −dSpot×qty
|
||||
const perpPnl = -dSpot * pq;
|
||||
const fees =
|
||||
feeRate > 0 ? S * feeRate * 3 * Math.max(oq, pq) /* 粗估开平 */ : 0;
|
||||
const net = optionPnl + perpPnl - fees;
|
||||
@@ -566,23 +567,50 @@ export default function PlanPage() {
|
||||
const movePct = pos?.move_pct ?? 0;
|
||||
const ooRatio = Number(plan?.oo_reward_ratio ?? 2);
|
||||
const ooBudget = plan?.risk_sizing_preview?.budget;
|
||||
const exitRuleLabel = isOo
|
||||
? exitTarget != null
|
||||
? `${riskLocked ? "锁定 " : ""}净盈≥${fmt(exitTarget)} U(${
|
||||
ooBudget != null ? `${fmt(ooBudget)}×` : "预算×"
|
||||
}${fmt(ooRatio, 1)})`
|
||||
: `净盈≥预算×${fmt(ooRatio, 1)}(达标只平盈利腿)`
|
||||
: exitMode === "premium_multiple"
|
||||
? riskBased
|
||||
? exitTarget != null
|
||||
? `${riskLocked ? "锁定 " : ""}净盈≥${fmt(exitTarget)} U(权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)})`
|
||||
: `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}(开仓后锁定)`
|
||||
: `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}`
|
||||
: riskBased
|
||||
? exitTarget != null
|
||||
? `${riskLocked ? "锁定 " : "固定 "}${fmt(exitTarget)} U(基数${fmt(plan?.risk_exit_unit ?? 15)})`
|
||||
: `待估算(基数${fmt(plan?.risk_exit_unit ?? 15)})`
|
||||
: `固定 ${fmt(plan?.net_profit_target ?? 15)} U`;
|
||||
const semiExitMove = Number(semiMove) || 50;
|
||||
const semiExitView: "long" | "short" =
|
||||
(semiDirty ? semiView : plan?.semi_view_side === "short" ? "short" : "long") ===
|
||||
"short"
|
||||
? "short"
|
||||
: "long";
|
||||
const semiExitStrike =
|
||||
open && pos?.strike != null && Number.isFinite(Number(pos.strike))
|
||||
? Number(pos.strike)
|
||||
: null;
|
||||
const semiOptTarget =
|
||||
semiExitStrike != null
|
||||
? semiExitView === "short"
|
||||
? semiExitStrike - semiExitMove
|
||||
: semiExitStrike + semiExitMove
|
||||
: null;
|
||||
const semiNetLock = Number(
|
||||
plan?.semi_net_exit_target ??
|
||||
(riskBased
|
||||
? Number(semiExitU) *
|
||||
Number(plan?.risk_last_k ?? plan?.risk_sizing_preview?.k ?? 1)
|
||||
: semiExitU),
|
||||
);
|
||||
const exitRuleLabel = semiOn
|
||||
? semiOptTarget != null
|
||||
? `期权目标 K${Math.round(semiExitStrike!)}→${fmtExPx("index", semiOptTarget)} · 永续净利锁定≥${fmt(semiNetLock, 2)}U`
|
||||
: `期权目标 行权价${semiExitView === "short" ? "−" : "+"}${fmt(semiExitMove, 0)} · 永续净利锁定≥${fmt(semiNetLock, 2)}U`
|
||||
: isOo
|
||||
? exitTarget != null
|
||||
? `${riskLocked ? "锁定 " : ""}净盈≥${fmt(exitTarget)} U(${
|
||||
ooBudget != null ? `${fmt(ooBudget)}×` : "预算×"
|
||||
}${fmt(ooRatio, 1)})`
|
||||
: `净盈≥预算×${fmt(ooRatio, 1)}(达标只平盈利腿)`
|
||||
: exitMode === "premium_multiple"
|
||||
? riskBased
|
||||
? exitTarget != null
|
||||
? `${riskLocked ? "锁定 " : ""}净盈≥${fmt(exitTarget)} U(权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)})`
|
||||
: `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}(开仓后锁定)`
|
||||
: `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}`
|
||||
: riskBased
|
||||
? exitTarget != null
|
||||
? `${riskLocked ? "锁定 " : "固定 "}${fmt(exitTarget)} U(基数${fmt(plan?.risk_exit_unit ?? 15)})`
|
||||
: `待估算(基数${fmt(plan?.risk_exit_unit ?? 15)})`
|
||||
: `固定 ${fmt(plan?.net_profit_target ?? 15)} U`;
|
||||
const riskRatioLabel = isOo
|
||||
? "预算平分 Call/Put"
|
||||
: `比例${Number(plan?.risk_perp_unit ?? 1)}:${Number(plan?.risk_option_unit ?? 2)}`;
|
||||
@@ -887,9 +915,9 @@ export default function PlanPage() {
|
||||
<li>人工定方向、行权类型、永续:期权配比与出场 →「授权开下一单」后机器盯选约/开/平。</li>
|
||||
<li>平仓后停在等待授权,不自动连开;与全自动循环无关。</li>
|
||||
<li>虚值:偏离 ≤ 设定点数;杠杆门 ≥180(默认 200)。实值/平值用本单最低杠杆。</li>
|
||||
<li>波动点:标的相对当前价顺向波动 N 点(多涨/空跌);下方按行权价内在价值变化 + 永续点位粗估盈利。</li>
|
||||
<li>顺向出场:开仓指数 ± 波动点,且组合净利 > 0 → 双腿全平(先期权后永续)。</li>
|
||||
<li>逆向兑现:组合净利 ≥ 净利基数 × k → 全平。</li>
|
||||
<li>波动点 N:期权目标位 = 行权价 ± N(多 Call:K+N;空 Put:K−N),不是现价±N。</li>
|
||||
<li>顺向出场:指数到期权目标位且组合净利 > 0 → 双腿全平(先期权后永续)。</li>
|
||||
<li>永续锁定:组合净利 ≥ 净利基数 × k → 全平(不必等点位)。</li>
|
||||
<li>以损定仓时配比为单位再乘 k;手动仓按配比直接开。</li>
|
||||
</ul>
|
||||
</details>
|
||||
@@ -981,7 +1009,7 @@ export default function PlanPage() {
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="semiMove">波动点(预估)</label>
|
||||
<label htmlFor="semiMove">波动点(相对K)</label>
|
||||
<input
|
||||
id="semiMove"
|
||||
className="mono"
|
||||
@@ -1121,15 +1149,15 @@ export default function PlanPage() {
|
||||
: semiExitU),
|
||||
);
|
||||
|
||||
if (idx == null || !Number.isFinite(idx)) {
|
||||
return `逆向兑现≥${fmt(reverseTgt, 2)}U`;
|
||||
}
|
||||
if (strike == null || !Number.isFinite(strike)) {
|
||||
return `期权目标 行权价${semiView === "short" ? "−" : "+"}${fmt(Number(semiMove) || 50, 0)} · 永续锁定≥${fmt(reverseTgt, 2)}U`;
|
||||
}
|
||||
if (idx == null || !Number.isFinite(idx)) {
|
||||
const tgt =
|
||||
semiView === "long"
|
||||
? idx + Number(semiMove)
|
||||
: idx - Number(semiMove);
|
||||
return `${fmtExPx("index", idx)}→${fmtExPx("index", tgt)} · 待行权价估盈利 · 逆向≥${fmt(reverseTgt, 2)}U`;
|
||||
? strike + Number(semiMove)
|
||||
: strike - Number(semiMove);
|
||||
return `期权目标 K${Math.round(strike)}→${fmtExPx("index", tgt)} · 永续锁定≥${fmt(reverseTgt, 2)}U`;
|
||||
}
|
||||
|
||||
const prof = estimateSemiMoveProfit({
|
||||
@@ -1139,22 +1167,23 @@ export default function PlanPage() {
|
||||
movePoints: Number(semiMove) || 50,
|
||||
optionQty: optQty,
|
||||
perpQty: perpQty,
|
||||
feeRate: 0, // 预估展示用内在+永续点位;手续费另计
|
||||
feeRate: 0,
|
||||
});
|
||||
if (!prof.ok) {
|
||||
return prof.detail;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{fmtExPx("index", idx)}→{fmtExPx("index", prof.targetPx!)} ·
|
||||
K{Math.round(strike)} · 顺向预估≈{fmt(prof.netEst, 2)}U
|
||||
期权目标 K{Math.round(strike)}→
|
||||
{fmtExPx("index", prof.targetPx!)} · 现价
|
||||
{fmtExPx("index", idx)} · 到点预估≈{fmt(prof.netEst, 2)}U
|
||||
<span className="meta">
|
||||
{" "}
|
||||
(期权{fmt(prof.optionPnl, 1)}/永续
|
||||
{fmt(prof.perpPnl, 1)} · 内在粗估)
|
||||
{fmt(prof.perpPnl, 1)})
|
||||
</span>
|
||||
{" · "}
|
||||
逆向≥{fmt(reverseTgt, 2)}U
|
||||
永续锁定≥{fmt(reverseTgt, 2)}U
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
@@ -1437,7 +1466,8 @@ export default function PlanPage() {
|
||||
? ` · 虚值≤${fmt(semiOtmOff, 0)}点`
|
||||
: ""}
|
||||
{" · "}
|
||||
顺向±{fmt(semiMove, 0)}点且净利>0 / 净利≥
|
||||
期权目标K{semiExitView === "short" ? "−" : "+"}
|
||||
{fmt(semiMove, 0)}且净利>0 / 永续锁定≥
|
||||
{fmt(semiExitU, 1)}×k
|
||||
</>
|
||||
) : isOo ? (
|
||||
|
||||
@@ -474,10 +474,22 @@ input {
|
||||
}
|
||||
|
||||
.plan-semi-tab {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.plan-semi-tab > .card {
|
||||
margin-bottom: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.plan-semi-tab {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-semi-compact {
|
||||
|
||||
Reference in New Issue
Block a user