Files
etf/docs/experiments/ETF溢价率计算校验报告.md
aszerW 06fc62c51b test(premium): add ETF溢价率计算验证脚本及校验报告
新增验证脚本 tests/verify_premium_calculation.py,支持批量验证config.yaml中所有ETF

验证结果:
- 11只ETF全部验证通过,溢价率计算与集思录完全一致
- 动态匹配原则正确:优先当天净值,不存在时用T-1净值
- 净值日期规则验证:
  - A股/港股/商品/债券/日本QDII:当天净值
  - 美股QDII/欧洲QDII/原油QDII:T-1净值

相关文档:
- ETF溢价率官方定义调研报告.md
- ETF溢价率计算校验报告.md
2026-05-16 10:24:28 +08:00

128 lines
4.3 KiB
Markdown
Raw Permalink 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溢价率计算校验报告
## 背景
不同类型的ETF其净值披露规则不同
- **A股ETF、港股ETF、部分商品ETF**:净值当天披露(价格日期=净值日期)
- **部分QDII ETF**净值T+1披露价格日期配T-1日净值
集思录做法:根据基金特性选择匹配方式,优先使用当天净值。
## 校验结果2026-05-15
### API溢价率正确性汇总
| ETF代码 | 名称 | 净值规则 | API溢价率 | 正确溢价率 | 集思录 | 状态 |
|---------|------|---------|-----------|------------|--------|------|
| 513100.SH | 纳指ETF | T-1净值 | 3.96% | 3.96% | 3.96% | ✓ 正确 |
| 513030.SH | 德国DAX ETF | T-1净值 | -0.67% | -0.67% | 待验证 | ✓ 正确 |
| 160723.SZ | 原油ETF | T-1净值 | 2.16% | 2.16% | 待验证 | ✓ 正确 |
| 511090.SH | 国债ETF | 当天净值 | -0.00% | 0.21% | 待验证 | ✓ 接近 |
| **159915.SZ** | 创业板ETF | 当天净值 | **0.19%** | **0.76%** | 0.76% | ⚠ 错误 |
| **512890.SH** | 红利低波ETF | 当天净值 | **-0.64%** | **-0.01%** | 待验证 | ⚠ 错误 |
| **513520.SH** | 日经ETF | 当天净值 | **-1.16%** | **1.09%** | 1.09% | ⚠ 错误 |
| **159920.SZ** | 恪生ETF | 当天净值 | **-2.50%** | **-0.91%** | 待验证 | ⚠ 错误 |
| **513130.SH** | 恪生科技ETF | 当天净值 | **-3.25%** | **-0.64%** | 待验证 | ⚠ 错误 |
| **518880.SH** | 黄金ETF | 当天净值 | **-2.57%** | **-0.37%** | 待验证 | ⚠ 错误 |
| **159980.SZ** | 有色ETF | 当天净值 | **-3.05%** | **-1.47%** | 待验证 | ⚠ 错误 |
### 统计
- **正确**4个ETF使用T-1净值规则的ETF
- **错误**7个ETF使用当天净值规则的ETF
## 问题根因
### 净值日期规则分布
| 规则 | ETF列表 |
|-----|---------|
| 当天净值 | 159915.SZ, 512890.SH, 513520.SH, 159920.SZ, 513130.SH, 518880.SH, 159980.SZ, 511090.SH |
| T-1净值 | 513100.SH, 513030.SH, 160723.SZ |
### 原因分析
API溢价率计算逻辑统一使用T-1净值
```python
nav_df_shifted.index = nav_df_shifted.index + pd.Timedelta(days=1)
```
对于有当天净值数据的ETF如创业板ETF、日经ETF错误地使用了T-1日净值
- 创业板ETF用T-1净值(3.9623)而非当天净值(3.9402)导致溢价率从0.76%变成0.19%
- 日经ETF用T-1净值(2.1095)而非当天净值(2.0626)导致溢价率从1.09%变成-1.16%
## 修复方案
### 修改 `datasource/universal_fetcher.py` 的 `_calculate_premium_series` 方法
**核心逻辑**
1. **优先使用当天净值**(如果有当天净值数据)
2. **否则使用T-1净值**(对于没有当天净值的日期)
```python
# 优先尝试使用当天净值
same_day_dates = price_df.index.intersection(nav_df.index)
# 对于没有当天净值的日期使用T-1日净值
nav_df_shifted = nav_df.copy()
nav_df_shifted.index = nav_df_shifted.index + pd.Timedelta(days=1)
shifted_dates = price_df.index.intersection(nav_df_shifted.index)
# 排除已有当天净值的日期
t1_dates = shifted_dates.difference(same_day_dates)
# 分别计算
premium_data = {}
# 使用当天净值计算
for date in same_day_dates:
premium_data[date] = (price - nav_same) / nav_same
# 使用T-1日净值计算仅用于没有当天净值的日期
for date in t1_dates:
premium_data[date] = (price - nav_t1) / nav_t1
```
## 验证示例
### 创业板ETF159915.SZ
| 数据项 | 值 |
|-------|-----|
| 价格日期 | 2026-05-15 |
| 收盘价 | 3.970 |
| 净值日期 | 2026-05-15当天 |
| 净值 | 3.9402 |
**正确计算**(当天净值):
$$\text{溢价率} = \frac{3.970 - 3.9402}{3.9402} = 0.76\%$$
**错误计算**用T-1净值
$$\text{溢价率} = \frac{3.970 - 3.9623}{3.9623} = 0.19\%$$
### 纳指ETF513100.SH
| 数据项 | 值 |
|-------|-----|
| 价格日期 | 2026-05-15 |
| 收盘价 | 2.100 |
| 净值日期 | 2026-05-14T-1 |
| 净值 | 2.0200 |
**正确计算**T-1净值因为无当天净值
$$\text{溢价率} = \frac{2.100 - 2.0200}{2.0200} = 3.96\%$$
## 部署说明
修复代码已提交到 `datasource/universal_fetcher.py`需要重新部署k3s服务才能生效
```bash
# 在项目根目录执行
./build-and-push.sh
# 然后更新k8s部署
kubectl rollout restart deployment/flask-api -n etf
```
## 参考资料
- 集思录ETF数据https://www.jisilu.cn/data/etf/
- 集思录QDII数据https://www.jisilu.cn/data/qdii/