experiment(rotation): 同大类扩充与纳指vs标普替换对比实验

技术修复:
- SOCKS5代理IPv6问题:socks5:// → socks5h:// (hybrid_source.py, yfinance_source.py)

目录整理:
- scripts/ → 仅保留策略入口(daily_scheduler, run_rotation, run_cci_screener)
- 实验脚本移至 tests/experiments/
- 工具脚本移至 tests/utils/
- 实验记录新增 docs/experiments/
- results/ 添加到 gitignore

实验结果:

实验001 - 同大类扩充(添加标普500):
├─ 累计收益: 1467.35% → 1176.26% (-291%)
├─ CAGR: 48.10% → 43.82% (-4.28%)
├─ 调仓次数: 459 → 501 (+42次)
└─ 结论: 添加同大类标的不增加跨类分散,反而侵蚀收益

实验002 - 纳指vs标普替换对比:
├─ 累计收益: 1467.35% → 1118.77% (-348%)
├─ CAGR: 48.10% → 42.87% (-5.22%)
├─ Sharpe: 2.21 → 2.08 (-0.13)
├─ MaxDD: -17.33% → -15.14% (+2.18%)
└─ 结论: 纳指100优于标普500,成长风格更适合动量策略

策略建议:
- 保持纳指100作为美股大类代表
- 不添加同大类新标的(避免类内切换成本)
- 新增标的应优先考虑新大类(增加跨类分散)
This commit is contained in:
2026-05-06 20:43:38 +08:00
parent a4e8a6050e
commit 6b59855c28
20 changed files with 1086 additions and 2 deletions

View File

@@ -0,0 +1,137 @@
"""
导出轮动策略回测所用的原始数据到本地文件夹
导出内容:
1. index_data.csv - 指数价格数据(宽格式,用于因子计算)
2. etf_data.csv - ETF价格数据宽格式用于收益计算
3. etf_nav_data.csv - ETF净值数据宽格式用于溢价率计算
4. benchmark_data.csv - 基准数据
5. config_snapshot.yaml - 当时使用的策略配置快照
"""
import sys
import os
import time
import shutil
from datetime import datetime
from pathlib import Path
# 添加项目根目录
sys.path.insert(0, str(Path(__file__).parent.parent))
import yaml
import pandas as pd
from dotenv import load_dotenv
load_dotenv()
from strategies.rotation.engine import RotationStrategy
from config.settings import DEFAULT_BENCHMARK_CODE
def main():
# 加载配置
config_path = Path(__file__).parent.parent / "config" / "strategies" / "rotation.yaml"
with open(config_path, "r", encoding="utf-8") as f:
config = yaml.safe_load(f)
# 如果未设置 end_date默认使用今天
if not config.get("end_date"):
config["end_date"] = datetime.now().strftime("%Y-%m-%d")
start_date = config["start_date"]
end_date = config["end_date"]
print("=" * 60)
print(" 导出轮动策略回测数据")
print("=" * 60)
print(f" 回测区间: {start_date} ~ {end_date}")
print(f" 候选标的: {len(config.get('code_list', {}))}")
# 创建输出目录
export_dir = Path(__file__).parent.parent / "data" / "rotation_backtest_data"
export_dir.mkdir(parents=True, exist_ok=True)
print(f" 输出目录: {export_dir}")
# 创建策略实例(仅用于获取数据)
strategy = RotationStrategy(config)
# 获取数据
print("\n" + "=" * 60)
print("开始下载数据...")
print("=" * 60)
benchmark_code = config.get("benchmark", {}).get("code", DEFAULT_BENCHMARK_CODE)
code_config = config.get("code_list", {})
with strategy.data_source:
index_data, etf_data, etf_nav_data, benchmark_data, valid_codes = (
strategy.data_source.fetch_all(
code_config, benchmark_code, start_date, end_date
)
)
# 保存数据
print("\n" + "=" * 60)
print("保存数据到本地...")
print("=" * 60)
saved_files = []
# 1. 指数价格数据
if index_data is not None:
path = export_dir / "index_data.csv"
index_data.to_csv(path)
saved_files.append(("index_data.csv", index_data.shape, "指数价格(因子计算用)"))
print(f" ✓ index_data.csv: {index_data.shape[0]}× {index_data.shape[1]}")
# 2. ETF价格数据
if etf_data is not None:
path = export_dir / "etf_data.csv"
etf_data.to_csv(path)
saved_files.append(("etf_data.csv", etf_data.shape, "ETF价格收益计算用"))
print(f" ✓ etf_data.csv: {etf_data.shape[0]}× {etf_data.shape[1]}")
# 3. ETF净值数据
if etf_nav_data is not None:
path = export_dir / "etf_nav_data.csv"
etf_nav_data.to_csv(path)
saved_files.append(("etf_nav_data.csv", etf_nav_data.shape, "ETF净值溢价率计算用"))
print(f" ✓ etf_nav_data.csv: {etf_nav_data.shape[0]}× {etf_nav_data.shape[1]}")
# 4. 基准数据
if benchmark_data is not None:
path = export_dir / "benchmark_data.csv"
benchmark_data.to_csv(path)
saved_files.append(("benchmark_data.csv", benchmark_data.shape, "基准指数"))
print(f" ✓ benchmark_data.csv: {benchmark_data.shape[0]}")
# 5. 有效代码列表
codes_path = export_dir / "valid_codes.txt"
with open(codes_path, "w") as f:
for code in valid_codes:
name = code_config.get(code, {}).get("name", code)
etf = code_config.get(code, {}).get("etf", "")
market = code_config.get(code, {}).get("market", "")
f.write(f"{code}\t{name}\t{etf or '-'}\t{market}\n")
print(f" ✓ valid_codes.txt: {len(valid_codes)} 只有效标的")
# 6. 策略配置快照
config_snapshot_path = export_dir / "config_snapshot.yaml"
shutil.copy2(config_path, config_snapshot_path)
print(f" ✓ config_snapshot.yaml: 策略配置快照")
# 汇总
print("\n" + "=" * 60)
print("导出完成!")
print("=" * 60)
print(f" 目录: {export_dir}")
print(f" 文件数: {len(saved_files) + 2}")
print(f" 数据区间: {start_date} ~ {end_date}")
print(f" 有效标的: {len(valid_codes)}")
for fname, shape, desc in saved_files:
print(f" - {fname}: {shape} ({desc})")
if __name__ == "__main__":
main()