当数据说谎:为什么你的量化策略可能只是捕获了一厢情愿的相关性

相关性不等于因果——这是统计学第一课,但也是被违背次数最多的原则。

2012 年,NASA 火星探测器“好奇号”发射升空。同一年,意大利帕勒莫地区冰淇淋销量创历史新高。如果我告诉你这两件事高度相关,你会相信帕勒默人靠吃冰淇淋把钱送到了火星吗?

荒谬。但同样的逻辑每天都在量化交易中以更隐蔽的方式上演。


一、为什么冰淇淋和溺水共享同一个节奏

让我们解剖这个被引用了无数次的经典案例。冰淇淋销量和溺水死亡人数确实呈现正相关,而且相关性相当强——两者都在夏季达到高峰,在冬季跌至谷底。

这种相关性不是谎言。但它也不是你想的那种“联系”。

真正连接这两个变量的是第三个变量:季节。夏季同时驱动了两个结果:更多人买冰淇淋,更多人去游泳。冰淇淋不导致溺水,溺水也不导致冰淇淋。他们只是被同一个原因拖着往前走。

统计学家给这种现象起了名字:伪相关(Spurious Correlation)。这个词第一次系统性地进入学术视野是在 20 世纪初,西利曼·赖特(Sewall Wright)在路径分析中提出:如果两个变量共享一个未观测的共同原因,直接计算它们的相关系数会严重高估它们的“真实联系”。

赖特的核心洞见:一个被忽视的第三变量可以通过两种方式制造伪相关。第一,它同时影响两个变量,让它们看起来在“一起运动”;第二,它掩盖了真实因果关系的方向——你以为 A 导致 B,但其实是 C 同时导致了 A 和 B。

在量化交易的语境下,这比冰淇淋问题危险一百倍。因为冰淇淋-溺水只影响直觉,但伪相关可能让你在真实市场上亏掉真实的钱。


二、量化交易中最常见的伪相关陷阱

2.1 共同驱动因素:流动性与波动率的共舞

想象你发现了一个因子:某科技股的成交量与次日收益率呈现出 +0.35 的相关性。成交量放大,次日往往上涨。这是一个可交易的信号吗?

也许不是。

成交量放大的真正驱动力可能是波动率上升。高波动时期,流动性提供商扩大买卖价差,成交量自然放大;同时,高波动时期市场情绪更不稳定,次日方向更难判断。

如果你只看到了成交量-收益率的表面相关性,你实际上是在交易波动率——但你的因子名称叫"volume"。当你在低波动环境下使用同一个因子时,+0.35 的相关性会迅速蒸发。

诊断方法:控制波动率变量后,重新检验成交量-收益率的相关性。如果相关系数从 0.35 跌至 0.08,说明你捕获的是波动率的影子。

2.2 时间趋势掩盖的真实关系:VIX 与期权溢价

2008 年之后,一个广泛传播的观察是:VIX 指数与标准普尔 500 期权的隐含波动率溢价高度同步。

许多量化策略据此构建:做多 VIX 相关产品,同时买入指数看跌期权,对冲尾部风险。这个结构听起来合理,相关性在过去十年也验证得不错。

但问题在于:这两者可能在同一个趋势里上涨,但原因并不相同。VIX 更多反映的是市场情绪的即时反应,而隐含波动率溢价(实际波动率与隐含波动率的差值)反映的是期权定价效率。两者的时间序列高度相关,可能只是因为市场在压力时期同时推高了两个指标,而压力时期本身就是高危时段。

当相关性在低波动环境下失效时,这类策略的尾部风险往往比回测揭示的要大得多。

2.3 幸存者偏差:当你的基准已经在骗你

你构建了一个多因子模型,回测过去五年,年化收益 23%,夏普比率 1.8。漂亮。

但你有没有想过,你的股票池是什么?

如果在选股时你已经排除了市值低于 5 亿、上市不满三年、日均成交额低于 100 万的标的,你实际上在进行幸存者筛选——你只保留了活下来的公司,而那些死去的公司已经被你的过滤器过滤掉了。

这意味着你的基准本身已经包含了“成功者偏见”。在此基础上发现的相关性,可能是历史筛选机制的产物,而非真实的预测关系。

诊断方法:使用包含已退市股票的完整历史数据库,从过去时点的视角重新构建回测。在 TickDB 的美股历史数据体系中,10 年级别的清洗 K 线数据可以帮助你构建更真实的历史快照。


三、格兰杰因果检验:时间序列的因果侦探

好了,问题是明确的:相关性会撒谎。那么我们怎么判断一个变量是否能“预测”另一个,而不是仅仅“同步”?

统计学提供了一套工具,其中最经典的是格兰杰因果检验(Granger Causality Test)

这个名字本身就容易误导:格兰杰因果并不是真正的因果——它检验的是预测性。如果你用变量 X 的历史信息可以显著提升对变量 Y 未来值的预测精度,格兰杰检验会说“X 对 Y 格兰杰因果”。但这仍然不等于 X 导致了 Y。

但这已经是统计工具能做到的极限了——在没有随机对照实验的情况下,我们无法证明真正的因果,只能验证预测能力。

3.1 核心思想

格兰杰因果的核心假设是:如果 X 导致 Y,那么 X 的历史信息应该包含关于 Y 未来状态的增量信息

用回归的语言来说,假设我们要检验 X 是否对 Y 格兰杰因果:

限制模型:$Y_t = \alpha + \sum_{i=1}^{p} \phi_i Y_{t-i} + \epsilon_t$

无限制模型:$Y_t = \alpha + \sum_{i=1}^{p} \phi_i Y_{t-i} + \sum_{j=1}^{q} \beta_j X_{t-j} + \eta_t$

如果加入 X 的滞后项后,模型的解释力显著提升(通过 F 检验或似然比检验),我们拒绝“X 不格兰杰导致 Y”的零假设。

3.2 金融市场的经典检验场景

场景一:VIX 能预测标普 500 的波动率吗?

直觉上,波动率指数应该包含关于未来实际波动率的信息——但这需要实证验证。

import numpy as np
import pandas as pd
from statsmodels.tsa.api import VAR
from statsmodels.stats.stattools import durbin_watson

class GrangerCausalityAnalyzer:
    """格兰杰因果检验分析器"""
    
    def __init__(self, significance_level=0.05):
        self.alpha = significance_level
        self.results = {}
    
    def test_granger(self, data, x_col, y_col, max_lag=10):
        """
        执行格兰杰因果检验
        
        参数:
            data: DataFrame,包含 x_col 和 y_col
            x_col: 疑似原因的变量名
            y_col: 疑似结果的变量名
            max_lag: 最大滞后阶数(自动选择最优)
        """
        # 构建 VAR 系统 [Y, X]
        endog = data[[y_col, x_col]].dropna()
        
        # 步骤1:确定最优滞后阶数(使用 AIC)
        model = VAR(endog)
        optimal_lag = model.select_order(maxlags=max_lag).aic
        
        # 步骤2:估计无限制模型(包含 X 的滞后)
        unrestricted = model.fit(optimal_lag)
        
        # 步骤3:估计限制模型(不含 X 的滞后)
        restricted = model.fit(optimal_lag, verbose=False)
        
        # 步骤4:似然比检验
        lr_stat = -2 * (restricted.llf - unrestricted.llf)
        from scipy.stats import chi2
        df = unrestricted.k_ar * unrestricted.neqs  # 自由度
        p_value = chi2.sf(lr_stat, df)
        
        # ⚠️ 重要说明:格兰杰因果≠真正因果
        # 格兰杰因果检验的是预测能力,不是因果机制
        # 两个变量可能格兰杰因果相关,但都由第三个变量驱动
        
        self.results[(x_col, y_col)] = {
            'optimal_lag': optimal_lag,
            'lr_statistic': lr_stat,
            'p_value': p_value,
            'significant': p_value < self.alpha,
            'conclusion': f"{x_col} {'格兰杰导致' if p_value < self.alpha else '不格兰杰导致'} {y_col}"
        }
        
        return self.results[(x_col, y_col)]
    
    def run_pairwise_analysis(self, data, columns, max_lag=10):
        """对多变量进行两两格兰杰因果检验"""
        results = {}
        for i, col1 in enumerate(columns):
            for j, col2 in enumerate(columns):
                if i != j:
                    key = (col1, col2)
                    try:
                        results[key] = self.test_granger(data, col1, col2, max_lag)
                    except Exception as e:
                        # ⚠️ 可能遇到平稳性问题,需要先做协整检验
                        results[key] = {'error': str(e)}
        return pd.DataFrame(results).T


# 示例使用
def analyze_vix_predictability(vix_data, realized_vol_data):
    """
    分析 VIX 对未来实际波动率的格兰杰因果关系
    
    注意:实际应用中需要:
    1. 对非平稳序列进行差分或使用 VECM
    2. 检查残差的序列相关性
    3. 考虑结构突变(市场极端时期)
    """
    analyzer = GrangerCausalityAnalyzer(significance_level=0.05)
    
    # 准备数据:日频收盘价
    data = pd.DataFrame({
        'VIX': vix_data,
        'RealizedVol': realized_vol_data
    })
    
    # 执行检验
    result = analyzer.test_granger(data, 'VIX', 'RealizedVol', max_lag=20)
    
    if result['significant']:
        print(f"✓ VIX 对实际波动率有显著预测能力(滞后 {result['optimal_lag']} 天)")
        print(f"  似然比统计量: {result['lr_statistic']:.4f}, p-value: {result['p_value']:.4f}")
    else:
        print(f"✗ VIX 对实际波动率的预测能力不显著(p={result['p_value']:.4f})")
    
    return result

3.3 格兰杰因果的局限性

但你必须清楚,格兰杰因果检验是有前提的

  1. 平稳性要求:如果变量是非平稳的(如价格序列),需要先进行差分。错误的平稳性假设会导致伪回归。

  2. 滞后阶数的主观性:最优滞后阶数的选择影响结果。不同准则(AIC/BIC/HQIC)可能给出不同结论。

  3. 遗漏变量偏误:如果真正的因果变量不在模型中,格兰杰因果检验可能给出误导性结论。

  4. 样本选择性偏差:在不同时期,变量间的关系可能发生结构性变化。2008 年之前的格兰杰关系可能在 2008 之后失效。


四、从伪相关到真信号:验证流程

面对一个相关性信号,如何系统性地排除伪相关的干扰?

4.1 四步验证框架

第一步:物理直觉检验

在计算任何统计量之前,先问自己:这个变量真的有可能影响那个变量吗?传导机制是什么?

如果无法给出物理上合理的解释(比如“成交量上涨导致次日上涨”没有清晰的传导路径),需要更谨慎的证据。

第二步:控制变量后重新检验

引入可能的第三变量,看原始相关性是否消失。如果加入控制变量后相关性大幅下降,说明你捕获的是第三变量的影子。

第三步:格兰杰因果检验

验证预测能力是否真实存在。注意滞后阶数的选择和序列平稳性。

第四步:样本外验证

在非样本区间进行验证。任何在样本内有效的相关性,都必须在样本外重复验证至少两次,才能进入策略池。

4.2 一个具体案例:美元指数与黄金的相关性

过去十年,美元指数(DXY)与黄金价格(XAUUSD)呈现约 -0.7 的强负相关。许多交易者直接使用这个关系进行均值回归交易。

但这个相关性可靠吗?

物理直觉:美元走强通常意味着美国资产吸引力上升,资金流向美元资产,黄金作为无息资产吸引力下降。这个逻辑是成立的,传导机制清晰。

控制变量检验:加入实际利率(名义利率减去通胀)后,美元-黄金的相关性显著减弱——因为实际利率才是驱动黄金的更直接因素,美元只是中间变量。

格兰杰因果:DXY 的滞后项对 XAUUSD 有显著的预测能力,反之亦然(存在双向引导关系)。

结论:美元-黄金的相关性是真实存在的,但它的来源是实际利率,而不是美元本身的内在属性。这意味着当实际利率成为主要矛盾时,美元-黄金的相关性可能失效。


五、你的因子正在捕获什么?

回到最开始的问题:两个变量走势高度一致,就能用来预测吗?

答案取决于你能否回答以下三个问题:

你的信号在控制第三变量后还存在吗? 如果加入流动性、波动率、市场情绪等常见第三变量后,信号消失,说明你捕获的是伪相关。

你的信号有格兰杰因果的支持吗? 如果 X 的历史信息无法提升对 Y 未来值的预测精度,相关性可能只是巧合。

你的信号在非样本期重复验证了吗? 样本内的漂亮相关性是最危险的自欺欺人。

这三个问题不是选答,是必答。任何一题的答案如果是否定的,你的“信号”很可能只是冰淇淋和溺水之间那种看起来很美、但毫无交易价值的统计数据。


下一步行动

如果你想系统性地检验你的因子
访问 tickdb.ai,在 API 控制台使用 /kline 接口获取 10 年级别的美股历史数据,配合 TickDB 提供的 depth 频道和 trades 数据,构建多维度的因子验证框架。历史数据的完整性是排除伪相关的第一步——你需要看到“死去”的股票,而不是只在“活着的”股票上做回测。

如果你习惯用 AI 辅助开发
在 AI 助手中搜索安装 tickdb-market-data SKILL,可以更高效地完成数据获取、因子计算和验证流程。

如果你想了解更多因子有效性评估方法
关注 TickDB 公众号,后续会推出系列文章,涵盖因子 IC 分析、夏普比率分解、样本外验证的系统性方法论。


回测局限性说明:本文涉及的验证方法基于历史数据,不构成未来收益保证。因子有效性会随市场结构变化而漂移,建议持续跟踪因子衰减情况并进行样本外验证。


风险提示:本文不构成任何投资建议。市场有风险,投资需谨慎。