重命名13个英文文档为中文: - etf_pool_selection.md → ETF候选池筛选报告.md - etf_tracking_error_calculation.md → ETF跟踪误差计算方法.md - FLASK_SERVICE_SUMMARY.md → Flask服务总结.md - flask_api_README.md → Flask_API接口说明.md - cross_market_effectiveness_survey.md → 跨市场有效性调研.md - etf_rotation_deep_analysis.md → ETF轮动深度分析.md - etf_rotation_framework.md → ETF轮动框架.md - momentum_rotation_survey.md → 动量轮动调研.md - strategy_evolution_report.md → 策略演进报告.md - universal_fetcher_*.md → 通用数据源*.md 同时更新文档内部的交叉引用链接
234 lines
6.1 KiB
Markdown
234 lines
6.1 KiB
Markdown
# ETF跟踪误差计算方法
|
||
|
||
**文档版本**: v1.0
|
||
**创建日期**: 2026-06-19
|
||
**适用范围**: 轮动策略标的池ETF跟踪准确率评估
|
||
|
||
---
|
||
|
||
## 一、定义
|
||
|
||
**跟踪误差(Tracking Error, TE)**:衡量ETF净值收益率与标的指数收益率之间偏离程度的指标,反映基金经理的追踪能力。
|
||
|
||
**核心公式**:
|
||
```
|
||
跟踪误差(TE) = STDEV(每日跟踪偏离度) × √252
|
||
```
|
||
|
||
---
|
||
|
||
## 二、计算步骤
|
||
|
||
### 2.1 数据准备
|
||
|
||
| 数据类型 | 字段 | 来源 | 说明 |
|
||
|---------|------|------|------|
|
||
| ETF单位净值 | `unit_nav` | Tushare `fund_nav` | 必须用单位净值,不能用累计净值 |
|
||
| 基准收盘价 | `close` | Tushare `index_daily` / `fut_daily` / Flask API | 根据标的类型选择数据源 |
|
||
|
||
### 2.2 计算流程
|
||
|
||
```
|
||
步骤1: 获取ETF单位净值序列
|
||
NAV[t], NAV[t-1], NAV[t-2], ...
|
||
|
||
步骤2: 获取基准收盘价序列
|
||
Index[t], Index[t-1], Index[t-2], ...
|
||
|
||
步骤3: 计算ETF日收益率
|
||
ETF_ret[t] = (NAV[t] - NAV[t-1]) / NAV[t-1]
|
||
|
||
步骤4: 计算基准日收益率
|
||
Index_ret[t] = (Index[t] - Index[t-1]) / Index[t-1]
|
||
|
||
步骤5: 计算每日跟踪偏离度
|
||
Deviation[t] = ETF_ret[t] - Index_ret[t]
|
||
|
||
步骤6: 计算偏离度标准差
|
||
Std = STDEV(Deviation序列)
|
||
|
||
步骤7: 年化处理
|
||
TE = Std × √252
|
||
```
|
||
|
||
### 2.3 Python代码示例
|
||
|
||
```python
|
||
import numpy as np
|
||
import pandas as pd
|
||
|
||
def calculate_tracking_error(etf_nav: pd.Series, benchmark_close: pd.Series) -> dict:
|
||
"""
|
||
计算ETF跟踪误差
|
||
|
||
Args:
|
||
etf_nav: ETF单位净值序列(index=date, value=unit_nav)
|
||
benchmark_close: 基准收盘价序列(index=date, value=close)
|
||
|
||
Returns:
|
||
dict: 包含跟踪误差、R²、相关系数等指标
|
||
"""
|
||
# 计算收益率
|
||
etf_ret = etf_nav.pct_change().dropna()
|
||
bench_ret = benchmark_close.pct_change().dropna()
|
||
|
||
# 对齐日期
|
||
common = etf_ret.index.intersection(bench_ret.index)
|
||
if len(common) < 20:
|
||
return None
|
||
|
||
e = etf_ret.loc[common]
|
||
b = bench_ret.loc[common]
|
||
|
||
# 每日偏离度
|
||
daily_deviation = e - b
|
||
|
||
# 跟踪误差 = 标准差 × √252
|
||
tracking_error = daily_deviation.std() * np.sqrt(252)
|
||
|
||
# 其他指标
|
||
correlation = e.corr(b)
|
||
r_squared = correlation ** 2
|
||
|
||
# 累计收益
|
||
etf_cum = (1 + e).prod() - 1
|
||
bench_cum = (1 + b).prod() - 1
|
||
excess = etf_cum - bench_cum
|
||
|
||
return {
|
||
'annual_tracking_error': round(tracking_error * 100, 4), # %
|
||
'correlation': round(correlation, 6),
|
||
'r_squared': round(r_squared, 6),
|
||
'etf_cum_return': round(etf_cum * 100, 2), # %
|
||
'benchmark_cum_return': round(bench_cum * 100, 2), # %
|
||
'excess_return': round(excess * 100, 2), # %
|
||
'common_days': len(common),
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 三、基准数据来源
|
||
|
||
### 3.1 数据源选择
|
||
|
||
| 标的类型 | 示例 | 基准来源 | Tushare接口 | 数据可用性 |
|
||
|---------|------|---------|------------|-----------|
|
||
| A股指数 | 创业板指、红利低波 | 指数收盘价 | `index_daily` | ✅ 完整 |
|
||
| 商品期货 | 黄金、有色金属 | 期货主力合约 | `fut_daily` | ✅ 完整 |
|
||
| 海外指数 | 纳指、恒生、日经、DAX | 指数收盘价 | Flask API (yfinance) | ✅ 完整 |
|
||
|
||
### 3.2 接口调用示例
|
||
|
||
```python
|
||
# A股指数
|
||
index_data = pro.index_daily(
|
||
ts_code='399006.SZ',
|
||
start_date='20250601',
|
||
end_date='20260619'
|
||
)
|
||
|
||
# 商品期货
|
||
futures_data = pro.fut_daily(
|
||
ts_code='AU.SHF',
|
||
start_date='20250601',
|
||
end_date='20260619'
|
||
)
|
||
|
||
# 海外指数(通过Flask API)
|
||
from datasource.flask_api_source import FlaskAPIDataSource
|
||
flask_source = FlaskAPIDataSource()
|
||
index_data = flask_source.fetch('^NDX', '2025-06-01', '2026-06-19')
|
||
```
|
||
|
||
---
|
||
|
||
## 四、关键注意事项
|
||
|
||
### 4.1 必须使用单位净值(unit_nav)
|
||
|
||
| 净值类型 | 含义 | 是否可用 |
|
||
|---------|------|---------|
|
||
| **单位净值** (unit_nav) | 当前每份基金的实际价值 | ✅ **必须用这个** |
|
||
| 累计净值 (accum_nav) | 单位净值 + 历史分红 | ❌ 会虚高规模 |
|
||
|
||
**原因**:累计净值包含了历史分红再投资,会导致规模计算偏大。
|
||
|
||
### 4.2 必须使用标的指数做基准
|
||
|
||
| 基准类型 | 计算结果 | 说明 |
|
||
|---------|---------|------|
|
||
| **标的指数** | 真实跟踪误差 | ✅ 反映基金经理追踪能力 |
|
||
| 另一只ETF价格 | 价格一致性 | ❌ 包含溢价率波动噪声 |
|
||
|
||
### 4.3 年化因子
|
||
|
||
- 使用 **√252**(假设一年252个交易日)
|
||
- 如果使用月度数据,则用 **√12**
|
||
- 如果使用周度数据,则用 **√52**
|
||
|
||
---
|
||
|
||
## 五、校验结果
|
||
|
||
### 5.1 与天天基金数据对比
|
||
|
||
我们用 Tushare 计算的创业板指 ETF 跟踪误差 vs 天天基金官方数据:
|
||
|
||
| ETF代码 | Tushare TE | 天天基金 TE | 差异 |
|
||
|--------|-----------|------------|------|
|
||
| 159948.SZ | 0.3302% | 0.32% | +0.0102% |
|
||
| 159952.SZ | 0.3559% | 0.35% | +0.0059% |
|
||
| 159205.SZ | 0.3698% | 0.36% | +0.0098% |
|
||
| 159977.SZ | 0.3727% | 0.36% | +0.0127% |
|
||
|
||
**平均差异:+0.0091%** → 高度一致
|
||
|
||
### 5.2 结论
|
||
|
||
- Tushare 数据计算的跟踪误差与天天基金官方数据**高度一致**
|
||
- 验证了计算方法的正确性
|
||
- 可用于日常跟踪误差监控
|
||
|
||
---
|
||
|
||
## 六、完整计算脚本
|
||
|
||
参考文件:`rotation/tracking_error_full.py`
|
||
|
||
### 6.1 主要功能
|
||
|
||
- 覆盖轮动策略标的池全部10个标的
|
||
- 自动选择合适的数据源(Tushare指数/期货/Flask API)
|
||
- 批量获取ETF净值数据
|
||
- 计算跟踪误差并排序
|
||
- 与天天基金数据对比校验
|
||
|
||
### 6.2 运行方式
|
||
|
||
```bash
|
||
cd /Users/aszer/code/etf
|
||
python3 rotation/tracking_error_full.py
|
||
```
|
||
|
||
### 6.3 输出结果
|
||
|
||
- JSON文件:`rotation/results/tracking_error_full.json`
|
||
- 包含每个标的下所有ETF的跟踪误差、R²、超额收益等指标
|
||
|
||
---
|
||
|
||
## 七、相关文档
|
||
|
||
- [ETF竞品分析报告](./etf_competitor_analysis_report.md)
|
||
- [跟踪误差校验报告](./tracking_error_validation_report.md)
|
||
- [ETF数据源说明](../datasource/README.md)
|
||
|
||
---
|
||
|
||
## 八、更新日志
|
||
|
||
| 版本 | 日期 | 变更内容 |
|
||
|------|------|---------|
|
||
| v1.0 | 2026-06-19 | 初始版本,包含完整计算方法和校验结果 |
|