币本位持仓卡权利金/回收/门控改为按ETH/BTC展示,避免误标USDC与两位小数抹零

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-20 13:36:34 +08:00
parent 6c9825284f
commit 6c49c7d51c
8 changed files with 240 additions and 45 deletions
+41 -6
View File
@@ -60,6 +60,14 @@ def is_close_gate_passed(inst_id: str) -> bool:
return bool((_gates.get(inst) or {}).get("passed"))
def _fmt_gate_amt(v: float, *, ccy: str) -> str:
unit = (ccy or "USDC").strip().upper() or "USDC"
if unit in ("ETH", "BTC"):
txt = f"{float(v):.8f}".rstrip("0").rstrip(".")
return txt or "0"
return f"{float(v):.4f}"
def update_close_gate(
inst_id: str,
*,
@@ -68,6 +76,7 @@ def update_close_gate(
now: float | None = None,
min_mult: float | None = None,
hold_seconds: float | None = None,
premium_ccy: str | None = None,
) -> dict[str, Any]:
"""
根据当前买盘可回收金额刷新门控.
@@ -89,9 +98,16 @@ def update_close_gate(
if hold < 0:
hold = 0.0
with _lock:
prev_ccy = (_gates.get(inst) or {}).get("premium_ccy")
ccy = (premium_ccy or prev_ccy or "USDC").strip().upper() or "USDC"
if ccy not in ("ETH", "BTC", "USDC"):
ccy = "USDC"
need_decimals = 8 if ccy in ("ETH", "BTC") else 4
prem = _safe_float(premium_paid)
recv = _safe_float(recycle_usdc)
need = round(prem * mult, 4) if prem is not None and prem > 0 else None
need = round(prem * mult, need_decimals) if prem is not None and prem > 0 else None
recycle_ok = bool(
prem is not None and prem > 0 and recv is not None and need is not None and recv + 1e-12 >= need
)
@@ -117,6 +133,7 @@ def update_close_gate(
"min_mult": mult,
"hold_seconds": hold,
"passed": passed,
"premium_ccy": ccy,
}
_gates[inst] = state
@@ -126,10 +143,13 @@ def update_close_gate(
elif recv is None:
msg = "暂无有效买盘可回收金额"
elif not recycle_ok:
msg = f"可回收 {recv:.4f} USDC < 权利金×{mult:g}({need:.4f}),目标平仓门控未过"
msg = (
f"可回收 {_fmt_gate_amt(recv, ccy=ccy)} {ccy} < 权利金×{mult:g}"
f"({_fmt_gate_amt(need, ccy=ccy)}),目标平仓门控未过"
)
elif not ready:
msg = (
f"可回收已达×{mult:g}({recv:.4f}/{need:.4f}),"
f"可回收已达×{mult:g}({_fmt_gate_amt(recv, ccy=ccy)}/{_fmt_gate_amt(need, ccy=ccy)} {ccy}),"
f"需再持续 {remain:.0f}s(已 {held:.0f}/{hold:.0f}s)门控才通过"
)
else:
@@ -144,6 +164,7 @@ def update_close_gate(
"recycle_usdc": recv,
"premium_paid": prem,
"need_recycle_usdc": need,
"premium_ccy": ccy,
"min_mult": mult,
"hold_seconds": hold,
"held_seconds": round(held, 1) if recycle_ok else 0.0,
@@ -160,25 +181,39 @@ def check_close_gate(
*,
recycle_usdc: float | None = None,
premium_paid: float | None = None,
premium_ccy: str | None = None,
refresh: bool = True,
) -> dict[str, Any]:
"""检查是否允许平仓;默认先用最新回收/权利金刷新."""
inst = (inst_id or "").strip()
if refresh:
if recycle_usdc is None or premium_paid is None:
if recycle_usdc is None or premium_paid is None or premium_ccy is None:
with _lock:
prev = _gates.get(inst) or {}
if recycle_usdc is None:
recycle_usdc = prev.get("recycle")
if premium_paid is None:
premium_paid = prev.get("premium")
return update_close_gate(inst, recycle_usdc=recycle_usdc, premium_paid=premium_paid)
if premium_ccy is None:
premium_ccy = prev.get("premium_ccy")
return update_close_gate(
inst,
recycle_usdc=recycle_usdc,
premium_paid=premium_paid,
premium_ccy=premium_ccy,
)
with _lock:
prev = _gates.get(inst)
if not prev:
return update_close_gate(inst, recycle_usdc=recycle_usdc, premium_paid=premium_paid)
return update_close_gate(
inst,
recycle_usdc=recycle_usdc,
premium_paid=premium_paid,
premium_ccy=premium_ccy,
)
return update_close_gate(
inst,
recycle_usdc=recycle_usdc if recycle_usdc is not None else prev.get("recycle"),
premium_paid=premium_paid if premium_paid is not None else prev.get("premium"),
premium_ccy=premium_ccy if premium_ccy is not None else prev.get("premium_ccy"),
)
+2 -1
View File
@@ -134,7 +134,8 @@ def sum_open_premium_paid(conn: sqlite3.Connection, inst_id: str) -> float | Non
).fetchone()
if not row or int(row["n"] or 0) < 1:
return None
return round(float(row["total"] or 0), 4)
# 币本位权利金常 <1e-4,保留 8 位避免被裁成 0
return round(float(row["total"] or 0), 8)
def sum_open_sheets(conn: sqlite3.Connection, inst_id: str) -> int | None:
+9 -2
View File
@@ -14,14 +14,21 @@ def enrich_position_row_display(
meta_cache: dict[str, dict[str, Any] | None] | None = None,
premium_override: float | None = None,
) -> dict[str, Any]:
from lib.exchange.okx_options_lib import format_position_row, format_usdc_amount, tick_sz_and_ct_mult
from lib.exchange.okx_options_lib import format_position_row, format_premium_amount, tick_sz_and_ct_mult
from lib.options.options_margin_mode_lib import margin_mode_from_inst_id, premium_ccy_for_mode
inst_id = str(raw_pos.get("instId") or "").strip()
tick_sz, ct_mult = tick_sz_and_ct_mult(ex, inst_id, meta_cache)
row = format_position_row(raw_pos, ct_mult=ct_mult, tick_sz=tick_sz)
row_mode = margin_mode_from_inst_id(inst_id) if inst_id else "usdc"
underly = str(row.get("underlying") or (inst_id.split("-")[0] if inst_id else "ETH") or "ETH")
premium_ccy = premium_ccy_for_mode(row_mode, underly)
row["margin_mode"] = row_mode
row["premium_ccy"] = premium_ccy
row["margin_mode_label"] = "币本位" if row_mode == "coin" else "USDC"
if premium_override is not None:
row["premium_paid"] = premium_override
row["premium_paid_fmt"] = format_usdc_amount(premium_override)
row["premium_paid_fmt"] = format_premium_amount(row.get("premium_paid"), ccy=premium_ccy)
return row
+5 -1
View File
@@ -51,9 +51,12 @@ def attach_close_preview(
intrinsic_px=intrinsic,
max_levels=1,
)
premium_ccy = str(row.get("premium_ccy") or "USDC").strip().upper() or "USDC"
# 残档时不累计 2×门控;有效买一时刷新计时(仅自动平仓需要)
if preview.get("bid_invalid") or preview.get("auto_close_blocked"):
gate = update_close_gate(inst_id, recycle_usdc=None, premium_paid=paid)
gate = update_close_gate(
inst_id, recycle_usdc=None, premium_paid=paid, premium_ccy=premium_ccy
)
preview["close_gate"] = gate
preview["close_gate_blocked"] = True
preview["close_gate_msg"] = preview.get("bid_invalid_reason") or gate.get("msg")
@@ -64,6 +67,7 @@ def attach_close_preview(
inst_id,
recycle_usdc=_safe_float(preview.get("total_received")),
premium_paid=paid,
premium_ccy=premium_ccy,
)
passed = bool(gate.get("passed") or is_close_gate_passed(inst_id) or gate.get("ready"))
preview["close_gate"] = gate
+15 -1
View File
@@ -533,7 +533,21 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
if mode == "close_preview":
paid = _open_premium_paid(cfg, inst_id)
target = sheet_count if sheet_count is not None else 0
return jsonify(_attach_close_preview(cfg, ex, {**q, "pos": target, "premium_paid": paid}, sheets=target, premium_paid=paid))
from lib.options.options_margin_mode_lib import margin_mode_from_inst_id, premium_ccy_for_mode
row_mode = margin_mode_from_inst_id(inst_id)
prem_ccy = premium_ccy_for_mode(row_mode, (inst_id.split("-")[0] if inst_id else "ETH"))
preview_row = {
**q,
"pos": target,
"premium_paid": paid,
"margin_mode": row_mode,
"premium_ccy": prem_ccy,
}
out = _attach_close_preview(cfg, ex, preview_row, sheets=target, premium_paid=paid)
out["options_margin_mode"] = row_mode
out["premium_ccy"] = prem_ccy
return jsonify(out)
mode, mode_note = _normalize_size_mode(mode)
# 币本位:报价预览走 USDT 预算→估币→张数,禁止再查 USDC