32746b0a34
翻倍出场按全仓买一判定且对冲表异常不再整段跳过;面板按当前倍数展示需回收公式。 Co-authored-by: Cursor <cursoragent@cursor.com>
220 lines
8.3 KiB
Python
220 lines
8.3 KiB
Python
"""中控资金统计:期权 USDC/USDT 按 1:1 计入 USDT 合计."""
|
||
from __future__ import annotations
|
||
|
||
from typing import Any, Optional
|
||
|
||
|
||
def _safe_float(value: Any) -> Optional[float]:
|
||
try:
|
||
if value is None or value == "":
|
||
return None
|
||
v = float(value)
|
||
return v if v >= 0 else None
|
||
except (TypeError, ValueError):
|
||
return None
|
||
|
||
|
||
def _account_total_usdt(funding: Any, trading: Any) -> Optional[float]:
|
||
fu = _safe_float(funding)
|
||
tu = _safe_float(trading)
|
||
if fu is None or tu is None:
|
||
return None
|
||
return round(fu + tu, 4)
|
||
|
||
|
||
def stablecoin_usdt_equiv(value: Any) -> Optional[float]:
|
||
"""USDC / USDT 按 1:1 折算为 USDT 统计口径."""
|
||
return _safe_float(value)
|
||
|
||
|
||
def _sum_optional(*values: Any) -> Optional[float]:
|
||
parts = [_safe_float(v) for v in values]
|
||
present = [p for p in parts if p is not None]
|
||
if not present:
|
||
return None
|
||
return round(sum(present), 4)
|
||
|
||
|
||
def options_balances_usdt_equiv(options_snap: dict[str, Any] | None) -> dict[str, Any]:
|
||
"""从期权 snapshot 提取资金户/交易户 USDT 等价余额.
|
||
|
||
- funding_usdt / trading_usdt: USDT+USDC(+USDG) 全账户(勿与永续 USDT 再加总)
|
||
- funding_usdc_equiv / trading_usdc_equiv: 仅非 USDT 稳定币,可安全加到永续 USDT 上
|
||
"""
|
||
snap = options_snap if isinstance(options_snap, dict) else {}
|
||
if snap.get("enabled") is False:
|
||
return {
|
||
"ok": False,
|
||
"funding_usdt": None,
|
||
"trading_usdt": None,
|
||
"funding_usdc_equiv": None,
|
||
"trading_usdc_equiv": None,
|
||
}
|
||
if snap.get("ok") is False:
|
||
return {
|
||
"ok": False,
|
||
"funding_usdt": None,
|
||
"trading_usdt": None,
|
||
"funding_usdc_equiv": None,
|
||
"trading_usdc_equiv": None,
|
||
}
|
||
bal = snap.get("balances") if isinstance(snap.get("balances"), dict) else snap
|
||
funding_usdc_equiv = _sum_optional(bal.get("funding_usdc"), bal.get("funding_usdg"))
|
||
trading_usdc_equiv = _sum_optional(bal.get("trading_usdc"), bal.get("trading_usdg"))
|
||
funding = _sum_optional(bal.get("funding_usdt"), funding_usdc_equiv)
|
||
trading = _sum_optional(bal.get("trading_usdt"), trading_usdc_equiv)
|
||
ok = funding is not None and trading is not None
|
||
return {
|
||
"ok": ok,
|
||
"funding_usdt": funding,
|
||
"trading_usdt": trading,
|
||
"funding_usdc_equiv": funding_usdc_equiv,
|
||
"trading_usdc_equiv": trading_usdc_equiv,
|
||
}
|
||
|
||
|
||
def options_float_pnl_usdt(options_snap: dict[str, Any] | None) -> Optional[float]:
|
||
"""期权浮盈合计(USDT).币本位按指数换算,勿把 ETH/BTC 数量当 U.
|
||
|
||
优先逐仓 display_pnl_usdt_from_option_row(已含 BTC/ETH × 指数);
|
||
回退 upl_total_usdc 时按「已是 USDT」处理——实例 hub 快照合计已折 U,
|
||
不可再 × 指数,否则币本位会双计.
|
||
"""
|
||
snap = options_snap if isinstance(options_snap, dict) else {}
|
||
if snap.get("enabled") is False or snap.get("ok") is False:
|
||
return None
|
||
|
||
try:
|
||
from lib.options.options_positions_lib import display_pnl_usdt_from_option_row
|
||
|
||
total_u = 0.0
|
||
found = False
|
||
for p in snap.get("positions") or []:
|
||
if not isinstance(p, dict):
|
||
continue
|
||
pnl = display_pnl_usdt_from_option_row(p)
|
||
if pnl is None:
|
||
continue
|
||
found = True
|
||
total_u += float(pnl)
|
||
if found:
|
||
return round(total_u, 4)
|
||
except Exception:
|
||
pass
|
||
|
||
upl = snap.get("upl_total_usdc")
|
||
if upl is not None:
|
||
try:
|
||
return round(float(upl), 4)
|
||
except (TypeError, ValueError):
|
||
return None
|
||
return None
|
||
|
||
|
||
def options_open_position_count(options_snap: dict[str, Any] | None) -> int:
|
||
snap = options_snap if isinstance(options_snap, dict) else {}
|
||
if snap.get("enabled") is False or snap.get("ok") is False:
|
||
return 0
|
||
if snap.get("position_count") is not None:
|
||
try:
|
||
return max(0, int(snap.get("position_count")))
|
||
except (TypeError, ValueError):
|
||
pass
|
||
pos = snap.get("positions")
|
||
return len(pos) if isinstance(pos, list) else 0
|
||
|
||
|
||
def merge_perp_options_balances(
|
||
perpetual_funding_usdt: Any,
|
||
perpetual_trading_usdt: Any,
|
||
options_snap: dict[str, Any] | None,
|
||
) -> dict[str, Any]:
|
||
"""永续 USDT + 期权非 USDT 稳定币合并为中控总资金(避免 OKX 同账户 USDT 双计).
|
||
|
||
与实例顶栏 total_funds_usdt(..., options_usdc, None, None) 口径一致:
|
||
期权 snapshot 里的 USDT 与永续资金/交易户是同一钱包,只把 USDC/USDG 加上.
|
||
"""
|
||
opt = options_balances_usdt_equiv(options_snap)
|
||
# 展示用期权户:优先非 USDT 稳定币;若仅有 USDT 则仍给出全量以便辨识
|
||
opt_fund_disp = opt.get("funding_usdc_equiv")
|
||
opt_trade_disp = opt.get("trading_usdc_equiv")
|
||
if opt_fund_disp is None and opt_trade_disp is None and opt.get("ok"):
|
||
opt_fund_disp = opt.get("funding_usdt")
|
||
opt_trade_disp = opt.get("trading_usdt")
|
||
|
||
if opt.get("ok"):
|
||
if perpetual_funding_usdt is None and perpetual_trading_usdt is None:
|
||
# 永续账户未取到时,期权 snapshot 已含同账户 USDT+USDC,直接用全量
|
||
funding = opt.get("funding_usdt")
|
||
trading = opt.get("trading_usdt")
|
||
else:
|
||
funding = _sum_optional(perpetual_funding_usdt, opt.get("funding_usdc_equiv"))
|
||
trading = _sum_optional(perpetual_trading_usdt, opt.get("trading_usdc_equiv"))
|
||
else:
|
||
funding = _safe_float(perpetual_funding_usdt)
|
||
trading = _safe_float(perpetual_trading_usdt)
|
||
|
||
total = _account_total_usdt(funding, trading)
|
||
# 任一侧齐全即可展示;永续缺一侧但有期权 USDC 时仍尽量给出合计
|
||
if total is None:
|
||
total = _sum_optional(funding, trading)
|
||
perp_total = _account_total_usdt(perpetual_funding_usdt, perpetual_trading_usdt)
|
||
opt_total = _account_total_usdt(opt.get("funding_usdt"), opt.get("trading_usdt"))
|
||
data_ok = total is not None
|
||
return {
|
||
"perpetual_funding_usdt": _safe_float(perpetual_funding_usdt),
|
||
"perpetual_trading_usdt": _safe_float(perpetual_trading_usdt),
|
||
"options_funding_usdt": opt_fund_disp,
|
||
"options_trading_usdt": opt_trade_disp,
|
||
"options_funding_full_usdt": opt.get("funding_usdt"),
|
||
"options_trading_full_usdt": opt.get("trading_usdt"),
|
||
"options_ok": bool(opt.get("ok")),
|
||
"funding_usdt": funding,
|
||
"trading_usdt": trading,
|
||
"total_usdt": total,
|
||
"perpetual_total_usdt": perp_total,
|
||
"options_total_usdt": opt_total,
|
||
"data_ok": data_ok,
|
||
}
|
||
|
||
|
||
def repair_double_counted_fund_entry(ac: dict[str, Any]) -> dict[str, Any]:
|
||
"""识别并修复历史快照中「永续 USDT + 期权(USDT+USDC)」的双计.
|
||
|
||
旧口径 options_* 存的是 USDT+USDC 全量,且 funding≈2×期权资金户 USDT 部分.
|
||
新口径 options_* 多为纯 USDC,不会误伤.
|
||
"""
|
||
if not isinstance(ac, dict):
|
||
return {}
|
||
out = dict(ac)
|
||
ofu = _safe_float(ac.get("options_funding_usdt"))
|
||
otu = _safe_float(ac.get("options_trading_usdt"))
|
||
fu = _safe_float(ac.get("funding_usdt"))
|
||
tu = _safe_float(ac.get("trading_usdt"))
|
||
if ofu is None or otu is None or fu is None or tu is None:
|
||
return out
|
||
if ofu < 1.0:
|
||
return out
|
||
ratio = fu / ofu if ofu > 0 else 0.0
|
||
# 经典双计:合并资金户 ≈ 2 × 期权资金户(同钱包 USDT 加了两遍)
|
||
if 1.8 <= ratio <= 2.25:
|
||
out["funding_usdt"] = ofu
|
||
out["trading_usdt"] = otu
|
||
out["total_usdt"] = round(ofu + otu, 4)
|
||
out["repaired_double_count"] = True
|
||
return out
|
||
|
||
|
||
def merge_board_row_balances(row: dict[str, Any]) -> dict[str, Any]:
|
||
"""监控板行 → 含期权的资金统计."""
|
||
caps = row.get("capabilities") or []
|
||
options_snap = row.get("options") if "options" in caps else None
|
||
merged = merge_perp_options_balances(
|
||
row.get("funding_usdt") if row.get("account_ok") else None,
|
||
row.get("trading_usdt") if row.get("account_ok") else None,
|
||
options_snap,
|
||
)
|
||
merged["options_float_pnl_u"] = options_float_pnl_usdt(options_snap)
|
||
merged["options_open_position_count"] = options_open_position_count(options_snap)
|
||
return merged
|