""" 批量处理 Trade 测试配置文件 自动运行所有测试配置并保存结果 """ import os import json import sys from datetime import datetime from typing import Dict, List import glob # 添加当前目录到路径 current_dir = os.path.dirname(os.path.abspath(__file__)) parent_dir = os.path.dirname(current_dir) if parent_dir not in sys.path: sys.path.insert(0, parent_dir) # 直接导入同目录下的模块 from auto_trade_solver import run_auto_trade import progress_manager def find_all_trade_configs(test_data_dir: str) -> List[str]: """查找所有交易测试配置文件""" pattern = os.path.join(test_data_dir, "test_trade_config_*.json") config_files = sorted(glob.glob(pattern), key=lambda x: int(os.path.basename(x).split('_')[-1].split('.')[0])) return config_files def process_all_configs(test_data_dir: str, output_dir: str = "output", user_id: str = "batch_auto", save_dir: str = "user_progress", verbose: bool = False) -> Dict: """ 批量处理所有配置文件 Args: test_data_dir: 测试数据目录 output_dir: 结果输出目录(相对于脚本所在目录) user_id: 用户ID(用于保存进度) save_dir: 进度保存目录 verbose: 是否打印详细信息 Returns: 处理结果汇总 """ # 获取脚本所在目录 script_dir = os.path.dirname(os.path.abspath(__file__)) # 构建测试数据目录路径(相对于脚本目录) if not os.path.isabs(test_data_dir): test_data_dir = os.path.join(script_dir, test_data_dir) # 构建输出目录路径(相对于脚本目录) if not os.path.isabs(output_dir): output_dir = os.path.join(script_dir, output_dir) # 构建保存目录路径(相对于脚本目录) if not os.path.isabs(save_dir): save_dir = os.path.join(script_dir, save_dir) # 创建输出目录 os.makedirs(output_dir, exist_ok=True) # 查找所有配置文件 config_files = find_all_trade_configs(test_data_dir) if not config_files: print(f"❌ 未找到任何配置文件在 {test_data_dir}") return {} print(f"📊 找到 {len(config_files)} 个配置文件") print("=" * 60) # 用于存储标准格式的环境数据 environments_data = {} results = [] total_profit_ratio = 0.0 successful_runs = 0 failed_runs = 0 for idx, config_path in enumerate(config_files, 1): config_name = os.path.basename(config_path) env_idx = idx - 1 # 环境索引从0开始 print(f"\n[{idx}/{len(config_files)}] 处理: {config_name}") print("-" * 60) try: # 读取配置获取基本信息 with open(config_path, 'r') as f: cfg = json.load(f) num_stocks = len(cfg.get('stocks', [])) num_factors = len(cfg.get('variables', [])) num_days = cfg.get('num_days', 0) initial_cash = cfg.get('initial_cash', 0) print(f" 股票数: {num_stocks}, 因子数: {num_factors}, 天数: {num_days}") # 运行自动交易(限制为120天) result = run_auto_trade( config_path, verbose=verbose, user_id=user_id, save_dir=save_dir, env_idx=env_idx, load_progress=False, # 批量处理时从头开始 save_interval=0, # 批量处理时不自动保存(最后统一保存) max_days=120 # 限制为120天 ) # 从进度文件中读取完整的环境信息 env_progress = progress_manager.get_task_environment_progress( user_id, save_dir, "trade", env_idx ) if env_progress: # 按照标准格式组织环境数据 env_key = str(env_idx) environments_data[env_key] = { "user_id": env_progress.get("user_id", user_id), "env_idx": env_progress.get("env_idx", env_idx), "env_idx_display": env_progress.get("env_idx_display", env_idx + 1), "day": env_progress.get("day", result.get('total_days', 0)), "cash": env_progress.get("cash", 0), "positions": env_progress.get("positions", []), "prices": env_progress.get("prices", []), "variables_state": env_progress.get("variables_state", []), "history": env_progress.get("history", []), "num_steps": env_progress.get("num_steps", result.get('total_days', 0)), "done": env_progress.get("done", False), "success": env_progress.get("success", False) } else: # 如果无法读取进度文件,使用基本信息 env_key = str(env_idx) environments_data[env_key] = { "user_id": user_id, "env_idx": env_idx, "env_idx_display": env_idx + 1, "day": result.get('total_days', 0), "cash": 0, "positions": [], "prices": [], "variables_state": [], "history": [], "num_steps": result.get('total_days', 0), "done": False, "success": False } # 记录结果(用于汇总报告) result_summary = { "config_file": config_name, "env_idx": env_idx, "num_stocks": num_stocks, "num_factors": num_factors, "num_days": num_days, "initial_cash": initial_cash, "initial_value": result.get('initial_value', 0), "final_value": result.get('final_value', 0), "profit": result.get('profit', 0), "profit_ratio": result.get('profit_ratio', 0), "total_days": result.get('total_days', 0), "observation_days": result.get('observation_days', 0), "success": True, "error": None } results.append(result_summary) total_profit_ratio += result.get('profit_ratio', 0) successful_runs += 1 print(f" ✅ 完成: 收益率 {result.get('profit_ratio', 0):.2f}%") except Exception as e: print(f" ❌ 失败: {str(e)}") failed_runs += 1 result_summary = { "config_file": config_name, "env_idx": env_idx, "success": False, "error": str(e) } results.append(result_summary) # 生成标准格式的输出(参考 lqk.json 格式) standard_output = { "trade": { "environments": environments_data } } # 生成汇总报告(保留原有格式用于兼容) summary = { "timestamp": datetime.now().isoformat(), "total_configs": len(config_files), "successful_runs": successful_runs, "failed_runs": failed_runs, "average_profit_ratio": total_profit_ratio / successful_runs if successful_runs > 0 else 0, "results": results } # 保存标准格式结果到JSON文件 output_file = os.path.join(output_dir, f"batch_trade_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json") with open(output_file, 'w', encoding='utf-8') as f: json.dump(standard_output, f, ensure_ascii=False, indent=2) # 同时保存汇总报告(可选,用于兼容) summary_file = os.path.join(output_dir, f"batch_trade_summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json") with open(summary_file, 'w', encoding='utf-8') as f: json.dump(summary, f, ensure_ascii=False, indent=2) # 生成文本报告 report_file = os.path.join(output_dir, f"batch_trade_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt") with open(report_file, 'w', encoding='utf-8') as f: f.write("=" * 60 + "\n") f.write("批量交易测试结果报告\n") f.write("=" * 60 + "\n\n") f.write(f"生成时间: {summary['timestamp']}\n") f.write(f"总配置文件数: {summary['total_configs']}\n") f.write(f"成功运行: {summary['successful_runs']}\n") f.write(f"失败运行: {summary['failed_runs']}\n") f.write(f"平均收益率: {summary['average_profit_ratio']:.2f}%\n\n") f.write("-" * 60 + "\n") f.write("详细结果:\n") f.write("-" * 60 + "\n\n") for result in results: f.write(f"配置文件: {result['config_file']}\n") if result.get('success'): f.write(f" 环境索引: {result.get('env_idx', 'N/A')}\n") f.write(f" 股票数: {result.get('num_stocks', 'N/A')}, 因子数: {result.get('num_factors', 'N/A')}\n") f.write(f" 初始价值: {result.get('initial_value', 0):.2f}\n") f.write(f" 最终价值: {result.get('final_value', 0):.2f}\n") f.write(f" 总收益: {result.get('profit', 0):.2f}\n") f.write(f" 收益率: {result.get('profit_ratio', 0):.2f}%\n") f.write(f" 运行天数: {result.get('total_days', 0)}\n") else: f.write(f" ❌ 失败: {result.get('error', 'Unknown error')}\n") f.write("\n") # 收益率排名 successful_results = [r for r in results if r.get('success')] if successful_results: f.write("-" * 60 + "\n") f.write("收益率排名 (Top 10):\n") f.write("-" * 60 + "\n\n") sorted_results = sorted(successful_results, key=lambda x: x.get('profit_ratio', 0), reverse=True) for i, result in enumerate(sorted_results[:10], 1): f.write(f"{i}. {result['config_file']}: {result.get('profit_ratio', 0):.2f}%\n") print("\n" + "=" * 60) print("📊 批量处理完成!") print("=" * 60) print(f"总配置文件数: {len(config_files)}") print(f"成功运行: {successful_runs}") print(f"失败运行: {failed_runs}") if successful_runs > 0: print(f"平均收益率: {total_profit_ratio / successful_runs:.2f}%") print(f"\n结果已保存到:") print(f" 标准格式 JSON: {output_file}") print(f" 汇总报告 JSON: {summary_file}") print(f" 文本报告: {report_file}") return standard_output if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(description="批量处理 Trade 测试配置文件") parser.add_argument("--test_data_dir", type=str, default="../test_data/trade", help="测试数据目录路径") parser.add_argument("--output_dir", type=str, default="output", help="结果输出目录") parser.add_argument("--user_id", type=str, default="batch_auto", help="用户ID(用于保存进度)") parser.add_argument("--save_dir", type=str, default="user_progress", help="进度保存目录") parser.add_argument("--verbose", action="store_true", help="打印详细信息") args = parser.parse_args() # 处理所有配置 summary = process_all_configs( test_data_dir=args.test_data_dir, output_dir=args.output_dir, user_id=args.user_id, save_dir=args.save_dir, verbose=args.verbose )