Files
etf/scripts/export_rotation_data.py
aszerW e301a08724 注释加密货币(BTC/ETH)并新增回测数据导出脚本
变更内容:
- rotation.yaml: 注释掉BTC和ETH加密货币配置,候选池从22只缩减为20只
- 新增 scripts/export_rotation_data.py: 导出回测原始数据到本地文件夹

回测结果 (2020-02-14 ~ 2026-04-28, 20只标的, 无加密货币):
- 累计收益: 171.36% (基准沪深300: 20.16%)
- CAGR(年化): 17.48% (基准: 2.89%)
- 年化夏普比率: 0.90 (基准: 0.26)
- 最大回撤: -30.85% (基准: -45.60%)
- Calmar比率: 0.57
- 日胜率: 52.49%
- 调仓次数: 478次 (年均80次, 平均持仓3.1天)
2026-04-28 20:28:35 +08:00

138 lines
4.6 KiB
Python
Raw 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.

"""
导出轮动策略回测所用的原始数据到本地文件夹
导出内容:
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()