It worked
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
output/
 | 
			
		||||
config/
 | 
			
		||||
__pycache__
 | 
			
		||||
							
								
								
									
										4
									
								
								.pylintrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.pylintrc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
[MASTER]
 | 
			
		||||
disable=
 | 
			
		||||
    broad-exception-caught,
 | 
			
		||||
    line-too-long
 | 
			
		||||
							
								
								
									
										845
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										845
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,845 @@
 | 
			
		||||
import time
 | 
			
		||||
import logging
 | 
			
		||||
from typing import Dict, List, Optional, Tuple
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
 | 
			
		||||
import mexc_spot_v3
 | 
			
		||||
 | 
			
		||||
# 配置日志
 | 
			
		||||
CUSTOM_DEBUG_LEVEL = 15
 | 
			
		||||
logging.addLevelName(CUSTOM_DEBUG_LEVEL, "CUSTOM_DEBUG")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CustomLogger(logging.getLoggerClass()):
 | 
			
		||||
    def custom_debug(self, msg, *args, **kwargs):
 | 
			
		||||
        if self.isEnabledFor(CUSTOM_DEBUG_LEVEL):
 | 
			
		||||
            self._log(CUSTOM_DEBUG_LEVEL, msg, args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
logging.setLoggerClass(CustomLogger)
 | 
			
		||||
logging.basicConfig(
 | 
			
		||||
    level=CUSTOM_DEBUG_LEVEL,
 | 
			
		||||
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
 | 
			
		||||
    handlers=[
 | 
			
		||||
        logging.FileHandler("output/mexc_spot_grid_bot.log", encoding="utf-8"),
 | 
			
		||||
        logging.StreamHandler(),
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
logger = logging.getLogger(__name__)
 | 
			
		||||
logger.custom_debug = lambda msg, *args, **kwargs: logger._log(  # pylint: disable=W0212
 | 
			
		||||
    CUSTOM_DEBUG_LEVEL, msg, args, **kwargs
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class Order:
 | 
			
		||||
    order_id: str
 | 
			
		||||
    price: float
 | 
			
		||||
    quantity: float
 | 
			
		||||
    side: str  # 'BUY' or 'SELL'
 | 
			
		||||
    status: str  # 'NEW', 'FILLED', 'CANCELED'
 | 
			
		||||
    filled_time: Optional[float] = None  # 订单成交时间戳
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GridTradingBot:
 | 
			
		||||
    def __init__(self, conf: Dict):
 | 
			
		||||
        """
 | 
			
		||||
        初始化网格交易机器人
 | 
			
		||||
 | 
			
		||||
        参数:
 | 
			
		||||
            conf (Dict): 配置字典,包含:
 | 
			
		||||
                - symbol: 交易对 (如 'BTCUSDC')
 | 
			
		||||
                - grid_percentage: 每格百分比 (如 0.005 表示 0.5%)
 | 
			
		||||
                - grid_count: 单边网格数量 (如 3)
 | 
			
		||||
                - order_amount: 每单加密货币数量 (如 0.00001 BTC)
 | 
			
		||||
                - min_order_value: 最小挂单价值 (如 1 USDC)
 | 
			
		||||
                - reserve_base: 保留的基础货币数量 (如 1 BTC)
 | 
			
		||||
                - reserve_quote: 保留的报价货币数量 (如 100 USDC)
 | 
			
		||||
        """
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.__init__] Initializing GridTradingBot with config: %r",
 | 
			
		||||
            conf,
 | 
			
		||||
        )
 | 
			
		||||
        self.config = conf
 | 
			
		||||
        self.symbol = conf["symbol"]
 | 
			
		||||
        self.grid_percentage = conf["grid_percentage"]
 | 
			
		||||
        self.grid_count = conf["grid_count"]
 | 
			
		||||
        self.order_amount = conf["order_amount"]
 | 
			
		||||
        self.min_order_value = conf["min_order_value"]
 | 
			
		||||
        self.reserve_base = conf["reserve_base"]
 | 
			
		||||
        self.reserve_quote = conf["reserve_quote"]
 | 
			
		||||
 | 
			
		||||
        # 当前活跃的订单
 | 
			
		||||
        self.active_orders: Dict[str, Order] = {}  # order_id -> Order
 | 
			
		||||
 | 
			
		||||
        # 当前网格范围
 | 
			
		||||
        self.current_buy_levels = 0
 | 
			
		||||
        self.current_sell_levels = 0
 | 
			
		||||
 | 
			
		||||
        # 初始化API
 | 
			
		||||
        logger.custom_debug("[GridTradingBot.__init__] Initializing MEXC API clients")
 | 
			
		||||
        self.api_trade = mexc_spot_v3.mexc_trade()
 | 
			
		||||
        self.api_market = mexc_spot_v3.mexc_market()
 | 
			
		||||
 | 
			
		||||
        # 运行标志
 | 
			
		||||
        self.running = False
 | 
			
		||||
        logger.info("[GridTradingBot.__init__] GridTradingBot initialized successfully")
 | 
			
		||||
 | 
			
		||||
    def api_get_price(self) -> float:
 | 
			
		||||
        """获取当前市场价格"""
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.api_get_price] Fetching current market price for symbol: %s",
 | 
			
		||||
            self.symbol,
 | 
			
		||||
        )
 | 
			
		||||
        try:
 | 
			
		||||
            ticker = self.api_market.get_price({"symbol": self.symbol})
 | 
			
		||||
            price = float(ticker["price"])
 | 
			
		||||
            logger.custom_debug(
 | 
			
		||||
                "[GridTradingBot.api_get_price] Current market price: %f", price
 | 
			
		||||
            )
 | 
			
		||||
            return price
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error(
 | 
			
		||||
                "[GridTradingBot.api_get_price] Failed to get price for %s: %s",
 | 
			
		||||
                self.symbol,
 | 
			
		||||
                str(e),
 | 
			
		||||
            )
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
    def api_get_balances(self) -> Tuple[float, float]:
 | 
			
		||||
        """获取基础货币和报价货币的可用余额(不包括冻结金额)"""
 | 
			
		||||
        base_currency = self.symbol[:-4]  # 假设报价货币是4个字母,如USDC
 | 
			
		||||
        quote_currency = self.symbol[-4:]
 | 
			
		||||
        base_available = 0.0
 | 
			
		||||
        quote_available = 0.0
 | 
			
		||||
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.api_get_balances] Fetching balances for %s and %s",
 | 
			
		||||
            base_currency,
 | 
			
		||||
            quote_currency,
 | 
			
		||||
        )
 | 
			
		||||
        try:
 | 
			
		||||
            balances = self.api_trade.get_account_info()
 | 
			
		||||
            for balance in balances.get("balances", []):
 | 
			
		||||
                if balance["asset"] == base_currency:
 | 
			
		||||
                    base_available = float(balance["free"])
 | 
			
		||||
                    logger.custom_debug(
 | 
			
		||||
                        "[GridTradingBot.api_get_balances] Base currency (%s) available: %f",
 | 
			
		||||
                        base_currency,
 | 
			
		||||
                        base_available,
 | 
			
		||||
                    )
 | 
			
		||||
                elif balance["asset"] == quote_currency:
 | 
			
		||||
                    quote_available = float(balance["free"])
 | 
			
		||||
                    logger.custom_debug(
 | 
			
		||||
                        "[GridTradingBot.api_get_balances] Quote currency (%s) available: %f",
 | 
			
		||||
                        quote_currency,
 | 
			
		||||
                        quote_available,
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
            logger.custom_debug(
 | 
			
		||||
                "[GridTradingBot.api_get_balances] Final balances - base: %f, quote: %f",
 | 
			
		||||
                base_available,
 | 
			
		||||
                quote_available,
 | 
			
		||||
            )
 | 
			
		||||
            return base_available, quote_available
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error(
 | 
			
		||||
                "[GridTradingBot.api_get_balances] Failed to get balances: %s", str(e)
 | 
			
		||||
            )
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
    def calculate_order_value(self, price: float) -> float:
 | 
			
		||||
        """计算订单价值 (价格 * 数量)"""
 | 
			
		||||
        value = price * self.order_amount
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.calculate_order_value] Calculated order value: price=%f * amount=%f = %f",
 | 
			
		||||
            price,
 | 
			
		||||
            self.order_amount,
 | 
			
		||||
            value,
 | 
			
		||||
        )
 | 
			
		||||
        return value
 | 
			
		||||
 | 
			
		||||
    def is_order_value_valid(self, price: float) -> bool:
 | 
			
		||||
        """检查订单价值是否满足最小挂单要求"""
 | 
			
		||||
        value = self.calculate_order_value(price)
 | 
			
		||||
        is_valid = value >= self.min_order_value
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.is_order_value_valid] Order value validation: %f >= %f? %s",
 | 
			
		||||
            value,
 | 
			
		||||
            self.min_order_value,
 | 
			
		||||
            is_valid,
 | 
			
		||||
        )
 | 
			
		||||
        return is_valid
 | 
			
		||||
 | 
			
		||||
    def calculate_grid_price(self, base_price: float, level: int, side: str) -> float:
 | 
			
		||||
        """
 | 
			
		||||
        计算网格价格
 | 
			
		||||
 | 
			
		||||
        参数:
 | 
			
		||||
            base_price: 基础价格
 | 
			
		||||
            level: 网格级别 (正数)
 | 
			
		||||
            side: 'BUY' 或 'SELL'
 | 
			
		||||
 | 
			
		||||
        返回:
 | 
			
		||||
            计算后的价格
 | 
			
		||||
        """
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.calculate_grid_price] Calculating grid price: base=%f, level=%d, side=%s",
 | 
			
		||||
            base_price,
 | 
			
		||||
            level,
 | 
			
		||||
            side,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if side == "BUY":
 | 
			
		||||
            price = base_price * (1 - self.grid_percentage) ** level
 | 
			
		||||
            logger.custom_debug(
 | 
			
		||||
                "[GridTradingBot.calculate_grid_price] Calculated BUY price: %f * (1 - %f)^%d = %f",
 | 
			
		||||
                base_price,
 | 
			
		||||
                self.grid_percentage,
 | 
			
		||||
                level,
 | 
			
		||||
                price,
 | 
			
		||||
            )
 | 
			
		||||
            return price
 | 
			
		||||
        elif side == "SELL":
 | 
			
		||||
            price = base_price * (1 + self.grid_percentage) ** level
 | 
			
		||||
            logger.custom_debug(
 | 
			
		||||
                "[GridTradingBot.calculate_grid_price] Calculated SELL price: %f * (1 + %f)^%d = %f",
 | 
			
		||||
                base_price,
 | 
			
		||||
                self.grid_percentage,
 | 
			
		||||
                level,
 | 
			
		||||
                price,
 | 
			
		||||
            )
 | 
			
		||||
            return price
 | 
			
		||||
        else:
 | 
			
		||||
            logger.error(
 | 
			
		||||
                "[GridTradingBot.calculate_grid_price] Invalid side for grid price calculation: %s",
 | 
			
		||||
                side,
 | 
			
		||||
            )
 | 
			
		||||
            raise ValueError(f"Invalid side: {side}")
 | 
			
		||||
 | 
			
		||||
    def api_place_order(self, price: float, side: str) -> Optional[str]:
 | 
			
		||||
        """
 | 
			
		||||
        下订单
 | 
			
		||||
 | 
			
		||||
        参数:
 | 
			
		||||
            price: 价格
 | 
			
		||||
            side: 'BUY' 或 'SELL'
 | 
			
		||||
 | 
			
		||||
        返回:
 | 
			
		||||
            订单ID (如果下单成功) 或 None (如果失败)
 | 
			
		||||
        """
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.api_place_order] Attempting to place %s order at price: %f",
 | 
			
		||||
            side,
 | 
			
		||||
            price,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if not self.is_order_value_valid(price):
 | 
			
		||||
            logger.error(
 | 
			
		||||
                "[GridTradingBot.api_place_order] Order value too small: price=%f, amount=%f, value=%f < min=%f",
 | 
			
		||||
                price,
 | 
			
		||||
                self.order_amount,
 | 
			
		||||
                price * self.order_amount,
 | 
			
		||||
                self.min_order_value,
 | 
			
		||||
            )
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            # logger.custom_debug(
 | 
			
		||||
            #     "[GridTradingBot.api_place_order] Sending order request to exchange"
 | 
			
		||||
            # )
 | 
			
		||||
            order_detail = self.api_trade.post_order(
 | 
			
		||||
                {
 | 
			
		||||
                    "symbol": self.symbol,
 | 
			
		||||
                    "side": side,
 | 
			
		||||
                    "type": "LIMIT",
 | 
			
		||||
                    "price": price,
 | 
			
		||||
                    "quantity": self.order_amount,
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            order_id = order_detail.get("orderId")
 | 
			
		||||
            if order_id:
 | 
			
		||||
                logger.info(
 | 
			
		||||
                    "[GridTradingBot.api_place_order] Successfully placed %s order - price: %f, amount: %f, order_id: %s",
 | 
			
		||||
                    side,
 | 
			
		||||
                    price,
 | 
			
		||||
                    self.order_amount,
 | 
			
		||||
                    order_id,
 | 
			
		||||
                )
 | 
			
		||||
                return order_id
 | 
			
		||||
            else:
 | 
			
		||||
                logger.error(
 | 
			
		||||
                    "[GridTradingBot.api_place_order] Failed to place %s order at %f: %r",
 | 
			
		||||
                    side,
 | 
			
		||||
                    price,
 | 
			
		||||
                    order_detail,
 | 
			
		||||
                )
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error(
 | 
			
		||||
                "[GridTradingBot.api_place_order] Exception occurred while placing %s order at %f: %s",
 | 
			
		||||
                side,
 | 
			
		||||
                price,
 | 
			
		||||
                str(e),
 | 
			
		||||
            )
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    def api_cancel_order(self, order_id: str):
 | 
			
		||||
        """取消订单"""
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.api_cancel_order] Attempting to cancel order: %s", order_id
 | 
			
		||||
        )
 | 
			
		||||
        try:
 | 
			
		||||
            self.api_trade.delete_order({"symbol": self.symbol, "orderId": order_id})
 | 
			
		||||
            logger.info(
 | 
			
		||||
                "[GridTradingBot.api_cancel_order] Successfully canceled order: %s",
 | 
			
		||||
                order_id,
 | 
			
		||||
            )
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error(
 | 
			
		||||
                "[GridTradingBot.api_cancel_order] Failed to cancel order %s: %s",
 | 
			
		||||
                order_id,
 | 
			
		||||
                str(e),
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def api_cancel_all_orders(self):
 | 
			
		||||
        """取消所有活跃订单"""
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.api_cancel_all_orders] Attempting to cancel all open orders for symbol: %s",
 | 
			
		||||
            self.symbol,
 | 
			
		||||
        )
 | 
			
		||||
        try:
 | 
			
		||||
            self.api_trade.delete_openorders({"symbol": self.symbol})
 | 
			
		||||
            logger.info(
 | 
			
		||||
                "[GridTradingBot.api_cancel_all_orders] Successfully canceled all open orders for %s",
 | 
			
		||||
                self.symbol,
 | 
			
		||||
            )
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error(
 | 
			
		||||
                "[GridTradingBot.api_cancel_all_orders] Failed to cancel all open orders: %s",
 | 
			
		||||
                str(e),
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def api_update_order_statuses(self):
 | 
			
		||||
        """更新所有活跃订单的状态"""
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.api_update_order_statuses] Updating status for %d active orders",
 | 
			
		||||
            len(self.active_orders),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        for order_id in list(self.active_orders.keys()):
 | 
			
		||||
            try:
 | 
			
		||||
                logger.custom_debug(
 | 
			
		||||
                    "[GridTradingBot.api_update_order_statuses] Checking status for order: %s",
 | 
			
		||||
                    order_id,
 | 
			
		||||
                )
 | 
			
		||||
                order_info = self.api_trade.get_order(
 | 
			
		||||
                    {"symbol": self.symbol, "orderId": order_id}
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                # 更新订单状态
 | 
			
		||||
                old_status = self.active_orders[order_id].status
 | 
			
		||||
                new_status = order_info["status"]
 | 
			
		||||
                self.active_orders[order_id].status = new_status
 | 
			
		||||
 | 
			
		||||
                # 记录状态变化
 | 
			
		||||
                if old_status != new_status:
 | 
			
		||||
                    logger.info(
 | 
			
		||||
                        "[GridTradingBot.api_update_order_statuses] Order %s status changed from %s to %s",
 | 
			
		||||
                        order_id,
 | 
			
		||||
                        old_status,
 | 
			
		||||
                        new_status,
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                # 如果订单已完成,记录成交时间
 | 
			
		||||
                if new_status == "FILLED":
 | 
			
		||||
                    self.active_orders[order_id].filled_time = time.time()
 | 
			
		||||
                    logger.custom_debug(
 | 
			
		||||
                        "[GridTradingBot.api_update_order_statuses] Order %s filled at %f",
 | 
			
		||||
                        order_id,
 | 
			
		||||
                        self.active_orders[order_id].filled_time,
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                # 如果订单已完成或已取消,从活跃订单中移除
 | 
			
		||||
                # if new_status in ["FILLED", "CANCELED"]:
 | 
			
		||||
                if new_status in ["CANCELED"]:
 | 
			
		||||
                    logger.custom_debug(
 | 
			
		||||
                        "[GridTradingBot.api_update_order_statuses] Removing order %s from active orders (status: %s)",
 | 
			
		||||
                        order_id,
 | 
			
		||||
                        new_status,
 | 
			
		||||
                    )
 | 
			
		||||
                    self.active_orders.pop(order_id)
 | 
			
		||||
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                logger.error(
 | 
			
		||||
                    "[GridTradingBot.api_update_order_statuses] Failed to get status for order %s: %s",
 | 
			
		||||
                    order_id,
 | 
			
		||||
                    str(e),
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
    def get_active_orders_by_side(self, side: str) -> List[Order]:
 | 
			
		||||
        """获取指定方向的所有活跃订单"""
 | 
			
		||||
        orders = [order for order in self.active_orders.values() if order.side == side]
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.get_active_orders_by_side] Found %d active %s orders",
 | 
			
		||||
            len(orders),
 | 
			
		||||
            side,
 | 
			
		||||
        )
 | 
			
		||||
        return orders
 | 
			
		||||
 | 
			
		||||
    def get_extreme_prices(self) -> Tuple[Optional[float], Optional[float]]:
 | 
			
		||||
        """
 | 
			
		||||
        获取当前最极端的买单和卖单价格
 | 
			
		||||
 | 
			
		||||
        返回:
 | 
			
		||||
            (最低买单价格, 最高卖单价格) 如果没有订单则为 (None, None)
 | 
			
		||||
        """
 | 
			
		||||
        buy_orders = self.get_active_orders_by_side("BUY")
 | 
			
		||||
        sell_orders = self.get_active_orders_by_side("SELL")
 | 
			
		||||
 | 
			
		||||
        lowest_buy = min([order.price for order in buy_orders]) if buy_orders else None
 | 
			
		||||
        highest_sell = (
 | 
			
		||||
            max([order.price for order in sell_orders]) if sell_orders else None
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.get_extreme_prices] Extreme prices - lowest buy: %s, highest sell: %s",
 | 
			
		||||
            lowest_buy,
 | 
			
		||||
            highest_sell,
 | 
			
		||||
        )
 | 
			
		||||
        return lowest_buy, highest_sell
 | 
			
		||||
 | 
			
		||||
    def initialize_grid(self):
 | 
			
		||||
        """初始化网格"""
 | 
			
		||||
        logger.info(
 | 
			
		||||
            "[GridTradingBot.initialize_grid] Initializing grid for symbol: %s",
 | 
			
		||||
            self.symbol,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            market_price = self.api_get_price()
 | 
			
		||||
            logger.info(
 | 
			
		||||
                "[GridTradingBot.initialize_grid] Current market price: %f",
 | 
			
		||||
                market_price,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # 初始下单:买1和卖1
 | 
			
		||||
            buy_price = self.calculate_grid_price(market_price, 1, "BUY")
 | 
			
		||||
            sell_price = self.calculate_grid_price(market_price, 1, "SELL")
 | 
			
		||||
            logger.custom_debug(
 | 
			
		||||
                "[GridTradingBot.initialize_grid] Initial buy price: %f, sell price: %f",
 | 
			
		||||
                buy_price,
 | 
			
		||||
                sell_price,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # 检查余额并下单
 | 
			
		||||
            base_balance, quote_balance = self.api_get_balances()
 | 
			
		||||
            logger.info(
 | 
			
		||||
                "[GridTradingBot.initialize_grid] Current balances - base: %f, quote: %f",
 | 
			
		||||
                base_balance,
 | 
			
		||||
                quote_balance,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # 下单买1
 | 
			
		||||
            if quote_balance > self.reserve_quote:
 | 
			
		||||
                logger.custom_debug(
 | 
			
		||||
                    "[GridTradingBot.initialize_grid] Quote balance sufficient (%f > %f), checking order value",
 | 
			
		||||
                    quote_balance,
 | 
			
		||||
                    self.reserve_quote,
 | 
			
		||||
                )
 | 
			
		||||
                if self.is_order_value_valid(buy_price):
 | 
			
		||||
                    logger.custom_debug(
 | 
			
		||||
                        "[GridTradingBot.initialize_grid] Order value valid, placing buy order"
 | 
			
		||||
                    )
 | 
			
		||||
                    buy_order_id = self.api_place_order(buy_price, "BUY")
 | 
			
		||||
                    if buy_order_id:
 | 
			
		||||
                        self.active_orders[buy_order_id] = Order(
 | 
			
		||||
                            order_id=buy_order_id,
 | 
			
		||||
                            price=buy_price,
 | 
			
		||||
                            quantity=self.order_amount,
 | 
			
		||||
                            side="BUY",
 | 
			
		||||
                            status="NEW",
 | 
			
		||||
                        )
 | 
			
		||||
                        self.current_buy_levels = 1
 | 
			
		||||
                        logger.info(
 | 
			
		||||
                            "[GridTradingBot.initialize_grid] Initial buy order placed at %f",
 | 
			
		||||
                            buy_price,
 | 
			
		||||
                        )
 | 
			
		||||
                else:
 | 
			
		||||
                    logger.error(
 | 
			
		||||
                        "[GridTradingBot.initialize_grid] Initial buy order value too small: %f < %f",
 | 
			
		||||
                        buy_price * self.order_amount,
 | 
			
		||||
                        self.min_order_value,
 | 
			
		||||
                    )
 | 
			
		||||
            else:
 | 
			
		||||
                logger.error(
 | 
			
		||||
                    "[GridTradingBot.initialize_grid] Insufficient quote balance for initial buy: %f <= %f",
 | 
			
		||||
                    quote_balance,
 | 
			
		||||
                    self.reserve_quote,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            # 下单卖1
 | 
			
		||||
            if base_balance > self.reserve_base:
 | 
			
		||||
                logger.custom_debug(
 | 
			
		||||
                    "[GridTradingBot.initialize_grid] Base balance sufficient (%f > %f), checking order value",
 | 
			
		||||
                    base_balance,
 | 
			
		||||
                    self.reserve_base,
 | 
			
		||||
                )
 | 
			
		||||
                if self.is_order_value_valid(sell_price):
 | 
			
		||||
                    logger.custom_debug(
 | 
			
		||||
                        "[GridTradingBot.initialize_grid] Order value valid, placing sell order"
 | 
			
		||||
                    )
 | 
			
		||||
                    sell_order_id = self.api_place_order(sell_price, "SELL")
 | 
			
		||||
                    if sell_order_id:
 | 
			
		||||
                        self.active_orders[sell_order_id] = Order(
 | 
			
		||||
                            order_id=sell_order_id,
 | 
			
		||||
                            price=sell_price,
 | 
			
		||||
                            quantity=self.order_amount,
 | 
			
		||||
                            side="SELL",
 | 
			
		||||
                            status="NEW",
 | 
			
		||||
                        )
 | 
			
		||||
                        self.current_sell_levels = 1
 | 
			
		||||
                        logger.info(
 | 
			
		||||
                            "[GridTradingBot.initialize_grid] Initial sell order placed at %f",
 | 
			
		||||
                            sell_price,
 | 
			
		||||
                        )
 | 
			
		||||
                else:
 | 
			
		||||
                    logger.error(
 | 
			
		||||
                        "[GridTradingBot.initialize_grid] Initial sell order value too small: %f < %f",
 | 
			
		||||
                        sell_price * self.order_amount,
 | 
			
		||||
                        self.min_order_value,
 | 
			
		||||
                    )
 | 
			
		||||
            else:
 | 
			
		||||
                logger.error(
 | 
			
		||||
                    "[GridTradingBot.initialize_grid] Insufficient base balance for initial sell: %f <= %f",
 | 
			
		||||
                    base_balance,
 | 
			
		||||
                    self.reserve_base,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            for _ in range(1, self.grid_count):
 | 
			
		||||
                self.extend_grid()
 | 
			
		||||
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error(
 | 
			
		||||
                "[GridTradingBot.initialize_grid] Failed to initialize grid: %s", str(e)
 | 
			
		||||
            )
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
    def extend_grid(self):
 | 
			
		||||
        """扩展网格,保证两侧都有指定个数的挂单"""
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.extend_grid] Extending grid - current buy levels: %d/%d, sell levels: %d/%d",
 | 
			
		||||
            self.current_buy_levels,
 | 
			
		||||
            self.grid_count,
 | 
			
		||||
            self.current_sell_levels,
 | 
			
		||||
            self.grid_count,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            lowest_buy, highest_sell = self.get_extreme_prices()
 | 
			
		||||
            base_balance, quote_balance = self.api_get_balances()
 | 
			
		||||
 | 
			
		||||
            # 扩展买单网格 (向下)
 | 
			
		||||
            if lowest_buy is not None and self.current_buy_levels < self.grid_count:
 | 
			
		||||
                new_buy_price = self.calculate_grid_price(lowest_buy, 1, "BUY")
 | 
			
		||||
                logger.custom_debug(
 | 
			
		||||
                    "[GridTradingBot.extend_grid] Extending buy grid - current lowest: %f, new price: %f",
 | 
			
		||||
                    lowest_buy,
 | 
			
		||||
                    new_buy_price,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                if quote_balance > self.reserve_quote:
 | 
			
		||||
                    logger.custom_debug(
 | 
			
		||||
                        "[GridTradingBot.extend_grid] Quote balance sufficient (%f > %f), checking order value",
 | 
			
		||||
                        quote_balance,
 | 
			
		||||
                        self.reserve_quote,
 | 
			
		||||
                    )
 | 
			
		||||
                    if self.is_order_value_valid(new_buy_price):
 | 
			
		||||
                        logger.custom_debug(
 | 
			
		||||
                            "[GridTradingBot.extend_grid] Order value valid, placing extended buy order"
 | 
			
		||||
                        )
 | 
			
		||||
                        buy_order_id = self.api_place_order(new_buy_price, "BUY")
 | 
			
		||||
                        if buy_order_id:
 | 
			
		||||
                            self.active_orders[buy_order_id] = Order(
 | 
			
		||||
                                order_id=buy_order_id,
 | 
			
		||||
                                price=new_buy_price,
 | 
			
		||||
                                quantity=self.order_amount,
 | 
			
		||||
                                side="BUY",
 | 
			
		||||
                                status="NEW",
 | 
			
		||||
                            )
 | 
			
		||||
                            self.current_buy_levels += 1
 | 
			
		||||
                            logger.info(
 | 
			
		||||
                                "[GridTradingBot.extend_grid] Extended buy grid to level %d at price %f",
 | 
			
		||||
                                self.current_buy_levels,
 | 
			
		||||
                                new_buy_price,
 | 
			
		||||
                            )
 | 
			
		||||
                    else:
 | 
			
		||||
                        logger.error(
 | 
			
		||||
                            "[GridTradingBot.extend_grid] Extended buy order value too small: %f < %f",
 | 
			
		||||
                            new_buy_price * self.order_amount,
 | 
			
		||||
                            self.min_order_value,
 | 
			
		||||
                        )
 | 
			
		||||
                else:
 | 
			
		||||
                    logger.error(
 | 
			
		||||
                        "[GridTradingBot.extend_grid] Insufficient quote balance for extended buy: %f <= %f",
 | 
			
		||||
                        quote_balance,
 | 
			
		||||
                        self.reserve_quote,
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
            # 扩展卖单网格 (向上)
 | 
			
		||||
            if highest_sell is not None and self.current_sell_levels < self.grid_count:
 | 
			
		||||
                new_sell_price = self.calculate_grid_price(highest_sell, 1, "SELL")
 | 
			
		||||
                logger.custom_debug(
 | 
			
		||||
                    "[GridTradingBot.extend_grid] Extending sell grid - current highest: %f, new price: %f",
 | 
			
		||||
                    highest_sell,
 | 
			
		||||
                    new_sell_price,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                if base_balance > self.reserve_base:
 | 
			
		||||
                    logger.custom_debug(
 | 
			
		||||
                        "[GridTradingBot.extend_grid] Base balance sufficient (%f > %f), checking order value",
 | 
			
		||||
                        base_balance,
 | 
			
		||||
                        self.reserve_base,
 | 
			
		||||
                    )
 | 
			
		||||
                    if self.is_order_value_valid(new_sell_price):
 | 
			
		||||
                        logger.custom_debug(
 | 
			
		||||
                            "[GridTradingBot.extend_grid] Order value valid, placing extended sell order"
 | 
			
		||||
                        )
 | 
			
		||||
                        sell_order_id = self.api_place_order(new_sell_price, "SELL")
 | 
			
		||||
                        if sell_order_id:
 | 
			
		||||
                            self.active_orders[sell_order_id] = Order(
 | 
			
		||||
                                order_id=sell_order_id,
 | 
			
		||||
                                price=new_sell_price,
 | 
			
		||||
                                quantity=self.order_amount,
 | 
			
		||||
                                side="SELL",
 | 
			
		||||
                                status="NEW",
 | 
			
		||||
                            )
 | 
			
		||||
                            self.current_sell_levels += 1
 | 
			
		||||
                            logger.info(
 | 
			
		||||
                                "[GridTradingBot.extend_grid] Extended sell grid to level %d at price %f",
 | 
			
		||||
                                self.current_sell_levels,
 | 
			
		||||
                                new_sell_price,
 | 
			
		||||
                            )
 | 
			
		||||
                    else:
 | 
			
		||||
                        logger.error(
 | 
			
		||||
                            "[GridTradingBot.extend_grid] Extended sell order value too small: %f < %f",
 | 
			
		||||
                            new_sell_price * self.order_amount,
 | 
			
		||||
                            self.min_order_value,
 | 
			
		||||
                        )
 | 
			
		||||
                else:
 | 
			
		||||
                    logger.error(
 | 
			
		||||
                        "[GridTradingBot.extend_grid] Insufficient base balance for extended sell: %f <= %f",
 | 
			
		||||
                        base_balance,
 | 
			
		||||
                        self.reserve_base,
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error(
 | 
			
		||||
                "[GridTradingBot.extend_grid] Failed to extend grid: %s", str(e)
 | 
			
		||||
            )
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
    def adjust_grid(self):
 | 
			
		||||
        """调整网格 - 优化后的对称逻辑:
 | 
			
		||||
        买单成交时:
 | 
			
		||||
          1. 取消最高价卖单
 | 
			
		||||
          2. 在卖单侧挂一个更低价的卖单(卖0)
 | 
			
		||||
          3. 在买单侧挂一个更低价的买单(买n)
 | 
			
		||||
        卖单成交时:
 | 
			
		||||
          1. 取消最低价买单
 | 
			
		||||
          2. 在买单侧挂一个更高价的买单(买0)
 | 
			
		||||
          3. 在卖单侧挂一个更高价的卖单(卖n)"""
 | 
			
		||||
        logger.custom_debug(
 | 
			
		||||
            "[GridTradingBot.adjust_grid] Adjusting grid with symmetric logic"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            # 获取当前所有订单的快照
 | 
			
		||||
            current_order_ids = set(self.active_orders.keys())
 | 
			
		||||
 | 
			
		||||
            # 找出新成交的订单(之前存在且现在状态为FILLED)
 | 
			
		||||
            newly_filled_orders = [
 | 
			
		||||
                order
 | 
			
		||||
                for order in self.active_orders.values()
 | 
			
		||||
                if order.order_id in current_order_ids and order.status == "FILLED"
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
            if not newly_filled_orders:
 | 
			
		||||
                logger.custom_debug(
 | 
			
		||||
                    "[GridTradingBot.adjust_grid] No newly filled orders found"
 | 
			
		||||
                )
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            # 处理每个新成交的订单
 | 
			
		||||
            for filled_order in newly_filled_orders:
 | 
			
		||||
                filled_side = filled_order.side
 | 
			
		||||
                filled_price = filled_order.price
 | 
			
		||||
                filled_id = filled_order.order_id
 | 
			
		||||
                self.active_orders.pop(filled_id, None)
 | 
			
		||||
                logger.info(
 | 
			
		||||
                    "[GridTradingBot.adjust_grid] Processing newly filled %s order at price %f",
 | 
			
		||||
                    filled_side,
 | 
			
		||||
                    filled_price,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                # 获取当前活跃订单
 | 
			
		||||
                active_buys = [
 | 
			
		||||
                    o
 | 
			
		||||
                    for o in self.active_orders.values()
 | 
			
		||||
                    if o.side == "BUY" and o.status == "NEW"
 | 
			
		||||
                ]
 | 
			
		||||
                active_sells = [
 | 
			
		||||
                    o
 | 
			
		||||
                    for o in self.active_orders.values()
 | 
			
		||||
                    if o.side == "SELL" and o.status == "NEW"
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
                if filled_side == "BUY" and active_sells:
 | 
			
		||||
                    # 买单成交时的处理逻辑
 | 
			
		||||
                    self.current_buy_levels -= 1
 | 
			
		||||
                    # 1. 取消最高价卖单
 | 
			
		||||
                    highest_sell = max(active_sells, key=lambda x: x.price)
 | 
			
		||||
                    self.api_cancel_order(highest_sell.order_id)
 | 
			
		||||
                    self.active_orders.pop(highest_sell.order_id, None)
 | 
			
		||||
                    logger.info(
 | 
			
		||||
                        "[GridTradingBot.adjust_grid] Cancelled highest SELL order at %f (order_id: %s)",
 | 
			
		||||
                        highest_sell.price,
 | 
			
		||||
                        highest_sell.order_id,
 | 
			
		||||
                    )
 | 
			
		||||
                    # 2. 在卖单侧挂一个更低价的卖单(卖0)
 | 
			
		||||
                    # 使用最近成交的买单价格作为基准
 | 
			
		||||
                    new_sell_price = self.calculate_grid_price(filled_price, 1, "SELL")
 | 
			
		||||
                    # 检查余额和订单价值
 | 
			
		||||
                    base_balance, _ = self.api_get_balances()
 | 
			
		||||
                    if base_balance > self.reserve_base and self.is_order_value_valid(
 | 
			
		||||
                        new_sell_price
 | 
			
		||||
                    ):
 | 
			
		||||
                        sell_order_id = self.api_place_order(new_sell_price, "SELL")
 | 
			
		||||
                        if sell_order_id:
 | 
			
		||||
                            self.active_orders[sell_order_id] = Order(
 | 
			
		||||
                                order_id=sell_order_id,
 | 
			
		||||
                                price=new_sell_price,
 | 
			
		||||
                                quantity=self.order_amount,
 | 
			
		||||
                                side="SELL",
 | 
			
		||||
                                status="NEW",
 | 
			
		||||
                            )
 | 
			
		||||
                            logger.info(
 | 
			
		||||
                                "[GridTradingBot.adjust_grid] Placed new lower SELL order at %f (order_id: %s)",
 | 
			
		||||
                                new_sell_price,
 | 
			
		||||
                                sell_order_id,
 | 
			
		||||
                            )
 | 
			
		||||
 | 
			
		||||
                elif filled_side == "SELL" and active_buys:
 | 
			
		||||
                    # 卖单成交时的处理逻辑
 | 
			
		||||
                    self.current_sell_levels -= 1
 | 
			
		||||
                    # 1. 取消最低价买单
 | 
			
		||||
                    lowest_buy = min(active_buys, key=lambda x: x.price)
 | 
			
		||||
                    self.api_cancel_order(lowest_buy.order_id)
 | 
			
		||||
                    self.active_orders.pop(lowest_buy.order_id, None)
 | 
			
		||||
 | 
			
		||||
                    logger.info(
 | 
			
		||||
                        "[GridTradingBot.adjust_grid] Cancelled lowest BUY order at %f (order_id: %s)",
 | 
			
		||||
                        lowest_buy.price,
 | 
			
		||||
                        lowest_buy.order_id,
 | 
			
		||||
                    )
 | 
			
		||||
                    # 2. 在买单侧挂一个更高价的买单(买0)
 | 
			
		||||
                    # 使用最近成交的卖单价格作为基准
 | 
			
		||||
                    new_buy_price = self.calculate_grid_price(filled_price, 1, "BUY")
 | 
			
		||||
                    # 检查余额和订单价值
 | 
			
		||||
                    _, quote_balance = self.api_get_balances()
 | 
			
		||||
                    if (
 | 
			
		||||
                        quote_balance > self.reserve_quote
 | 
			
		||||
                        and self.is_order_value_valid(new_buy_price)
 | 
			
		||||
                    ):
 | 
			
		||||
                        buy_order_id = self.api_place_order(new_buy_price, "BUY")
 | 
			
		||||
                        if buy_order_id:
 | 
			
		||||
                            self.active_orders[buy_order_id] = Order(
 | 
			
		||||
                                order_id=buy_order_id,
 | 
			
		||||
                                price=new_buy_price,
 | 
			
		||||
                                quantity=self.order_amount,
 | 
			
		||||
                                side="BUY",
 | 
			
		||||
                                status="NEW",
 | 
			
		||||
                            )
 | 
			
		||||
                            logger.info(
 | 
			
		||||
                                "[GridTradingBot.adjust_grid] Placed new higher BUY order at %f (order_id: %s)",
 | 
			
		||||
                                new_buy_price,
 | 
			
		||||
                                buy_order_id,
 | 
			
		||||
                            )
 | 
			
		||||
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error(
 | 
			
		||||
                "[GridTradingBot.adjust_grid] Failed to adjust grid: %s", str(e)
 | 
			
		||||
            )
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        """运行网格交易机器人"""
 | 
			
		||||
        self.running = True
 | 
			
		||||
        logger.info(
 | 
			
		||||
            "[GridTradingBot.run] Starting grid trading bot for symbol: %s", self.symbol
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            # 初始化网格
 | 
			
		||||
            self.initialize_grid()
 | 
			
		||||
 | 
			
		||||
            # 主循环
 | 
			
		||||
            while self.running:
 | 
			
		||||
                try:
 | 
			
		||||
                    logger.custom_debug(
 | 
			
		||||
                        "[GridTradingBot.run] Starting main loop iteration"
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                    # 更新订单状态
 | 
			
		||||
                    self.api_update_order_statuses()
 | 
			
		||||
 | 
			
		||||
                    # 调整网格
 | 
			
		||||
                    self.adjust_grid()
 | 
			
		||||
 | 
			
		||||
                    # 尝试扩展网格
 | 
			
		||||
                    self.extend_grid()
 | 
			
		||||
 | 
			
		||||
                    # 等待一段时间再检查
 | 
			
		||||
                    sleep_time = 0
 | 
			
		||||
                    logger.custom_debug(
 | 
			
		||||
                        "[GridTradingBot.run] Sleeping for %d seconds", sleep_time
 | 
			
		||||
                    )
 | 
			
		||||
                    time.sleep(sleep_time)
 | 
			
		||||
 | 
			
		||||
                except Exception as e:
 | 
			
		||||
                    logger.error("[GridTradingBot.run] Error in main loop: %s", str(e))
 | 
			
		||||
                    sleep_time = 3
 | 
			
		||||
                    logger.custom_debug(
 | 
			
		||||
                        "[GridTradingBot.run] Error occurred, sleeping for %d seconds",
 | 
			
		||||
                        sleep_time,
 | 
			
		||||
                    )
 | 
			
		||||
                    time.sleep(sleep_time)  # 出错后等待更长时间
 | 
			
		||||
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            logger.info("[GridTradingBot.run] Received keyboard interrupt, stopping...")
 | 
			
		||||
        finally:
 | 
			
		||||
            self.stop()
 | 
			
		||||
 | 
			
		||||
    def stop(self):
 | 
			
		||||
        """停止机器人"""
 | 
			
		||||
        logger.info("[GridTradingBot.stop] Stopping grid trading bot...")
 | 
			
		||||
        self.running = False
 | 
			
		||||
        self.api_cancel_all_orders()
 | 
			
		||||
        logger.info("[GridTradingBot.stop] Grid trading bot stopped successfully")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 示例配置和使用
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    config = {
 | 
			
		||||
        "symbol": "BTCUSDC",  # 交易对
 | 
			
		||||
        "grid_percentage": 0.001,  # 等比网格的公比
 | 
			
		||||
        "grid_count": 3,  # 单侧的挂单数,实时平衡
 | 
			
		||||
        "order_amount": 0.00001,  # BTC数量
 | 
			
		||||
        "min_order_value": 1,  # 交易所限制订单价值至少 1 USDC
 | 
			
		||||
        "reserve_base": 0,  # 保留 0 BTC 不参与网格
 | 
			
		||||
        "reserve_quote": 0,  # 保留 0 USDC 不参与网格
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    logger.info("[__main__] Creating GridTradingBot instance with config: %r", config)
 | 
			
		||||
    bot = GridTradingBot(config)
 | 
			
		||||
    bot.run()
 | 
			
		||||
							
								
								
									
										535
									
								
								mexc_spot_v3.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										535
									
								
								mexc_spot_v3.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,535 @@
 | 
			
		||||
import requests
 | 
			
		||||
import hmac
 | 
			
		||||
import hashlib
 | 
			
		||||
from urllib.parse import urlencode, quote
 | 
			
		||||
from config import config
 | 
			
		||||
 | 
			
		||||
# ServerTime、Signature
 | 
			
		||||
class TOOL(object):
 | 
			
		||||
 | 
			
		||||
    def _get_server_time(self):
 | 
			
		||||
        return requests.request('get', 'https://api.mexc.com/api/v3/time').json()['serverTime']
 | 
			
		||||
 | 
			
		||||
    def _sign_v3(self, req_time, sign_params=None):
 | 
			
		||||
        if sign_params:
 | 
			
		||||
            sign_params = urlencode(sign_params, quote_via=quote)
 | 
			
		||||
            to_sign = "{}×tamp={}".format(sign_params, req_time)
 | 
			
		||||
        else:
 | 
			
		||||
            to_sign = "timestamp={}".format(req_time)
 | 
			
		||||
        sign = hmac.new(self.mexc_secret.encode('utf-8'), to_sign.encode('utf-8'), hashlib.sha256).hexdigest()
 | 
			
		||||
        return sign
 | 
			
		||||
 | 
			
		||||
    def public_request(self, method, url, params=None):
 | 
			
		||||
        url = '{}{}'.format(self.hosts, url)
 | 
			
		||||
        return requests.request(method, url, params=params)
 | 
			
		||||
 | 
			
		||||
    def sign_request(self, method, url, params=None):
 | 
			
		||||
        url = '{}{}'.format(self.hosts, url)
 | 
			
		||||
        req_time = self._get_server_time()
 | 
			
		||||
        if params:
 | 
			
		||||
            params['signature'] = self._sign_v3(req_time=req_time, sign_params=params)
 | 
			
		||||
        else:
 | 
			
		||||
            params = {}
 | 
			
		||||
            params['signature'] = self._sign_v3(req_time=req_time)
 | 
			
		||||
        params['timestamp'] = req_time
 | 
			
		||||
        headers = {
 | 
			
		||||
            'x-mexc-apikey': self.mexc_key,
 | 
			
		||||
            'Content-Type': 'application/json',
 | 
			
		||||
        }
 | 
			
		||||
        return requests.request(method, url, params=params, headers=headers)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Market Data
 | 
			
		||||
class mexc_market(TOOL):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.api = '/api/v3'
 | 
			
		||||
        self.hosts = config.mexc_host
 | 
			
		||||
        self.method = 'GET'
 | 
			
		||||
 | 
			
		||||
    def get_ping(self):
 | 
			
		||||
        """test connectivity"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/ping')
 | 
			
		||||
        response = self.public_request(self.method, url)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_timestamp(self):
 | 
			
		||||
        """get sever time"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/time')
 | 
			
		||||
        response = self.public_request(self.method, url)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_defaultSymbols(self):
 | 
			
		||||
        """get defaultSymbols"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/defaultSymbols')
 | 
			
		||||
        response = self.public_request(self.method, url)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_exchangeInfo(self, params=None):
 | 
			
		||||
        """get exchangeInfo"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/exchangeInfo')
 | 
			
		||||
        response = self.public_request(self.method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_depth(self, params):
 | 
			
		||||
        """get symbol depth"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/depth')
 | 
			
		||||
        response = self.public_request(self.method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_deals(self, params):
 | 
			
		||||
        """get current trade deals list"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/trades')
 | 
			
		||||
        response = self.public_request(self.method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_aggtrades(self, params):
 | 
			
		||||
        """get aggregate trades list"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/aggTrades')
 | 
			
		||||
        response = self.public_request(self.method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_kline(self, params):
 | 
			
		||||
        """get k-line data"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/klines')
 | 
			
		||||
        response = self.public_request(self.method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_avgprice(self, params):
 | 
			
		||||
        """get current average prcie(default : 5m)"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/avgPrice')
 | 
			
		||||
        response = self.public_request(self.method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_24hr_ticker(self, params=None):
 | 
			
		||||
        """get 24hr prcie ticker change statistics"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/ticker/24hr')
 | 
			
		||||
        response = self.public_request(self.method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_price(self, params=None):
 | 
			
		||||
        """get symbol price ticker"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/ticker/price')
 | 
			
		||||
        response = self.public_request(self.method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_bookticker(self, params=None):
 | 
			
		||||
        """get symbol order book ticker"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/ticker/bookTicker')
 | 
			
		||||
        response = self.public_request(self.method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_ETF_info(self, params=None):
 | 
			
		||||
        """get ETF information"""
 | 
			
		||||
        url = '{}{}'.format(self.api, '/etf/info')
 | 
			
		||||
        response = self.public_request(self.method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Spot Trade
 | 
			
		||||
class mexc_trade(TOOL):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.api = '/api/v3'
 | 
			
		||||
        self.hosts = config.mexc_host
 | 
			
		||||
        self.mexc_key = config.api_key
 | 
			
		||||
        self.mexc_secret = config.secret_key
 | 
			
		||||
 | 
			
		||||
    def get_selfSymbols(self):
 | 
			
		||||
        """get currency information"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/selfSymbols')
 | 
			
		||||
        response = self.sign_request(method, url)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def post_order_test(self, params):
 | 
			
		||||
        """test new order"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/order/test')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def post_order(self, params):
 | 
			
		||||
        """place order"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/order')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def post_batchorders(self, params):
 | 
			
		||||
        """place batch orders(same symbol)"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/batchOrders')
 | 
			
		||||
        params = {"batchOrders": str(params)}
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        print(response.url)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def delete_order(self, params):
 | 
			
		||||
        """
 | 
			
		||||
        cancel order
 | 
			
		||||
        'origClientOrderId' or 'orderId' must be sent
 | 
			
		||||
        """
 | 
			
		||||
        method = 'DELETE'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/order')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def delete_openorders(self, params):
 | 
			
		||||
        """
 | 
			
		||||
        cancel all order for a single symbol
 | 
			
		||||
        """
 | 
			
		||||
        method = 'DELETE'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/openOrders')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_order(self, params):
 | 
			
		||||
        """
 | 
			
		||||
        get order
 | 
			
		||||
        'origClientOrderId' or 'orderId' must be sent
 | 
			
		||||
        """
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/order')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_openorders(self, params):
 | 
			
		||||
        """get current pending order """
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/openOrders')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_allorders(self, params):
 | 
			
		||||
        """
 | 
			
		||||
        get current all order
 | 
			
		||||
        startTime and endTime need to use at the same time
 | 
			
		||||
        """
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/allOrders')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_mytrades(self, params):
 | 
			
		||||
        """
 | 
			
		||||
        get current all order
 | 
			
		||||
        orderId need to use with symbol at the same time
 | 
			
		||||
        """
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/myTrades')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def post_mxDeDuct(self, params):
 | 
			
		||||
        """Enable MX DeDuct"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/mxDeduct/enable')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_mxDeDuct(self):
 | 
			
		||||
        """MX DeDuct status"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/mxDeduct/enable')
 | 
			
		||||
        response = self.sign_request(method, url)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_account_info(self):
 | 
			
		||||
        """get account information"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/account')
 | 
			
		||||
        response = self.sign_request(method, url)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Wallet
 | 
			
		||||
class mexc_wallet(TOOL):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.api = '/api/v3/capital'
 | 
			
		||||
        self.hosts = config.mexc_host
 | 
			
		||||
        self.mexc_key = config.api_key
 | 
			
		||||
        self.mexc_secret = config.secret_key
 | 
			
		||||
 | 
			
		||||
    def get_coinlist(self):
 | 
			
		||||
        """get currency information"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/config/getall')
 | 
			
		||||
        response = self.sign_request(method, url)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def post_withdraw(self, params):
 | 
			
		||||
        """withdraw"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/withdraw/apply')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def cancel_withdraw(self, params):
 | 
			
		||||
        """withdraw"""
 | 
			
		||||
        method = 'DELETE'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/withdraw')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_deposit_list(self, params):
 | 
			
		||||
        """deposit history list"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/deposit/hisrec')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_withdraw_list(self, params):
 | 
			
		||||
        """withdraw history list"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/withdraw/history')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def post_deposit_address(self, params):
 | 
			
		||||
        """generate deposit address"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/deposit/address')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_deposit_address(self, params):
 | 
			
		||||
        """get deposit address"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/deposit/address')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_withdraw_address(self, params):
 | 
			
		||||
        """get deposit address"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/withdraw/address')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def post_transfer(self, params):
 | 
			
		||||
        """universal transfer"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/transfer')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_transfer_list(self, params):
 | 
			
		||||
        """universal transfer history"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/transfer')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_transfer_list_byId(self, params):
 | 
			
		||||
        """universal transfer history (by tranId)"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/transfer/tranId')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def post_transfer_internal(self, params):
 | 
			
		||||
        """universal transfer"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/transfer/internal')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_transfer_internal_list(self, params=None):
 | 
			
		||||
        """universal transfer"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/transfer/internal')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_smallAssets_list(self):
 | 
			
		||||
        """small Assets convertible list"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/convert/list')
 | 
			
		||||
        response = self.sign_request(method, url)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def post_smallAssets_convert(self, params):
 | 
			
		||||
        """small Assets convert"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/convert')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_smallAssets_history(self, params=None):
 | 
			
		||||
        """small Assets convertible history"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/convert')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Sub-Account
 | 
			
		||||
class mexc_subaccount(TOOL):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.api = '/api/v3'
 | 
			
		||||
        self.hosts = config.mexc_host
 | 
			
		||||
        self.mexc_key = config.api_key
 | 
			
		||||
        self.mexc_secret = config.secret_key
 | 
			
		||||
 | 
			
		||||
    def post_virtualSubAccount(self, params):
 | 
			
		||||
        """create a sub-account"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/sub-account/virtualSubAccount')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_SubAccountList(self, params=None):
 | 
			
		||||
        """get sub-account list"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/sub-account/list')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def post_virtualApiKey(self, params):
 | 
			
		||||
        """create sub-account's apikey"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/sub-account/apiKey')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_virtualApiKey(self, params):
 | 
			
		||||
        """get sub-account's apikey"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/sub-account/apiKey')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def delete_virtualApiKey(self, params):
 | 
			
		||||
        """delete sub-account's apikey"""
 | 
			
		||||
        method = 'DELETE'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/sub-account/apiKey')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def post_universalTransfer(self, params):
 | 
			
		||||
        """universal transfer between accounts"""
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/capital/sub-account/universalTransfer')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_universalTransfer(self, params):
 | 
			
		||||
        """universal transfer history between accounts"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/capital/sub-account/universalTransfer')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Rebate
 | 
			
		||||
class mexc_rebate(TOOL):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.api = '/api/v3/rebate'
 | 
			
		||||
        self.hosts = config.mexc_host
 | 
			
		||||
        self.mexc_key = config.api_key
 | 
			
		||||
        self.mexc_secret = config.secret_key
 | 
			
		||||
 | 
			
		||||
    def get_taxQuery(self, params=None):
 | 
			
		||||
        """get the rebate commission record"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/taxQuery')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_rebate_detail(self, params=None):
 | 
			
		||||
        """get rebate record details"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/detail')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_kickback_detail(self, params=None):
 | 
			
		||||
        """get self-return record details"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/detail/kickback')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_inviter(self, params=None):
 | 
			
		||||
        """get self-return record details"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/referCode')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_affiliate_commission(self, params=None):
 | 
			
		||||
        """get affiliate commission history"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/affiliate/commission')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_affiliate_withdraw(self, params=None):
 | 
			
		||||
        """get affiliate withdraw history"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/affiliate/withdraw')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_affiliate_commission_detail(self, params=None):
 | 
			
		||||
        """get affiliate commission details"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/affiliate/commission/detail')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_affiliate_referral(self, params=None):
 | 
			
		||||
        """get affiliate referral"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/affiliate/referral')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_affiliate_subaffiliates(self, params=None):
 | 
			
		||||
        """get affiliate subaffiliates"""
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/affiliate/subaffiliates')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# WebSocket ListenKey
 | 
			
		||||
class mexc_listenkey(TOOL):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.api = '/api/v3'
 | 
			
		||||
        self.hosts = config.mexc_host
 | 
			
		||||
        self.mexc_key = config.api_key
 | 
			
		||||
        self.mexc_secret = config.secret_key
 | 
			
		||||
 | 
			
		||||
    def post_listenKey(self):
 | 
			
		||||
        """ generate ListenKey """
 | 
			
		||||
        method = 'POST'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/userDataStream')
 | 
			
		||||
        response = self.sign_request(method, url)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def get_listenKey(self):
 | 
			
		||||
        """ get valid ListenKey """
 | 
			
		||||
        method = 'GET'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/userDataStream')
 | 
			
		||||
        response = self.sign_request(method, url)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def put_listenKey(self, params):
 | 
			
		||||
        """ extend ListenKey validity """
 | 
			
		||||
        method = 'PUT'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/userDataStream')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
 | 
			
		||||
    def delete_listenKey(self, params):
 | 
			
		||||
        """ delete ListenKey """
 | 
			
		||||
        method = 'DELETE'
 | 
			
		||||
        url = '{}{}'.format(self.api, '/userDataStream')
 | 
			
		||||
        response = self.sign_request(method, url, params=params)
 | 
			
		||||
        return response.json()
 | 
			
		||||
		Reference in New Issue
	
	Block a user