注释加密货币(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天)
This commit is contained in:
137
scripts/export_rotation_data.py
Normal file
137
scripts/export_rotation_data.py
Normal 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()
|
||||
Reference in New Issue
Block a user