From df634a8f02108948a2d48fea2746461c92f6d8dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Tue, 13 Jan 2026 23:50:13 +0800 Subject: [PATCH] a --- trading_system/binance_client.py | 48 ++++++++++++++++++++++++++++---- trading_system/risk_manager.py | 28 ++++++++++++++++++- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/trading_system/binance_client.py b/trading_system/binance_client.py index 477ffa6..79a32fd 100644 --- a/trading_system/binance_client.py +++ b/trading_system/binance_client.py @@ -316,19 +316,26 @@ class BinanceClient: # 提取数量精度信息 quantity_precision = s.get('quantityPrecision', 8) - # 从filters中提取minQty和stepSize + # 从filters中提取minQty、stepSize和minNotional min_qty = None step_size = None + min_notional = None for f in s.get('filters', []): if f['filterType'] == 'LOT_SIZE': min_qty = float(f.get('minQty', 0)) step_size = float(f.get('stepSize', 0)) - break + elif f['filterType'] == 'MIN_NOTIONAL': + min_notional = float(f.get('notional', 0)) + + # 如果没有从filters获取到minNotional,使用默认值5 USDT + if min_notional is None or min_notional == 0: + min_notional = 5.0 info = { 'quantityPrecision': quantity_precision, 'minQty': min_qty or 0, - 'stepSize': step_size or 0 + 'stepSize': step_size or 0, + 'minNotional': min_notional } # 缓存信息 @@ -416,7 +423,33 @@ class BinanceClient: logger.error(f"调整后的数量无效: {adjusted_quantity} (原始: {quantity})") return None - logger.info(f"下单: {symbol} {side} {adjusted_quantity} (原始: {quantity}) @ {order_type}") + # 获取当前价格以计算名义价值 + if price is None: + ticker = await self.get_ticker_24h(symbol) + if not ticker: + logger.error(f"无法获取 {symbol} 的价格信息") + return None + current_price = ticker['price'] + else: + current_price = price + + # 计算订单名义价值 + notional_value = adjusted_quantity * current_price + min_notional = symbol_info.get('minNotional', 5.0) if symbol_info else 5.0 + + logger.info(f"下单检查: {symbol} {side} {adjusted_quantity} (原始: {quantity}) @ {order_type}") + logger.info(f" 当前价格: {current_price:.4f} USDT") + logger.info(f" 订单名义价值: {notional_value:.2f} USDT") + logger.info(f" 最小名义价值: {min_notional:.2f} USDT") + + # 检查名义价值是否满足最小要求 + if notional_value < min_notional: + logger.warning( + f"❌ {symbol} 订单名义价值不足: {notional_value:.2f} USDT < " + f"最小要求: {min_notional:.2f} USDT" + ) + logger.warning(f" 需要增加数量或提高仓位大小") + return None if order_type == 'MARKET': order = await self.client.futures_create_order( @@ -437,7 +470,7 @@ class BinanceClient: price=price ) - logger.info(f"下单成功: {symbol} {side} {adjusted_quantity} @ {order_type}") + logger.info(f"下单成功: {symbol} {side} {adjusted_quantity} @ {order_type} (名义价值: {notional_value:.2f} USDT)") return order except BinanceAPIException as e: error_code = e.code if hasattr(e, 'code') else None @@ -446,6 +479,11 @@ class BinanceClient: logger.error(f" 原始数量: {quantity}") if symbol_info: logger.error(f" 交易对精度: {symbol_info}") + elif error_code == -4164: + logger.error(f"下单失败 {symbol} {side}: 订单名义价值不足 - {e}") + logger.error(f" 订单名义价值必须至少为 5 USDT (除非选择 reduce only)") + if symbol_info: + logger.error(f" 最小名义价值: {symbol_info.get('minNotional', 5.0)} USDT") else: logger.error(f"下单失败 {symbol} {side}: {e}") return None diff --git a/trading_system/risk_manager.py b/trading_system/risk_manager.py index 210f76f..a12387a 100644 --- a/trading_system/risk_manager.py +++ b/trading_system/risk_manager.py @@ -252,14 +252,40 @@ class RiskManager: position_value = available_balance * position_percent logger.info(f" 计算仓位价值: {position_value:.2f} USDT ({position_percent*100:.1f}% of {available_balance:.2f})") + # 确保仓位价值满足最小名义价值要求(币安要求至少5 USDT) + min_notional = 5.0 # 币安合约最小名义价值 + if position_value < min_notional: + logger.warning(f" ⚠ 仓位价值 {position_value:.2f} USDT < 最小名义价值 {min_notional:.2f} USDT") + # 尝试增加仓位价值到最小名义价值,但不超过最大仓位限制 + max_allowed_value = available_balance * max_position_percent + if min_notional <= max_allowed_value: + position_value = min_notional + logger.info(f" ✓ 调整仓位价值到最小名义价值: {position_value:.2f} USDT") + else: + logger.warning( + f" ❌ 无法满足最小名义价值要求: " + f"需要 {min_notional:.2f} USDT,但最大允许 {max_allowed_value:.2f} USDT" + ) + return None + # 计算数量(考虑合约的最小数量精度) quantity = position_value / current_price logger.info(f" 计算数量: {quantity:.4f} (价值: {position_value:.2f} / 价格: {current_price:.4f})") + # 验证计算出的数量对应的名义价值 + calculated_notional = quantity * current_price + if calculated_notional < min_notional: + # 如果计算出的名义价值仍然不足,增加数量 + required_quantity = min_notional / current_price + logger.warning(f" ⚠ 计算出的名义价值 {calculated_notional:.2f} USDT < {min_notional:.2f} USDT") + logger.info(f" ✓ 调整数量从 {quantity:.4f} 到 {required_quantity:.4f}") + quantity = required_quantity + position_value = required_quantity * current_price + # 检查是否通过风险控制 logger.info(f" 检查仓位大小是否符合风险控制要求...") if await self.check_position_size(symbol, quantity): - logger.info(f"✓ {symbol} 仓位计算成功: {quantity:.4f} (价值: {position_value:.2f} USDT)") + logger.info(f"✓ {symbol} 仓位计算成功: {quantity:.4f} (价值: {position_value:.2f} USDT, 名义价值: {quantity * current_price:.2f} USDT)") return quantity else: logger.warning(f"❌ {symbol} 仓位检查未通过,无法开仓")