Fix btc_eth live card option premium USDT conversion.
Use option (BTC) index for coin-to-U; never fall back to ETH perp mark. Also add no-server-ops Cursor rule. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -104,9 +104,57 @@ def _has_open_leg(legs: list[dict[str, Any]]) -> bool:
|
||||
return any(str(x.get("status") or "").lower() == "open" for x in legs)
|
||||
|
||||
|
||||
def resolve_option_index_px(
|
||||
cfg: dict[str, Any],
|
||||
option_uly: str,
|
||||
*,
|
||||
ex_opt: Any = None,
|
||||
cross_asset: bool = False,
|
||||
perp_fallback: Optional[float] = None,
|
||||
) -> Optional[float]:
|
||||
"""期权币→U 必须用期权标的指数;跨币禁止回落永续价(否则 BTC 权利金会×ETH).
|
||||
|
||||
优先公共 HTTP(单参),再 cfg.fetch_index_price(ex, uly);同币才可用永续价兜底.
|
||||
"""
|
||||
uly = str(option_uly or "ETH").strip().upper() or "ETH"
|
||||
try:
|
||||
from lib.exchange.okx_public_http_lib import fetch_index_px
|
||||
|
||||
px = _sf(fetch_index_px(uly))
|
||||
if px is not None and px > 0:
|
||||
return px
|
||||
except Exception:
|
||||
pass
|
||||
fetch_idx = cfg.get("fetch_index_price") if isinstance(cfg, dict) else None
|
||||
if callable(fetch_idx):
|
||||
try:
|
||||
if ex_opt is not None:
|
||||
px = _sf(fetch_idx(ex_opt, uly))
|
||||
else:
|
||||
# 兼容已绑定 ex 的单参包装;勿把 uly 当成 ex
|
||||
px = _sf(fetch_idx(uly))
|
||||
if px is not None and px > 0:
|
||||
return px
|
||||
except TypeError:
|
||||
try:
|
||||
px = _sf(fetch_idx(uly))
|
||||
if px is not None and px > 0:
|
||||
return px
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
if not cross_asset:
|
||||
fb = _sf(perp_fallback)
|
||||
if fb is not None and fb > 0:
|
||||
return fb
|
||||
return None
|
||||
|
||||
|
||||
def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any]]:
|
||||
"""当前持仓:未平完的永期仓(含计划已结束但腿仍 open)."""
|
||||
from lib.hedge_plan.hedge_plan_db import attach_legs_to_plans, list_plans_for_live_positions
|
||||
from lib.hedge_plan.hedge_po_mode_lib import combo_net_usdt, get_hedge_po_mode, is_cross_asset, option_underlying
|
||||
|
||||
rows = list_plans_for_live_positions(conn, plan_type="perp_options", limit=40)
|
||||
if not rows:
|
||||
@@ -129,7 +177,6 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
|
||||
|
||||
get_perp_metrics = cfg.get("get_live_position_exchange_metrics")
|
||||
get_cs = cfg.get("get_contract_size")
|
||||
fetch_idx = cfg.get("fetch_index_price")
|
||||
ex_perp = None
|
||||
try:
|
||||
get_pex = cfg.get("exchange")
|
||||
@@ -161,7 +208,13 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
|
||||
continue
|
||||
|
||||
view = str(plan.get("direction") or "long").lower()
|
||||
uly = str(plan.get("underlying") or "ETH").upper()
|
||||
mode = str(plan.get("po_mode") or get_hedge_po_mode())
|
||||
uly = str(
|
||||
plan.get("option_underlying") or plan.get("underlying") or option_underlying(mode) or "ETH"
|
||||
).upper()
|
||||
cross = is_cross_asset(mode) or (
|
||||
str(plan.get("perp_underlying") or "").upper() not in ("", uly)
|
||||
)
|
||||
inst_id = str((opt_leg or {}).get("inst_id") or "")
|
||||
opt_raw = _pick_option_row(opt_rows, inst_id) if inst_id else None
|
||||
|
||||
@@ -230,14 +283,14 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
|
||||
perp_top_sz = _sf(book.get("top_sz"))
|
||||
book_side = str(book.get("book_side") or "")
|
||||
|
||||
idx = None
|
||||
if callable(fetch_idx):
|
||||
try:
|
||||
idx = _sf(fetch_idx(uly))
|
||||
except Exception:
|
||||
idx = None
|
||||
if idx is None or idx <= 0:
|
||||
idx = perp_vwap or perp_mark or strike or 0.0
|
||||
# 币→U:期权标的指数(btc_eth=BTC);禁止用 ETH 永续价折算
|
||||
idx = resolve_option_index_px(
|
||||
cfg,
|
||||
uly,
|
||||
ex_opt=ex_opt,
|
||||
cross_asset=cross,
|
||||
perp_fallback=(perp_vwap or perp_mark or strike),
|
||||
)
|
||||
|
||||
net = estimate_combo_net_pnl(
|
||||
view_side=view,
|
||||
@@ -256,12 +309,15 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
|
||||
option_fee=fr_opt,
|
||||
)
|
||||
opt_net_coin = float(net.get("opt_net") or 0)
|
||||
# 币→U:用当前指数(与买币参考价同口径)
|
||||
opt_net_usdt = round(opt_net_coin * float(idx or 0), 4) if idx else None
|
||||
opt_net_usdt = (
|
||||
round(opt_net_coin * float(idx), 4) if idx is not None and idx > 0 else None
|
||||
)
|
||||
perp_net = float(net.get("perp_net") or 0)
|
||||
combo_usdt = round((opt_net_usdt or 0) + perp_net, 4) if opt_net_usdt is not None else round(perp_net, 4)
|
||||
combo_usdt = combo_net_usdt(net, option_index=float(idx or 0))
|
||||
if combo_usdt is None:
|
||||
combo_usdt = round(perp_net, 4)
|
||||
prem_coin = float(net.get("premium") or (float(avg_open or 0) * float(sheets or 0) * float(ct or 0.01)))
|
||||
prem_usdt = round(prem_coin * float(idx or 0), 2) if idx else None
|
||||
prem_usdt = round(prem_coin * float(idx), 2) if idx is not None and idx > 0 else None
|
||||
|
||||
opened_at = plan.get("opened_at")
|
||||
opened_ms = _parse_opened_ms(opened_at)
|
||||
|
||||
@@ -1775,14 +1775,31 @@ 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(粗):用指数
|
||||
# 期权币本位盈亏换算到 U:用期权标的指数(跨币=BTC,禁止×ETH)
|
||||
try:
|
||||
fetch_idx = cfg.get("fetch_index_price")
|
||||
idx = float(fetch_idx(uly) or 0) if callable(fetch_idx) else 0.0
|
||||
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
|
||||
|
||||
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,
|
||||
)
|
||||
or 0
|
||||
)
|
||||
# opt_pnl 可能已是币;若为币且绝对值很小则 × 指数
|
||||
# resolve_option_leg_realized_pnl 对币本位通常返回币;统一展示用 preview 风格
|
||||
if abs(opt_pnl) < 10 and idx > 0 and abs(opt_pnl * idx) > abs(opt_pnl):
|
||||
# 更可能是币单位
|
||||
opt_pnl_u = opt_pnl * idx
|
||||
else:
|
||||
opt_pnl_u = opt_pnl
|
||||
|
||||
Reference in New Issue
Block a user