451ffa33d2
clean(rotation): add simple rotation strategy and remove unused files
...
New:
- rotation/simple_rotation.py: daily-iteration rotation strategy (584 lines)
- rotation/config_loader.py: standalone config loader
- rotation/config_simple.yaml: 11 assets, 7 groups
- rotation/README_SIMPLE.md: usage guide
- scripts/get_trading_calendar.py: trading calendar fetcher
Removed:
- rotation/example_usage.py, run_strategy.py (replaced by simple_rotation.py)
- rotation/results/ output files (gitignored)
- scripts/verify_*.py, calculate_returns_from_detail.py (one-off scripts)
- scripts/README_TRADING_CALENDAR.md
Backtest result (2020-01-10 ~ 2026-06-01):
- Total return: 1237.6%, Annual: 52.66%
- Max drawdown: -11.71%, Sharpe: 2.50
2026-06-01 22:28:26 +08:00
ee2453f65e
fix(rotation): 修复 backtest detail 中指数和 ETF 累计收益计算 bug
...
- 问题:cum_return_idx 和 cum_return_etf 使用相同的 ETF 价格计算
- 修复:分别使用指数价格(raw)和 ETF 价格(hfq)独立计算
- 验证:72.6% 的持仓记录显示差异(0.06%~0.48%),符合预期
- 新增验证脚本:verify_cum_return_fix.py
2026-05-26 23:22:26 +08:00
6a86a27108
test(scripts): 新增ETF数据获取验证脚本
...
新增脚本:
- verify_etf_hfq_fix.py: 验证指数使用raw、ETF使用hfq
- compare_index_vs_etf_returns.py: 对比指数收益vs ETF收益的KPI指标
验证内容:
- 指数数据完整性检查
- ETF数据完整性检查
- ETF是否正确使用hfq后复权价格(抽样对比raw和hfq)
- 验证510300.SH等ETF的hfq/raw比值(应>1.0)
2026-05-26 19:55:01 +08:00
537e7ccc45
feat(v2): 将导出功能内建到策略 run() 方法
...
- 修改 StrategyBase.run() 支持 export_detail 参数
- 保存 self._data 供导出方法复用
- 简化 export_backtest_detail.py 从 441 行到 62 行
- 消除策略重复执行,提升运行效率 40%
- API 请求减少 50%(溢价率数据复用)
2026-05-26 01:04:20 +08:00
a62cfb4cd5
fix: 修复因子前向填充不生效的 bug(清理调试代码)
...
问题根因:
- pandas reindex(method='ffill') 只填充新增行的 NaN,不填充已存在的 NaN
- 当 factor_df 中已有境外市场放假日期的 NaN 值时,reindex 无法填充
修复方案:
- 改为两步操作:reindex() 然后 ffill()
- ffill() 会填充所有 NaN,包括已存在的
验证结果:
- 2026-04-30 HSI: None → 0.2388 ✅
- 2026-04-30 GDAXI: None → 0.5647 ✅
- 2026-05-08 HSI: None → 0.1144 ✅
2026-05-25 19:16:14 +08:00
b8d433d519
fix: 修复因子前向填充不生效的 bug
...
问题根因:
- reindex(method='ffill') 不会填充已存在的 NaN 值
- 当 factor_df 中已有 NaN(境外市场放假),reindex 无法填充
修复方案:
- 改为两步操作:reindex() 然后 ffill()
- ffill() 会填充所有 NaN,包括已存在的
影响范围:
- rotation.py: positions 对齐到 A 股日历
- export_backtest_detail.py: 因子对齐到展示日历
验证结果:
- 2026-04-30 HSI: nan → 0.2388 ✅
- 2026-05-08 HSI: nan → 0.1144 ✅
2026-05-25 08:53:42 +08:00
2f6b031361
feat: 添加因子对齐调试输出
...
- 检查 factor_df 索引类型
- 检查 reindex 后 2026-04-30 的 HSI 动量值
- 待进一步分析为何 ffill 未生效
2026-05-25 02:45:27 +08:00
0ef0623538
fix: 导出脚本因子前向填充对齐到展示日历
...
- 先定义 common_dates = equity_curve.index
- 再执行 factor_df.reindex(common_dates, method='ffill')
- 修复境外市场放假时动量显示为 None 的问题
2026-05-25 02:30:58 +08:00
959a863b5e
fix: 导出脚本因子对齐到A股日历
...
- 因子在原始数据上计算(正确)
- 导出时将因子前向填充到A股交易日历
- 修复境外市场放假时动量显示为None的问题
2026-05-25 02:07:18 +08:00
b89e975aed
refactor: 删除 SimpleRotationStrategy 简化版
...
- 删除 simple.py(已被 GlobalRotationStrategy 替代)
- 删除 backtest_simple_rotation.py 回测脚本
- 删除 test_simple_rotation.py 测试脚本
- 更新 __init__.py 移除 SimpleRotationStrategy 导出
- 现在只保留 GlobalRotationStrategy 正式版
2026-05-25 01:33:23 +08:00
e8e4e9c3ac
fix: GlobalRotationStrategy select_num 未生效
...
- 修复分散化选股逻辑:每个 group 选 Top 1 后,需要再按动量排序选 Top select_num
- 之前:所有 group 的 Top 1 都标记为信号(忽略 select_num)
- 现在:先从每个 group 选 Top 1,再从中按动量选 Top select_num 个
- 影响:配置 select_num=3 时,实际持仓 3 只而不是 4 只(group 数量)
2026-05-25 01:23:00 +08:00
2be81ba00d
feat(v2): 添加回测逐日明细导出脚本
...
新增功能:
- 创建 framework_v2/scripts/export_backtest_detail.py
- 导出 GlobalRotationStrategy 回测细节到 JSON
- 输出路径: framework_v2/results/backtest_detail_v2.json
导出数据内容:
1. 元数据(meta)
- 策略版本、模式、日期范围
- 动态阈值配置
- 调仓次数、最终净值
- 标的列表(名称、ETF、分组)
2. 逐日明细(days)
- 日期、净值、日收益率
- 调仓信息(is_rebalance、added、removed)
- 持仓列表(holdings)
- 每标的详情(11 个标的 × 1539 天)
* 动量得分、排名、阈值
* 持仓状态、权重
* 入场日期、入场价格、持仓天数
* 累计收益、当日收益、收益贡献
技术特性:
- 使用 CrossMarketAligner 对齐数据
- 支持动态短债阈值
- 支持强制分散化
- 包含交易成本计算
- Pydantic Schema 验证
回测验证(2020-01-10 ~ 2026-05-22):
- 总收益:137.64%
- 年化收益:15.23%
- 调仓次数:829 次
- 交易天数:1539 天
- 文件大小:4.5 MB
用途:
- 供 HTML 回放器加载
- 策略分析和调试
- 信号可视化
- 持仓明细查询
2026-05-25 00:39:47 +08:00
1807258176
feat(v2): 实现全球轮动策略正式版(GlobalRotationStrategy)
...
核心功能:
- 交易成本计算:每次调仓扣除 0.1%(829 次调仓)
- 动态短债阈值:标的动量 < 短债动量 × 1.0 → 不持有
- 强制分散化:每个 group 内竞争,只选 Top 1
- 溢价过滤:预留接口(阈值 10%)
- 调仓控制:rebalance_days + rebalance_threshold(预留接口)
- A 股交易日过滤:只保留 SSE 交易日(1539 天)
策略逻辑:
1. 计算各指数标的动量得分(加权线性回归)
2. 使用动态短债阈值过滤负动量标的
3. 每个 group 内竞争,只选 Top 1(强制分散化)
4. 溢价过滤:排除溢价率 > 阈值的 ETF
5. 调仓控制:最低持仓天数 + 调仓阈值
6. 等权分配仓位
7. 扣除交易成本(0.1%)
回测验证(2020-01-10 ~ 2026-05-22):
- 总收益:135.63%(vs V1 的 103.29%,+32.34%)
- 年化收益:15.07%(vs V1 的 12.32%,+2.75%)
- 最大回撤:-17.57%(vs V1 的 -17.72%,略好)
- 夏普比率:1.15(vs V1 的 0.78,+47%)
- 调仓次数:829 次(vs V1 的 404 次)
新增文件:
- rotation.py: GlobalRotationStrategy 正式版实现(456 行)
- __init__.py: 导出 SimpleRotationStrategy 和 GlobalRotationStrategy
- backtest_global_rotation.py: 正式版回测脚本(117 行)
2026-05-24 22:54:21 +08:00
e6657bd2cc
feat(framework_v2): 对齐 V1 配置,实现指数信号→ETF收益回测
...
配置对齐:
- config_simple.yaml 严格对齐 V1 config.yaml
* 11 个标的覆盖 7 个策略分组
* 回测区间: 2020-01-01 ~ 至今
* 选股数量: Top-3,强制分散化
* V3 动态阈值(短债动量参考)
* 溢价控制启用(HK/US 10%阈值)
策略实现:
- SimpleRotationStrategy 支持 signal_source/trade_source 分离
* get_codes() 同时获取信号和交易标的
* compute_factors() 只使用 signal_source 计算因子
* _execute_backtest() 使用 trade_source 计算收益
* 支持跨市场场景(指数信号 → ETF收益)
回测验证:
- 成功运行端到端回测
- 获取 21 个标的(11 signal + 10 trade)
- 平均仓位 84.42%
- ⚠️ 已知问题: Flask API 只返回缓存数据(2026年),需修复
修复项:
- StrategyBase.run() 兼容信号矩阵(移除 'weight' 列假设)
2026-05-24 14:58:41 +08:00