模拟盘统计改为现货买币/卖回的真实 USDT 净盈亏,对齐钱包。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-08 23:24:18 +08:00
parent 7943cd2167
commit bb9d14dd73
4 changed files with 312 additions and 2 deletions
+130
View File
@@ -465,9 +465,139 @@ def repair_double_counted_fee_pnl(conn, ex: Any = None) -> int:
return recompute_closed_realized_pnl_usdt(conn, ex)
def _sim_net_usdt_converts_for_trade(
conn,
*,
underlying: str,
start_ts: str,
end_ts: str,
) -> float | None:
"""从 sim_ledger 汇总该标的现货兑换的 USDT 净变动(买币为负、卖回为正)."""
coin = (underlying or "").strip().upper()
if coin not in ("ETH", "BTC") or not start_ts or not end_ts:
return None
try:
rows = conn.execute(
"""
SELECT amount, note FROM sim_ledger_entries
WHERE kind = 'convert'
AND UPPER(ccy) = 'USDT'
AND account = 'trading'
AND ts >= ?
AND ts <= ?
""",
(str(start_ts), str(end_ts)),
).fetchall()
except Exception:
return None
total = 0.0
n = 0
for row in rows or []:
note = str(_row_get(row, "note") or "").upper()
amt = _safe_float(_row_get(row, "amount"))
if amt is None:
continue
related = (
f"{coin}/USDT" in note
or f"{coin}-USDT" in note
or note.strip() == f"FROM {coin}"
)
if not related:
continue
total += float(amt)
n += 1
if n < 1:
return None
return round(total, 4)
def sync_sim_cash_realized_pnl_usdt(conn, ex: Any = None) -> int:
"""模拟盘:用现货买币/卖回的真实 USDT 净额覆盖冻结盈亏(对齐钱包)."""
del ex # 现金口径不依赖实时指数
init_options_tables(conn)
try:
from lib.options.options_spot_bridge_lib import (
cash_pnl_usdt_from_bridge,
ensure_bridge_table,
)
ensure_bridge_table(conn)
except Exception:
return 0
rows = conn.execute(
"""
SELECT id, bridge_id, underlying, budget_usdt, premium_ccy,
created_at, closed_at, realized_pnl_usdt
FROM options_trades
WHERE status = 'closed'
ORDER BY id DESC
LIMIT 300
"""
).fetchall()
updated = 0
for row in rows:
ccy = str(_row_get(row, "premium_ccy") or "").strip().upper()
underly = str(_row_get(row, "underlying") or "").strip().upper()
if ccy not in ("ETH", "BTC") and underly not in ("ETH", "BTC"):
continue
underly = underly or ccy
cash = None
bid = _row_get(row, "bridge_id")
try:
if bid is not None:
cash = cash_pnl_usdt_from_bridge(conn, int(bid))
except Exception:
cash = None
if cash is None:
# 旧桥未记 usdt_recovered:用流水回补(开仓前1分钟~平仓后30分钟)
start = str(_row_get(row, "created_at") or "")
end = str(_row_get(row, "closed_at") or "")
if start and end:
from datetime import datetime, timedelta
def _shift(ts: str, minutes: int) -> str:
try:
dt = datetime.fromisoformat(str(ts).strip().replace(" ", "T", 1))
return (dt + timedelta(minutes=minutes)).strftime("%Y-%m-%d %H:%M:%S")
except Exception:
return ts
cash = _sim_net_usdt_converts_for_trade(
conn,
underlying=underly,
start_ts=_shift(start, -2),
end_ts=_shift(end, 30),
)
if cash is None:
continue
old = _safe_float(_row_get(row, "realized_pnl_usdt"))
if old is not None and abs(float(old) - float(cash)) < 1e-6:
continue
conn.execute(
"""
UPDATE options_trades
SET realized_pnl_usdt = ?
WHERE id = ?
""",
(float(cash), int(row["id"])),
)
updated += 1
return updated
def load_local_options_stats_history(conn, ex: Any = None) -> list[dict[str, Any]]:
"""本地已平(模拟盘口径) + 当前 open."""
init_options_tables(conn)
try:
from lib.sim.mode_lib import is_sim_mode
# get_db 不可用时仍尝试现金同步(conn 已打开)
sync_sim_cash_realized_pnl_usdt(conn, ex)
except Exception:
try:
sync_sim_cash_realized_pnl_usdt(conn, ex)
except Exception:
pass
try:
freeze_missing_realized_pnl_usdt(conn, ex)
except Exception: