diff --git a/backend/app/crypto_agent/executor/hyperliquid_executor.py b/backend/app/crypto_agent/executor/hyperliquid_executor.py index cb92ae9..b70d6ce 100644 --- a/backend/app/crypto_agent/executor/hyperliquid_executor.py +++ b/backend/app/crypto_agent/executor/hyperliquid_executor.py @@ -281,13 +281,20 @@ class HyperliquidExecutor(BaseExecutor): # ==================== 辅助方法 ==================== def _calculate_position_size(self, symbol: str, margin: float, price: float, leverage: int) -> float: - """计算仓位大小""" + """计算仓位大小(按 Hyperliquid szDecimals 精度截断)""" try: + import math + # Hyperliquid 的仓位计算 position_value = margin * leverage position_size = position_value / price - logger.info(f" 仓位计算: ${margin:.2f} × {leverage}x = ${position_value:.2f} → {position_size:.6f} {symbol}") + # 按 Hyperliquid 要求的精度截断(szDecimals) + sz_decimals = self.hyperliquid.get_sz_decimals(symbol) + factor = 10 ** sz_decimals + position_size = math.floor(position_size * factor) / factor + + logger.info(f" 仓位计算: ${margin:.2f} × {leverage}x = ${position_value:.2f} → {position_size:.6f} {symbol} (精度: {sz_decimals}位)") return position_size diff --git a/backend/app/crypto_agent/executor/paper_trading_executor.py b/backend/app/crypto_agent/executor/paper_trading_executor.py index a12117e..edd0249 100644 --- a/backend/app/crypto_agent/executor/paper_trading_executor.py +++ b/backend/app/crypto_agent/executor/paper_trading_executor.py @@ -34,6 +34,17 @@ class PaperTradingExecutor(BaseExecutor): # 调整保证金(模拟盘无手续费) adjusted_margin = margin + # 根据 confidence 推算信号等级 + confidence = decision.get('confidence', 0) + if confidence >= 80: + signal_grade = 'A' + elif confidence >= 60: + signal_grade = 'B' + elif confidence >= 40: + signal_grade = 'C' + else: + signal_grade = 'D' + # 构建信号字典 signal = { 'symbol': symbol, @@ -42,6 +53,10 @@ class PaperTradingExecutor(BaseExecutor): 'entry_price': entry_price if order_type == 'limit' else current_price, 'stop_loss': stop_loss, 'take_profit': take_profit, + 'signal_grade': signal_grade, + 'confidence': confidence, + 'quantity': adjusted_margin, + 'position_size': decision.get('position_size', 'light'), } # 执行下单(统一调用方式) diff --git a/backend/app/services/hyperliquid_trading_service.py b/backend/app/services/hyperliquid_trading_service.py index 30bc8bc..ed874bb 100644 --- a/backend/app/services/hyperliquid_trading_service.py +++ b/backend/app/services/hyperliquid_trading_service.py @@ -160,6 +160,9 @@ class HyperliquidTradingService: # 风险检查 self.check_risk_limits() + # 精度保护:确保 size 符合 szDecimals 要求 + size = self._sanitize_size(symbol, size) + try: if reduce_only: # 平仓使用 market_close(不需要指定 is_buy,自动判断) @@ -216,6 +219,10 @@ class HyperliquidTradingService: """下限价单""" self.check_risk_limits() + # 精度保护:确保 size 和 price 符合要求 + size = self._sanitize_size(symbol, size) + price = round(float(price), 5) # Hyperliquid 价格最多 5 位小数 + try: result = self.exchange.order(symbol, is_buy, size, price, {"limit": {"tif": "Gtc"}}, @@ -582,6 +589,24 @@ class HyperliquidTradingService: logger.warning(f"获取 {symbol} szDecimals 失败,使用默认值 3") return 3 + def _sanitize_size(self, symbol: str, size: float) -> float: + """ + 精度保护:确保 size 符合 Hyperliquid szDecimals 要求 + + 这是防止 float_to_wire causes rounding 错误的最后防线。 + """ + import math + try: + sz_decimals = self.get_sz_decimals(symbol) + factor = 10 ** sz_decimals + sanitized = math.floor(float(size) * factor) / factor + if sanitized != size: + logger.info(f" 精度截断: {size} → {sanitized} ({symbol} szDecimals={sz_decimals})") + return sanitized + except Exception as e: + logger.warning(f" 精度截断失败: {e},使用原值 {size}") + return size + # 单例 _hyperliquid_service_instance = None