Files
etf/framework_v2/ALIGNMENT_GUIDE.md
aszerW 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

332 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 跨市场数据对齐方案
## 📋 问题背景
在跨市场 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*