fix(trade): LIMIT calculated quantity smaller than quoteAmountPrecision
				
					
				
			This commit is contained in:
		
							
								
								
									
										90
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								main.py
									
									
									
									
									
								
							@@ -54,6 +54,7 @@ class MexcSpotMarket:
 | 
				
			|||||||
    提供获取交易对价格等功能
 | 
					    提供获取交易对价格等功能
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    方法:
 | 
					    方法:
 | 
				
			||||||
 | 
					    - get_exchange_info(symbol): 获取交易对信息
 | 
				
			||||||
    - get_price(symbol): 获取指定交易对的当前价格
 | 
					    - get_price(symbol): 获取指定交易对的当前价格
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -61,6 +62,33 @@ class MexcSpotMarket:
 | 
				
			|||||||
        """初始化市场数据查询接口"""
 | 
					        """初始化市场数据查询接口"""
 | 
				
			||||||
        self.market = mexc_spot_v3.mexc_market(config)
 | 
					        self.market = mexc_spot_v3.mexc_market(config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_exchange_info(self, symbol: str) -> Optional[Dict[str, Any]]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        获取交易对信息
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            symbol: 交易对,如 "BTCUSDT"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            交易对信息字典或None(如果失败)
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        params = {"symbol": symbol}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            logger.info("查询交易对信息: %s", symbol)
 | 
				
			||||||
 | 
					            exchange_info = self.market.get_exchangeInfo(params)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if not exchange_info or "symbols" not in exchange_info:
 | 
				
			||||||
 | 
					                logger.error("获取交易对信息失败: %s", exchange_info)
 | 
				
			||||||
 | 
					                return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            logger.info("获取交易对信息成功")
 | 
				
			||||||
 | 
					            return exchange_info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            logger.error("查询交易所信息失败: %s", str(e))
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_price(self, symbol: str) -> Optional[float]:
 | 
					    def get_price(self, symbol: str) -> Optional[float]:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        获取指定交易对的当前价格
 | 
					        获取指定交易对的当前价格
 | 
				
			||||||
@@ -274,6 +302,44 @@ class MexcSpotTrade:
 | 
				
			|||||||
            logger.error("记录交易失败: %s", str(e))
 | 
					            logger.error("记录交易失败: %s", str(e))
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _tool_calculate_quantity(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        quantity: float,
 | 
				
			||||||
 | 
					        price: float,
 | 
				
			||||||
 | 
					        base_asset_precision: int,
 | 
				
			||||||
 | 
					        quote_amount_precision: float,
 | 
				
			||||||
 | 
					    ) -> float:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        调整下单数量以满足最小成交额要求。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        策略说明:
 | 
				
			||||||
 | 
					        - 计算出的quantity如果乘以price后小于交易对的最小成交额(quoteAmountPrecision),
 | 
				
			||||||
 | 
					          则将quantity增加一个最小单位(10^(-base_asset_precision)),确保下单金额满足交易所要求。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            quantity: 初步计算出的下单数量
 | 
				
			||||||
 | 
					            price: 当前价格
 | 
				
			||||||
 | 
					            base_asset_precision: 交易对基础资产的小数精度
 | 
				
			||||||
 | 
					            quote_amount_precision: 交易对最小成交额
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            满足最小成交额要求的下单数量
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        processed_quantity = round(quantity, base_asset_precision)
 | 
				
			||||||
 | 
					        if processed_quantity * price < quote_amount_precision:
 | 
				
			||||||
 | 
					            logger.info(
 | 
				
			||||||
 | 
					                "计算的quantity小于最低要求:%f * %f = %f < %f,进行调整",
 | 
				
			||||||
 | 
					                processed_quantity,
 | 
				
			||||||
 | 
					                price,
 | 
				
			||||||
 | 
					                processed_quantity * price,
 | 
				
			||||||
 | 
					                quote_amount_precision,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            processed_quantity = round(
 | 
				
			||||||
 | 
					                quantity + 10 ** (-base_asset_precision), base_asset_precision
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            logger.info("调整后的quantity: %f", processed_quantity)
 | 
				
			||||||
 | 
					        return processed_quantity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def trade(
 | 
					    def trade(
 | 
				
			||||||
        self, symbol: str, order_type: str, side: str, **kwargs
 | 
					        self, symbol: str, order_type: str, side: str, **kwargs
 | 
				
			||||||
    ) -> Optional[Dict[str, Any]]:
 | 
					    ) -> Optional[Dict[str, Any]]:
 | 
				
			||||||
@@ -322,12 +388,26 @@ class MexcSpotTrade:
 | 
				
			|||||||
            and "quantity" not in processed_kwargs
 | 
					            and "quantity" not in processed_kwargs
 | 
				
			||||||
        ):
 | 
					        ):
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
 | 
					                exchange_info = self.market.get_exchange_info(symbol)
 | 
				
			||||||
                quote_amount = float(processed_kwargs["quoteOrderQty"])
 | 
					                quote_amount = float(processed_kwargs["quoteOrderQty"])
 | 
				
			||||||
                price = float(processed_kwargs["price"])
 | 
					                price = (
 | 
				
			||||||
 | 
					                    float(current_price)
 | 
				
			||||||
 | 
					                    if not current_price is None
 | 
				
			||||||
 | 
					                    else float(processed_kwargs["price"])
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
                quantity = quote_amount / price
 | 
					                quantity = quote_amount / price
 | 
				
			||||||
                processed_kwargs["quantity"] = str(quantity)
 | 
					                base_asset_precision = int(
 | 
				
			||||||
 | 
					                    exchange_info["symbols"][0]["baseAssetPrecision"]
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                quote_amount_precision = float(
 | 
				
			||||||
 | 
					                    exchange_info["symbols"][0]["quoteAmountPrecision"]
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                processed_quantity = self._tool_calculate_quantity(
 | 
				
			||||||
 | 
					                    quantity, price, base_asset_precision, quote_amount_precision
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                logger.info("根据quoteOrderQty计算quantity: %f", processed_quantity)
 | 
				
			||||||
 | 
					                processed_kwargs["quantity"] = str(processed_quantity)
 | 
				
			||||||
                processed_kwargs.pop("quoteOrderQty")
 | 
					                processed_kwargs.pop("quoteOrderQty")
 | 
				
			||||||
                logger.info("根据quoteOrderQty计算quantity: %f", quantity)
 | 
					 | 
				
			||||||
            except (ValueError, KeyError) as e:
 | 
					            except (ValueError, KeyError) as e:
 | 
				
			||||||
                logger.error("计算quantity失败: %s", str(e))
 | 
					                logger.error("计算quantity失败: %s", str(e))
 | 
				
			||||||
                return None
 | 
					                return None
 | 
				
			||||||
@@ -484,6 +564,7 @@ class TradingConfig:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return today_trades
 | 
					        return today_trades
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def git_commit(repo_path: str = ".") -> str:
 | 
					def git_commit(repo_path: str = ".") -> str:
 | 
				
			||||||
    """获取Git仓库版本"""
 | 
					    """获取Git仓库版本"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -493,6 +574,7 @@ def git_commit(repo_path: str = ".") -> str:
 | 
				
			|||||||
    except Exception as _:
 | 
					    except Exception as _:
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
    """主函数"""
 | 
					    """主函数"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -527,7 +609,7 @@ def main():
 | 
				
			|||||||
            spot_trader = MexcSpotTrade(
 | 
					            spot_trader = MexcSpotTrade(
 | 
				
			||||||
                config.config_data.get("api", {}),
 | 
					                config.config_data.get("api", {}),
 | 
				
			||||||
                config.config_data.get("symbol_mapping", {}),
 | 
					                config.config_data.get("symbol_mapping", {}),
 | 
				
			||||||
                os.path.basename(config_file).replace('.json', '')
 | 
					                os.path.basename(config_file).replace(".json", ""),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            today_trades = config.get_today_trades()
 | 
					            today_trades = config.get_today_trades()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user