第28章 价格发现实证研究设计:数据清洗、事件研究、回归分析、稳健性检验

做价格发现研究,说白了就是跟数据较劲。我做了这么多年量化,最深的体会是:80%的时间花在数据清洗上,15%的时间在跑模型,5%的时间在写报告。这一章,我就把实证研究的完整流程拆开揉碎了讲给你听。

28.1 数据清洗:别让垃圾数据毁了你的研究

我记得刚入行那会儿,拿到一批高频数据就急着跑回归。结果R²高达0.99,兴奋得不行。后来发现——数据里混着未来信息。嗯,这种坑我踩过不止一次。

28.1.1 常见的数据污染问题

  • 前视偏差:用未来数据回填历史。比如用收盘价计算当日指标,但收盘价在盘中是未知的
  • 幸存者偏差:只保留当前还在交易的标的。退市的、被收购的全被剔除了
  • 时间戳错位:不同数据源的时间基准不一致。我遇到过某交易所的毫秒级时间戳和另一家的微秒级对不上
  • 异常值:比如某只股票突然涨了1000倍,其实是除权除息没处理
⚠️ 我曾经犯过的错: 用前复权数据做事件研究,结果事件日前后价格出现跳跃。后来才发现,复权调整把事件本身的影响也抹掉了。建议用未复权原始数据做事件研究,复权只用于技术分析。

28.1.2 清洗流程示例

我个人习惯用Python做数据清洗。下面这个流程,我用了好几年,基本没出过大问题:

# 伪代码示例:数据清洗流水线
def clean_market_data(df):
    # 1. 去重
    df = df.drop_duplicates(subset=['symbol', 'timestamp'])
    
    # 2. 时间对齐
    df = df.set_index('timestamp').resample('1min').last()
    
    # 3. 异常值处理
    df['return'] = df['close'].pct_change()
    df = df[df['return'].between(-0.1, 0.1)]  # 剔除涨跌幅超过10%的
    
    # 4. 填充缺失值
    df = df.fillna(method='ffill').fillna(method='bfill')
    
    # 5. 剔除交易量异常的
    df = df[df['volume'] > 0]
    
    return df
💡 小技巧: 清洗完数据后,一定要做可视化检查。把价格序列画出来,肉眼扫一遍。很多异常是统计方法发现不了的,但人眼一眼就能看出来。

28.2 事件研究:捕捉价格对信息的反应

事件研究是价格发现的核心工具。你想知道某个消息出来之后,价格是怎么反应的?用事件研究就对了。

28.2.1 事件窗口设计

我一般把事件窗口分成三段:

窗口名称 时间范围 作用
估计窗口 事件前[-120, -21]天 估计正常收益率
事件窗口 事件前后[-20, +20]天 计算异常收益率
事后窗口 事件后[+21, +60]天 检验价格是否反转

为什么会这样设计?因为太短的窗口可能捕捉不到完整的价格发现过程,太长的窗口又会混入其他噪音。我个人习惯用[-20, +20]作为标准窗口,如果是高频数据,可以缩到[-60min, +60min]。

28.2.2 异常收益率的计算

计算异常收益率,说白了就是「实际收益率 - 预期收益率」。预期收益率怎么算?最常用的有三种模型:

  1. 市场模型:用CAPM回归,Rit = α + βRmt + εit
  2. 市场调整模型:直接用市场收益率作为预期收益率
  3. 均值调整模型:用估计窗口的平均收益率作为预期收益率

我个人的经验: 市场模型虽然最常用,但有个前提——你的β系数要稳定。如果事件前后公司基本面发生重大变化(比如并购重组),β会漂移。这时候用市场调整模型反而更稳健。

28.3 回归分析:量化价格发现的驱动因素

事件研究告诉你「有没有异常收益」,回归分析告诉你「为什么会有」。这两者结合起来,才能讲好一个完整的故事。

28.3.1 核心回归模型

我常用的回归框架是这样的:

# 截面回归:解释累计异常收益率(CAR)
CAR_i = β₀ + β₁*Size_i + β₂*Turnover_i + β₃*Volatility_i + ε_i

# 时间序列回归:解释价格发现贡献度
ΔPrice_t = α + β₁*OrderFlow_t + β₂*NewsSentiment_t + ε_t

你想想看,如果β₁显著为正,说明大市值股票的价格发现效率更高。如果β₂显著为负,说明高换手率的股票反而价格发现更慢——这听起来反直觉,但我在A股市场确实观察到过这种现象。

28.3.2 内生性问题的处理

做回归最怕什么?内生性。比如你发现「新闻情绪」和「价格变化」正相关,但到底是新闻影响了价格,还是价格变化影响了新闻的报道?

我一般用两种方法处理:

  • 工具变量法:用同行业其他公司的新闻情绪作为工具变量
  • 格兰杰因果检验:先检验时间上的先后顺序
⚠️ 注意: 格兰杰因果检验检验的是「预测能力」,不是真正的因果关系。别搞混了。我曾经在报告里写「A格兰杰导致B」,被审稿人怼了一顿。

28.4 稳健性检验:别让你的结论一碰就碎

做实证研究,最怕的是「换个样本、换个时间、换个方法,结论就没了」。稳健性检验就是用来验证你的结论是不是真的靠谱。

28.4.1 常见的稳健性检验方法

检验类型 具体做法 我踩过的坑
样本敏感性 剔除极端值、分样本回归 剔除极端值后结果不显著,说明结论被少数样本驱动
时间敏感性 改变事件窗口长度、换样本期 窗口从[-20,+20]改成[-10,+10]后结果反转
方法敏感性 换异常收益率计算模型 市场模型显著,市场调整模型不显著
聚类标准误 按行业、时间聚类 不聚类时t值3.0,聚类后t值1.2

28.4.2 一个完整的稳健性检验流程

我个人习惯按这个顺序做:

  1. 基础回归:用最标准的方法跑一遍
  2. 剔除极端值:去掉上下1%的样本,看结果是否稳健
  3. 换窗口:把事件窗口从[-20,+20]改成[-10,+10]和[-30,+30]
  4. 换模型:从市场模型换成市场调整模型
  5. 聚类标准误:按行业和年份双聚类
  6. 安慰剂检验:随机生成事件日,看是否还能得到显著结果
💡 避坑指南: 安慰剂检验是个好东西。我曾经做过一个研究,发现某事件后价格显著上涨。结果安慰剂检验发现,随机选1000个事件日,有30%都能得到类似结果。这说明我的「显著结果」纯属偶然。

28.5 知识体系总览

下面这张图,是我做价格发现实证研究的心法总结。你照着这个框架走,基本不会跑偏:

价格发现实证研究框架 第一步:数据准备 原始数据 → 去重 → 时间对齐 → 异常值处理 → 缺失值填充 → 可视化检查 第二步:事件研究 定义事件窗口 → 计算异常收益率 → 累计异常收益率 → 统计检验 第三步:回归分析 截面回归 / 时间序列回归 → 内生性处理 → 工具变量 → 格兰杰因果 第四步:稳健性检验 样本敏感性 → 时间敏感性 → 方法敏感性 → 聚类标准误 → 安慰剂检验 从数据到结论的完整路径

这个框架我用了好几年,每次做实证研究都按这个流程走。你刚开始做的时候,可能会觉得步骤太多、太繁琐。但相信我——跳过任何一步,都可能让你的结论变成废纸

最后说一句: 实证研究不是跑代码,是讲故事。数据清洗是准备素材,事件研究是发现线索,回归分析是构建逻辑,稳健性检验是加固证据。四步走完,你的故事才能站得住脚。