feat(main): load trades form json, imporve LIMIT LIMIT_MAKER order handling
				
					
				
			- Read trading instructions from JSON configuration file - Support executing multiple trades by date (* means executing daily) - Automatically fetch real-time price as the limit price when no price is provided - For LIMIT, LIMIT_MAKER orders, support providing only quoteOrderQty to automatically calculate the quantity
This commit is contained in:
		
							
								
								
									
										197
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,197 @@
 | 
			
		||||
# MEXC 交易机器人
 | 
			
		||||
 | 
			
		||||
## 项目简介
 | 
			
		||||
 | 
			
		||||
MEXC 交易机器人是一个自动化交易工具,用于在 MEXC 交易所执行现货交易。它通过 JSON 配置文件管理交易指令,支持多种订单类型,并自动记录交易日志。
 | 
			
		||||
 | 
			
		||||
## 主要功能
 | 
			
		||||
 | 
			
		||||
1. **配置文件管理**:从 JSON 文件读取交易指令
 | 
			
		||||
2. **灵活调度**:支持按日期执行交易(`*`表示每天执行)
 | 
			
		||||
3. **多种订单类型**:支持 LIMIT、MARKET、LIMIT_MAKER、IMMEDIATE_OR_CANCEL、FILL_OR_KILL 等订单类型
 | 
			
		||||
4. **智能价格获取**:当无 price 参数时自动获取实时价格作为限价
 | 
			
		||||
5. **自动计算数量**:LIMIT 订单支持只提供 quoteOrderQty 自动计算 quantity
 | 
			
		||||
6. **证券代码映射**:支持不同代码格式间的转换
 | 
			
		||||
7. **完整交易记录**:记录交易到 CSV 文件和日志
 | 
			
		||||
 | 
			
		||||
## 安装与使用
 | 
			
		||||
 | 
			
		||||
1. 创建配置文件`trading_config.json`
 | 
			
		||||
2. 运行程序:
 | 
			
		||||
 | 
			
		||||
    ```bash
 | 
			
		||||
    python main.py
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
## 配置文件
 | 
			
		||||
 | 
			
		||||
### 配置文件结构
 | 
			
		||||
 | 
			
		||||
配置文件采用 JSON 格式,命名为`trading_config.json`,基本结构如下:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "trades": [
 | 
			
		||||
        {
 | 
			
		||||
            "symbol": "BTCUSDT",
 | 
			
		||||
            "order_type": "LIMIT",
 | 
			
		||||
            "side": "BUY",
 | 
			
		||||
            "execute_dates": ["*"],
 | 
			
		||||
            "params": {
 | 
			
		||||
                "quoteOrderQty": "100",
 | 
			
		||||
                "price": "50000"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 配置字段详解
 | 
			
		||||
 | 
			
		||||
#### 1. 交易列表 (`trades`)
 | 
			
		||||
 | 
			
		||||
- 类型:数组
 | 
			
		||||
- 描述:包含所有交易指令的列表
 | 
			
		||||
- 每个交易指令是一个包含以下字段的对象:
 | 
			
		||||
 | 
			
		||||
#### 2. 交易对 (`symbol`)
 | 
			
		||||
 | 
			
		||||
- 类型:字符串
 | 
			
		||||
- 必填:是
 | 
			
		||||
- 示例:`"BTCUSDT"`
 | 
			
		||||
- 描述:要交易的货币对,如 BTC/USDT
 | 
			
		||||
 | 
			
		||||
#### 3. 订单类型 (`order_type`)
 | 
			
		||||
 | 
			
		||||
- 类型:字符串
 | 
			
		||||
- 必填:是
 | 
			
		||||
- 可选值:
 | 
			
		||||
  - `"LIMIT"`:限价单
 | 
			
		||||
  - `"MARKET"`:市价单
 | 
			
		||||
  - `"LIMIT_MAKER"`:限价做市单
 | 
			
		||||
  - `"IMMEDIATE_OR_CANCEL"`:立即成交或取消
 | 
			
		||||
  - `"FILL_OR_KILL"`:全部成交或取消
 | 
			
		||||
 | 
			
		||||
#### 4. 交易方向 (`side`)
 | 
			
		||||
 | 
			
		||||
- 类型:字符串
 | 
			
		||||
- 必填:是
 | 
			
		||||
- 可选值:
 | 
			
		||||
  - `"BUY"`:买入
 | 
			
		||||
  - `"SELL"`:卖出
 | 
			
		||||
 | 
			
		||||
#### 5. 执行日期 (`execute_dates`)
 | 
			
		||||
 | 
			
		||||
- 类型:数组
 | 
			
		||||
- 必填:是
 | 
			
		||||
- 特殊值:
 | 
			
		||||
  - `"*"`:表示每天执行
 | 
			
		||||
- 示例:
 | 
			
		||||
  - `["2023-01-01", "2023-01-15"]`:仅在指定日期执行
 | 
			
		||||
  - `["*"]`:每天执行
 | 
			
		||||
- 格式:YYYY-MM-DD
 | 
			
		||||
 | 
			
		||||
#### 6. 订单参数 (`params`)
 | 
			
		||||
 | 
			
		||||
- 类型:对象
 | 
			
		||||
- 必填:是
 | 
			
		||||
- 内容根据订单类型不同而变化:
 | 
			
		||||
 | 
			
		||||
##### 参数
 | 
			
		||||
 | 
			
		||||
**必须包含 `quantity` 或 `quoteOrderQty` 之一**
 | 
			
		||||
 | 
			
		||||
- `quantity`:交易数量(字符串格式的数字)
 | 
			
		||||
- `quoteOrderQty`:交易金额(字符串格式的数字)
 | 
			
		||||
- `price`:限价价格(字符串格式的数字),如果为空则自动获取最新价
 | 
			
		||||
 | 
			
		||||
### 配置示例
 | 
			
		||||
 | 
			
		||||
#### 示例 1:每日限价买入
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "trades": [
 | 
			
		||||
        {
 | 
			
		||||
            "symbol": "BTCUSDT",
 | 
			
		||||
            "order_type": "LIMIT",
 | 
			
		||||
            "side": "BUY",
 | 
			
		||||
            "execute_dates": ["*"],
 | 
			
		||||
            "params": {
 | 
			
		||||
                "quoteOrderQty": "100",
 | 
			
		||||
                "price": "50000"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 示例 2:特定日期市价卖出
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "trades": [
 | 
			
		||||
        {
 | 
			
		||||
            "symbol": "ETHUSDT",
 | 
			
		||||
            "order_type": "MARKET",
 | 
			
		||||
            "side": "SELL",
 | 
			
		||||
            "execute_dates": ["2023-12-25", "2023-12-31"],
 | 
			
		||||
            "params": {
 | 
			
		||||
                "quantity": "1.5"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 示例 3:多种订单组合
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "trades": [
 | 
			
		||||
        {
 | 
			
		||||
            "symbol": "BTCUSDT",
 | 
			
		||||
            "order_type": "LIMIT",
 | 
			
		||||
            "side": "BUY",
 | 
			
		||||
            "execute_dates": ["2023-12-01"],
 | 
			
		||||
            "params": {
 | 
			
		||||
                "quantity": "0.01",
 | 
			
		||||
                "price": "42000"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "symbol": "ETHUSDT",
 | 
			
		||||
            "order_type": "MARKET",
 | 
			
		||||
            "side": "SELL",
 | 
			
		||||
            "execute_dates": ["*"],
 | 
			
		||||
            "params": {
 | 
			
		||||
                "quoteOrderQty": "200"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 注意事项
 | 
			
		||||
 | 
			
		||||
1. 价格和数量都使用字符串格式而非数字,以避免浮点数精度问题
 | 
			
		||||
2. 对于 LIMIT 订单,可以只提供`quoteOrderQty`,系统会自动计算`quantity`
 | 
			
		||||
3. 当`execute_dates`包含`"*"`时,会忽略其他日期设置
 | 
			
		||||
4. 系统会自动创建`output`目录存放日志和交易记录
 | 
			
		||||
5. 所有时间均以系统时区为准
 | 
			
		||||
 | 
			
		||||
### 错误处理
 | 
			
		||||
 | 
			
		||||
如果配置文件格式错误,程序会记录错误并退出。常见错误包括:
 | 
			
		||||
 | 
			
		||||
- JSON 格式错误
 | 
			
		||||
- 缺少必填字段
 | 
			
		||||
- 日期格式不正确
 | 
			
		||||
- 订单参数不符合订单类型要求
 | 
			
		||||
 | 
			
		||||
## 输出文件
 | 
			
		||||
 | 
			
		||||
- `output/mexc_trading_bot.log`: 交易日志
 | 
			
		||||
- `output/mexc_spot_trade.csv`: 交易记录 CSV 文件
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
							
								
								
									
										267
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										267
									
								
								main.py
									
									
									
									
									
								
							@@ -5,16 +5,30 @@
 | 
			
		||||
MEXC 交易机器人
 | 
			
		||||
 | 
			
		||||
功能:
 | 
			
		||||
1. 支持多种订单类型 (LIMIT, MARKET, LIMIT_MAKER, IMMEDIATE_OR_CANCEL, FILL_OR_KILL)
 | 
			
		||||
2. 证券代码映射功能
 | 
			
		||||
3. 完整的交易记录和日志
 | 
			
		||||
1. 从JSON配置文件读取交易指令
 | 
			
		||||
2. 支持按日期执行多个交易(*表示每天执行)
 | 
			
		||||
3. 支持多种订单类型 (LIMIT, MARKET, LIMIT_MAKER, IMMEDIATE_OR_CANCEL, FILL_OR_KILL)
 | 
			
		||||
4. 当无price时自动获取实时价格作为限价
 | 
			
		||||
5. LIMIT订单支持只提供quoteOrderQty自动计算quantity
 | 
			
		||||
6. 证券代码映射功能
 | 
			
		||||
7. 完整的交易记录和日志
 | 
			
		||||
 | 
			
		||||
类说明:
 | 
			
		||||
- MexcSpotMarket: 处理市场数据查询
 | 
			
		||||
- MexcSpotTrade: 处理现货交易逻辑
 | 
			
		||||
- TradingConfig: 管理交易配置
 | 
			
		||||
 | 
			
		||||
使用示例:
 | 
			
		||||
    python main.py
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import csv
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from typing import Dict, Any, Optional, Tuple
 | 
			
		||||
import time
 | 
			
		||||
import json
 | 
			
		||||
from datetime import datetime, date
 | 
			
		||||
from typing import Dict, Any, Optional, Tuple, List
 | 
			
		||||
 | 
			
		||||
import mexc_spot_v3
 | 
			
		||||
 | 
			
		||||
@@ -33,7 +47,13 @@ logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MexcSpotMarket:
 | 
			
		||||
    """MEXC 市场数据查询类"""
 | 
			
		||||
    """MEXC 市场数据查询类
 | 
			
		||||
 | 
			
		||||
    提供获取交易对价格等功能
 | 
			
		||||
 | 
			
		||||
    方法:
 | 
			
		||||
    - get_price(symbol): 获取指定交易对的当前价格
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        """初始化市场数据查询接口"""
 | 
			
		||||
@@ -48,7 +68,11 @@ class MexcSpotMarket:
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            当前价格(浮点数)或None(如果失败)
 | 
			
		||||
 | 
			
		||||
        Raises:
 | 
			
		||||
            Exception: 当API调用失败时抛出异常
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        params = {"symbol": symbol}
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
@@ -64,13 +88,27 @@ class MexcSpotMarket:
 | 
			
		||||
            logger.info("获取价格成功: %s = %f", symbol, price)
 | 
			
		||||
            return price
 | 
			
		||||
 | 
			
		||||
        except Exception as e:  # pylint: disable=W0703
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error("查询价格失败: %s", str(e))
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MexcSpotTrade:
 | 
			
		||||
    """MEXC 现货交易类"""
 | 
			
		||||
    """MEXC 现货交易类
 | 
			
		||||
 | 
			
		||||
    处理所有现货交易逻辑,包括订单创建、状态查询和记录
 | 
			
		||||
 | 
			
		||||
    属性:
 | 
			
		||||
    - SYMBOL_MAPPING: 证券代码映射字典
 | 
			
		||||
    - ORDER_TYPE_REQUIREMENTS: 各订单类型必需参数
 | 
			
		||||
 | 
			
		||||
    方法:
 | 
			
		||||
    - trade(): 执行现货交易
 | 
			
		||||
    - _api_get_order(): 查询订单状态
 | 
			
		||||
    - _tool_map_symbol(): 映射证券代码
 | 
			
		||||
    - _tool_validate_order_params(): 验证订单参数
 | 
			
		||||
    - _tool_record_transaction(): 记录交易到CSV
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    # 证券代码映射 (API代码: CSV记录代码)
 | 
			
		||||
    SYMBOL_MAPPING = {
 | 
			
		||||
@@ -80,8 +118,12 @@ class MexcSpotTrade:
 | 
			
		||||
 | 
			
		||||
    # 订单类型与必需参数
 | 
			
		||||
    ORDER_TYPE_REQUIREMENTS = {
 | 
			
		||||
        "LIMIT": ["quantity", "price"],
 | 
			
		||||
        "MARKET": ["quantity", "quoteOrderQty"],  # 任选其一
 | 
			
		||||
        "LIMIT": [
 | 
			
		||||
            "quantity",
 | 
			
		||||
            "price",
 | 
			
		||||
            "quoteOrderQty",
 | 
			
		||||
        ],  # quantity和price或quoteOrderQty和price
 | 
			
		||||
        "MARKET": ["quantity", "quoteOrderQty"],  # quantity或quoteOrderQty
 | 
			
		||||
        "LIMIT_MAKER": ["quantity", "price"],
 | 
			
		||||
        "IMMEDIATE_OR_CANCEL": ["quantity", "price"],
 | 
			
		||||
        "FILL_OR_KILL": ["quantity", "price"],
 | 
			
		||||
@@ -90,6 +132,7 @@ class MexcSpotTrade:
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        """初始化交易机器人"""
 | 
			
		||||
        self.trader = mexc_spot_v3.mexc_trade()
 | 
			
		||||
        self.market = MexcSpotMarket()
 | 
			
		||||
        self.csv_file = "output/mexc_spot_trade.csv"
 | 
			
		||||
 | 
			
		||||
    def _api_get_order(self, symbol: str, order_id: str) -> Optional[Dict[str, Any]]:
 | 
			
		||||
@@ -103,6 +146,7 @@ class MexcSpotTrade:
 | 
			
		||||
        Returns:
 | 
			
		||||
            订单详情字典或None(如果失败)
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        params = {
 | 
			
		||||
            "symbol": symbol,
 | 
			
		||||
            "orderId": order_id,
 | 
			
		||||
@@ -113,7 +157,7 @@ class MexcSpotTrade:
 | 
			
		||||
            order = self.trader.get_order(params)
 | 
			
		||||
            logger.info("订单状态: %s", order.get("status"))
 | 
			
		||||
            return order
 | 
			
		||||
        except Exception as e:  # pylint: disable=W0703
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error("查询订单失败: %s", str(e))
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
@@ -134,11 +178,23 @@ class MexcSpotTrade:
 | 
			
		||||
        Returns:
 | 
			
		||||
            元组(是否有效, 错误信息)
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        required_params = self.ORDER_TYPE_REQUIREMENTS.get(order_type, [])
 | 
			
		||||
 | 
			
		||||
        if not required_params:
 | 
			
		||||
            return False, f"未知的订单类型: {order_type}"
 | 
			
		||||
 | 
			
		||||
        # 特殊处理LIMIT订单
 | 
			
		||||
        if order_type == "LIMIT":
 | 
			
		||||
            # 需要price和(quantity或quoteOrderQty)
 | 
			
		||||
            if "price" not in params:
 | 
			
		||||
                return False, "LIMIT订单需要price参数"
 | 
			
		||||
 | 
			
		||||
            if "quantity" not in params and "quoteOrderQty" not in params:
 | 
			
		||||
                return False, "LIMIT订单需要quantity或quoteOrderQty参数"
 | 
			
		||||
 | 
			
		||||
            return True, ""
 | 
			
		||||
 | 
			
		||||
        # 特殊处理MARKET订单
 | 
			
		||||
        if order_type == "MARKET":
 | 
			
		||||
            if "quantity" not in params and "quoteOrderQty" not in params:
 | 
			
		||||
@@ -162,6 +218,7 @@ class MexcSpotTrade:
 | 
			
		||||
        Returns:
 | 
			
		||||
            是否成功记录
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            original_symbol = order_data["symbol"]
 | 
			
		||||
            mapped_symbol = self._tool_map_symbol(original_symbol)
 | 
			
		||||
@@ -216,7 +273,7 @@ class MexcSpotTrade:
 | 
			
		||||
 | 
			
		||||
            logger.info("交易记录成功, 订单ID: %s", order_id)
 | 
			
		||||
            return True
 | 
			
		||||
        except Exception as e:  # pylint: disable=W0703
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error("记录交易失败: %s", str(e))
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
@@ -234,16 +291,52 @@ class MexcSpotTrade:
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            订单信息字典或None(如果失败)
 | 
			
		||||
 | 
			
		||||
        Raises:
 | 
			
		||||
            ValueError: 当参数验证失败时
 | 
			
		||||
            Exception: 当API调用失败时
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # 基本参数验证
 | 
			
		||||
        if side not in ["BUY", "SELL"]:
 | 
			
		||||
            logger.error("无效的交易方向: %s", side)
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        order_type = order_type.upper()
 | 
			
		||||
        processed_kwargs = kwargs.copy()
 | 
			
		||||
 | 
			
		||||
        # 处理无price的情况,获取实时价格
 | 
			
		||||
        if order_type in ["LIMIT", "LIMIT_MAKER"] and "price" not in processed_kwargs:
 | 
			
		||||
            current_price = self.market.get_price(symbol)
 | 
			
		||||
            if current_price is None:
 | 
			
		||||
                logger.error("无法获取实时价格,交易取消")
 | 
			
		||||
                return None
 | 
			
		||||
            processed_kwargs["price"] = current_price
 | 
			
		||||
            logger.info("使用实时价格作为限价: %f", current_price)
 | 
			
		||||
 | 
			
		||||
        # 处理LIMIT订单只有quoteOrderQty没有quantity的情况
 | 
			
		||||
        if (
 | 
			
		||||
            order_type in ["LIMIT", "LIMIT_MAKER"]
 | 
			
		||||
            and "quoteOrderQty" in processed_kwargs
 | 
			
		||||
            and "quantity" not in processed_kwargs
 | 
			
		||||
        ):
 | 
			
		||||
            try:
 | 
			
		||||
                quote_amount = float(processed_kwargs["quoteOrderQty"])
 | 
			
		||||
                price = float(processed_kwargs["price"])
 | 
			
		||||
                quantity = quote_amount / price
 | 
			
		||||
                processed_kwargs["quantity"] = str(quantity)
 | 
			
		||||
                logger.info("根据quoteOrderQty计算quantity: %f", quantity)
 | 
			
		||||
            except (ValueError, KeyError) as e:
 | 
			
		||||
                logger.error("计算quantity失败: %s", str(e))
 | 
			
		||||
                return None
 | 
			
		||||
 | 
			
		||||
        # 准备订单参数
 | 
			
		||||
        base_params = {"symbol": symbol, "side": side, "type": order_type, **kwargs}
 | 
			
		||||
        base_params = {
 | 
			
		||||
            "symbol": symbol,
 | 
			
		||||
            "side": side,
 | 
			
		||||
            "type": order_type,
 | 
			
		||||
            **processed_kwargs,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        # 验证参数
 | 
			
		||||
        is_valid, error_msg = self._tool_validate_order_params(order_type, base_params)
 | 
			
		||||
@@ -253,6 +346,7 @@ class MexcSpotTrade:
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            logger.info("准备下单 %s %s 订单, 交易对: %s", side, order_type, symbol)
 | 
			
		||||
            logger.debug("订单参数: %s", base_params)
 | 
			
		||||
 | 
			
		||||
            # 测试订单
 | 
			
		||||
            test_result = self.trader.post_order_test(base_params.copy())
 | 
			
		||||
@@ -275,6 +369,23 @@ class MexcSpotTrade:
 | 
			
		||||
            logger.info("订单创建成功, 订单ID: %s", order_id)
 | 
			
		||||
 | 
			
		||||
            # 查询订单详情
 | 
			
		||||
            logger.info("等待1秒后查询订单状态...")
 | 
			
		||||
            time.sleep(1)
 | 
			
		||||
            order_detail = self._api_get_order(symbol, order_id)
 | 
			
		||||
            if not order_detail:
 | 
			
		||||
                logger.error("获取订单详情失败")
 | 
			
		||||
                return None
 | 
			
		||||
 | 
			
		||||
            # 如果不是FILLED则重复查询最多10次
 | 
			
		||||
            retry_count = 0
 | 
			
		||||
            while order_detail.get("status") != "FILLED" and retry_count < 10:
 | 
			
		||||
                retry_count += 1
 | 
			
		||||
                logger.info(
 | 
			
		||||
                    "订单未完成(状态: %s),等待1秒后第%s次重试查询...",
 | 
			
		||||
                    order_detail.get("status", "UNKNOWN"),
 | 
			
		||||
                    retry_count,
 | 
			
		||||
                )
 | 
			
		||||
                time.sleep(1)
 | 
			
		||||
                order_detail = self._api_get_order(symbol, order_id)
 | 
			
		||||
                if not order_detail:
 | 
			
		||||
                    logger.error("获取订单详情失败")
 | 
			
		||||
@@ -282,32 +393,146 @@ class MexcSpotTrade:
 | 
			
		||||
 | 
			
		||||
            # 记录交易
 | 
			
		||||
            if order_detail.get("status") == "FILLED":
 | 
			
		||||
                self._tool_record_transaction(order_detail)
 | 
			
		||||
                if not self._tool_record_transaction(order_detail):
 | 
			
		||||
                    logger.error("交易记录失败")
 | 
			
		||||
            else:
 | 
			
		||||
                logger.warning(
 | 
			
		||||
                    "订单未完成(状态: %s),未被记录到CSV。订单ID: %s",
 | 
			
		||||
                    order_detail.get("status", "UNKNOWN"),
 | 
			
		||||
                    order_id,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            return order_detail
 | 
			
		||||
 | 
			
		||||
        except Exception as e:  # pylint: disable=W0703
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error("交易执行失败: %s", str(e))
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TradingConfig:
 | 
			
		||||
    """交易配置管理类
 | 
			
		||||
 | 
			
		||||
    负责加载和管理交易配置
 | 
			
		||||
 | 
			
		||||
    方法:
 | 
			
		||||
    - get_today_trades(): 获取今天需要执行的交易列表
 | 
			
		||||
    - _load_config(): 加载JSON配置文件
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, config_file: str = "trading_config.json"):
 | 
			
		||||
        """
 | 
			
		||||
        初始化交易配置
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            config_file: 配置文件路径
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        self.config_file = config_file
 | 
			
		||||
        self.config_data = self._load_config()
 | 
			
		||||
 | 
			
		||||
    def _load_config(self) -> Dict[str, Any]:
 | 
			
		||||
        """加载JSON配置文件"""
 | 
			
		||||
        try:
 | 
			
		||||
            with open(self.config_file, "r", encoding="utf-8") as f:
 | 
			
		||||
                config = json.load(f)
 | 
			
		||||
                logger.info("成功加载配置文件: %s", self.config_file)
 | 
			
		||||
                return config
 | 
			
		||||
        except FileNotFoundError:
 | 
			
		||||
            logger.error("配置文件不存在: %s", self.config_file)
 | 
			
		||||
            return {}
 | 
			
		||||
        except json.JSONDecodeError:
 | 
			
		||||
            logger.error("配置文件格式错误,不是有效的JSON")
 | 
			
		||||
            return {}
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error("加载配置文件时出错: %s", str(e))
 | 
			
		||||
            return {}
 | 
			
		||||
 | 
			
		||||
    def get_today_trades(self) -> List[Dict[str, Any]]:
 | 
			
		||||
        """
 | 
			
		||||
        获取今天需要执行的交易列表
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            今天需要执行的交易配置列表
 | 
			
		||||
 | 
			
		||||
        Example:
 | 
			
		||||
            >>> config = TradingConfig()
 | 
			
		||||
            >>> today_trades = config.get_today_trades()
 | 
			
		||||
            >>> print(len(today_trades))
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        if not self.config_data or "trades" not in self.config_data:
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
        today = date.today().isoformat()
 | 
			
		||||
        today_trades = []
 | 
			
		||||
 | 
			
		||||
        for trade in self.config_data["trades"]:
 | 
			
		||||
            # 检查交易是否包含执行日期
 | 
			
		||||
            if "execute_dates" not in trade:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            # 检查是否设置了每天执行(*)
 | 
			
		||||
            if "*" in trade["execute_dates"]:
 | 
			
		||||
                today_trades.append(trade)
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            # 检查今天是否在执行日期列表中
 | 
			
		||||
            if today in trade["execute_dates"]:
 | 
			
		||||
                today_trades.append(trade)
 | 
			
		||||
 | 
			
		||||
        return today_trades
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    """主函数"""
 | 
			
		||||
 | 
			
		||||
    # 初始化交易配置
 | 
			
		||||
    config = TradingConfig()
 | 
			
		||||
 | 
			
		||||
    # 获取今天需要执行的交易
 | 
			
		||||
    today_trades = config.get_today_trades()
 | 
			
		||||
 | 
			
		||||
    if not today_trades:
 | 
			
		||||
        logger.info("今天没有需要执行的交易")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    logger.info("今天有 %d 个交易需要执行", len(today_trades))
 | 
			
		||||
 | 
			
		||||
    # 初始化交易类
 | 
			
		||||
    spot_trader = MexcSpotTrade()
 | 
			
		||||
 | 
			
		||||
    # 执行每个交易
 | 
			
		||||
    for trade_config in today_trades:
 | 
			
		||||
        try:
 | 
			
		||||
            # 提取交易参数
 | 
			
		||||
            symbol = trade_config.get("symbol")
 | 
			
		||||
            order_type = trade_config.get("order_type")
 | 
			
		||||
            side = trade_config.get("side")
 | 
			
		||||
            params = trade_config.get("params", {})
 | 
			
		||||
 | 
			
		||||
            if not all([symbol, order_type, side]):
 | 
			
		||||
                logger.error("交易配置缺少必要参数: %s", trade_config)
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            logger.info("执行交易: %s %s %s", symbol, order_type, side)
 | 
			
		||||
            logger.debug("交易参数: %s", params)
 | 
			
		||||
 | 
			
		||||
            # 执行交易
 | 
			
		||||
            result = spot_trader.trade(
 | 
			
		||||
        symbol="BTCUSDC",
 | 
			
		||||
        order_type="MARKET",
 | 
			
		||||
        side="BUY",
 | 
			
		||||
        quoteOrderQty="1.5",
 | 
			
		||||
                symbol=symbol, order_type=order_type, side=side, **params
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            if result:
 | 
			
		||||
        logger.info("交易执行成功")
 | 
			
		||||
        logger.info("订单详情: %s", result)
 | 
			
		||||
                logger.info("交易执行成功: %s", result)
 | 
			
		||||
            else:
 | 
			
		||||
                logger.error("交易执行失败")
 | 
			
		||||
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error("执行交易时出错: %s", str(e))
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
    logger.info("执行完毕")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    main()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user