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/ 例外
- 允许提交对齐器相关文件
This commit is contained in:
2026-05-24 10:29:20 +08:00
parent a16681bda9
commit 5f08e508ac
6 changed files with 2613 additions and 4 deletions

View File

@@ -0,0 +1,331 @@
# 跨市场数据对齐方案
## 📋 问题背景
在跨市场 ETF 轮动策略中,不同市场的交易日历不同:
| 市场 | 典型假日 | 影响 |
|------|----------|------|
| A 股 | 春节、国庆 | 每年约 115 个交易日 |
| 美股 | 马丁路德金日、感恩节 | 每年约 252 个交易日 |
| 港股 | 佛诞日、圣诞节 | 每年约 250 个交易日 |
**核心问题**:如何在不同交易日历之间对齐因子和收益率?
---
## ❌ 常见错误
### 错误 1先计算收益率再 ffill
```python
# ❌ 错误
returns = close.pct_change()
returns_aligned = returns.reindex(a_share_dates, method='ffill')
# 问题:
日期 价格 收益率 A股日历 对齐后收益率
2024-01-01 100 NaN 2024-01-01 NaN
2024-01-02 101 +1% 2024-01-02 +1%
2024-01-03 102 +1% 2024-01-03 +1% 错误复制了前一天的收益率
美股休市价格不变
# 结果A 股交易日"继承"了美股休市期间的收益率
# 导致:净值高估/低估
```
### 错误 2用 ffill 后的价格计算因子
```python
# ❌ 错误
close_aligned = close.reindex(a_share_dates, method='ffill')
factor = close_aligned.rolling(25).apply(weighted_momentum)
# 问题:
# 25 日窗口中包含 2-3 个重复值ffill 填充)
# 导致:
# 1. 因子计算偏差(重复值影响线性回归)
# 2. 动量得分虚高(价格"不变"被误认为稳定)
```
---
## ✅ 正确方案
### 原则 1因子在原始日历计算再对齐
```python
# ✅ 正确
# 1. 在原始交易日历计算因子
factor = close.rolling(25).apply(weighted_momentum) # 美股日历
# 2. 对齐因子值到 A 股日历
factor_aligned = factor.reindex(a_share_dates, method='ffill')
# 为什么正确:
# - 因子计算使用原始日历25 个真实交易日)
# - 对齐的是因子值,不是价格
# - ffill 填充的是"最新因子值",而不是"重复价格"
```
### 原则 2价格先对齐再计算收益率
```python
# ✅ 正确
# 1. 价格对齐到 A 股日历
close_aligned = close.reindex(a_share_dates, method='ffill')
# 2. 计算收益率
returns = close_aligned.pct_change(fill_method=None)
# 为什么正确:
# - 休市日价格不变ffill
# - 收益率 = (今日价格 - 昨日价格) / 昨日价格 = 0%
# - 不会复制前一天的非零收益率
```
---
## 🏗️ CrossMarketAligner 实现
### 核心功能
```python
from framework_v2.shared.data.alignment import CrossMarketAligner
# 初始化
aligner = CrossMarketAligner(target_calendar=a_share_dates)
# 1. 对齐因子值
aligned_factor = aligner.align_factor(
factor_series,
source_calendar=us_dates,
code='^GSPC'
)
# 返回 DataFrame:
# - value: 对齐后的因子值
# - is_filled: 是否为 ffill 填充值
# 2. 对齐收益率
returns = aligner.align_returns(
close_series,
code='^GSPC'
)
# 返回 Series收益率A 股日历)
# 3. 对齐多标的
returns_df = aligner.align_multi_asset({
'^GSPC': close_sp500,
'^IXIC': close_nasdaq,
'931862.CSI': close_bond
})
# 返回 DataFrame所有标的同索引
# 4. 验证信号与收益率对齐
aligned_signals, aligned_returns = aligner.validate_alignment(
signals,
returns_df
)
```
### 验证逻辑
```python
class CrossMarketAligner:
def _validate_factor_alignment(self, aligned, is_filled, code):
"""验证因子对齐"""
# 1. 检查 NaN 比例
nan_ratio = aligned.isna().sum() / len(aligned)
if nan_ratio > 0.1:
warnings.warn(f"{code}: 因子 NaN 比例过高")
# 2. 检查填充比例
fill_ratio = is_filled.sum() / len(is_filled)
if fill_ratio > 0.3:
warnings.warn(f"{code}: 因子填充比例过高")
def _validate_returns(self, returns, code):
"""验证收益率"""
# 1. 检查 NaN 比例
nan_ratio = returns.isna().sum() / len(returns)
if nan_ratio > 0.1:
raise ValueError(f"{code}: 收益率 NaN 比例过高")
# 2. 检查异常值
max_return = returns.abs().max()
if max_return > 0.5: # 单日涨跌 > 50%
warnings.warn(f"{code}: 发现异常收益率")
# 3. 检查索引
if not returns.index.equals(self.target_calendar):
raise ValueError(f"{code}: 收益率索引与目标日历不匹配")
```
---
## 📊 测试验证
### 测试 1因子对齐
```
源日历(美股): 8 天
目标日历A股: 10 天
对齐后因子值:
value is_filled
2024-01-01 0.10 False ← 真实值
2024-01-02 0.15 False ← 真实值
2024-01-03 0.15 True ← ffill 填充
2024-01-04 0.18 False ← 真实值
...
✓ 填充值正确标记
✓ NaN 比例检查通过
```
### 测试 2收益率对齐
```
原始价格(美股日历):
2024-01-01 100.0
2024-01-02 101.0
2024-01-04 102.0 ← 2024-01-03 休市
对齐后收益率A股日历:
2024-01-01 0.000000 ← 首日
2024-01-02 0.010000 ← +1%
2024-01-03 0.000000 ← 0%(休市,价格不变)✓
2024-01-04 0.009901 ← +0.99%
✓ 休市日收益率 = 0%
✓ 无 NaN
✓ 索引匹配 A 股日历
```
### 测试 3ffill 陷阱对比
```
❌ 错误做法:先 pct_change再 reindex
步骤 1 - 收益率:
2024-01-01 NaN
2024-01-02 0.010000
2024-01-04 0.009901
步骤 2 - reindex + ffill:
2024-01-01 NaN
2024-01-02 0.010000
2024-01-03 0.010000 ← 错误!复制了前一天的收益率
2024-01-04 0.009901
✅ 正确做法:先 reindex 价格,再 pct_change
步骤 1 - 价格 reindex:
2024-01-01 100.0
2024-01-02 101.0
2024-01-03 101.0 ← ffill价格不变
2024-01-04 102.0
步骤 2 - pct_change:
2024-01-01 0.000000
2024-01-02 0.010000
2024-01-03 0.000000 ← 正确!收益率 = 0%
2024-01-04 0.009901
```
### 测试结果
```
============================================================
测试总结
============================================================
✓ 通过 - 因子对齐
✓ 通过 - 收益率对齐
✓ 通过 - 多标的对齐
✓ 通过 - 信号与收益对齐
✓ 通过 - ffill 陷阱
总计: 5/5 通过
```
---
## 🎯 在策略中使用
### 完整流程
```python
from framework_v2.shared.data.alignment import CrossMarketAligner
class RotationStrategy(StrategyBase):
def run_backtest(self, data: dict) -> dict:
# 1. 获取数据
index_data = data['index_data']
a_share_dates = data['a_share_dates']
valid_codes = data['valid_codes']
# 2. 创建对齐器
aligner = CrossMarketAligner(target_calendar=a_share_dates)
# 3. 计算因子(原始日历)→ 对齐到 A 股日历
factor_dict = {}
for code in valid_codes:
close_series = index_data[code]['close']
# 在原始日历计算因子
factor_series = self._factor.compute(
pd.DataFrame({'close': close_series})
)
# 对齐到 A 股日历
aligned = aligner.align_factor(
factor_series,
source_calendar=close_series.index,
code=code
)
factor_dict[code] = aligned['value']
factor_df = pd.DataFrame(factor_dict)
# 4. 生成信号
signals = self._selector.generate(factor_df)
# 5. 计算收益率(价格对齐 → 收益率)
returns_df = aligner.align_multi_asset({
code: index_data[code]['close']
for code in valid_codes
})
# 6. 验证信号与收益率对齐
aligned_signals, aligned_returns = aligner.validate_alignment(
signals,
returns_df
)
# 7. 执行回测
...
```
---
## ⚠️ 注意事项
1. **填充值标记**`is_filled` 列标记哪些是 ffill 填充的,可用于后续分析
2. **NaN 处理**:收益率对齐后自动填充为 0表示"无数据,收益率为 0"
3. **异常检测**:单日收益率 > 50% 会发出警告
4. **索引验证**:对齐后严格验证索引是否匹配目标日历
5. **统计信息**:通过 `aligner.get_stats()` 获取对齐统计
---
## 📚 相关文档
- **[数据架构方案](DATA_ARCHITECTURE.md)** - 完整的数据架构设计
- **[数据流完整推演](DATA_FLOW_DEMO.md)** - 从 OHLCV 到最终收益的 7 个阶段推演
- **[框架 V2 README](README.md)** - 框架总览
- 实现:`framework_v2/shared/data/alignment.py`
- 测试:`framework_v2/tests/test_alignment.py`
---
*创建日期: 2026-05-06*
*版本: 1.0.0*