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:
@@ -978,9 +978,9 @@ class StrategyEngine:
|
|||||||
rk = 1.0
|
rk = 1.0
|
||||||
semi_d = check_semi_exits(
|
semi_d = check_semi_exits(
|
||||||
net_pnl=float(upl.get("net_pnl") or 0),
|
net_pnl=float(upl.get("net_pnl") or 0),
|
||||||
entry_index=(
|
strike=(
|
||||||
float(upl["entry_index_px"])
|
float(upl["strike"])
|
||||||
if upl.get("entry_index_px") is not None
|
if upl.get("strike") is not None
|
||||||
else None
|
else None
|
||||||
),
|
),
|
||||||
index_px=(
|
index_px=(
|
||||||
@@ -992,6 +992,11 @@ class StrategyEngine:
|
|||||||
option_move_points=float(sp["option_move_points"]),
|
option_move_points=float(sp["option_move_points"]),
|
||||||
perp_exit_unit=float(sp["perp_exit_unit"]),
|
perp_exit_unit=float(sp["perp_exit_unit"]),
|
||||||
risk_k=rk,
|
risk_k=rk,
|
||||||
|
entry_index=(
|
||||||
|
float(upl["entry_index_px"])
|
||||||
|
if upl.get("entry_index_px") is not None
|
||||||
|
else None
|
||||||
|
),
|
||||||
)
|
)
|
||||||
# 复用 ExitDecision 形态
|
# 复用 ExitDecision 形态
|
||||||
from .exits import ExitDecision
|
from .exits import ExitDecision
|
||||||
|
|||||||
@@ -201,17 +201,18 @@ class SemiExitDecision:
|
|||||||
def check_semi_exits(
|
def check_semi_exits(
|
||||||
*,
|
*,
|
||||||
net_pnl: float,
|
net_pnl: float,
|
||||||
entry_index: float | None,
|
strike: float | None,
|
||||||
index_px: float | None,
|
index_px: float | None,
|
||||||
view_side: str,
|
view_side: str,
|
||||||
option_move_points: float,
|
option_move_points: float,
|
||||||
perp_exit_unit: float,
|
perp_exit_unit: float,
|
||||||
risk_k: float = 1.0,
|
risk_k: float = 1.0,
|
||||||
|
entry_index: float | None = None,
|
||||||
) -> SemiExitDecision:
|
) -> SemiExitDecision:
|
||||||
"""
|
"""
|
||||||
顺方向:标的波动达到目标点 且 组合净利>0 → 全平。
|
顺方向:指数到达「行权价 ± 波动点」且组合净利>0 → 全平。
|
||||||
逆方向兑现:组合净利 ≥ 永续出场基数×k → 全平。
|
多/Call:目标 = K + N;空/Put:目标 = K − N(N 为设置的波动点,不是现价±N)。
|
||||||
流动性在 close_group 内再验;平仓顺序已是先期权后永续。
|
逆方向兑现(永续锁定净利):组合净利 ≥ 净利基数×k → 全平。
|
||||||
"""
|
"""
|
||||||
view = (view_side or "long").strip().lower()
|
view = (view_side or "long").strip().lower()
|
||||||
if view not in ("long", "short"):
|
if view not in ("long", "short"):
|
||||||
@@ -221,34 +222,39 @@ def check_semi_exits(
|
|||||||
net_tgt = max(0.0, float(perp_exit_unit)) * k
|
net_tgt = max(0.0, float(perp_exit_unit)) * k
|
||||||
net = float(net_pnl)
|
net = float(net_pnl)
|
||||||
|
|
||||||
# 逆方向 / 对冲兑现:净利达标即可离场(不必等点位)
|
# 逆方向 / 永续净利锁定:达标即可离场(不必等点位)
|
||||||
if net_tgt > 0 and net + 1e-9 >= net_tgt:
|
if net_tgt > 0 and net + 1e-9 >= net_tgt:
|
||||||
return SemiExitDecision(
|
return SemiExitDecision(
|
||||||
True,
|
True,
|
||||||
REASON_PERP_NET,
|
REASON_PERP_NET,
|
||||||
f"半自动·净利≥{net_tgt:.2f}U(基数×k)",
|
f"半自动·永续净利锁定≥{net_tgt:.2f}U(基数×k)",
|
||||||
net_target=net_tgt,
|
net_target=net_tgt,
|
||||||
)
|
)
|
||||||
|
|
||||||
if entry_index is None or index_px is None:
|
if index_px is None:
|
||||||
return SemiExitDecision(False, "", "缺指数")
|
return SemiExitDecision(False, "", "缺指数")
|
||||||
entry = float(entry_index)
|
|
||||||
idx = float(index_px)
|
idx = float(index_px)
|
||||||
if entry <= 0 or idx <= 0 or move <= 0:
|
# 锚定行权价;无 strike 时才回退开仓指数(兼容旧仓)
|
||||||
|
anchor = None
|
||||||
|
if strike is not None and float(strike) > 0:
|
||||||
|
anchor = float(strike)
|
||||||
|
elif entry_index is not None and float(entry_index) > 0:
|
||||||
|
anchor = float(entry_index)
|
||||||
|
if anchor is None or idx <= 0 or move <= 0:
|
||||||
return SemiExitDecision(False, "", "点位无效")
|
return SemiExitDecision(False, "", "点位无效")
|
||||||
|
|
||||||
if view == "long":
|
if view == "long":
|
||||||
target_idx = entry + move
|
target_idx = anchor + move
|
||||||
hit = idx + 1e-9 >= target_idx
|
hit = idx + 1e-9 >= target_idx
|
||||||
else:
|
else:
|
||||||
target_idx = entry - move
|
target_idx = anchor - move
|
||||||
hit = idx - 1e-9 <= target_idx
|
hit = idx - 1e-9 <= target_idx
|
||||||
|
|
||||||
if hit and net > 0:
|
if hit and net > 0:
|
||||||
return SemiExitDecision(
|
return SemiExitDecision(
|
||||||
True,
|
True,
|
||||||
REASON_POINTS,
|
REASON_POINTS,
|
||||||
f"半自动·标的到{target_idx:.2f}且组合净利>0",
|
f"半自动·指数到期权目标{target_idx:.2f}(K{anchor:g}±{move:g})且净利>0",
|
||||||
target_index=target_idx,
|
target_index=target_idx,
|
||||||
net_target=0.0,
|
net_target=0.0,
|
||||||
)
|
)
|
||||||
@@ -256,13 +262,13 @@ def check_semi_exits(
|
|||||||
return SemiExitDecision(
|
return SemiExitDecision(
|
||||||
False,
|
False,
|
||||||
"",
|
"",
|
||||||
f"已到点位{target_idx:.2f}但组合净利≤0({net:.2f}),继续持有",
|
f"已到期权目标{target_idx:.2f}但组合净利≤0({net:.2f}),继续持有",
|
||||||
target_index=target_idx,
|
target_index=target_idx,
|
||||||
)
|
)
|
||||||
return SemiExitDecision(
|
return SemiExitDecision(
|
||||||
False,
|
False,
|
||||||
"",
|
"",
|
||||||
f"未到点位(目标{target_idx:.2f})",
|
f"未到期权目标(K{anchor:g}→{target_idx:.2f})",
|
||||||
target_index=target_idx,
|
target_index=target_idx,
|
||||||
net_target=net_tgt,
|
net_target=net_tgt,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ from app.strategy.semi_auto import (
|
|||||||
|
|
||||||
|
|
||||||
def test_semi_points_long_needs_net_positive() -> None:
|
def test_semi_points_long_needs_net_positive() -> None:
|
||||||
# 到点但净利≤0 → 不平
|
# 目标 = 行权价 1800 + 50 = 1850;到点但净利≤0 → 不平
|
||||||
d = check_semi_exits(
|
d = check_semi_exits(
|
||||||
net_pnl=-1.0,
|
net_pnl=-1.0,
|
||||||
entry_index=1800,
|
strike=1800,
|
||||||
index_px=1850,
|
index_px=1850,
|
||||||
view_side="long",
|
view_side="long",
|
||||||
option_move_points=50,
|
option_move_points=50,
|
||||||
@@ -27,7 +27,7 @@ def test_semi_points_long_needs_net_positive() -> None:
|
|||||||
|
|
||||||
d2 = check_semi_exits(
|
d2 = check_semi_exits(
|
||||||
net_pnl=1.0,
|
net_pnl=1.0,
|
||||||
entry_index=1800,
|
strike=1800,
|
||||||
index_px=1850,
|
index_px=1850,
|
||||||
view_side="long",
|
view_side="long",
|
||||||
option_move_points=50,
|
option_move_points=50,
|
||||||
@@ -36,12 +36,41 @@ def test_semi_points_long_needs_net_positive() -> None:
|
|||||||
)
|
)
|
||||||
assert d2.should_close is True
|
assert d2.should_close is True
|
||||||
assert d2.reason == REASON_POINTS
|
assert d2.reason == REASON_POINTS
|
||||||
|
assert d2.target_index == 1850.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_semi_points_uses_strike_not_spot() -> None:
|
||||||
|
# 现价 1915、K1930、+50 → 目标 1980;现价未到则不平
|
||||||
|
d = check_semi_exits(
|
||||||
|
net_pnl=5.0,
|
||||||
|
strike=1930,
|
||||||
|
index_px=1915,
|
||||||
|
view_side="long",
|
||||||
|
option_move_points=50,
|
||||||
|
perp_exit_unit=5,
|
||||||
|
risk_k=1,
|
||||||
|
)
|
||||||
|
assert d.should_close is False
|
||||||
|
assert d.target_index == 1980.0
|
||||||
|
|
||||||
|
d2 = check_semi_exits(
|
||||||
|
net_pnl=5.0,
|
||||||
|
strike=1930,
|
||||||
|
index_px=1980,
|
||||||
|
view_side="long",
|
||||||
|
option_move_points=50,
|
||||||
|
perp_exit_unit=5,
|
||||||
|
risk_k=1,
|
||||||
|
)
|
||||||
|
assert d2.should_close is True
|
||||||
|
assert d2.reason == REASON_POINTS
|
||||||
|
|
||||||
|
|
||||||
def test_semi_points_short() -> None:
|
def test_semi_points_short() -> None:
|
||||||
|
# Put:目标 = K − 50
|
||||||
d = check_semi_exits(
|
d = check_semi_exits(
|
||||||
net_pnl=2.0,
|
net_pnl=2.0,
|
||||||
entry_index=1800,
|
strike=1800,
|
||||||
index_px=1750,
|
index_px=1750,
|
||||||
view_side="short",
|
view_side="short",
|
||||||
option_move_points=50,
|
option_move_points=50,
|
||||||
@@ -50,13 +79,14 @@ def test_semi_points_short() -> None:
|
|||||||
)
|
)
|
||||||
assert d.should_close is True
|
assert d.should_close is True
|
||||||
assert d.reason == REASON_POINTS
|
assert d.reason == REASON_POINTS
|
||||||
|
assert d.target_index == 1750.0
|
||||||
|
|
||||||
|
|
||||||
def test_semi_net_exit_with_k() -> None:
|
def test_semi_net_exit_with_k() -> None:
|
||||||
# 未到点,但净利 ≥ 5×2=10
|
# 未到点,但净利 ≥ 5×2=10(永续锁定)
|
||||||
d = check_semi_exits(
|
d = check_semi_exits(
|
||||||
net_pnl=10.0,
|
net_pnl=10.0,
|
||||||
entry_index=1800,
|
strike=1800,
|
||||||
index_px=1810,
|
index_px=1810,
|
||||||
view_side="long",
|
view_side="long",
|
||||||
option_move_points=50,
|
option_move_points=50,
|
||||||
@@ -71,7 +101,7 @@ def test_semi_net_exit_with_k() -> None:
|
|||||||
def test_semi_not_yet() -> None:
|
def test_semi_not_yet() -> None:
|
||||||
d = check_semi_exits(
|
d = check_semi_exits(
|
||||||
net_pnl=3.0,
|
net_pnl=3.0,
|
||||||
entry_index=1800,
|
strike=1800,
|
||||||
index_px=1820,
|
index_px=1820,
|
||||||
view_side="long",
|
view_side="long",
|
||||||
option_move_points=50,
|
option_move_points=50,
|
||||||
|
|||||||
+4
-4
@@ -59,15 +59,15 @@
|
|||||||
|
|
||||||
| 规则 | 条件 | close_reason |
|
| 规则 | 条件 | close_reason |
|
||||||
|------|------|----------------|
|
|------|------|----------------|
|
||||||
| 顺方向 | 指数相对**开仓指数**达到目标点数,**且**组合净利 > 0 | `semi_target_points` |
|
| 顺方向(期权目标) | 指数到达 **行权价 ± 波动点**(多 Call:K+N;空 Put:K−N),**且**组合净利 > 0 | `semi_target_points` |
|
||||||
| 逆方向 / 兑现 | 组合净利 ≥ `semi_perp_exit_unit × k`(k=以损倍数,手动仓视为 1) | `semi_perp_exit` |
|
| 永续净利锁定 | 组合净利 ≥ `semi_perp_exit_unit × k`(k=以损倍数,手动仓视为 1) | `semi_perp_exit` |
|
||||||
|
|
||||||
|
- 波动点 N 相对**行权价**,不是现价/开仓价 ±N。
|
||||||
- 到点但净利 ≤ 0:继续持有(状态提示,不平)。
|
- 到点但净利 ≤ 0:继续持有(状态提示,不平)。
|
||||||
- 流动性不足:进入 `liquidity_wait`;回落未达标则取消挂起。
|
- 流动性不足:进入 `liquidity_wait`;回落未达标则取消挂起。
|
||||||
- 半自动平仓**禁止**远虚「只平永续、期权归档」;顺序仍为**先期权后永续**。
|
- 半自动平仓**禁止**远虚「只平永续、期权归档」;顺序仍为**先期权后永续**。
|
||||||
|
|
||||||
首页预览「指数 → 到点」用**当前指数**示意;真实触发达标用**开仓指数**。
|
首页预览「期权目标 K→K±N」+ 到点粗估盈亏;「永续锁定」为净利基数×k。
|
||||||
「波动点」旁的**顺向预估**按选定行权价的内在价值变化 + 永续点位粗算(不含 IV/时间价值);与「净利基数×k」逆向兑现目标分开显示。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,15 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-08-08 — 半自动出场锚定行权价 + 左右布局
|
||||||
|
|
||||||
|
### 变更
|
||||||
|
|
||||||
|
1. 顺向出场目标改为 **行权价 ± 波动点**(非现价±N);出场规则展示「期权目标 / 永续净利锁定」。
|
||||||
|
2. 半自动单页两卡片改为左右并排(窄屏仍上下)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-08-08 — 半自动波动点顺向预估盈利
|
## 2026-08-08 — 半自动波动点顺向预估盈利
|
||||||
|
|
||||||
### 变更
|
### 变更
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ const CLOSE_REASON_ZH: Record<string, string> = {
|
|||||||
premium_multiple: "权利金倍数达标·双腿全平",
|
premium_multiple: "权利金倍数达标·双腿全平",
|
||||||
target_perp_only: "净盈利达标·只平永续(期权归档)",
|
target_perp_only: "净盈利达标·只平永续(期权归档)",
|
||||||
residual_premium_close: "残留期权·权利金回收中途平",
|
residual_premium_close: "残留期权·权利金回收中途平",
|
||||||
semi_target_points: "半自动·标的到点且组合净利>0·双腿全平",
|
semi_target_points: "半自动·指数到行权价±波动点且净利>0·双腿全平",
|
||||||
semi_perp_exit: "半自动·净利基数达标·双腿全平",
|
semi_perp_exit: "半自动·永续净利锁定达标·双腿全平",
|
||||||
expiry: "到期结算",
|
expiry: "到期结算",
|
||||||
emergency: "紧急全平",
|
emergency: "紧急全平",
|
||||||
manual: "手动平仓",
|
manual: "手动平仓",
|
||||||
|
|||||||
+68
-38
@@ -226,8 +226,8 @@ function estimateSemiRiskQty(args: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标的顺向波动 move 点后的组合盈亏粗估(期权用内在价值变化,永续按点位)。
|
* 指数走到「行权价 ± 波动点」时的组合盈亏粗估。
|
||||||
* 多=Call+永续空;空=Put+永续多。不含权利金时间价值/IV。
|
* 多=Call 目标 K+N;空=Put 目标 K−N。不含 IV/时间价值。
|
||||||
*/
|
*/
|
||||||
function estimateSemiMoveProfit(args: {
|
function estimateSemiMoveProfit(args: {
|
||||||
view: "long" | "short";
|
view: "long" | "short";
|
||||||
@@ -263,16 +263,17 @@ function estimateSemiMoveProfit(args: {
|
|||||||
if (!(S > 0) || !(K > 0) || !(move > 0) || !(oq > 0) || !(pq > 0)) {
|
if (!(S > 0) || !(K > 0) || !(move > 0) || !(oq > 0) || !(pq > 0)) {
|
||||||
return { ok: false, detail: "缺指数/行权价/数量" };
|
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 callIntr = (px: number) => Math.max(0, px - K);
|
||||||
const putIntr = (px: number) => Math.max(0, K - px);
|
const putIntr = (px: number) => Math.max(0, K - px);
|
||||||
const dIntr =
|
const dIntr =
|
||||||
view === "long"
|
view === "long"
|
||||||
? callIntr(tgt) - callIntr(S)
|
? callIntr(tgt) - callIntr(S)
|
||||||
: putIntr(tgt) - putIntr(S);
|
: putIntr(tgt) - putIntr(S);
|
||||||
// 多:空永续随涨亏;空:多永续随跌亏 → 均为 -move×qty
|
|
||||||
const optionPnl = dIntr * oq;
|
const optionPnl = dIntr * oq;
|
||||||
const perpPnl = -move * pq;
|
// 多:空永续;空:多永续 → 指数变化 dSpot 时永续盈亏 = −dSpot×qty
|
||||||
|
const perpPnl = -dSpot * pq;
|
||||||
const fees =
|
const fees =
|
||||||
feeRate > 0 ? S * feeRate * 3 * Math.max(oq, pq) /* 粗估开平 */ : 0;
|
feeRate > 0 ? S * feeRate * 3 * Math.max(oq, pq) /* 粗估开平 */ : 0;
|
||||||
const net = optionPnl + perpPnl - fees;
|
const net = optionPnl + perpPnl - fees;
|
||||||
@@ -566,23 +567,50 @@ export default function PlanPage() {
|
|||||||
const movePct = pos?.move_pct ?? 0;
|
const movePct = pos?.move_pct ?? 0;
|
||||||
const ooRatio = Number(plan?.oo_reward_ratio ?? 2);
|
const ooRatio = Number(plan?.oo_reward_ratio ?? 2);
|
||||||
const ooBudget = plan?.risk_sizing_preview?.budget;
|
const ooBudget = plan?.risk_sizing_preview?.budget;
|
||||||
const exitRuleLabel = isOo
|
const semiExitMove = Number(semiMove) || 50;
|
||||||
? exitTarget != null
|
const semiExitView: "long" | "short" =
|
||||||
? `${riskLocked ? "锁定 " : ""}净盈≥${fmt(exitTarget)} U(${
|
(semiDirty ? semiView : plan?.semi_view_side === "short" ? "short" : "long") ===
|
||||||
ooBudget != null ? `${fmt(ooBudget)}×` : "预算×"
|
"short"
|
||||||
}${fmt(ooRatio, 1)})`
|
? "short"
|
||||||
: `净盈≥预算×${fmt(ooRatio, 1)}(达标只平盈利腿)`
|
: "long";
|
||||||
: exitMode === "premium_multiple"
|
const semiExitStrike =
|
||||||
? riskBased
|
open && pos?.strike != null && Number.isFinite(Number(pos.strike))
|
||||||
? exitTarget != null
|
? Number(pos.strike)
|
||||||
? `${riskLocked ? "锁定 " : ""}净盈≥${fmt(exitTarget)} U(权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)})`
|
: null;
|
||||||
: `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}(开仓后锁定)`
|
const semiOptTarget =
|
||||||
: `权利金×${fmt(plan?.premium_exit_multiple ?? 1, 2)}`
|
semiExitStrike != null
|
||||||
: riskBased
|
? semiExitView === "short"
|
||||||
? exitTarget != null
|
? semiExitStrike - semiExitMove
|
||||||
? `${riskLocked ? "锁定 " : "固定 "}${fmt(exitTarget)} U(基数${fmt(plan?.risk_exit_unit ?? 15)})`
|
: semiExitStrike + semiExitMove
|
||||||
: `待估算(基数${fmt(plan?.risk_exit_unit ?? 15)})`
|
: null;
|
||||||
: `固定 ${fmt(plan?.net_profit_target ?? 15)} U`;
|
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
|
const riskRatioLabel = isOo
|
||||||
? "预算平分 Call/Put"
|
? "预算平分 Call/Put"
|
||||||
: `比例${Number(plan?.risk_perp_unit ?? 1)}:${Number(plan?.risk_option_unit ?? 2)}`;
|
: `比例${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>平仓后停在等待授权,不自动连开;与全自动循环无关。</li>
|
<li>平仓后停在等待授权,不自动连开;与全自动循环无关。</li>
|
||||||
<li>虚值:偏离 ≤ 设定点数;杠杆门 ≥180(默认 200)。实值/平值用本单最低杠杆。</li>
|
<li>虚值:偏离 ≤ 设定点数;杠杆门 ≥180(默认 200)。实值/平值用本单最低杠杆。</li>
|
||||||
<li>波动点:标的相对当前价顺向波动 N 点(多涨/空跌);下方按行权价内在价值变化 + 永续点位粗估盈利。</li>
|
<li>波动点 N:期权目标位 = 行权价 ± N(多 Call:K+N;空 Put:K−N),不是现价±N。</li>
|
||||||
<li>顺向出场:开仓指数 ± 波动点,且组合净利 > 0 → 双腿全平(先期权后永续)。</li>
|
<li>顺向出场:指数到期权目标位且组合净利 > 0 → 双腿全平(先期权后永续)。</li>
|
||||||
<li>逆向兑现:组合净利 ≥ 净利基数 × k → 全平。</li>
|
<li>永续锁定:组合净利 ≥ 净利基数 × k → 全平(不必等点位)。</li>
|
||||||
<li>以损定仓时配比为单位再乘 k;手动仓按配比直接开。</li>
|
<li>以损定仓时配比为单位再乘 k;手动仓按配比直接开。</li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
@@ -981,7 +1009,7 @@ export default function PlanPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label htmlFor="semiMove">波动点(预估)</label>
|
<label htmlFor="semiMove">波动点(相对K)</label>
|
||||||
<input
|
<input
|
||||||
id="semiMove"
|
id="semiMove"
|
||||||
className="mono"
|
className="mono"
|
||||||
@@ -1121,15 +1149,15 @@ export default function PlanPage() {
|
|||||||
: semiExitU),
|
: semiExitU),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (idx == null || !Number.isFinite(idx)) {
|
|
||||||
return `逆向兑现≥${fmt(reverseTgt, 2)}U`;
|
|
||||||
}
|
|
||||||
if (strike == null || !Number.isFinite(strike)) {
|
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 =
|
const tgt =
|
||||||
semiView === "long"
|
semiView === "long"
|
||||||
? idx + Number(semiMove)
|
? strike + Number(semiMove)
|
||||||
: idx - Number(semiMove);
|
: strike - Number(semiMove);
|
||||||
return `${fmtExPx("index", idx)}→${fmtExPx("index", tgt)} · 待行权价估盈利 · 逆向≥${fmt(reverseTgt, 2)}U`;
|
return `期权目标 K${Math.round(strike)}→${fmtExPx("index", tgt)} · 永续锁定≥${fmt(reverseTgt, 2)}U`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const prof = estimateSemiMoveProfit({
|
const prof = estimateSemiMoveProfit({
|
||||||
@@ -1139,22 +1167,23 @@ export default function PlanPage() {
|
|||||||
movePoints: Number(semiMove) || 50,
|
movePoints: Number(semiMove) || 50,
|
||||||
optionQty: optQty,
|
optionQty: optQty,
|
||||||
perpQty: perpQty,
|
perpQty: perpQty,
|
||||||
feeRate: 0, // 预估展示用内在+永续点位;手续费另计
|
feeRate: 0,
|
||||||
});
|
});
|
||||||
if (!prof.ok) {
|
if (!prof.ok) {
|
||||||
return prof.detail;
|
return prof.detail;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{fmtExPx("index", idx)}→{fmtExPx("index", prof.targetPx!)} ·
|
期权目标 K{Math.round(strike)}→
|
||||||
K{Math.round(strike)} · 顺向预估≈{fmt(prof.netEst, 2)}U
|
{fmtExPx("index", prof.targetPx!)} · 现价
|
||||||
|
{fmtExPx("index", idx)} · 到点预估≈{fmt(prof.netEst, 2)}U
|
||||||
<span className="meta">
|
<span className="meta">
|
||||||
{" "}
|
{" "}
|
||||||
(期权{fmt(prof.optionPnl, 1)}/永续
|
(期权{fmt(prof.optionPnl, 1)}/永续
|
||||||
{fmt(prof.perpPnl, 1)} · 内在粗估)
|
{fmt(prof.perpPnl, 1)})
|
||||||
</span>
|
</span>
|
||||||
{" · "}
|
{" · "}
|
||||||
逆向≥{fmt(reverseTgt, 2)}U
|
永续锁定≥{fmt(reverseTgt, 2)}U
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
@@ -1437,7 +1466,8 @@ export default function PlanPage() {
|
|||||||
? ` · 虚值≤${fmt(semiOtmOff, 0)}点`
|
? ` · 虚值≤${fmt(semiOtmOff, 0)}点`
|
||||||
: ""}
|
: ""}
|
||||||
{" · "}
|
{" · "}
|
||||||
顺向±{fmt(semiMove, 0)}点且净利>0 / 净利≥
|
期权目标K{semiExitView === "short" ? "−" : "+"}
|
||||||
|
{fmt(semiMove, 0)}且净利>0 / 永续锁定≥
|
||||||
{fmt(semiExitU, 1)}×k
|
{fmt(semiExitU, 1)}×k
|
||||||
</>
|
</>
|
||||||
) : isOo ? (
|
) : isOo ? (
|
||||||
|
|||||||
@@ -474,10 +474,22 @@ input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.plan-semi-tab {
|
.plan-semi-tab {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: column;
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-bottom: 12px;
|
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 {
|
.plan-semi-compact {
|
||||||
|
|||||||
Reference in New Issue
Block a user