3. 数据准备与清洗:归因分析所需的数据源
做归因分析,说白了就是给策略的收益算一笔「明白账」。但账本好不好,全看原始数据干不干净。我见过太多人,模型建得花里胡哨,结果数据一塌糊涂,最后归因出来的结论全是错的。
嗯,这一章我们就来聊聊,归因分析到底需要哪些数据,以及怎么把这些数据收拾得服服帖帖。
3.1 归因分析的四大数据源
我个人习惯把归因分析的数据源分成四类。少一个,分析就不完整。
| 数据源 | 核心字段 | 频率要求 | 常见问题 |
|---|---|---|---|
| 持仓数据 | 证券代码、持仓数量、成本价、市值 | 日频或更高 | 除权除息未调整、停牌数据缺失 |
| 交易数据 | 成交时间、价格、数量、买卖方向 | 逐笔或分钟级 | 撤单记录干扰、成交价与行情价不一致 |
| 基准数据 | 基准指数点位、成分股权重 | 日频 | 指数编制规则变更、权重数据滞后 |
| 因子数据 | 因子值、因子暴露、因子收益率 | 日频或月频 | 幸存者偏差、前视偏差 |
你想想看,如果持仓数据里少了停牌股的复牌信息,那归因出来的行业配置收益可能直接偏掉几个基点。我在项目中遇到过一回,就是因为某只停牌股复牌后连续涨停,但持仓数据没更新,结果归因出来的超额收益全算到了选股上——这明显不合理。
3.2 数据清洗的实战技巧
数据清洗这件事,听起来枯燥,但做不好真的会翻车。我总结了几个高频坑点,你直接拿去用。
3.2.1 持仓数据的除权除息处理
这是最容易被忽略的。股票分红送股后,持仓成本如果不做复权处理,那归因出来的收益全是错的。
# 一个简单的复权处理逻辑
def adjust_holdings(holdings_df, corporate_actions_df):
"""
holdings_df: 持仓数据,包含证券代码、持仓数量、成本价
corporate_actions_df: 公司行为数据,包含除权除息日、送股比例、分红金额
"""
for idx, action in corporate_actions_df.iterrows():
code = action['证券代码']
ex_date = action['除权除息日']
# 找到该股票在除权日之前的持仓
mask = (holdings_df['证券代码'] == code) & (holdings_df['日期'] < ex_date)
# 调整持仓数量(送股)
holdings_df.loc[mask, '持仓数量'] *= (1 + action['送股比例'])
# 调整成本价(分红+送股)
holdings_df.loc[mask, '成本价'] = (
holdings_df.loc[mask, '成本价'] - action['分红金额']
) / (1 + action['送股比例'])
return holdings_df
3.2.2 交易数据的时间对齐
交易数据和行情数据的时间戳往往不一致。比如交易所的逐笔成交是微秒级的,但行情快照是3秒一张。怎么对齐?
我的做法是:以行情快照时间为基准,把交易数据聚合到最近的快照时间点上。
# 交易数据对齐到行情快照
def align_trades_to_snapshot(trades_df, snapshot_times):
"""
trades_df: 逐笔交易数据,包含成交时间、价格、数量
snapshot_times: 行情快照时间列表(如每3秒一个)
"""
# 使用pd.cut将成交时间分桶到最近的快照时间
trades_df['snapshot_time'] = pd.cut(
trades_df['成交时间'],
bins=snapshot_times,
labels=snapshot_times[:-1],
right=False
)
# 按快照时间聚合
aligned = trades_df.groupby('snapshot_time').agg({
'成交价格': 'vwap', # 成交量加权均价
'成交数量': 'sum'
}).reset_index()
return aligned
说白了,就是别让微秒级的交易数据去匹配秒级的行情数据,否则你会多出一堆「幽灵交易」。
3.2.3 基准数据的权重对齐
基准指数的成分股权重是归因分析的核心输入。但很多数据源给的权重是「日末权重」,而你的持仓可能是盘中调整的。这里有个小技巧:用前一天的权重匹配当天的持仓。
3.3 数据对齐的核心逻辑
数据对齐这件事,我画了一张图帮你理解。你看完就明白了。
你看,整个流程其实就三步:清洗 → 对齐 → 输出。但每一步都有细节。
3.4 实战中的避坑指南
我直接给你列几个最常见的坑,你写代码的时候对照着检查。
- 幸存者偏差:因子数据里只包含当前还在的股票,退市的、被ST的全没了。这样算出来的因子收益率会偏高。我曾经因为这个原因,回测出来的因子IC值高达0.15,实盘直接腰斩。
- 前视偏差:用未来的数据去计算当天的因子值。比如用当天的收盘价去算前一天的因子暴露。嗯,这个坑我见过太多人踩了。
- 停牌处理:停牌期间没有交易数据,但持仓数据还在。如果不做特殊处理,归因出来的行业配置收益会失真。我的做法是:停牌期间用「前收盘价」计算持仓市值,不纳入交易归因。
- 分红再投资:基准指数通常假设分红再投资,但你的持仓可能没有。如果不做调整,基准收益和持仓收益的基准点就不一致。
核心原则: 数据清洗不是越复杂越好,而是「够用就好」。你只需要保证:
- 所有数据的时间戳是统一的(比如都用北京时间)
- 所有价格都是复权后的(前复权或后复权,保持一致)
- 所有权重都是归一化的(加起来等于1)
- 所有缺失值都有明确的处理策略(填充、删除、还是标记)
说实话,数据准备这部分占了我整个归因分析项目60%的时间。但一旦把数据管道搭好了,后面的模型验证、结果解读就快多了。你想想看,如果数据本身就有问题,那再牛的归因模型也救不了你。
好了,这一章的内容就到这里。数据准备好了,下一章我们就可以开始真正的归因计算了。
公众号:蓝海资料掘金营,微信deep3321