Show editable exit on live cards; price perp PnL via close-side book VWAP.

Align monitor profit-exit with the same book VWAP so card and auto-exit stay consistent.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-14 17:29:58 +08:00
parent 3f98ba1e88
commit 9295dcffe3
10 changed files with 597 additions and 10 deletions
@@ -4,6 +4,7 @@ from __future__ import annotations
from datetime import datetime, timedelta, timezone
from typing import Any, Optional
from lib.hedge_plan.hedge_plan_book_lib import fetch_perp_close_vwap
from lib.hedge_plan.hedge_plan_option_primary_lib import (
estimate_combo_net_pnl,
fee_rate,
@@ -129,6 +130,12 @@ 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")
ex_perp = get_pex() if callable(get_pex) else get_pex
except Exception:
ex_perp = None
fr = fee_rate()
fr_opt = option_fee_rate()
now_ms = int(datetime.now(tz=_BJ).timestamp() * 1000)
@@ -205,6 +212,21 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
if (margin is None or margin <= 0) and contracts and perp_mark and perp_lev and perp_lev > 0:
margin = round(float(contracts) * float(cs) * float(perp_mark) / float(perp_lev), 2)
# 永续净利:按平仓方向扫盘口 VWAP(方案 A);标记价仅参考
book = fetch_perp_close_vwap(
ex_perp,
symbol=sym,
position_side=perp_dir,
contracts=float(contracts or 0),
fallback_px=perp_mark or perp_entry,
)
perp_vwap = _sf(book.get("vwap")) or perp_mark or perp_entry
perp_liq_ok = bool(book.get("liq_ok")) if book.get("ok") else False
perp_liq_msg = str(book.get("msg") or "")
perp_top = _sf(book.get("top_px"))
perp_top_sz = _sf(book.get("top_sz"))
book_side = str(book.get("book_side") or "")
idx = None
if callable(fetch_idx):
try:
@@ -212,7 +234,7 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
except Exception:
idx = None
if idx is None or idx <= 0:
idx = perp_mark or strike or 0.0
idx = perp_vwap or perp_mark or strike or 0.0
net = estimate_combo_net_pnl(
view_side=view,
@@ -224,7 +246,7 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
ct_mult=float(ct or 0.01),
perp_direction=perp_dir,
perp_entry=float(perp_entry or 0),
perp_mark=float(perp_mark or 0),
perp_mark=float(perp_vwap or 0),
contracts=float(contracts or 0),
contract_size=float(cs or 0.01),
fee=fr,
@@ -245,6 +267,10 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
hold_sec = int((now_ms - opened_ms) / 1000) if opened_ms else None
remain_sec = int((exp_ms - now_ms) / 1000) if exp_ms else None
exit_mode = str(plan.get("exit_mode") or "").strip().lower()
if exit_mode not in ("profit", "target"):
exit_mode = "profit" if _sf(plan.get("profit_target_usdt")) else "target"
out.append(
{
"plan_id": plan.get("id"),
@@ -260,6 +286,12 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
"index_px": idx,
"fee_rate": fr,
"option_fee_rate": fr_opt,
"exit": {
"mode": exit_mode,
"profit_target_usdt": _sf(plan.get("profit_target_usdt")),
"option_target_points": _sf(plan.get("option_target_points")),
"perp_target_points": _sf(plan.get("perp_target_points")),
},
"option": {
"inst_id": inst_id or "—",
"opt_type": opt_type,
@@ -286,17 +318,23 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
"contract_size": cs,
"avg_open": perp_entry,
"mark_px": perp_mark,
"vwap": perp_vwap,
"book_side": book_side,
"top_px": perp_top,
"top_sz": perp_top_sz,
"liq_ok": perp_liq_ok,
"liq_msg": perp_liq_msg,
"leverage": perp_lev or None,
"margin": margin,
"upl": perp_upl,
"net_usdt": perp_net,
"live": perp_upl is not None or (perp_mark is not None and perp_mark > 0),
"live": bool(book.get("ok")) or perp_upl is not None,
},
"combo": {
"net_usdt": combo_usdt,
"opt_net_usdt": opt_net_usdt,
"perp_net_usdt": perp_net,
"note": "扣开平双边手续费后估",
"note": "扣开平双边手续费;永续按盘口均价估",
},
}
)