|
|
341611c32b
|
feat(framework_v2): 实现通用配置系统,支持扁平化资产池和策略分组
- 使用 Pydantic Schema 验证配置类型安全
- 实现扁平化 AssetPool,移除预设分类(equity/commodity/fixed_income)
- 移除 MarketType 枚举,改用 group 字符串字段实现策略分组
- AssetConfig 引入 signal_source/trade_source 分离,支持跨市场场景
- ConfigLoader 支持通用 StrategyConfig,向后兼容 RotationStrategyConfig
- 新增 GroupConfig 替代 MarketGroupConfig,支持分散化选股
重构核心:
- market → group(策略分组语义,组内竞争强制分散)
- by_market → by_group
- MarketGroupConfig → GroupConfig
|
2026-05-24 14:25:25 +08:00 |
|
|
|
99d3584d05
|
docs(framework_v2): 更新 FlaskAPIFetcher 文档(API 日历集成)
## 使用指南更新(FLASK_API_FETCHER_GUIDE.md)
- get_trading_calendar() 方法签名更新
- 新增 start, end 参数(支持动态日期范围)
- 返回类型: pd.DatetimeIndex(准确日历)
- 使用示例更新(API 调用方式)
- 注意事项更新:交易日历准确性 ✅ 已解决
## 架构设计更新(FLASK_API_FETCHER_ARCHITECTURE.md)
- get_trading_calendar() 实现更新
- 从临时 pandas BDay → API 准确日历
- API 端点: GET /api/v1/trading-calendar
- 未来优化: 移除交易日历 TODO(已完成)
## 文档一致性
- 所有示例代码使用 API 日历
- 架构描述与实际实现一致
- 版本历史更新(2024-04-16)
|
2026-05-24 12:38:55 +08:00 |
|
|
|
b462c0520c
|
docs(framework_v2): 添加端到端测试报告 + API 集成记录
## 测试报告(END_TO_END_TEST_REPORT.md, 345 行)
- 5 个阶段详细测试结果
- 关键验证总结(跨市场对齐、数据完整性、策略表现)
- 性能指标(总耗时 ~7 秒)
- 数据流图(完整流程可视化)
- 发现的问题(因子值异常、日历精度)
## 集成记录(TRADING_CALENDAR_API_INTEGRATION.md, 247 行)
- 变更前后对比(pandas BDay → API)
- API 端点文档(请求/响应格式)
- 使用示例(基础使用 + 数据对齐)
- 测试验证结果(484 天准确日历)
- 影响分析(正面影响 + 无破坏性变更)
## 文档特色
- 大量代码示例
- 表格总结
- 测试结果截图
- 版本历史记录
|
2026-05-24 12:38:28 +08:00 |
|
|
|
e7ab8a2755
|
feat(framework_v2): 集成交易日历 API + 端到端测试
## 核心功能
- get_trading_calendar(): 通过 API 获取准确交易日历
- 替换临时 pandas BDay 实现
- 调用 /api/v1/trading-calendar 端点
- 支持动态日期范围(start, end 参数)
- 支持 A/US/HK 多市场
## 端到端测试
- test_end_to_end.py: 完整流程测试(5 个阶段)
- 阶段 1: 数据获取(纳指 502 天,创业板 484 天)
- 阶段 2: 因子计算(MomentumFactor n_days=20)
- 阶段 3: 数据对齐(CrossMarketAligner 到 A 股 484 天)
- 阶段 4: 信号生成(Top-1,469 个信号)
- 阶段 5: 收益计算(年化 51.71%,超额 96.37%)
## 测试验证
- 5/5 阶段通过
- API 日历: 484 个交易日(准确)
- 纳指休市日: 18 天收益率 = 0%
- 收益率 NaN: 0
- 跨市场对齐成功
## 架构改进
- 从近似日历 → 准确 API 日历
- 无需手动维护节假日列表
- API 失败时抛出异常(不静默降级)
|
2026-05-24 12:38:06 +08:00 |
|
|
|
d07fb8de6d
|
feat: 为 FlaskAPIDataSource 添加交易日历获取功能
- 新增 get_trading_calendar() 方法,支持 A股/美股/港股
- 新增 get_calendar_info() 方法,获取服务信息
- 支持自动重试、超时保护、详细错误提示
- 返回标准 DatetimeIndex 格式
- 添加端到端测试验证所有市场
|
2026-05-24 12:26:35 +08:00 |
|
|
|
e2050e319d
|
fix: 添加 pandas_market_calendars 依赖到 requirements.txt
|
2026-05-24 11:30:59 +08:00 |
|
|
|
33e14b7eae
|
fix: 添加 pydantic 依赖到 requirements.txt
- Flask Server 使用 Pydantic 模型验证 API 响应
- 修复线上服务 ModuleNotFoundError: No module named 'pydantic'
|
2026-05-24 11:24:04 +08:00 |
|
|
|
100eed455d
|
feat: 统一交易日历为 pandas_market_calendars
- 移除 Tushare 交易日历依赖,A股/美股/港股统一使用 pandas_market_calendars
- 简化 get_trading_calendar() 接口,移除 exchange 参数(沪深日历一致)
- 删除冗余的 _get_china/us/hk_calendar() 独立函数,直接调用 mcal
- 新增 Flask API 端点: /api/v1/trading-calendar, /api/v1/calendar/info
- 代码减少 73 行 (-61%),逻辑更集中易维护
- 更新 API 文档描述,三个市场数据源统一
|
2026-05-24 11:08:26 +08:00 |
|
|
|
1bf91bdcd0
|
docs(framework_v2): 添加 FlaskAPIFetcher 文档体系
## 文档(2 个文档,互相关联)
- FLASK_API_FETCHER_GUIDE.md - 使用指南(365 行)
- 快速开始示例
- 完整 API 参考
- 结合 CrossMarketAligner 示例
- 错误处理 + 性能优化
- 注意事项(交易日历、净值数据量)
- FLASK_API_FETCHER_ARCHITECTURE.md - 架构设计(367 行)
- 架构层次图
- 设计原则(DIP, SRP, OCP)
- 数据流图(指数、ETF)
- 与 CrossMarketAligner 集成
- 未来优化方向(缓存、异步)
## 更新
- README.md: 添加文档链接(5 个文档)
- 形成完整文档网络(6 个文档互链)
|
2026-05-24 10:39:02 +08:00 |
|
|
|
40116f436f
|
feat(framework_v2): 添加 FlaskAPIFetcher 数据获取器
## 核心功能
- FlaskAPIFetcher: 继承 DataFetcher 抽象基类
- fetch_indices(): 获取指数 OHLCV 数据
- fetch_etf(): 获取 ETF 数据(自动附加净值+溢价率)
- get_trading_calendar(): 获取交易日历
- get_benchmark(): 获取基准数据
## 技术实现
- 委托调用 FlaskAPIDataSource(HTTP API)
- 自动重试 3 次,超时 120 秒
- Pydantic Schema 验证响应
- 进度显示(批量获取)
- 无需本地 SSH 隧道配置
## 测试验证
- 5/5 测试通过(健康检查、指数、ETF、日历、基准)
- 成功获取线上数据(000300.SH, 510300.SH)
- ETF 自动附加净值(3695 条)和溢价率
## 架构设计
- shared/data/flask_api_fetcher.py - 实现(262 行)
- tests/test_flask_api_fetcher.py - 测试(199 行)
- 依赖倒置原则(策略依赖抽象接口)
|
2026-05-24 10:38:34 +08:00 |
|
|
|
5f08e508ac
|
docs(framework_v2): 完善文档体系 + 修复 .gitignore
## 文档体系(5 个文档,互相关联)
- README.md - 框架总览 + 文档索引
- DATA_ARCHITECTURE.md - 数据架构方案(Schema、验证、性能优化)
- ALIGNMENT_GUIDE.md - CrossMarketAligner 使用指南
- DATA_FLOW_DEMO.md - 从 OHLCV 到最终收益的 7 个阶段推演
- ALIGNMENT_SCHEMA_INTEGRATION.md - Aligner + Schema 整合方案
## 文档特色
- 大量代码示例(✅ 正确 vs ❌ 错误对比)
- 数据流可视化(ASCII 图)
- 表格总结(问题、严重度、解决方案)
- 实际场景推演(2024-01-01 ~ 2024-01-31)
- 文档互链(形成知识网络)
## 修复
- .gitignore: 添加 !framework_v2/shared/data/ 例外
- 允许提交对齐器相关文件
|
2026-05-24 10:29:20 +08:00 |
|
|
|
a16681bda9
|
feat(framework_v2): 添加跨市场数据对齐器 + Pydantic Schema 验证
## 核心功能
- CrossMarketAligner: 跨市场数据对齐(解决 ffill 陷阱)
- Pydantic Schema: 数据结构验证(OHLCVInputSchema, AlignedFactorSchema 等)
- 验证装饰器: @validate_factor_after_align, @validate_returns_after_align
## 解决的问题
- 跨市场交易日历不同(美股/港股/A股)
- ffill 收益率陷阱(休市日复制非零收益率)
- NaN 传播问题
- 日期不一致问题
## 测试验证
- 5/5 测试通过(因子对齐、收益率对齐、多标的对齐、信号验证、ffill陷阱)
- 休市日收益率 = 0%(正确)
- 无 NaN 传播
## 架构设计
- shared/data/alignment.py - 对齐器实现
- shared/data/schemas.py - Pydantic Schema 定义
- tests/test_alignment.py - 完整测试套件
|
2026-05-24 10:28:35 +08:00 |
|
|
|
908b28473f
|
feat(framework_v2): 创建框架V2骨架 - 三层架构+因子验证通过
## 架构设计
- 三层架构:core(抽象接口) → shared(通用实现) → tests(验证测试)
- 5个核心抽象基类:StrategyBase, FactorBase, SignalGenerator, Executor, DataFetcher
- 零侵入:与现有框架并行开发,不修改生产代码
## 已完成
✓ 核心接口层(5个ABC类)
✓ 通用因子层(MomentumFactor完全复制现有逻辑)
✓ 对比验证测试(新旧因子输出差异=0,测试通过)
## 验证结果
- 最大差异: 0.000000e+00
- 平均差异: 0.000000e+00
- 容差: < 1e-10
## 下一步
- 阶段3: 信号层迁移(TopNSelector, DynamicThreshold, RebalanceController)
- 阶段4: 执行层迁移(BacktestRunner)
- 阶段5: 数据层迁移(DataFetcher实现)
- 阶段6: 完整策略对比验证
## 设计原则
- 按需抽象,不预先设计
- 职责分离,避免框架膨胀
- 测试驱动,每个组件必须有对比测试
- 渐进式迁移,验证通过再替换
|
2026-05-24 09:12:29 +08:00 |
|
|
|
226a27361f
|
feat(pydantic): 集成 Pydantic 模型到 Flask API 层
1. models.py:
- 添加 dataframe_to_ohlcv_response() 转换函数
- 支持 DataFrame → OHLCVResponse 自动转换
- 自动处理 nav、premium、attrs 等业务数据
2. flask_server.py:
- 使用 Pydantic 模型构建响应(替代手动 Dict)
- 错误响应使用 ErrorResponse 模型
- 代码减少 20+ 行,类型安全提升
3. flask_api_source.py:
- 使用 validate_ohlcv_response() 验证 API 响应
- 类型安全访问 nav、premium、info 等字段
- ETF 数据解析更可靠
测试通过:
✅ DataFrame → Pydantic 转换正常
✅ ETF 净值和溢价率正确处理
✅ 线上 API 响应验证成功
✅ FlaskAPIDataSource 集成正常
|
2026-05-24 01:13:33 +08:00 |
|
|
|
72df18a28b
|
feat(models): 添加 Pydantic 数据模型(Phase 1)
- 定义请求模型:OHLCVRequest, AssetTypeRequest
- 定义响应模型:OHLCVResponse, AssetTypeResponse, ErrorResponse
- 定义枚举类型:AssetTypeEnum, AdjTypeEnum, TimeframeEnum
- 提供类型安全的 API 响应验证
- 支持 IDE 自动补全和类型检查
- 为 Phase 2 (Flask-Pydantic 集成) 做准备
测试通过:
✅ 请求参数自动验证(日期格式、adj 值)
✅ 响应数据验证(美股 META、ETF 513100.SH、BTC)
✅ 序列化/反序列化正常
✅ 类型安全检查(缺失字段、类型错误)
|
2026-05-24 00:42:22 +08:00 |
|
|
|
11a0a6502b
|
fix(flask_server): 根据资产类型动态选择日期格式精度
问题修复:
- 加密货币分钟级数据需要保留时分秒
- 日线数据只需日期(年月日)
- 之前硬编码 '%Y-%m-%d' 导致分钟级数据丢失时间信息
修复方案:
- API 层根据 final_type 判断:CRYPTO 使用 '%Y-%m-%d %H:%M:%S',其他使用 '%Y-%m-%d'
- build_premium_result_from_attrs 自动检测索引是否包含时间部分
- build_premium_result 同样支持动态日期格式
- dataframe_to_json 已支持 asset_type 参数(之前已实现)
测试验证:
- BTC 小时级数据: '2024-01-01 00:00:00' ✅
- 000300.SH 日线数据: '2024-01-02' ✅
- ETF 溢价率日期: '2026-05-22' ✅
|
2026-05-24 00:06:34 +08:00 |
|
|
|
82d57af666
|
fix(flask_server): 修复 pickle 反序列化后 JSON 序列化问题
问题修复:
- pickle 反序列化后 attrs 包含真实 DataFrame/Series 对象
- build_premium_result_from_attrs 需要兼容 Series 对象和字典格式
- API 层需要将 DataFrame/Series 转换为 JSON 可序列化格式
修复方案:
- build_premium_result_from_attrs 支持 pd.Series 和 dict 两种输入
- API 层将 DataFrame 转换为 records 格式(orient='records')
- API 层将 Series 索引转换为字符串日期
- 过滤内部缓存元数据(_cache_*)
|
2026-05-23 23:57:39 +08:00 |
|
|
|
3619e26bf1
|
refactor(datasource): 统一数据获取架构,使用 df.attrs 传递元数据
核心改进:
- CCXTSource 添加 df.attrs 支持(source, exchange, symbol, timeframe, adj)
- UniversalDataFetcher 简化透传方法,保留兼容接口
- fetch_etf_with_nav 标记为 deprecated,推荐使用 fetch_etf + df.attrs
- 所有数据源统一契约:返回 DataFrame + df.attrs
架构改进:
- 统一返回单 DataFrame,元数据通过 attrs 传递
- 消除多返回值接口(price_df, nav_df, premium_series)
- 文档注释更新,反映新接口用法
- 添加 DeprecationWarning 提示迁移路径
|
2026-05-23 23:40:18 +08:00 |
|
|
|
7446d1b2e8
|
refactor(flask_server): 使用 pickle 重构缓存层序列化逻辑
核心改进:
- 使用 pickle.dumps/loads 替代手动 JSON 序列化
- 代码减少 60 行(890 → 830)
- 自动保留 df.attrs 元数据(nav, premium 等)
- 消除手动处理 DataFrame/Series 转换的复杂逻辑
- 缓存层职责更清晰:只负责存储,不处理业务逻辑
架构改进:
- 序列化代码:25 行 → 1 行(-96%)
- 反序列化代码:58 行 → 1 行(-98%)
- attrs 完整性:自动保留,无需手动转换
- 性能提升:pickle C 实现,比 JSON 快 3-5 倍
|
2026-05-23 23:39:54 +08:00 |
|
|
|
feb7c78e68
|
refactor: 统一ETF获取接口为单个DataFrame返回
重构说明:
- TushareSource.fetch_etf(): 新增 adj 参数,统一接口
- 返回单个 DataFrame
- df.attrs['nav']: 净值 DataFrame
- df.attrs['premium']: 溢价率 Series
- 移除冗余方法:
- fetch_etf_with_nav() → 合并到 fetch_etf()
- fetch_etf_adj() → 重命名为 _fetch_etf_hfq()(内部方法)
- UniversalDataFetcher: 适配新接口
- fetch_etf_with_nav(): 从 df.attrs 提取元数据(兼容旧接口)
- fetch_etf_adj(): 调用 fetch_etf(adj='hfq')
- Flask: 更新注释说明
架构优势:
- 单一接口:一个方法搞定所有 ETF 数据获取
- 数据一致:所有数据在一个 DataFrame 对象中
- 缓存友好:只需缓存一个 DataFrame
- 扩展性强:新增数据直接添加到 attrs
|
2026-05-23 22:36:23 +08:00 |
|
|
|
2867ae8d21
|
refactor: 将ETF净值和溢价率逻辑下移到TushareSource层
重构说明:
- TushareSource: 新增 fetch_etf_with_nav() 和 _calculate_premium_series()
- UniversalDataFetcher: 简化 fetch_etf_with_nav() 为透传调用
- Flask: 更新注释说明数据层已处理
架构优势:
- 职责分离:TushareSource 封装完整数据获取逻辑
- 可复用性:任何调用 TushareSource 的地方都有净值
- 维护性:业务逻辑集中在数据源层
- 符合单一职责原则
|
2026-05-23 22:28:21 +08:00 |
|
|
|
50b5f09d84
|
fix: ETF始终返回净值和溢价率数据(与adj无关)
修改原因:
- 净值是客观数据,不存在复权概念
- 溢价率 = (原始价格 - 净值) / 净值,与用户请求的 adj 无关
- 用户请求 adj='hfq' 时也需要查看溢价率数据
变更内容:
- 移除 'adj == raw' 限制条件
- 所有 ETF 请求都附加净值和溢价率数据
- 更新 API 文档说明
- 溢价率始终基于原始价格计算
|
2026-05-23 22:21:50 +08:00 |
|
|
|
3697c9d38b
|
fix: 修复数据获取架构逻辑Bug
修复内容:
1. Bug #1: TushareSource.fetch(adj='raw') ETF 无法获取
- 在 adj='raw' 分支优先判断 ETF
- ETF 代码现在正确路由到 fetch_etf()
2. Bug #2: is_china_index 判断范围过宽
- 添加 ETF 排除逻辑
- ETF 不再被误判为指数
3. 接口一致性:CCXTSource 添加 adj 参数
- fetch(code, start, end, adj='raw', timeframe)
- 加密货币仅支持 adj='raw'
- UniversalDataFetcher._fetch_crypto() 同步更新
影响:
- ETF 原始价格数据获取恢复正常
- 类型判断逻辑更准确
- 数据源接口签名统一
|
2026-05-23 21:46:01 +08:00 |
|
|
|
b7f7a756b6
|
refactor: SSH配置完全封装到UniversalDataFetcher
变更内容:
1. UniversalDataFetcher 新增方法:
- get_ssh_config_from_env(): 从环境变量读取 SSH 配置
- from_env(): 工厂方法,自动读取环境变量创建实例
- get_ssh_status(): 返回 SSH 状态信息字典
2. flask_server.py 简化:
- 移除 get_ssh_config() 函数(18行)
- 移除 ssh_config 全局变量
- get_fetcher() 使用 from_env()
- / 和 /health 路由使用 get_ssh_status()
架构改进:
- SSH 配置逻辑完全封装在 UniversalDataFetcher
- flask_server.py 只依赖 fetcher 接口
- 减少 24 行重复代码
|
2026-05-23 21:20:43 +08:00 |
|
|
|
0f4295a144
|
refactor(flask_server): 多项优化重构
优化内容:
1. fetch_with_adj → fetch(adj=adj)
- _fetch_full_data_cached 使用 fetch(adj=adj) 替代 fetch_with_adj
2. 加密货币分支添加 adj 参数
- fetch_data_with_ttl 中加密货币调用添加 adj='raw'
3. ETF 净值溢价率逻辑抽取
- 新增 build_premium_result() 函数
- get_ohlcv 和 get_etf_nav 使用该函数,减少 42 行重复代码
4. SSH 配置信息增强
- ssh_status 改为 ssh 对象,包含更多详细信息
- 添加 required_types(需要 SSH 的资产类型列表)
代码行数:减少约 40 行重复代码
|
2026-05-23 19:03:09 +08:00 |
|
|
|
67d67b1eea
|
refactor(universal_fetcher): SSH隧道按资产类型统一启动
定义 SSH_REQUIRED_TYPES 常量集合,在 fetch() 入口处统一启动隧道
改进:
- 新增 SSH_REQUIRED_TYPES 常量(港美股/加密货币需要 SSH)
- fetch() 入口统一启动隧道,移除各分支重复调用
- fetch_us_adj/fetch_hk_adj 简化为调用 fetch()
- fetch_batch 移除冗余的隧道启动
- 移除废弃的 _fetch_xxx 内部方法(减少 55 行)
SSH 调用次数:10次 → 3次(仅保留必要场景)
|
2026-05-23 18:53:26 +08:00 |
|
|
|
56f0cd60e7
|
fix(flask_api): 保留code列确保本地与线上数据结构一致
FlaskAPIDataSource.fetch() 原硬编码只选择 OHLCV 列,丢失 code 列
修改为动态判断:若 API 返回包含 code 列则保留
确保本地 UniversalDataFetcher 与线上 FlaskAPIDataSource 返回数据结构一致
|
2026-05-23 18:41:20 +08:00 |
|
|
|
7f2af6b470
|
refactor(flask_api): fetch添加adj参数,fetch_with_adj简化
FlaskAPIDataSource.fetch() 新增 adj 参数,fetch_with_adj() 简化
- FlaskAPIDataSource.fetch(adj='raw'): 请求参数包含 adj
- fetch_with_adj(): 简化为 return self.fetch(adj=adj)(减少 ~120行)
- flask_server.py: 缓存逻辑已支持 adj 参数,无需修改
|
2026-05-23 18:32:20 +08:00 |
|
|
|
c319fd42be
|
refactor(universal_fetcher): fetch添加adj参数,fetch_with_adj简化
UniversalDataFetcher.fetch() 新增 adj 参数,直接传递给底层
- fetch(adj='raw/qfq/hfq'): 统一入口,参数校验和路由
- fetch_with_adj(): 简化为 return self.fetch(adj=adj)
- 删除重复的 VALID_ADJ_BY_TYPE 定义和路由逻辑(~70行)
- VALID_ADJ_BY_TYPE 移到类级别作为静态配置
|
2026-05-23 18:32:10 +08:00 |
|
|
|
02dbc7bd7d
|
refactor(datasource): 底层fetch方法添加adj参数
TushareSource.fetch() 和 YFinanceSource.fetch() 新增 adj 参数支持 raw/qfq/hfq
- TushareSource.fetch(adj='raw'): 内部路由到 fetch_index/fetch_stock_adj/fetch_etf_adj
- YFinanceSource.fetch(adj='raw'): 内部路由到 fetch_adj() 或原始逻辑
- 添加 is_china_stock() 和 _is_etf_code() 方法用于资产类型判断
|
2026-05-23 18:32:00 +08:00 |
|
|
|
1148d3166c
|
refactor(datasource): 分层接口设计,移除HybridDataSource
架构改动:
- 移除 HybridDataSource(功能被 UniversalDataFetcher 覆盖)
- 新增分层接口设计:基础层 + 扩展层
基础层(统一接口):
- fetch(): 统一 OHLCV 接口,自动识别资产类型
- fetch_batch(): 批量获取
扩展层(资产类型特有):
- fetch_etf_adj(): A股 ETF 后复权价格
- fetch_us_adj(): 美股复权价格
- fetch_etf_with_nav(): ETF 价格 + 净值 + 溢价率
其他修改:
- YFinanceSource: 新增 fetch_adj() 方法
- strategy.py: 改用 UniversalDataFetcher 替代 HybridDataSource
- __init__.py: 移除 HybridDataSource 导出
|
2026-05-23 12:46:48 +08:00 |
|
|
|
209dd7fd83
|
refactor(tushare): 移除代理清除/恢复逻辑
Tushare是国内服务,不需要代理切换操作
移除内容:
- _clear_proxy 方法
- _restore_proxy 方法
- 所有方法中的代理清除和恢复调用
代码精简37行,保持原有功能不变
|
2026-05-23 11:55:02 +08:00 |
|
|
|
b066b23495
|
feat(tushare): 新增ETF后复权价格和交易日历获取方法
新增方法:
- fetch_etf_adj: 获取ETF后复权价格数据,消除份额拆分对收益率的影响
通过 fund_daily + fund_adj 手动计算后复权价格
fund_adj 单次限2000条,按5年分段请求
- fetch_trade_cal: 获取A股SSE官方交易日历
验证结果:
- 纳指ETF后复权正确识别2022-01-14拆分(复权因子5.0)
- 累计收益100.54%与纳指100指数一致
|
2026-05-23 11:51:32 +08:00 |
|
|
|
8e8093e0fd
|
chore(config): 调整回测起始日期为2020-01-01
配合Mode B(指数信号+ETF收益)回测需求,缩短回测区间以提高ETF数据可用性
|
2026-05-23 11:18:00 +08:00 |
|
|
|
982fbe250b
|
fix: 修复跨市场收益率计算Bug
Bug机制:
- 先pct_change再ffill对齐,导致海外标的休市日复制前一天非零收益率
- 例如:美股圣诞节放假,但ffill填入前一天的+1.2%,重复计算收益
修复方案:
- 先ffill价格对齐到A股日历(休市日价格不变)
- 再pct_change计算收益率(休市日自然为0%)
影响:
- 修复前净值: 394.80(高估)
- 修复后净值: 16.23(真实)
- 该Bug导致海外标的在A股独有的交易日被重复计算收益
验证:
- 语法检查通过
- 回测运行正常
|
2026-05-20 22:34:12 +08:00 |
|
|
|
61b6f0b0a3
|
docs: V3轮动策略核心逻辑文档
核心内容:
- V2 vs V3核心差异对比表
- 动态阈值机制详解(三层守卫)
- 短债双重角色:阈值参考 + 空余仓位填充
- 三步筛选流程:动态阈值 → 类内竞争 → 跨类排序+填充
- 具体示例:正常/危机/极端危机三种场景
- V3关键修复:重复代码计分 + 数据守卫 + 空信号状态保持
- 三次修复历程:74a664d → be8ca02 → 18bc9b8
- V3配置示例与回滚方案
回测绩效:
- CAGR: 27.81%
- 最大回撤: -24.36%
- 夏普: 1.40
- Calmar: 1.14
|
2026-05-19 01:13:43 +08:00 |
|
|
|
18bc9b8c44
|
fix: V3动态阈值根因2子问题和根因3修复
根因2子问题: bond无数据时不填充
- 问题: be8ca02无条件填充导致2002-2007年信号中出现931862.CSI
- 但执行器找不到收益数据,2/3仓位收益为0,引发-31.92%回撤
- 修复: generate()中过滤前检查bond_has_data,传入_grouped_selection
- 填充条件改为: bond_code存在 AND bond_has_data
- bond有数据(含负值)→填充 ✓; bond无数据(NaN)→不填充 ✓
根因3: 空target时保持旧持仓而非清仓
- 问题: _apply_rebalance_control中target为空时清仓
- 但独立脚本的行为是保持旧持仓
- 修复: else分支改为pass,保持current_held不变
- 在有bond数据期target不会为空,空target仅发生在2002-2007
修复后验证结果:
- CAGR: 27.81% (预期28.03%, 差异-0.22%)
- 最大回撤: -24.36% (预期-24.35%, 差异≈0) ✓
- 夏普: 1.40 (预期1.40) ✓
- 回撤区间: 2015-05~2016-06 (与预期一致) ✓
- Calmar: 1.14 (预期1.15)
净值: 334.46 → 394.80 (+18%)
|
2026-05-19 01:06:20 +08:00 |
|
|
|
be8ca023f7
|
fix: V3动态阈值两处根因修复
根因1: _check_rebalance重复代码计分Bug
- 问题: "CL=F,931862.CSI,931862.CSI"中短债只计一次得分
- 修复: 直接遍历持仓列表计算得分,而非遍历factor_cols做in判断
- 影响: 278天应防御时继续持有风险资产
根因2: 短债填充依赖阈值过滤
- 问题: 短债动量为负时(钱荒等)不填充防御仓位
- 修复: 无条件填充短债(防御机制不应依赖动量阈值)
- 影响: 17天仓位不满(1/3或2/3空置)
修复后结果:
- 净值: 292.56 → 334.46 (+14.4%)
- 夏普: 1.33 → 1.41 (+0.08)
- 持仓3只: 92.3% → 99.4% (满仓率提升)
- 短债填充更积极: 28.7%时间持有短债
|
2026-05-19 00:38:04 +08:00 |
|
|
|
957769b501
|
docs: 添加V3动态阈值实施方案文档
详细说明了:
1. 当前代码流程分析
2. 需要修改的文件及具体代码
3. V2 vs V3逻辑对照表
4. 关键注意事项(BOND角色转变、信号格式兼容等)
5. 验证方式与预期结果
6. 回滚方案
|
2026-05-19 00:01:25 +08:00 |
|
|
|
74a664d4ff
|
feat: V3动态阈值实施方案落地
核心逻辑:
1. config.yaml新增bond_threshold配置块
2. selectors.py新增动态阈值逻辑:
- _get_dynamic_threshold(): 阈值=短债动量×ratio
- _grouped_selection(): BOND不参与竞争,空余仓位填充短债
3. strategy.py传入bond_threshold_config
回测验证:
- 最终净值: 292.56
- 累计收益: 29155.96%
- 持仓3只: 92.3%(满仓率提升)
- 短债填充: 27.7%时间启用(空余仓位)
信号特征:
- 短债可重复出现表示仓位占比
- 例如 "NDX,931862.CSI,931862.CSI" → NDX 33%, 短债 67%
|
2026-05-18 23:58:10 +08:00 |
|
|
|
3e6d9d1fdb
|
refactor(config): 统一钉钉多群配置命名格式
.env 配置命名统一:
- 群1: DINGTALK_WEBHOOK_1 + DINGTALK_SECRET_1
- 群2: DINGTALK_WEBHOOK_2 + DINGTALK_SECRET_2
settings.py 读取逻辑简化:
- 从 i=1 开始读取编号配置
- 移除不带编号的默认配置处理
|
2026-05-18 22:23:01 +08:00 |
|
|
|
3ca403f38a
|
fix(config): 修复钉钉多群配置读取逻辑
问题:原逻辑从 i=1 开始查找 DINGTALK_WEBHOOK_1(不存在),
导致群2(DINGTALK_WEBHOOK_2)未被读取。
修复:
- 群1使用不带编号的 DINGTALK_WEBHOOK + DINGTALK_SECRET
- 从 i=2 开始读取编号配置
验证:成功读取到2个钉钉群配置
|
2026-05-18 22:20:28 +08:00 |
|
|
|
8476d0e7cd
|
docs(config): 补充短债指数收益归因实证结论
实证分析结论:
- 标的收益占比:约17%(债券本身增长)
- 决策收益占比:约83%(避险决策贡献)
核心发现:
短债指数的价值主要来自正确避险决策,而非债券本身增长。
这验证了动量轮动策略的核心逻辑:通过仓位选择规避下跌风险。
|
2026-05-18 01:02:58 +08:00 |
|
|
|
7b41bb8c6d
|
feat(scripts): 迁移轮动策略定时调度器
新增文件:
- scripts/daily_scheduler.py: 定时调度器,支持交易日判断、回测执行、OSS上传、钉钉推送
- scripts/run_rotation.py: 回测入口脚本,支持Flask API和本地数据源切换
- config/settings.py: 配置管理模块,支持钉钉多群配置
功能:
1. 每天15:30自动检查交易日
2. 交易日执行策略回测生成报告
3. 上传报告图片到OSS
4. 发送图片链接到钉钉群
修复:
- 添加oss2库SyntaxWarning过滤(Python 3.12兼容)
- 钉钉消息精简为标题+图片格式
|
2026-05-18 00:57:59 +08:00 |
|
|
|
79c6ab8620
|
feat(rotation): 恢复短债指数931862.CSI作为BOND大类防御资产
配置变更:
- 恢复使用931862.CSI(中证0-9个月国债指数,短债指数)
- name从'30年国债'修正为'短债指数'(修正标注错误)
- etf设为null(无对应ETF可交易,直接用指数数据)
- 注释掉000012.SH配置(上证国债指数)
收益对比验证:
| 配置 | 最终净值 | 累计收益 | 说明 |
|------|---------|---------|------|
| 931862.CSI(短债) | 264.54 | 26354% | ✓ 当前配置 |
| 000012.SH(综合国债) | 216.30 | 21530% | 已注释 |
| 收益差异 | +48.24 | +4824% | 短债高18.2% |
决策原因:
1. 短债指数久期<1年,波动极小,熊市防御效果最佳
2. 000012.SH是综合国债指数,包含短债到长债,波动较大
3. 之前000012.SH错误映射到511520.SH(政金债ETF),指数-ETF不匹配
4. 931862.CSI虽无对应ETF,但数据覆盖19年(2007开始),回测充分
数据验证:
- 931862.CSI: 4335条数据 (2007-12-31 ~ 2026-05-14)
- 数据年数: 约19年
关键发现:
- 短债指数因其极低波动特性,在熊市动量稳定
- 红利低波(H30269)归类为A股,与创业板竞争A股Top1
- 短债指数(931862)单独作为BOND大类,自动入选
- 实现红利低波+短债同时持仓的双重防御机制
|
2026-05-17 00:55:03 +08:00 |
|
|
|
b419701c77
|
feat(rotation): 红利低波归类A股提升收益52%
配置变更:
- H30269.CSI(红利低波)从BOND改为A股大类
- 931862.CSI(短债指数)保持BOND大类
- start_date调整为2002-01-01
回测验证:
- 新配置净值264.54,累计收益26354%
- 旧配置净值173.83,累计收益17283%
- 收益提升52%
关键发现:
- 红利低波作为A股可与创业板指竞争,优胜者入选
- 短债单独作为BOND自动入选
- 之前同属BOND时只能选1个,无法同时持有
- 新配置可实现'红利低波+短债'同时持仓
|
2026-05-16 23:28:23 +08:00 |
|
|
|
969385f39c
|
feat(rotation): 采用红利低波+短债指数组合作为防御类资产
配置变更:
- H30269.CSI(红利低波指数)归类为BOND大类
- 931862.CSI(短债指数)归类为BOND大类
- 添加正确的标注说明(注明实际是红利低波和短债指数)
防御效果分析:
- 红利低波提供'类债券'股票防御(高股息+低波动)
- 短债指数提供真正的债券防御(极低波动)
- 2008年熊市短债指数持仓172天(55%),贡献主要防御效果
回测验证:
- 组合配置净值173.83,累计收益17283%
- 比单红利低波配置高50%(115.14 → 173.83)
- 2008年少亏23%(-43.87% → -20.85%)
删除旧报告:红利低波防御配置分析报告.md
创建新报告:防御类资产组合配置分析报告.md
|
2026-05-16 22:54:51 +08:00 |
|
|
|
48bf3fde2e
|
fix(rotation): 纠正指数代码标注,H30269.CSI为红利低波而非国债
配置变更:
- H30269.CSI正确标注为'红利低波'(中证红利低波动指数)
- 移除错误配置的931862.CSI(中证0-9个月国债指数,无对应ETF)
- 红利低波归类为DEFENSIVE大类(防御类资产)
分析发现:
- 之前'双国债配置'实际是红利低波+短债指数组合
- 红利低波具有'类债券'属性(高股息+低波动)
- 但本质仍是股票,熊市防御有限
- 短债指数才是2008年防御的主力
收益对比:
- 错误标注(双国债): 净值173.83
- 正确配置(红利低波): 净值115.14
- 差异原因:移除短债指数后防御能力下降
删除旧报告:国债配置实证分析报告.md
创建新报告:红利低波防御配置分析报告.md
|
2026-05-16 22:41:53 +08:00 |
|
|
|
306b4022da
|
feat(rotation): 增加双国债配置实现动态久期选择
配置变更:
- 移除错误标注的H30269.CSI(标注为中证红利低波)
- 添加10年国债(931862.CSI)配置,久期8年,波动3-5%
- 添加30年国债(H30269.CSI)配置,久期20年,波动10-15%
- 修正ETF映射:931862→512890, H30269→511090
实证分析结果:
- 方案C(双国债)累计收益17283%,为三种配置最优
- 动态久期选择机制:牛市选30年获取弹性,熊市选10年增强防御
- 债券持仓占比43.9%(10年18.6% + 30年25.3%)
- 详细分析见 docs/experiments/国债配置实证分析报告.md
|
2026-05-16 22:21:27 +08:00 |
|
|
|
63c56f0001
|
feat(execution): 回测调仓事件记录功能增强
新增调仓事件记录功能,详细记录每次调仓的信息:
核心改进:
1. BacktestExecutor新增_apply_trade_cost_with_events方法
- 记录每次调仓的基本信息(持仓变化、调入调出标的)
- 记录换手率、调仓成本、持仓天数、当日收益
2. 新增_enrich_rebalance_events方法
- 补充净值信息(调仓前净值、调仓后净值、净值变化%)
3. strategy.py保存调仓记录到CSV
- 新增rebalances.csv文件
- 返回结果包含rebalance_events
调仓记录字段:
- 调仓前持仓、调仓后持仓
- 调入标的、调出标的
- 换手率、调仓成本
- 持仓天数、当日收益
- 调仓前净值、调仓后净值、净值变化%
应用场景:
- 分析每次调仓对收益的影响
- 评估调仓决策质量
- 统计调仓频率与效果
|
2026-05-16 21:15:31 +08:00 |
|