diff --git a/trading_system/binance_client.py b/trading_system/binance_client.py index 3df847d..c70a983 100644 --- a/trading_system/binance_client.py +++ b/trading_system/binance_client.py @@ -713,24 +713,54 @@ class BinanceClient: Returns: 持仓列表 """ - try: - positions = await self.client.futures_position_information() - open_positions = [ - { - 'symbol': pos['symbol'], - 'positionAmt': float(pos['positionAmt']), - 'entryPrice': float(pos['entryPrice']), - 'markPrice': float(pos.get('markPrice', 0)), - 'unRealizedProfit': float(pos['unRealizedProfit']), - 'leverage': int(pos['leverage']) - } - for pos in positions - if float(pos['positionAmt']) != 0 - ] - return open_positions - except BinanceAPIException as e: - logger.error(f"获取持仓信息失败: {e}") - return [] + retries = 3 + last_error = None + + for attempt in range(retries): + try: + # 增加 recvWindow 以避免 -1021 错误 + positions = await self.client.futures_position_information(recvWindow=20000) + open_positions = [ + { + 'symbol': pos['symbol'], + 'positionAmt': float(pos['positionAmt']), + 'entryPrice': float(pos['entryPrice']), + 'markPrice': float(pos.get('markPrice', 0)), + 'unRealizedProfit': float(pos['unRealizedProfit']), + 'leverage': int(pos['leverage']) + } + for pos in positions + if float(pos['positionAmt']) != 0 + ] + return open_positions + except (asyncio.TimeoutError, BinanceAPIException) as e: + last_error = e + # 如果是API异常,检查是否是网络相关或服务器错误 + is_network_error = False + if isinstance(e, BinanceAPIException): + # -1021: Timestamp for this request is outside of the recvWindow + # 5xx: Server Error + if e.code == -1021 or str(e.code).startswith('5'): + is_network_error = True + else: + # TimeoutError + is_network_error = True + + if is_network_error: + if attempt < retries - 1: + logger.warning(f"获取持仓信息失败 (第 {attempt + 1}/{retries} 次): {e},将在 1秒后重试...") + await asyncio.sleep(1) + continue + + logger.error(f"获取持仓信息失败: {e}") + return [] + except Exception as e: + logger.error(f"获取持仓信息失败: {e}") + return [] + + if last_error: + logger.error(f"获取持仓信息最终失败 (已重试 {retries} 次): {last_error}") + return [] async def get_recent_trades(self, symbol: str, limit: int = 50) -> List[Dict]: """ @@ -1309,6 +1339,9 @@ class BinanceClient: logger.info(f"{symbol} 使用 reduceOnly=true 平仓订单") async def _submit(params: Dict[str, Any]) -> Dict[str, Any]: + # 增加 recvWindow 以避免 -1021 错误 + params['recvWindow'] = 20000 + if order_type == 'MARKET': return await self.client.futures_create_order(**params) if price is None: diff --git a/trading_system/position_manager.py b/trading_system/position_manager.py index 92e94cf..972ba5b 100644 --- a/trading_system/position_manager.py +++ b/trading_system/position_manager.py @@ -133,7 +133,7 @@ class PositionManager: last_status = None while time.time() < deadline: try: - info = await self.client.client.futures_get_order(symbol=symbol, orderId=order_id) + info = await self.client.client.futures_get_order(symbol=symbol, orderId=order_id, recvWindow=20000) status = info.get("status") last_status = status if status == "FILLED": @@ -913,7 +913,7 @@ class PositionManager: # 从币安获取订单详情,获取实际成交价格 try: - order_info = await self.client.client.futures_get_order(symbol=symbol, orderId=order_id) + order_info = await self.client.client.futures_get_order(symbol=symbol, orderId=order_id, recvWindow=20000) if order_info: # 优先使用平均成交价格(avgPrice),如果没有则使用价格字段 exit_price = float(order_info.get('avgPrice', 0)) or float(order_info.get('price', 0)) @@ -1160,7 +1160,7 @@ class PositionManager: try: if not getattr(self.client, "client", None): return None - res = await self.client.client.futures_position_information(symbol=symbol) + res = await self.client.client.futures_position_information(symbol=symbol, recvWindow=20000) if not isinstance(res, list): return None ps = (position_side or "").upper() @@ -2082,7 +2082,8 @@ class PositionManager: orders = await self.client.client.futures_get_all_orders( symbol=symbol, startTime=start_time, - endTime=end_time + endTime=end_time, + recvWindow=20000 ) # 验证 orders 的类型