fix卖回VWAP必须落库并覆盖指数占位价,禁止预算反推开仓价。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-13 11:55:42 +08:00
parent dd979d9dd0
commit 11a083470d
7 changed files with 378 additions and 37 deletions
+10 -3
View File
@@ -495,9 +495,16 @@ def close_option_by_bid1(
row_budget = _safe_float(row["budget_usdt"])
row_open_idx = _safe_float(row["open_index_px"])
if (row_open_idx is None or row_open_idx <= 0) and row_budget and paid > 1e-12:
# 无开仓指数时用预算/已付反推
row_open_idx = float(row_budget) / paid
# 禁止 budget/premium 反推开仓价(会把缓冲算进均价,开仓价被抬飞)
if (row_open_idx is None or row_open_idx <= 0) and row.get("bridge_id") is not None:
try:
from lib.options.options_spot_bridge_lib import bridge_buy_vwap
bv = bridge_buy_vwap(conn, int(row["bridge_id"]))
if bv is not None and float(bv) > 0:
row_open_idx = float(bv)
except Exception:
pass
settled = settle_closed_pnl(
premium_paid=paid,
premium_received=recv,
+10 -8
View File
@@ -511,6 +511,16 @@ def open_coin_option_buy_full(
except Exception:
pass
# 开仓价必须在缩减 coin_bought 之前按买币 VWAP 冻住
# (buy_usdt_spent/buy_coin_accum;禁止事后 budget/缩水权利金量反推)
open_px_freeze = idx_open
try:
bv = _spot_bridge.bridge_buy_vwap(conn, bridge_id)
if bv is not None and float(bv) > 0:
open_px_freeze = float(bv)
except Exception:
pass
_spot_bridge.update_bridge(
conn,
bridge_id,
@@ -520,14 +530,6 @@ def open_coin_option_buy_full(
message="期权持仓中",
)
# 开仓价一次冻结:优先买币 VWAP(之后不再改);权利金币量 premium_paid 固定
open_px_freeze = idx_open
try:
bv = _spot_bridge.bridge_buy_vwap(conn, bridge_id)
if bv is not None and float(bv) > 0:
open_px_freeze = float(bv)
except Exception:
pass
trade_id = _insert_coin_trade(
conn,
inst_id=inst_id,
+18 -22
View File
@@ -585,52 +585,48 @@ def sync_options_from_local_trades(
status_label = "已平"
pnl_u = None
# 价已冻则只用库内价;缺价才问桥 VWAP(避免刷新改均价)
prices_frozen = (
open_idx is not None
and open_idx > 0
and sell_idx is not None
and sell_idx > 0
# 始终问桥:买/卖回 VWAP 优先于库内指数占位价(防预算反推开仓价、平仓指数冒充卖回价)
opx, cpx, _gate = resolve_spot_vwap_prices(
conn,
bridge_id=r["bridge_id"],
open_index_px=open_idx,
close_index_px=sell_idx,
)
use_bridge = None if prices_frozen else r["bridge_id"]
pnl_u, pnl_src = resolve_true_pnl_usdt_from_trade(
premium_paid=prem,
premium_received=r["premium_received"],
realized_pnl=r["realized_pnl"],
open_index_px=open_idx if (open_idx is not None and open_idx > 0) else None,
close_index_px=sell_idx,
open_index_px=opx,
close_index_px=cpx,
open_fee=ofee,
close_fee=cfee,
premium_ccy=ccy,
fee_ccy=str(r["premium_ccy"] or ccy or ""),
conn=conn if use_bridge is not None else None,
bridge_id=use_bridge,
conn=conn,
bridge_id=r["bridge_id"],
)
if pnl_src == "pending_sell":
status_label = "待卖回"
pnl_u = None
elif pnl_u is not None and pnl_src not in ("none", "pending_sell"):
try:
opx, cpx = open_idx, sell_idx
if not prices_frozen:
opx, cpx, _g = resolve_spot_vwap_prices(
conn,
bridge_id=r["bridge_id"],
open_index_px=open_idx,
close_index_px=sell_idx,
)
# 真盈亏U每次刷新按固定量+价重算;均价只补缺不覆盖
# VWAP 非空时覆盖指数占位;币量字段不改
conn.execute(
"""
UPDATE options_trades
SET realized_pnl_usdt = ?,
open_index_px = COALESCE(open_index_px, ?),
close_index_px = COALESCE(close_index_px, ?),
open_index_px = COALESCE(?, open_index_px),
close_index_px = COALESCE(?, close_index_px),
pnl_settled = 1
WHERE id = ?
""",
(pnl_u, opx, cpx, trade_id),
)
if opx is not None and opx > 0:
open_idx = opx
open_idx_for_prem = opx
if cpx is not None and cpx > 0:
sell_idx = cpx
except Exception:
pass
else:
+22 -1
View File
@@ -778,6 +778,13 @@ def sell_residual_after_option_flat(
}
recovered = _safe_float(sell.get("usdt_recovered"))
sold_amt = _safe_float(sell.get("coin_sold"))
# 缺 USDT 回报时用成交价×币量补,保证 close_sell VWAP 能落库
if (recovered is None or recovered <= 0) and sold_amt is not None and sold_amt > 0:
fill_px = _safe_float(sell.get("fill_px"))
if fill_px is not None and fill_px > 0:
recovered = round(float(sold_amt) * float(fill_px), 8)
sell = dict(sell)
sell["usdt_recovered"] = recovered
# 平仓后卖回(可多笔):累加 close_sell_* 供卖回 VWAP;开仓多余卖回不走这里
add_bridge_close_sell_fill(
c,
@@ -809,6 +816,7 @@ def sell_residual_after_option_flat(
"bridge_id": bid,
"bridge_status": BRIDGE_PENDING_SELL,
}
sell_vwap = bridge_sell_vwap(c, bid)
update_bridge(
c,
bid,
@@ -818,13 +826,26 @@ def sell_residual_after_option_flat(
message="期权已平,币已卖回 USDT",
close=True,
)
# 卖回 VWAP 写入关联已平仓(覆盖平仓时指数占位价)
if sell_vwap is not None and float(sell_vwap) > 0:
try:
c.execute(
"""
UPDATE options_trades
SET close_index_px = ?
WHERE bridge_id = ? AND status = 'closed'
""",
(float(sell_vwap), int(bid)),
)
except Exception:
pass
return {
"ok": True,
"sell": sell,
"bridge_id": bid,
"bridge_status": BRIDGE_CLOSED,
"sell_complete": True,
"sell_vwap": bridge_sell_vwap(c, bid),
"sell_vwap": sell_vwap,
}
if get_db is not None: