Commit Graph

9 Commits

Author SHA1 Message Date
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
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
6749f8cf61 feat(v2): GlobalRotationStrategy 使用 CrossMarketAligner 进行数据对齐
核心改进:
- 替换手动对齐逻辑为框架标准的 CrossMarketAligner
- 修复收益率计算顺序:先对齐价格 → 再计算收益率
- 修复首日收益率 NaN 问题(填充为 0%)
- 添加 Pydantic Schema 验证(数据质量保证)

对齐逻辑变更:
修复前(错误):
  1. returns = close_df.pct_change()  # 在原始日历计算
  2. returns = returns[returns.index.isin(trading_calendar)]  # 过滤

修复后(正确):
  1. aligner = CrossMarketAligner(target_calendar)
  2. returns_df = aligner.align_multi_asset(close_dict)
     - 内部:先 ffill 价格到 A 股日历
     - 内部:再计算收益率(休市日 = 0%)
     - 内部:填充首日 NaN 为 0%
     - 内部:Pydantic Schema 验证

回测验证(2020-01-10 ~ 2026-05-22):
- 修复前:总收益 135.63%,年化 15.07%,夏普 1.15
- 修复后:总收益 137.88%,年化 15.25%,夏普 1.16
- 收益提升:+2.25%(修复首日 NaN 和跨日收益率问题)

关键修复:
1. 首日收益率从 NaN 改为 0%(避免收益丢失)
2. 休市日收益率正确 = 0%(价格 ffill 后不变)
3. 消除跨多日收益率被当作单日收益率的 bug
4. 统一使用框架标准组件(CrossMarketAligner)
2026-05-25 00:29:49 +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
94b9ef165b feat(v2): 增强框架核心功能与ETF复权修复
- 修复 end_date=None 导致 Flask API 返回错误时间范围的 bug
  * strategy.py: 自动使用今天日期作为 end_date
  * 验证:回测区间从 77 天恢复到 1539 天

- ETF 收益计算从原始价格改为后复权价格
  * flask_api_fetcher.py: adj='raw' → adj='hfq'
  * 自动处理 ETF 份额拆分事件,确保收益率准确

- V2 简单版添加 A 股交易日过滤
  * simple.py: 获取 SSE 交易日历,过滤非交易日
  * 验证:1999 天 → 1539 天(与 V1 一致)

- 配置严格对齐 V1 config.yaml
  * config_simple.yaml: start_date 从 2020-01-01 改为 2020-01-10
  * group 字段值严格映射 V1 的 market 字段

关键验证:
- V2 简单版回测:1539 天,981.95% 收益(未计入交易成本)
- V2 正式版回测:1539 天,135.63% 收益(已计入交易成本)
- V1 旧版框架:1539 天,103.29% 收益(基准)
2026-05-24 22:53:45 +08:00
86fce7a975 fix: group 字段严格对齐 V1 market 字段值
修正分组命名,不再自行创造:
- CN_GROWTH/CN_VALUE → A
- US_TECH → US
- JP_BROAD → JP
- EU_BROAD → EU
- HK_BROAD/HK_TECH → HK
- FIXED_INCOME → BOND
- COMMODITY → COMMODITY (不变)

同步更新 market_overrides:
- CN_EQUITY → A
- HK_EQUITY → HK
- US_EQUITY → US

现在 group 字段完全映射 V1 的 market 字段值
2026-05-24 15:00:48 +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
de988b919b feat(framework_v2): 实现 StrategyBase 抽象基类和简单轮动策略
StrategyBase ABC:
- 定义标准回测流程:get_data → compute_factors → generate_signals → manage_positions → execute
- 实现通用数据获取(使用 FlaskAPIFetcher.fetch_indices)
- 提供 run() 方法执行完整回测流程

SimpleRotationStrategy:
- 实现 4 个抽象方法:get_codes, compute_factors, generate_signals, manage_positions
- 支持动量因子计算(MomentumFactor)
- 实现全局选股和等权仓位管理
- 修复 int64 → float 转换问题

框架定位:
- 通用量化回测框架,支持轮动、CTA、趋势跟踪等多种策略
- 策略只需实现 4 个抽象方法即可接入框架
2026-05-24 14:25:47 +08:00