第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. 取交集:只保留两者都有数据的日期
  2. 按日期排序:确保时间顺序一致
  3. 计算收益率:用复权净值计算日收益率

收益率计算公式:

日收益率 = (当日复权净值 / 前一日复权净值) - 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%的时间在模型上,结果往往是最好的

记住三个关键点:

  • 复权净值:用累计净值或复权因子,避免分红拆分的干扰
  • 缺失值处理:前向填充+线性插值,别偷懒
  • 收益率对齐:取交集,确保日期一致

下一章,我们会进入真正的归因分析环节。但在此之前,请确保你的数据已经准备好了。否则,后面的分析都是空中楼阁。


蓝海数据掘金营,专注资料整理