fix复盘真盈亏改为期权币盈亏×平仓价,不再绑卖回与初始权利金差。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -245,7 +245,7 @@ def settle_closed_trades_from_trading_account(
|
||||
) -> int:
|
||||
"""卖回后:拍 after 快照,并冻结复盘真盈亏.
|
||||
|
||||
realized_pnl_usdt = (权利金+币盈亏)×卖回VWAP − 初始权利金U;
|
||||
realized_pnl_usdt = 期权币盈亏×平仓价(卖回VWAP优先);
|
||||
不含现货多买.账户 after 仅审计.已 pnl_settled=1 的行永不改写.
|
||||
"""
|
||||
from lib.options.options_pnl_settle_lib import resolve_true_pnl_usdt_from_trade
|
||||
|
||||
@@ -505,26 +505,20 @@ def close_option_by_bid1(
|
||||
row_open_idx = float(bv)
|
||||
except Exception:
|
||||
pass
|
||||
row_pc = str(row["premium_ccy"] or row_ccy or premium_ccy or "").strip().upper()
|
||||
settled = settle_closed_pnl(
|
||||
premium_paid=paid,
|
||||
premium_received=recv,
|
||||
open_fee=row_ofee,
|
||||
close_fee=row_cfee,
|
||||
premium_ccy=row_pc or premium_ccy,
|
||||
premium_ccy=str(row["premium_ccy"] or row_ccy or premium_ccy),
|
||||
index_px=close_idx,
|
||||
open_index_px=row_open_idx,
|
||||
budget_usdt=row_budget,
|
||||
fee_ccy=str(row["fee_ccy"] or row["premium_ccy"] or row_ccy or ""),
|
||||
)
|
||||
pnl = settled.get("realized_pnl")
|
||||
# BTC/ETH:币盈亏可落库;真盈亏U与平仓价等卖回VWAP,禁止用指数占位
|
||||
if row_pc in ("ETH", "BTC"):
|
||||
pnl_usdt = None
|
||||
close_px_store = None
|
||||
else:
|
||||
pnl_usdt = settled.get("realized_pnl_usdt")
|
||||
close_px_store = settled.get("close_index_px")
|
||||
# 真盈亏U=期权币盈亏×平仓价
|
||||
pnl_usdt = settled.get("realized_pnl_usdt")
|
||||
note_sql = ""
|
||||
|
||||
# 占位: close_quote/recv/pnl/ord/closed_at/open_fee/close_fee/fee_ccy/close_idx/open_idx/pnl_u/[note]/id
|
||||
@@ -537,7 +531,7 @@ def close_option_by_bid1(
|
||||
row_ofee,
|
||||
row_cfee,
|
||||
str(row["premium_ccy"] or row_ccy or premium_ccy or ""),
|
||||
close_px_store,
|
||||
settled.get("close_index_px"),
|
||||
settled.get("open_index_px") or row_open_idx,
|
||||
pnl_usdt,
|
||||
]
|
||||
|
||||
@@ -403,14 +403,7 @@ def sync_open_options_trades(
|
||||
fee_ccy=fee_ccy,
|
||||
)
|
||||
realized_pnl = settled.get("realized_pnl")
|
||||
# BTC/ETH:真盈亏U与平仓价等卖回VWAP,禁止用指数占位
|
||||
pc = str(prem_ccy or "").strip().upper()
|
||||
if pc in ("ETH", "BTC"):
|
||||
pnl_usdt = None
|
||||
close_px_store = None
|
||||
else:
|
||||
pnl_usdt = settled.get("realized_pnl_usdt")
|
||||
close_px_store = settled.get("close_index_px")
|
||||
pnl_usdt = settled.get("realized_pnl_usdt")
|
||||
|
||||
conn.execute(
|
||||
"""
|
||||
@@ -438,7 +431,7 @@ def sync_open_options_trades(
|
||||
realized_pnl,
|
||||
close_ord_id,
|
||||
closed_at,
|
||||
close_px_store,
|
||||
settled.get("close_index_px"),
|
||||
settled.get("open_index_px"),
|
||||
pnl_usdt,
|
||||
prem_ccy,
|
||||
|
||||
@@ -177,33 +177,22 @@ def fee_to_usdt(
|
||||
def true_pnl_usdt_premium_move(
|
||||
*,
|
||||
coin_pnl: Any,
|
||||
premium_paid: Any,
|
||||
sell_back_px: Any,
|
||||
premium_paid: Any = None,
|
||||
sell_back_px: Any = None,
|
||||
initial_premium_usdt: Any = None,
|
||||
open_index_px: Any = None,
|
||||
) -> float | None:
|
||||
"""复盘真盈亏(U):币本位×卖回价 − 初始权利金U.
|
||||
"""复盘真盈亏(U)=期权历史币盈亏×平仓价.
|
||||
|
||||
币本位 = 权利金 + 币盈亏
|
||||
真盈亏U = 币本位 × 卖回VWAP − 初始权利金U
|
||||
初始权利金U 优先用开仓冻结值;缺失时才用 权利金×开仓价 补算.
|
||||
现货多买数量不进公式(只影响均价).
|
||||
平仓价优先卖回 VWAP,否则用冻结/传入的 close 价.
|
||||
premium_paid / initial_premium_usdt / open_index_px 仅兼容旧调用,不参与计算.
|
||||
"""
|
||||
del premium_paid, initial_premium_usdt, open_index_px
|
||||
pnl = _safe_float(coin_pnl)
|
||||
paid = _safe_float(premium_paid)
|
||||
close_px = _safe_float(sell_back_px)
|
||||
if pnl is None or paid is None:
|
||||
if pnl is None or close_px is None or close_px <= 0:
|
||||
return None
|
||||
if close_px is None or close_px <= 0:
|
||||
return None
|
||||
initial = _safe_float(initial_premium_usdt)
|
||||
if initial is None:
|
||||
open_px = _safe_float(open_index_px)
|
||||
if open_px is None or open_px <= 0:
|
||||
return None
|
||||
initial = float(paid) * float(open_px)
|
||||
coin_basis = float(paid) + float(pnl)
|
||||
return round(coin_basis * float(close_px) - float(initial), 4)
|
||||
return round(float(pnl) * float(close_px), 4)
|
||||
|
||||
|
||||
def resolve_spot_vwap_prices(
|
||||
@@ -213,18 +202,17 @@ def resolve_spot_vwap_prices(
|
||||
open_index_px: Any = None,
|
||||
close_index_px: Any = None,
|
||||
) -> tuple[float | None, float | None, str]:
|
||||
"""开仓价/卖回价解析.
|
||||
"""开仓价/平仓价解析(期权盈亏折U用).
|
||||
|
||||
开仓价:优先已冻结 open_index_px,缺失才补桥买 VWAP(不覆盖已冻值).
|
||||
卖回价:有桥时只认 close_sell VWAP;桥未关→pending_sell;已关无卖回→missing.
|
||||
无桥时 close_index_px 视为调用方传入的卖回价(测试/无现货桥).
|
||||
平仓价:桥上卖回 VWAP > 冻结 close_index_px > 开仓价.
|
||||
桥未关不再阻断折U(现货卖回与期权盈亏脱钩).
|
||||
|
||||
返回 (open_px, close_px, gate):
|
||||
gate='' 可算; 'pending_sell' 卖回未完成; 'missing' 缺价
|
||||
gate='' 可算; 'missing' 缺价
|
||||
"""
|
||||
open_px = _safe_float(open_index_px)
|
||||
close_hint = _safe_float(close_index_px)
|
||||
close_px: float | None = None
|
||||
close_px = close_hint
|
||||
bid = None
|
||||
try:
|
||||
if bridge_id is not None and str(bridge_id).strip() != "":
|
||||
@@ -234,40 +222,23 @@ def resolve_spot_vwap_prices(
|
||||
if conn is not None and bid is not None:
|
||||
try:
|
||||
from lib.options.options_spot_bridge_lib import (
|
||||
BRIDGE_CLOSED,
|
||||
bridge_buy_vwap,
|
||||
bridge_sell_vwap,
|
||||
ensure_bridge_table,
|
||||
)
|
||||
|
||||
ensure_bridge_table(conn)
|
||||
row = conn.execute(
|
||||
"SELECT status FROM options_spot_bridge WHERE id=?",
|
||||
(bid,),
|
||||
).fetchone()
|
||||
if row is None:
|
||||
# 桥记录不存在:按无桥处理,close_index 视为调用方卖回价
|
||||
close_px = close_hint
|
||||
else:
|
||||
if open_px is None or open_px <= 0:
|
||||
bv = bridge_buy_vwap(conn, bid)
|
||||
if bv is not None and bv > 0:
|
||||
open_px = bv
|
||||
st = str(
|
||||
row["status"] if hasattr(row, "keys") else (row[0] if row else "") or ""
|
||||
)
|
||||
if st and st != BRIDGE_CLOSED:
|
||||
return open_px, None, "pending_sell"
|
||||
sv = bridge_sell_vwap(conn, bid)
|
||||
if sv is not None and sv > 0:
|
||||
close_px = sv
|
||||
else:
|
||||
# 有桥却无卖回成交:禁止用指数/旧 close_index 冒充
|
||||
return open_px, None, "missing"
|
||||
if open_px is None or open_px <= 0:
|
||||
bv = bridge_buy_vwap(conn, bid)
|
||||
if bv is not None and bv > 0:
|
||||
open_px = bv
|
||||
sv = bridge_sell_vwap(conn, bid)
|
||||
if sv is not None and sv > 0:
|
||||
close_px = sv
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
close_px = close_hint
|
||||
if close_px is None or close_px <= 0:
|
||||
close_px = open_px
|
||||
if close_px is None or close_px <= 0:
|
||||
return open_px, close_px, "missing"
|
||||
return open_px, close_px, ""
|
||||
@@ -288,11 +259,11 @@ def resolve_true_pnl_usdt_from_trade(
|
||||
conn: Any = None,
|
||||
bridge_id: Any = None,
|
||||
) -> tuple[float | None, str]:
|
||||
"""由成交字段算复盘真盈亏: (权利金+币盈亏)×卖回价 − 初始权利金U.
|
||||
"""由成交字段算复盘真盈亏: 期权币盈亏×平仓价.
|
||||
|
||||
返回 (pnl_u, source): premium_move | stable | pending_sell | none
|
||||
返回 (pnl_u, source): premium_move | stable | none
|
||||
"""
|
||||
del fee_ccy
|
||||
del fee_ccy, initial_premium_usdt
|
||||
pc = str(premium_ccy or "").strip().upper()
|
||||
paid = _safe_float(premium_paid)
|
||||
recv = _safe_float(premium_received)
|
||||
@@ -308,20 +279,17 @@ def resolve_true_pnl_usdt_from_trade(
|
||||
return round(float(coin), 4), "stable"
|
||||
return None, "none"
|
||||
|
||||
open_px, close_px, gate = resolve_spot_vwap_prices(
|
||||
_open_px, close_px, gate = resolve_spot_vwap_prices(
|
||||
conn,
|
||||
bridge_id=bridge_id,
|
||||
open_index_px=open_index_px,
|
||||
close_index_px=close_index_px,
|
||||
)
|
||||
if gate == "pending_sell":
|
||||
return None, "pending_sell"
|
||||
if gate == "missing":
|
||||
return None, "none"
|
||||
usdt = true_pnl_usdt_premium_move(
|
||||
coin_pnl=coin,
|
||||
premium_paid=paid,
|
||||
sell_back_px=close_px,
|
||||
initial_premium_usdt=initial_premium_usdt,
|
||||
open_index_px=open_px,
|
||||
)
|
||||
if usdt is not None:
|
||||
return usdt, "premium_move"
|
||||
@@ -339,10 +307,10 @@ def settle_sim_coin_pnl_usdt(
|
||||
fee_ccy: str | None = None,
|
||||
premium_ccy: str = "ETH",
|
||||
) -> dict[str, float | None]:
|
||||
"""由毛权利金生成净币盈亏,再按用户式折 U(费只扣进币盈亏一次).
|
||||
"""由毛权利金生成净币盈亏,再按期权盈亏折 U(费只扣进币盈亏一次).
|
||||
|
||||
币盈亏 = 收回 − 已付 − 开/平手续费
|
||||
真盈亏U = (权利金+币盈亏)×卖回价 − 初始权利金U
|
||||
真盈亏U = 币盈亏 × 平仓价
|
||||
"""
|
||||
del fee_ccy
|
||||
paid = float(_safe_float(premium_paid) or 0.0)
|
||||
@@ -357,33 +325,39 @@ def settle_sim_coin_pnl_usdt(
|
||||
fee_u = round(ofee * float(open_idx) + cfee * float(sell_px), 4)
|
||||
elif sell_px is not None and sell_px > 0 and (ofee > 0 or cfee > 0):
|
||||
fee_u = round((ofee + cfee) * float(sell_px), 4)
|
||||
if open_idx is None or open_idx <= 0 or sell_px is None or sell_px <= 0:
|
||||
if sell_px is None or sell_px <= 0:
|
||||
return {
|
||||
"realized_pnl": net_coin,
|
||||
"close_index_px": float(sell_px) if sell_px is not None and sell_px > 0 else None,
|
||||
"close_index_px": None,
|
||||
"open_index_px": float(open_idx) if open_idx is not None and open_idx > 0 else None,
|
||||
"realized_pnl_usdt": None,
|
||||
"total_fee": round(ofee + cfee, 8),
|
||||
"initial_premium_usdt": None,
|
||||
"initial_premium_usdt": (
|
||||
round(float(paid) * float(open_idx), 4)
|
||||
if open_idx is not None and open_idx > 0 and paid > 0
|
||||
else None
|
||||
),
|
||||
"proceeds_usdt": None,
|
||||
"fee_usdt": fee_u,
|
||||
"premium_ccy": str(premium_ccy or "").strip().upper() or "ETH",
|
||||
}
|
||||
initial_u = float(paid) * float(open_idx)
|
||||
initial_u = (
|
||||
float(paid) * float(open_idx)
|
||||
if open_idx is not None and open_idx > 0
|
||||
else None
|
||||
)
|
||||
proceeds_u = float(recv) * float(sell_px)
|
||||
pnl_u = true_pnl_usdt_premium_move(
|
||||
coin_pnl=net_coin,
|
||||
premium_paid=paid,
|
||||
sell_back_px=sell_px,
|
||||
initial_premium_usdt=initial_u,
|
||||
)
|
||||
return {
|
||||
"realized_pnl": net_coin,
|
||||
"close_index_px": float(sell_px),
|
||||
"open_index_px": float(open_idx),
|
||||
"open_index_px": float(open_idx) if open_idx is not None and open_idx > 0 else None,
|
||||
"realized_pnl_usdt": pnl_u,
|
||||
"total_fee": round(ofee + cfee, 8),
|
||||
"initial_premium_usdt": round(initial_u, 4),
|
||||
"initial_premium_usdt": round(initial_u, 4) if initial_u is not None else None,
|
||||
"proceeds_usdt": round(proceeds_u, 4),
|
||||
"fee_usdt": fee_u,
|
||||
"premium_ccy": str(premium_ccy or "").strip().upper() or "ETH",
|
||||
@@ -406,9 +380,7 @@ def settle_closed_pnl(
|
||||
由「毛权利金」结算.
|
||||
币数量净盈亏 = 收回 − 已付 − 开/平手续费(只扣一次).
|
||||
|
||||
币本位 U:
|
||||
(权利金+币盈亏)×卖回价 − 初始权利金U
|
||||
(开仓价缺失时回退为净币×卖回价;币量不含现货多余仓)
|
||||
币本位 U: 净币盈亏×平仓价(币量不含现货多余仓)
|
||||
USDC: 净额原样为 U.
|
||||
|
||||
注意:不要用 budget_usdt 当开仓成本——它含现货买入缓冲.
|
||||
@@ -428,8 +400,8 @@ def settle_closed_pnl(
|
||||
if pc in ("ETH", "BTC"):
|
||||
open_idx = _safe_float(open_index_px)
|
||||
sell_px = _safe_float(index_px)
|
||||
if open_idx is not None and open_idx > 0 and sell_px is not None and sell_px > 0:
|
||||
out = settle_sim_coin_pnl_usdt(
|
||||
if sell_px is not None and sell_px > 0:
|
||||
return settle_sim_coin_pnl_usdt(
|
||||
premium_paid=paid,
|
||||
premium_received=recv,
|
||||
open_fee=ofee,
|
||||
@@ -439,8 +411,6 @@ def settle_closed_pnl(
|
||||
fee_ccy=fee_ccy,
|
||||
premium_ccy=pc,
|
||||
)
|
||||
return out
|
||||
# 无开仓指数:回退净币×平仓价
|
||||
return freeze_pnl_to_usdt(
|
||||
net,
|
||||
premium_ccy=pc,
|
||||
|
||||
@@ -607,10 +607,7 @@ def sync_options_from_local_trades(
|
||||
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"):
|
||||
if pnl_u is not None and pnl_src not in ("none", "pending_sell"):
|
||||
try:
|
||||
# 卖回 VWAP 写入 close;开仓价/初始权利金仅缺省时补
|
||||
heal_initial = stored_initial
|
||||
|
||||
@@ -229,7 +229,7 @@
|
||||
<span class="or-step" aria-hidden="true">1</span>
|
||||
<div>
|
||||
<div class="or-section-title" id="or-list-title">期权交易记录</div>
|
||||
<p class="or-section-desc">真盈亏=历史币盈亏×卖回价 − 权利金×(开仓价−卖回价);开/卖回价为多笔 VWAP。卖回未完标「待卖回」,缺价标「缺价未核」。期权历史仅记币收益。</p>
|
||||
<p class="or-section-desc">真盈亏=期权历史币盈亏×平仓价(有卖回 VWAP 优先,否则用冻结平仓价)。缺价标「缺价未核」。期权历史仅记币收益。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="options-strike-table-wrap or-trades-table-wrap" id="or-trades-wrap">
|
||||
@@ -239,7 +239,7 @@
|
||||
<th>类型</th>
|
||||
<th>标的/合约</th>
|
||||
<th>初始权利金</th>
|
||||
<th title="币盈亏×卖回价 − 权利金×(开仓价−卖回价)">真盈亏</th>
|
||||
<th title="期权历史币盈亏×平仓价">真盈亏</th>
|
||||
<th>盈利率</th>
|
||||
<th title="仅期权开/平手续费">手续费</th>
|
||||
<th>开仓时间</th>
|
||||
|
||||
Reference in New Issue
Block a user