OdysseyArena / TradeEnv /batch_process_trade.py
beatccjiang's picture
Upload project files to Hugging Face Spaces
4269996 verified
Raw
History Blame Contribute Delete
12 kB
"""
批量处理 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
)