Fix btc_eth watching entry, preview sizing, and coin-to-U settlement.

Use dual indices for watch/preview; perp entry uses ETH mark; expiry/close convert coin PnL via option index; align legacy long→Call; omit combo U when index missing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-14 21:52:20 +08:00
parent 450296e04b
commit a841c8f31b
8 changed files with 296 additions and 72 deletions
+33 -24
View File
@@ -1282,10 +1282,15 @@ def validate_start_body(plan_type: str, body: dict[str, Any]) -> Optional[str]:
opt_type = "C"
if opt_type not in ("P", "C"):
return "缺少期权类型(Put/Call)"
if direction == "long" and opt_type != "P":
return "做多永期对冲须用 Put"
if direction == "short" and opt_type != "C":
return "做空永期对冲须用 Call"
# 与 opt_type_for_view 一致:做多→Call+空永续,做空→Put+多永续
from lib.hedge_plan.hedge_plan_option_primary_lib import opt_type_for_view
want = opt_type_for_view(direction)
if opt_type != want:
return (
f"做{'多' if direction == 'long' else '空'}永期对冲须用 "
f"{'Call' if want == 'C' else 'Put'}"
)
if direction == "long" and not (sl < entry < tp):
return "做多须满足 止损 < 入场 < 止盈"
if direction == "short" and not (tp < entry < sl):
@@ -1775,34 +1780,38 @@ def execute_manual_close_plan(cfg: dict[str, Any], conn: Any, plan_id: int) -> d
notes.append("卖币失败: " + str(sell_r.get("msg") or ""))
total = float(opt_pnl) + float(perp_pnl)
# 期权币本位盈亏换算到 U:用期权标的指数(跨币=BTC,禁止×ETH)
# 币本位期权盈亏一律 × 期权指数折 U(跨币=BTC);不再用 |pnl|<10 启发式
try:
from lib.hedge_plan.hedge_plan_live_positions_lib import resolve_option_index_px
from lib.hedge_plan.hedge_po_mode_lib import get_hedge_po_mode, is_cross_asset, option_underlying
from lib.options.options_pricing_lib import _quote_in_coin_from_context
mode = str(plan.get("po_mode") or get_hedge_po_mode())
opt_u = str(plan.get("option_underlying") or plan.get("underlying") or option_underlying(mode) or uly).upper()
ex_idx = cfg.get("exchange_options") or cfg.get("exchange")
if callable(ex_idx):
try:
ex_idx = ex_idx()
except Exception:
ex_idx = None
idx = float(
resolve_option_index_px(
cfg,
opt_u,
ex_opt=ex_idx,
cross_asset=is_cross_asset(mode),
perp_fallback=None,
inst = ""
if opt:
inst = str(opt.get("inst_id") or "")
coin = bool(_quote_in_coin_from_context(inst_id=inst)) if inst else True
if coin:
ex_idx = cfg.get("exchange_options") or cfg.get("exchange")
if callable(ex_idx):
try:
ex_idx = ex_idx()
except Exception:
ex_idx = None
idx = float(
resolve_option_index_px(
cfg,
opt_u,
ex_opt=ex_idx,
cross_asset=is_cross_asset(mode),
perp_fallback=None,
)
or 0
)
or 0
)
# opt_pnl 可能已是币;若为币且绝对值很小则 × 指数
if abs(opt_pnl) < 10 and idx > 0 and abs(opt_pnl * idx) > abs(opt_pnl):
opt_pnl_u = opt_pnl * idx
opt_pnl_u = float(opt_pnl) * idx if idx > 0 else float(opt_pnl)
else:
opt_pnl_u = opt_pnl
opt_pnl_u = float(opt_pnl)
except Exception:
opt_pnl_u = opt_pnl