修复 BTC 币本位期权净盈亏被 round4 抹成 0 的精度问题。

币本位权利金/回收/盈亏改用 8 位小数,避免小额 BTC 报价显示为 0。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-02 14:45:54 +08:00
parent c1f783df9b
commit 34322d1731
5 changed files with 59 additions and 15 deletions
+21 -7
View File
@@ -11,9 +11,11 @@ from lib.options.options_close_gate_lib import (
update_close_gate,
)
from lib.options.options_pricing_lib import (
PREMIUM_DECIMALS_COIN,
estimate_close_by_bids,
fetch_option_mark_px,
is_stub_bid_px,
round_premium_amount,
total_premium,
)
@@ -307,7 +309,9 @@ def close_option_by_bid1(
px = float(order.get("px", locked_bid_px))
oid = str((order.get("data") or {}).get("ordId") or "")
# 本批预估收回(按挂单张数);成交后按实际减仓张数重算并入累计
prem_recv_submit = round(total_premium(px, level_sheets * ct_mult), 4)
prem_recv_submit = round_premium_amount(
total_premium(px, level_sheets * ct_mult), decimals=PREMIUM_DECIMALS_COIN
)
time.sleep(0.6)
invalidate_option_positions_cache()
raw2 = cfg["fetch_option_positions"](ex)
@@ -331,7 +335,9 @@ def close_option_by_bid1(
if fully_closed and filled_sheets < 1:
filled_sheets = level_sheets
batch_recv = (
round(total_premium(px, filled_sheets * ct_mult), 4) if filled_sheets > 0 else 0.0
round_premium_amount(total_premium(px, filled_sheets * ct_mult), decimals=PREMIUM_DECIMALS_COIN)
if filled_sheets > 0
else 0.0
)
total_recv = batch_recv
@@ -360,7 +366,9 @@ def close_option_by_bid1(
ord_id=oid or None,
)
accum = get_open_close_accum(conn, inst_id)
total_recv = round(float(accum.get("recv") or 0), 4)
total_recv = round_premium_amount(
float(accum.get("recv") or 0), decimals=PREMIUM_DECIMALS_COIN
) or 0.0
accum_sheets = int(accum.get("sheets") or 0)
accum_notional = float(accum.get("notional") or 0)
if accum_sheets > 0 and accum_notional > 0:
@@ -380,14 +388,20 @@ def close_option_by_bid1(
for i, row in enumerate(open_rows):
paid = float(row["premium_paid"] or 0)
if i == len(open_rows) - 1:
recv = round(total_recv - allocated, 4)
recv = round_premium_amount(
total_recv - allocated, decimals=PREMIUM_DECIMALS_COIN
) or 0.0
elif total_paid > 0:
recv = round(total_recv * (paid / total_paid), 4)
recv = round_premium_amount(
total_recv * (paid / total_paid), decimals=PREMIUM_DECIMALS_COIN
) or 0.0
allocated += recv
else:
recv = round(total_recv / len(open_rows), 4)
recv = round_premium_amount(
total_recv / len(open_rows), decimals=PREMIUM_DECIMALS_COIN
) or 0.0
allocated += recv
pnl = round(recv - paid, 4)
pnl = round_premium_amount(recv - paid, decimals=PREMIUM_DECIMALS_COIN) or 0.0
note_sql = ""
from lib.options.options_time_lib import options_app_now_str
+5 -1
View File
@@ -95,6 +95,8 @@ def forget_close_gate_for_inst(inst_id: str) -> None:
def net_pnl_from_display_row(row: dict[str, Any]) -> float | None:
"""与持仓卡「净盈亏」同口径:买一可回收 − 权利金;残档买一则无净值."""
from lib.options.options_pricing_lib import PREMIUM_DECIMALS_COIN, round_premium_amount
preview = row.get("close_preview") if isinstance(row.get("close_preview"), dict) else {}
if preview.get("bid_invalid"):
return None
@@ -112,7 +114,9 @@ def net_pnl_from_display_row(row: dict[str, Any]) -> float | None:
recv = _safe_float(preview.get("total_received"))
paid = _safe_float(row.get("premium_paid"))
if covered > 0 and recv is not None and paid is not None:
return round(recv - paid, 4)
ccy = str(row.get("premium_ccy") or "").strip().upper()
decimals = PREMIUM_DECIMALS_COIN if ccy in ("ETH", "BTC") else None
return round_premium_amount(recv - paid, ccy=ccy or None, decimals=decimals)
return None
+24 -5
View File
@@ -4,6 +4,24 @@ from __future__ import annotations
import math
from typing import Any
# 币本位权利金常为 1e-4~1e-5 量级(尤其 BTC);round(...,4) 会把净盈亏抹成 0
PREMIUM_DECIMALS_COIN = 8
PREMIUM_DECIMALS_FIAT = 4
def round_premium_amount(v: float | None, *, ccy: str | None = None, decimals: int | None = None) -> float | None:
"""权利金/回收/币盈亏入账精度:ETH/BTC 用 8 位,USDC 等用法币 4 位."""
if v is None:
return None
try:
n = float(v)
except (TypeError, ValueError):
return None
if decimals is None:
unit = (ccy or "").strip().upper()
decimals = PREMIUM_DECIMALS_COIN if unit in ("ETH", "BTC") else PREMIUM_DECIMALS_FIAT
return round(n, int(decimals))
def ct_mult_from_meta(meta: dict[str, Any] | None) -> float:
if not meta:
@@ -298,7 +316,7 @@ def estimate_close_by_bids(
"available_sheets": sz,
"sheets": take,
"eth_amount": eth_amount,
"received": round(received, 4),
"received": round_premium_amount(received, decimals=PREMIUM_DECIMALS_COIN),
}
)
total_received += received
@@ -310,15 +328,16 @@ def estimate_close_by_bids(
estimated_pnl_ratio_pct = None
if premium_paid is not None and covered > 0:
paid = float(premium_paid)
estimated_pnl = round(total_received - paid, 4)
if paid > 0:
# 必须用 8 位:BTC 权利金常约 1e-4,round(...,4) 会把盈亏抹成 0
estimated_pnl = round_premium_amount(total_received - paid, decimals=PREMIUM_DECIMALS_COIN)
if paid > 0 and estimated_pnl is not None:
estimated_pnl_ratio_pct = round(estimated_pnl / paid * 100.0, 2)
return {
"levels": levels,
"covered_sheets": covered,
"uncovered_sheets": remaining,
"total_received": round(total_received, 4),
"avg_px": round(avg_px, 4) if avg_px is not None else None,
"total_received": round_premium_amount(total_received, decimals=PREMIUM_DECIMALS_COIN),
"avg_px": round(avg_px, 8) if avg_px is not None else None,
"estimated_pnl": estimated_pnl,
"estimated_pnl_ratio_pct": estimated_pnl_ratio_pct,
"bid_invalid": False,
+4 -1
View File
@@ -59,11 +59,14 @@ def profit_exit_hit(
def required_recycle_usdc(premium_paid: float, mult: float) -> float | None:
from lib.options.options_pricing_lib import PREMIUM_DECIMALS_COIN, round_premium_amount
prem = float(premium_paid or 0)
m = float(mult or 0)
if prem <= 0 or m <= 0:
return None
return round(prem * (1.0 + m), 4)
# 币本位权利金很小,必须用 8 位,否则翻倍门槛会被 round4 抹歪
return round_premium_amount(prem * (1.0 + m), decimals=PREMIUM_DECIMALS_COIN)
def set_profit_exit(
+5 -1
View File
@@ -1159,7 +1159,11 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
sizing = dict(sizing)
sizing["sheets"] = sheets
sizing["eth_amount"] = round(sheets * ct_mult, 8)
sizing["total_premium"] = round(fill_px * sheets * ct_mult, 4)
from lib.options.options_pricing_lib import PREMIUM_DECIMALS_COIN, round_premium_amount
sizing["total_premium"] = round_premium_amount(
fill_px * sheets * ct_mult, decimals=PREMIUM_DECIMALS_COIN
)
conn = cfg["get_db"]()
trade_id = None
target_mon = None