Files
etf/archive/legacy_scripts/scripts/run_rotation.py
aszerW 1fca536c95 refactor: 归档旧代码,保留新框架结构
归档内容:
- core/ (数据源、因子计算、通用工具) → archive/legacy_core/
- strategies/rotation/engine.py, portfolio.py, report.py → archive/legacy_core/
- scripts/ (run_rotation, daily_scheduler) → archive/legacy_scripts/
- examples/ → archive/legacy_examples/
- tests/ (实验、对比测试) → archive/legacy_tests/
- 单独文件 (fetch_*.py, 动量.py, 全球市场.py等) → archive/single_files/

保留新结构:
- framework/ (抽象接口)
- strategies/shared/ (定制组件)
- strategies/rotation/strategy.py (新策略)
- 外层配置: .env, .dockerignore, build-and-push.sh, hk_ecs.pem, README.md, requirements.txt
- Docker相关: Dockerfile, Dockerfile_base, docker-compose.yml

更新README反映新框架架构
2026-05-11 23:34:23 +08:00

152 lines
4.7 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
ETF轮动策略回测入口
用法:
python scripts/run_rotation.py
python scripts/run_rotation.py --config config/strategies/rotation.yaml
"""
import sys
import time
import yaml
import argparse
from pathlib import Path
# 添加项目根目录到路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
from strategies.rotation.engine import RotationStrategy
from strategies.rotation.portfolio import track_positions, save_trades
from strategies.rotation.report import generate_performance_report
from config.settings import DEFAULT_CODE_NAME_MAP, DEFAULT_BENCHMARK_NAME
def load_config(config_path: str) -> dict:
"""加载配置文件"""
with open(config_path, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
def main():
parser = argparse.ArgumentParser(description="ETF轮动策略回测")
parser.add_argument(
"--config",
type=str,
default="config/strategies/rotation.yaml",
help="配置文件路径",
)
parser.add_argument(
"--save-path",
type=str,
default="results/report",
help="报告保存路径前缀",
)
args = parser.parse_args()
start_time = time.time()
print("=" * 60)
print(" ETF轮动策略 回测系统")
print("=" * 60)
# 加载配置
config = load_config(args.config)
# 如果未设置 end_date默认使用最新日期
if not config.get('end_date'):
from datetime import datetime
config['end_date'] = datetime.now().strftime('%Y-%m-%d')
# 从配置中读取 code_list新的配置格式{代码: {name, etf, market}}
code_list_config = config.get('code_list', {})
# 提取代码列表和名称映射
if isinstance(code_list_config, dict):
code_list = list(code_list_config.keys())
# 构建 code_name_map: {代码: 名称}
code_name_map = {}
for code, cfg in code_list_config.items():
if isinstance(cfg, dict):
code_name_map[code] = cfg.get('name', code)
else:
# 兼容旧格式
code_name_map[code] = cfg
else:
# 兼容旧格式(列表)
code_list = code_list_config
code_name_map = DEFAULT_CODE_NAME_MAP
code_list_config = {}
benchmark_config = config.get('benchmark', {})
benchmark_name = benchmark_config.get('name', DEFAULT_BENCHMARK_NAME)
print(f"\n配置文件: {args.config}")
print(f"候选标的: {len(code_list)}")
# 统计ETF映射情况
etf_count = sum(1 for cfg in code_list_config.values() if isinstance(cfg, dict) and cfg.get('etf'))
crypto_count = sum(1 for cfg in code_list_config.values() if isinstance(cfg, dict) and cfg.get('market') == 'CRYPTO')
print(f" - ETF映射: {etf_count}")
print(f" - 直接交易: {crypto_count} 只(加密货币)")
print(f"回测区间: {config['start_date']} ~ {config['end_date']}")
print(f"因子类型: {config['factor_type']}")
print(f"窗口天数: {config['n_days']}")
print(f"选中数量: {config['select_num']}")
print(f"调仓周期: {config['rebalance_days']}")
print(f"交易成本: {config['trade_cost']:.2%}")
# 保持 config 中的 code_list 为完整配置格式(用于引擎内部解析)
# 不需要修改 config['code_list'],引擎会直接使用原始配置
# 创建策略实例
strategy = RotationStrategy(config)
# 运行回测
print("\n" + "=" * 60)
print("开始回测...")
print("=" * 60)
backtest_result = strategy.run()
# 持仓跟踪
print("\n" + "=" * 60)
print("持仓跟踪...")
print("=" * 60)
trades_df, summary_df = track_positions(
backtest_result,
code_name_map=code_name_map,
select_num=config["select_num"],
)
save_trades(trades_df, summary_df, save_path=args.save_path)
# 生成绩效报告
print("\n" + "=" * 60)
print("生成绩效报告...")
print("=" * 60)
metrics = generate_performance_report(
backtest_result,
strategy.valid_codes,
code_name_map=code_name_map,
benchmark_name=benchmark_name,
save_path=args.save_path,
select_num=config["select_num"],
code_config=code_list_config, # 传入完整配置以显示ETF映射
index_data=strategy.index_data, # 传入指数数据
etf_price_data=strategy.etf_data, # 传入ETF价格数据
etf_nav_data_raw=strategy.etf_nav_data, # 传入ETF净值数据
)
elapsed = time.time() - start_time
print(f"\n总耗时: {elapsed:.1f}")
return metrics
if __name__ == "__main__":
main()