Fix dashboard PnL using spot contract size of 1.

Prefer perpetual symbols and normalize before market.contractSize lookup so Gate BTC float matches ~0.4U not thousands.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-06 14:20:43 +08:00
parent 75a4175522
commit d41028b766
5 changed files with 91 additions and 22 deletions
+53 -19
View File
@@ -358,18 +358,52 @@ def collect_options_items(
return out
def _swap_symbol_candidates(row: dict[str, Any]) -> list[str]:
"""优先永续 symbol(含 settle),避免用现货 BTC/USDT 查到 contractSize=1."""
raw: list[str] = []
for key in ("symbol", "exchange_symbol", "price_symbol"):
s = str(row.get(key) or "").strip()
if s and s not in raw:
raw.append(s)
swapish: list[str] = []
others: list[str] = []
for s in raw:
if ":" in s:
swapish.append(s)
continue
others.append(s)
if "/" in s:
base, quote = s.split("/", 1)
q = quote.split(":")[0].strip()
if base and q:
swapish.append(f"{base}/{q}:{q}")
out: list[str] = []
for s in swapish + others:
if s and s not in out:
out.append(s)
return out
def _resolve_contract_size(
sym: str,
row_or_sym: Any,
*,
get_contract_size: Optional[Callable[[str], Any]] = None,
) -> float:
if not callable(get_contract_size) or not sym:
return 1.0
try:
cs = float(get_contract_size(sym) or 1.0)
return cs if cs > 0 else 1.0
except Exception:
if not callable(get_contract_size):
return 1.0
if isinstance(row_or_sym, dict):
candidates = _swap_symbol_candidates(row_or_sym)
else:
sym = str(row_or_sym or "").strip()
candidates = _swap_symbol_candidates({"symbol": sym}) if sym else []
for sym in candidates:
try:
cs = float(get_contract_size(sym) or 0)
if cs > 0:
return cs
except Exception:
continue
return 1.0
def _fill_order_pnl_fields(row: dict[str, Any], *, mark: Optional[float], contract_size: float) -> None:
@@ -415,23 +449,23 @@ def enrich_order_items_with_marks(
out: list[dict[str, Any]] = []
for it in items:
row = dict(it)
sym = str(row.get("price_symbol") or row.get("symbol") or "").strip()
# 标记价:先试 price_symbol,再试永续候选
mark = _safe_float(row.get("mark_price"))
if callable(get_price) and sym:
try:
px = get_price(sym)
except Exception:
px = None
mark = _safe_float(px)
if mark is None and ":" in sym:
if callable(get_price):
ordered: list[str] = []
for s in [str(row.get("price_symbol") or "").strip()] + _swap_symbol_candidates(row):
if s and s not in ordered:
ordered.append(s)
for sym in ordered:
try:
px = get_price(sym.split(":", 1)[0])
px = get_price(sym)
except Exception:
px = None
mark = _safe_float(px)
if mark is not None:
row["mark_price"] = mark
cs = _resolve_contract_size(sym, get_contract_size=get_contract_size)
if mark is not None:
row["mark_price"] = mark
break
cs = _resolve_contract_size(row, get_contract_size=get_contract_size)
_fill_order_pnl_fields(row, mark=mark, contract_size=cs)
out.append(row)
return out