修复币本位统计:开仓成本按预算/开仓指数计,不再用平仓价重估。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-08 23:11:45 +08:00
parent 2369aae362
commit d05629c362
7 changed files with 258 additions and 120 deletions
+88 -8
View File
@@ -148,6 +148,32 @@ def freeze_pnl_to_usdt(
}
def fee_to_usdt(
fee_amt: Any,
fee_ccy: Any,
*,
index_px: float | None,
) -> float:
"""手续费折 USDT."""
amt = fee_amount_abs(fee_amt)
if amt <= 0:
return 0.0
fc = str(fee_ccy or "").strip().upper()
if fc in ("USDT", "USDC", "USD", ""):
# 空币种时按稳定币近似,避免把小数额 ETH 费当成整 U
if fc == "" and amt < 0.05:
idx = _safe_float(index_px)
if idx is not None and idx > 100:
return round(amt * float(idx), 4)
return round(amt, 4)
if fc in ("ETH", "BTC"):
idx = _safe_float(index_px)
if idx is None or idx <= 0:
return 0.0
return round(amt * float(idx), 4)
return round(amt, 4)
def settle_closed_pnl(
*,
premium_paid: Any,
@@ -156,11 +182,18 @@ def settle_closed_pnl(
close_fee: Any = 0,
premium_ccy: str,
index_px: float | None,
open_index_px: float | None = None,
budget_usdt: float | None = None,
fee_ccy: str | None = None,
) -> dict[str, float | None]:
"""
由「毛权利金」结算:净盈亏 = 收回 − 已付 − 开/平手续费(权利金单位).
仅用于本地开平仓权利金口径;不要对 OKX realizedPnl 再调此函数扣费
(交易所 realizedPnl = pnl + fee + ..., 已含手续费).
由「毛权利金」结算.
币数量净盈亏 = 收回 − 已付 − 开/平手续费.
冻结 U(币本位):
平仓收回×平仓指数 − 开仓成本U − 手续费U
开仓成本优先 budget_usdt,否则 已付×开仓指数(缺省才用平仓指数).
这样不会把开仓成本也按平仓价重估,避免和余额对不上.
不要对 OKX realizedPnl 再调此函数扣费.
"""
paid = float(_safe_float(premium_paid) or 0.0)
recv = float(_safe_float(premium_received) or 0.0)
@@ -171,12 +204,59 @@ def settle_closed_pnl(
if cfee < 0:
cfee = abs(cfee)
net = round(recv - paid - ofee - cfee, 8)
return freeze_pnl_to_usdt(
net,
premium_ccy=premium_ccy,
index_px=index_px,
total_fee=ofee + cfee,
pc = str(premium_ccy or "").strip().upper()
close_idx = _safe_float(index_px)
open_idx = _safe_float(open_index_px)
budget = _safe_float(budget_usdt)
fc = str(fee_ccy or pc or "").strip().upper()
if pc not in ("ETH", "BTC"):
return freeze_pnl_to_usdt(
net,
premium_ccy=premium_ccy,
index_px=close_idx,
total_fee=ofee + cfee,
)
if close_idx is None or close_idx <= 0:
return {
"realized_pnl": net,
"close_index_px": None,
"open_index_px": float(open_idx) if open_idx and open_idx > 0 else None,
"realized_pnl_usdt": None,
"total_fee": round(ofee + cfee, 8),
}
# 开仓成本 U:预算(真实花出去的 USDT) > 已付×开仓指数 > 已付×平仓指数
if budget is not None and budget > 0:
entry_u = float(budget)
if open_idx is None or open_idx <= 0:
open_idx = (entry_u / paid) if paid > 1e-12 else close_idx
elif open_idx is not None and open_idx > 0:
entry_u = paid * float(open_idx)
else:
entry_u = paid * float(close_idx)
open_idx = close_idx
exit_u = recv * float(close_idx)
# 开仓费按开仓指数、平仓费按平仓指数折 U
fee_u = fee_to_usdt(ofee, fc or pc, index_px=open_idx) + fee_to_usdt(
cfee, fc or pc, index_px=close_idx
)
# 若开仓成本已是 budget(含买币),ETH 开仓费已体现在币变少/卖回变少,不再重复扣 ETH 开仓费
if budget is not None and budget > 0 and (fc or pc) in ("ETH", "BTC"):
fee_u = fee_to_usdt(cfee, fc or pc, index_px=close_idx)
if str(fee_ccy or "").strip().upper() in ("USDT", "USDC", "USD"):
fee_u += fee_to_usdt(ofee, fee_ccy, index_px=open_idx)
usdt = round(exit_u - entry_u - fee_u, 4)
return {
"realized_pnl": net,
"close_index_px": float(close_idx),
"open_index_px": float(open_idx) if open_idx and open_idx > 0 else None,
"realized_pnl_usdt": usdt,
"total_fee": round(ofee + cfee, 8),
}
def settle_exchange_realized_pnl(