第1章:数据准备与清洗——打好地基才能盖高楼
做指数增强基金业绩归因,说白了就是回答一个问题:基金经理到底有没有超额收益?这超额收益又是从哪来的?
但别急着分析。我见过太多人,一上来就搞各种归因模型,结果数据全是脏的——缺失值、复权没做、收益率序列对不齐。最后分析出来的结论,自己都不信。
所以,咱们先从最基础也最关键的环节开始:数据准备与清洗。
核心观点:数据质量决定了归因分析的上限。数据是脏的,再牛的模型也是白搭。
1.1 获取基金净值数据
基金净值数据,通常可以从Wind、聚宽、Tushare等数据源获取。我个人习惯用Tushare做快速验证,因为免费且方便。
你需要获取的字段包括:
- 基金代码:唯一标识
- 交易日期:注意是交易日,不是自然日
- 单位净值:通常用NAV表示
- 累计净值:考虑了分红、拆分等因素
这里有个坑:很多数据源的单位净值是未复权的。如果你直接用单位净值算收益率,遇到分红或拆分,收益率会突然跳变。
避坑指南:我曾经在分析一只中证500指数增强基金时,发现某天的收益率异常高。查了半天,原来是那天基金分红了,单位净值突然下降,导致收益率计算错误。从那以后,我养成了用复权净值的习惯。
1.2 获取基准指数数据
基准指数数据相对简单。沪深300、中证500这些主流指数,数据源都很全。
你需要获取:
- 指数代码:如000300.SH(沪深300)、000905.SH(中证500)
- 交易日期:与基金数据对齐
- 收盘价:用于计算收益率
嗯,这里要注意:指数数据通常不需要复权,因为指数本身已经考虑了成分股的分红调整。但如果你用的是价格指数(而非全收益指数),那就要小心了——价格指数不考虑分红,会导致基准收益率偏低。
我的建议:尽量使用全收益指数作为基准。如果数据源没有全收益指数,可以用价格指数加上分红调整来近似。我在项目中通常用Wind的"沪深300全收益指数"(H00300.CSI)。
1.3 处理缺失值
数据拿到手,第一件事就是检查缺失值。你想想看,如果某天基金净值缺失,那天的收益率怎么算?
常见的缺失值处理方法:
| 方法 | 适用场景 | 注意事项 |
|---|---|---|
| 前向填充 | 非交易日(如周末、节假日) | 基金净值在非交易日通常不变 |
| 线性插值 | 短期缺失(1-3天) | 适用于指数数据 |
| 删除缺失行 | 缺失比例很低(<1%) | 注意不要删除过多数据 |
| 标记为异常 | 无法确定原因时 | 后续分析中单独处理 |
我个人习惯:先用前向填充处理非交易日,再用线性插值处理短期缺失。如果缺失超过5天,我会标记为异常,回头去查数据源。
1.4 复权净值计算
复权净值,说白了就是把分红、拆分等因素考虑进去,让净值曲线连续。这样算出来的收益率才是真实的。
复权净值的计算公式:
复权因子 = 累计净值 / 单位净值
复权净值 = 单位净值 × 复权因子
举个例子:
| 日期 | 单位净值 | 累计净值 | 复权因子 | 复权净值 |
|---|---|---|---|---|
| 2024-01-01 | 1.000 | 1.000 | 1.000 | 1.000 |
| 2024-06-01 | 1.200 | 1.200 | 1.000 | 1.200 |
| 2024-06-15 | 1.150 | 1.250 | 1.087 | 1.250 |
你看,6月15日基金分红了,单位净值从1.200降到1.150,但累计净值从1.200涨到1.250。复权净值用累计净值来算,收益率就不会出现跳变。
关键点:复权净值 = 累计净值。但有些数据源不提供累计净值,那就需要自己算。我一般用Wind的复权因子API,省时省力。
1.5 收益率序列对齐
最后一步,也是最容易出问题的一步:对齐基金和基准的收益率序列。
为什么要对齐?因为基金和指数的交易日可能不完全一致。比如,某天指数有交易,但基金净值没更新(因为基金暂停申购)。如果不对齐,收益率序列的长度就不一样,后续的回归分析会报错。
对齐的方法:
- 取交集:只保留两者都有数据的日期
- 按日期排序:确保时间顺序一致
- 计算收益率:用复权净值计算日收益率
收益率计算公式:
日收益率 = (当日复权净值 / 前一日复权净值) - 1
代码示例(Python):
import pandas as pd
# 假设 fund_nav 和 index_price 是已经清洗好的DataFrame
# 对齐日期
common_dates = fund_nav.index.intersection(index_price.index)
fund_aligned = fund_nav.loc[common_dates].sort_index()
index_aligned = index_price.loc[common_dates].sort_index()
# 计算日收益率
fund_return = fund_aligned['adj_nav'].pct_change().dropna()
index_return = index_aligned['close'].pct_change().dropna()
# 再次对齐(因为pct_change会丢失第一个值)
common_dates_2 = fund_return.index.intersection(index_return.index)
fund_return = fund_return.loc[common_dates_2]
index_return = index_return.loc[common_dates_2]
避坑指南:我曾经在回测中发现,基金和指数的收益率序列长度差了10天。查了半天,原来是基金在某个时间段暂停了申购,净值没更新。如果直接用原始数据,回归结果会偏差很大。所以,对齐这一步绝对不能省。
1.6 本章知识体系
下面这张图,帮你梳理本章的核心逻辑:
这张图展示了从数据获取到最终对齐的完整流程。每一步都环环相扣,缺一不可。
1.7 本章小结
数据准备与清洗,听起来枯燥,但它是整个业绩归因分析的基石。我做了这么多年量化,最大的体会就是:花80%的时间在数据上,20%的时间在模型上,结果往往是最好的。
记住三个关键点:
- 复权净值:用累计净值或复权因子,避免分红拆分的干扰
- 缺失值处理:前向填充+线性插值,别偷懒
- 收益率对齐:取交集,确保日期一致
下一章,我们会进入真正的归因分析环节。但在此之前,请确保你的数据已经准备好了。否则,后面的分析都是空中楼阁。