diff --git a/.gitignore b/.gitignore index fbb00e1..5715b4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ output/ public/ +config/ __pycache__/ \ No newline at end of file diff --git a/README.md b/README.md index 97b1036..50716b9 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ MEXC 交易机器人是一个自动化交易工具,用于在 MEXC 交易所执 ## 安装与使用 -1. 创建配置文件`trading_config.json` +1. 创建配置文件`config/trading_config.json`(可有多个不同名字的`.json`文件,程序会自动遍历所有) 2. 运行程序: ```bash @@ -31,6 +31,11 @@ MEXC 交易机器人是一个自动化交易工具,用于在 MEXC 交易所执 ```json { + "api": { + "mexc_host": "https://api.mexc.com", + "api_key": "mxxxxxxxxxx", + "secret_key": "xxxxxxxxxxxxxxxxx" + }, "symbol_mapping": { "BTCUSDC": "BTCUSDT" }, @@ -51,12 +56,14 @@ MEXC 交易机器人是一个自动化交易工具,用于在 MEXC 交易所执 ### 配置字段详解 -#### 1. 证券代码映射 (`symbol_mapping`) +#### 1. API 配置 (`api`) + +#### 2. 证券代码映射 (`symbol_mapping`) - 类型:数组 -- 描述:API代码: CSV记录代码,如`"BTCUSDC": "BTCUSDT"`代表向API请求`BTCUSDC`,但CSV中记录证券代码`BTCUSDT` +- 描述:API 代码: CSV 记录代码,如`"BTCUSDC": "BTCUSDT"`代表向 API 请求`BTCUSDC`,但 CSV 中记录证券代码`BTCUSDT` -#### 2. 交易列表 (`trades`) +#### 3. 交易列表 (`trades`) - 类型:数组 - 描述:包含所有交易指令的列表 diff --git a/config.py b/config.py deleted file mode 100644 index e0647bf..0000000 --- a/config.py +++ /dev/null @@ -1,3 +0,0 @@ -mexc_host = "https://api.mexc.com" -api_key = "mx0vglky5BuzlcK5HQ" -secret_key = "e2a0c4737b4643bbac4ad5f8b26dcce2" diff --git a/generate_calendar.py b/generate_calendar.py index 97007f7..b7aa6af 100644 --- a/generate_calendar.py +++ b/generate_calendar.py @@ -13,7 +13,7 @@ logging.basicConfig( logger = logging.getLogger(__name__) -def load_config(config_path="trading_config.json"): +def load_config(config_path="config/trading_config.json"): """加载交易配置文件。 Args: @@ -100,7 +100,9 @@ def generate_ics(config): date_obj = datetime.strptime(date_str, "%Y-%m-%d").date() event = create_event(symbol, comment, date_obj, description) cal.add_component(event) - logger.info("已创建项目:%s 交易 (%s) %s", symbol, comment, date_str) + logger.info( + "已创建项目:%s 交易 (%s) %s", symbol, comment, date_str + ) except ValueError as ex: logger.warning("跳过无效日期 %s: %s", date_str, ex) @@ -121,13 +123,33 @@ def save_ics(calendar, output_path="public/trading.ics"): def main(): - """主执行函数。""" + """主执行函数,遍历config目录下的所有json文件并生成对应的ics文件。""" + try: - config = load_config() - calendar = generate_ics(config) - save_ics(calendar) - except Exception as ex: # pylint: disable=broad-except - logger.error("生成失败: %s", ex, exc_info=True) + # 遍历config目录下的所有json文件 + for filename in os.listdir("config"): + if filename.endswith(".json"): + config_path = os.path.join("config", filename) + try: + # 加载配置 + config = load_config(config_path) + + # 生成ics文件名(保留原文件名,只改扩展名) + ics_filename = os.path.splitext(filename)[0] + ".ics" + ics_path = os.path.join("public", ics_filename) + + # 生成并保存ics文件 + calendar = generate_ics(config) + save_ics(calendar, ics_path) + + logger.info("成功生成: %s → %s", config_path, ics_path) + except Exception as ex: + logger.error("处理文件 %s 失败: %s", config_path, ex, exc_info=True) + continue # 继续处理下一个文件 + + logger.info("所有文件处理完成") + except Exception as ex: + logger.error("程序执行失败: %s", ex, exc_info=True) raise diff --git a/main.py b/main.py index 3e51403..a377ded 100644 --- a/main.py +++ b/main.py @@ -55,9 +55,9 @@ class MexcSpotMarket: - get_price(symbol): 获取指定交易对的当前价格 """ - def __init__(self): + def __init__(self, config): """初始化市场数据查询接口""" - self.market = mexc_spot_v3.mexc_market() + self.market = mexc_spot_v3.mexc_market(config) def get_price(self, symbol: str) -> Optional[float]: """ @@ -123,12 +123,12 @@ class MexcSpotTrade: "FILL_OR_KILL": ["quantity", "price"], } - def __init__(self): + def __init__(self, config, symbol_mapping): """初始化交易机器人""" - self.trader = mexc_spot_v3.mexc_trade() - self.market = MexcSpotMarket() + self.trader = mexc_spot_v3.mexc_trade(config) + self.market = MexcSpotMarket(config) self.csv_file = "output/mexc_spot_trade.csv" - self.symbol_mapping = TradingConfig().config_data.get("symbol_mapping", {}) + self.symbol_mapping = symbol_mapping def _api_get_order(self, symbol: str, order_id: str) -> Optional[Dict[str, Any]]: """ @@ -414,7 +414,7 @@ class TradingConfig: - _load_config(): 加载JSON配置文件 """ - def __init__(self, config_file: str = "public/trading_config.json"): + def __init__(self, config_file: str = "config/trading_config.json"): """ 初始化交易配置 @@ -481,52 +481,74 @@ class TradingConfig: def main(): """主函数""" - # 初始化交易配置 - config = TradingConfig() - - # 获取今天需要执行的交易 - today_trades = config.get_today_trades() - - if not today_trades: - logger.info("今天没有需要执行的交易") + # 确保config目录存在 + if not os.path.exists("config"): + logger.error("配置目录 config 不存在") return - logger.info("今天有 %d 个交易需要执行", len(today_trades)) + # 获取config目录下所有json文件 + config_files = list(Path("config").glob("*.json")) - # 初始化交易类 - spot_trader = MexcSpotTrade() + if not config_files: + logger.info("配置目录中没有找到任何JSON文件") + return - # 执行每个交易 - for trade_config in today_trades: + logger.info("找到 %d 个配置文件需要处理", len(config_files)) + + for config_file in config_files: try: # 提取交易参数 - symbol = trade_config.get("symbol") - order_type = trade_config.get("order_type") - side = trade_config.get("side") - params = trade_config.get("params", {}) + logger.info("处理配置文件: %s", config_file) + config = TradingConfig(str(config_file)) + spot_trader = MexcSpotTrade( + config.config_data.get("api", {}), + config.config_data.get("symbol_mapping", {}), + ) + today_trades = config.get_today_trades() - if not all([symbol, order_type, side]): - logger.error("交易配置缺少必要参数: %s", trade_config) + if not today_trades: + logger.info("%s - 今天没有需要执行的交易", config_file) continue - logger.info("执行交易: %s %s %s", symbol, order_type, side) - logger.debug("交易参数: %s", params) + logger.info("%s - 今天有 %d 个交易需要执行", config_file, len(today_trades)) - # 执行交易 - result = spot_trader.trade( - symbol=symbol, order_type=order_type, side=side, **params - ) + 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 result: - logger.info("交易执行成功: %s", result) - else: - logger.error("交易执行失败") + if not all([symbol, order_type, side]): + logger.error( + "%s - 交易配置缺少必要参数: %s", config_file, trade_config + ) + continue + + logger.info( + "%s - 执行交易: %s %s %s", config_file, symbol, order_type, side + ) + logger.debug("%s - 交易参数: %s", config_file, params) + + # 执行交易 + result = spot_trader.trade( + symbol=symbol, order_type=order_type, side=side, **params + ) + + if result: + logger.info("%s - 交易执行成功: %s", config_file, result) + else: + logger.error("%s - 交易执行失败", config_file) + + except Exception as e: + logger.error("%s - 执行交易时出错: %s", config_file, str(e)) + continue except Exception as e: - logger.error("执行交易时出错: %s", str(e)) + logger.error("处理配置文件 %s 时出错: %s", config_file, str(e)) continue - logger.info("执行完毕") + logger.info("所有配置文件处理完毕") if __name__ == "__main__": diff --git a/mexc_spot_v3.py b/mexc_spot_v3.py index 77a751b..c6cbbc8 100644 --- a/mexc_spot_v3.py +++ b/mexc_spot_v3.py @@ -2,7 +2,6 @@ import requests import hmac import hashlib from urllib.parse import urlencode, quote -import config # ServerTime、Signature class TOOL(object): @@ -42,9 +41,9 @@ class TOOL(object): # Market Data class mexc_market(TOOL): - def __init__(self): + def __init__(self, config): self.api = '/api/v3' - self.hosts = config.mexc_host + self.hosts = config["mexc_host"] self.method = 'GET' def get_ping(self): @@ -129,11 +128,11 @@ class mexc_market(TOOL): # Spot Trade class mexc_trade(TOOL): - def __init__(self): + def __init__(self, config): self.api = '/api/v3' - self.hosts = config.mexc_host - self.mexc_key = config.api_key - self.mexc_secret = config.secret_key + self.hosts = config["mexc_host"] + self.mexc_key = config["api_key"] + self.mexc_secret = config["secret_key"] def get_selfSymbols(self): """get currency information""" @@ -246,11 +245,11 @@ class mexc_trade(TOOL): # Wallet class mexc_wallet(TOOL): - def __init__(self): + def __init__(self, config): self.api = '/api/v3/capital' - self.hosts = config.mexc_host - self.mexc_key = config.api_key - self.mexc_secret = config.secret_key + self.hosts = config["mexc_host"] + self.mexc_key = config["api_key"] + self.mexc_secret = config["secret_key"] def get_coinlist(self): """get currency information""" @@ -368,11 +367,11 @@ class mexc_wallet(TOOL): # Sub-Account class mexc_subaccount(TOOL): - def __init__(self): + def __init__(self, config): self.api = '/api/v3' - self.hosts = config.mexc_host - self.mexc_key = config.api_key - self.mexc_secret = config.secret_key + 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""" @@ -427,11 +426,11 @@ class mexc_subaccount(TOOL): # Rebate class mexc_rebate(TOOL): - def __init__(self): + def __init__(self, config): self.api = '/api/v3/rebate' - self.hosts = config.mexc_host - self.mexc_key = config.api_key - self.mexc_secret = config.secret_key + 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""" @@ -500,11 +499,11 @@ class mexc_rebate(TOOL): # WebSocket ListenKey class mexc_listenkey(TOOL): - def __init__(self): + def __init__(self, config): self.api = '/api/v3' - self.hosts = config.mexc_host - self.mexc_key = config.api_key - self.mexc_secret = config.secret_key + self.hosts = config["mexc_host"] + self.mexc_key = config["api_key"] + self.mexc_secret = config["secret_key"] def post_listenKey(self): """ generate ListenKey """