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
+54 -4
View File
@@ -509,11 +509,24 @@ def _activate_watching_po(
str((perp or {}).get("direction") or "")
or perp_direction_for_view(view)
)
# 永续开仓价:优先成交回写,否则 body.entry(应为永续标的价,跨币=ETH)
perp_avg = None
try:
order = (perp or {}).get("order") or {}
perp_avg = float(
order.get("average")
or order.get("avgPx")
or (order.get("info") or {}).get("avgPx")
or body.get("entry")
or 0
) or None
except (TypeError, ValueError):
perp_avg = float(body.get("entry") or 0) or None
update_plan(
conn,
int(plan_id),
status="partial" if is_partial else "active",
entry_mark=float(body.get("entry") or body.get("index_px") or 0) or None,
entry_mark=perp_avg,
perp_size=float((perp or {}).get("contracts") or body.get("contracts") or 0),
leverage=float(body.get("leverage") or 100),
premium_total=premium,
@@ -531,7 +544,7 @@ def _activate_watching_po(
"symbol": str(body.get("exchange_symbol") or ""),
"side": perp_dir,
"size": float((perp or {}).get("contracts") or body.get("contracts") or 0),
"avg_open": float(body.get("entry") or 0) if perp_ok else None,
"avg_open": perp_avg if perp_ok else None,
"status": "open" if perp_ok else "pending",
"exchange_ord_id": str((perp or {}).get("exchange_ord_id") or ""),
"opened_at": result.get("opened_at") if perp_ok else None,
@@ -1455,12 +1468,43 @@ def _preview_po(body: dict[str, Any]) -> dict[str, Any]:
err = validate_option_primary_start(body)
if err:
raise ValueError(err)
from lib.hedge_plan.hedge_po_mode_lib import (
apply_mode_to_start_body,
get_hedge_po_mode,
is_cross_asset,
option_underlying,
perp_underlying,
)
body = apply_mode_to_start_body(body)
mode = str(body.get("po_mode") or get_hedge_po_mode())
opt_idx = None
perp_idx = None
try:
from lib.exchange.okx_public_http_lib import fetch_index_px
opt_idx = float(fetch_index_px(option_underlying(mode)) or 0) or None
if is_cross_asset(mode):
perp_idx = float(fetch_index_px(perp_underlying(mode)) or 0) or None
else:
perp_idx = opt_idx
except Exception:
opt_idx = perp_idx = None
if opt_idx is None:
try:
opt_idx = float(body.get("index_px") or body.get("entry") or 0) or None
except (TypeError, ValueError):
opt_idx = None
if perp_idx is None:
perp_idx = opt_idx
sized = size_from_premium(
premium_budget=float(body.get("premium_budget") or 0),
ask=float(body.get("ask") or 0),
ct_mult=float(body.get("ct_mult") or 0.01),
ratio=float(body.get("option_perp_ratio") or 2),
contract_size=float(body.get("contract_size") or 0.01),
option_index=opt_idx,
perp_index=perp_idx,
)
if not sized.get("ok"):
raise ValueError(sized.get("msg") or "定仓失败")
@@ -1468,19 +1512,25 @@ def _preview_po(body: dict[str, Any]) -> dict[str, Any]:
body["sheets"] = sized["sheets"]
body["contracts"] = sized["contracts"]
body["eth_qty"] = sized["eth_qty"]
if not body.get("index_px") and opt_idx:
body["index_px"] = opt_idx
# entry=永续参考价(跨币用 ETH 指数);勿把 BTC 指数写入永续开仓价
if not body.get("entry"):
body["entry"] = body.get("index_px") or 0
body["entry"] = perp_idx or opt_idx or body.get("index_px") or 0
out = build_option_primary_preview(body)
out["sizing"] = sized
return out
from lib.hedge_plan.hedge_plan_option_primary_lib import opt_type_for_view
direction = str(body.get("direction") or "long").lower()
entry = float(body["entry"])
tp = float(body["tp"])
sl = float(body["sl"])
contracts = float(body["contracts"])
contract_size = float(body.get("contract_size") or 0.01)
opt_type = str(body.get("opt_type") or ("P" if direction == "long" else "C"))
# 与以期权为主同口径:long→Call, short→Put(旧保险 long→Put 已废弃)
opt_type = str(body.get("opt_type") or opt_type_for_view(direction))
strike = float(body["strike"])
sheets = float(body.get("sheets") or 1)
ct_mult = float(body.get("ct_mult") or 0.01)