在 `models.py` 中新增 `get_pending_recent` 方法,用于获取最近的待处理交易记录。`binance_client.py` 中添加 `get_order_by_client_order_id` 方法,以支持按 `client_order_id` 查询订单。`position_manager.py` 中实现 `_reconcile_pending_with_binance` 方法,增强对待处理记录的对账能力。`user_data_stream.py` 中在重连前执行待处理记录对账,确保系统在断线期间的交易状态得到及时更新。这些改进提升了系统的稳定性与交易记录的准确性。
86 lines
3.4 KiB
Python
86 lines
3.4 KiB
Python
"""
|
||
正向流程加固:pending 对账模块
|
||
供 sync_positions_with_binance 和 UserDataStream 重连后调用,补齐因 WS 断线/进程重启漏掉的 pending→open。
|
||
"""
|
||
import logging
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
DB_AVAILABLE = False
|
||
Trade = None
|
||
try:
|
||
import sys
|
||
from pathlib import Path
|
||
project_root = Path(__file__).parent.parent
|
||
backend_path = project_root / "backend"
|
||
if backend_path.exists():
|
||
sys.path.insert(0, str(backend_path))
|
||
from database.models import Trade
|
||
DB_AVAILABLE = True
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
async def reconcile_pending_with_binance(client, account_id: int, limit: int = 50, max_age_sec: int = 86400) -> int:
|
||
"""
|
||
对 status=pending 记录查币安订单,若已 FILLED 则更新为 open。
|
||
返回成功对账数量。
|
||
"""
|
||
if not DB_AVAILABLE or not Trade:
|
||
return 0
|
||
try:
|
||
pending_list = Trade.get_pending_recent(account_id, limit=limit, max_age_sec=max_age_sec)
|
||
if not pending_list:
|
||
return 0
|
||
reconciled = 0
|
||
for row in pending_list:
|
||
try:
|
||
symbol = (row.get("symbol") or "").strip()
|
||
client_order_id = (row.get("client_order_id") or "").strip() or None
|
||
entry_order_id = row.get("entry_order_id")
|
||
if not symbol:
|
||
continue
|
||
order_info = None
|
||
if client_order_id and hasattr(client, "get_order_by_client_order_id"):
|
||
order_info = await client.get_order_by_client_order_id(symbol, client_order_id)
|
||
if not order_info and entry_order_id:
|
||
try:
|
||
oid = int(entry_order_id)
|
||
order_info = await client.client.futures_get_order(
|
||
symbol=symbol, orderId=oid, recvWindow=20000
|
||
)
|
||
except Exception:
|
||
pass
|
||
if not order_info or str(order_info.get("status")).upper() != "FILLED":
|
||
continue
|
||
ap = order_info.get("avgPrice") or order_info.get("price") or 0
|
||
z = order_info.get("executedQty") or 0
|
||
try:
|
||
ap_f = float(ap)
|
||
z_f = float(z)
|
||
except (TypeError, ValueError):
|
||
continue
|
||
if ap_f <= 0 or z_f <= 0:
|
||
continue
|
||
order_id = order_info.get("orderId")
|
||
if client_order_id:
|
||
ok = Trade.update_pending_to_filled(
|
||
client_order_id, account_id, order_id, ap_f, z_f
|
||
)
|
||
else:
|
||
ok = Trade.update_pending_by_entry_order_id(
|
||
symbol, account_id, order_id, ap_f, z_f
|
||
)
|
||
if ok:
|
||
reconciled += 1
|
||
logger.info(
|
||
f"[账号{account_id}] pending 对账: {symbol} client_order_id={client_order_id!r} "
|
||
f"已完善为 open (orderId={order_id} 成交价={ap_f:.4f} 数量={z_f:.4f})"
|
||
)
|
||
except Exception as e:
|
||
logger.debug(f"[账号{account_id}] pending 对账单条失败: {e}")
|
||
return reconciled
|
||
except Exception as e:
|
||
logger.debug(f"[账号{account_id}] pending 对账失败: {e}")
|
||
return 0
|