4. 价差建模与均值回归:价差的平稳性检验(ADF检验)、均值回归速度(半衰期)、Ornstein-Uhlenbeck过程简介

做统计套利,说白了就是赌价差会回归。但有个前提——你得先确认这个价差确实有回归的“习惯”。如果它像脱缰的野马一样越跑越远,那你的策略就变成接飞刀了。

我个人习惯,拿到一组价差数据后,第一件事不是建模,而是问三个问题:

  • 它平稳吗?
  • 回归速度有多快?
  • 用什么模型来描述它?

这一章,我们就来逐一拆解。

4.1 平稳性检验:ADF检验

平稳性,简单理解就是价差的统计性质(均值、方差)不随时间变化。如果价差均值一直在漂移,那你没法判断当前是“高”还是“低”。

ADF检验(Augmented Dickey-Fuller Test)是最常用的方法。它的原假设是:序列存在单位根(即不平稳)。

我在项目中遇到过很多次,新手看到p值小于0.05就欢呼“平稳了”,其实没那么简单。ADF检验对滞后阶数很敏感,选错了阶数,结果可能完全相反。

核心判断标准:
  • p值 < 0.05:拒绝原假设,序列平稳
  • p值 ≥ 0.05:无法拒绝原假设,序列不平稳
  • 同时检查检验统计量是否小于1%、5%、10%临界值

下面是我常用的Python实现:

import statsmodels.tsa.stattools as ts

def check_stationarity(spread):
    result = ts.adfuller(spread, autolag='AIC')
    print(f'ADF统计量: {result[0]:.4f}')
    print(f'p值: {result[1]:.4f}')
    print(f'临界值:')
    for key, value in result[4].items():
        print(f'  {key}: {value:.4f}')
    
    if result[1] < 0.05:
        print('结论:价差平稳,适合做均值回归策略')
    else:
        print('结论:价差不平稳,需要差分或换标的')
我的小技巧: 用autolag='AIC'让系统自动选择最优滞后阶数,比手动指定更靠谱。但如果你发现结果不稳定,可以试试BIC准则。

4.2 均值回归速度:半衰期

假设价差现在是偏离的,它需要多久才能回到均值?这个时间就是半衰期(Half-life)。

为什么要关心这个?你想想看,如果半衰期是3天,那你可以放心持仓几天。如果半衰期是3个月,那你的资金成本可能就把利润吃掉了。

半衰期的计算,其实来自Ornstein-Uhlenbeck过程。具体公式是:

半衰期 = ln(2) / θ

其中θ是回归速度参数,可以通过线性回归估计。

我曾经踩过一个坑:用日线数据算出的半衰期是5天,但实际交易中发现价差经常2天就回归了。后来才发现,是因为数据频率太低,丢失了日内回归的细节。所以,半衰期要跟你实际交易的频率匹配

计算代码:

import numpy as np
from sklearn.linear_model import LinearRegression

def half_life(spread):
    spread_lag = spread.shift(1).dropna()
    spread_diff = spread.diff().dropna()
    
    # 对齐数据
    y = spread_diff.values
    x = spread_lag.values.reshape(-1, 1)
    
    model = LinearRegression().fit(x, y)
    theta = -model.coef_[0]
    
    hl = np.log(2) / theta
    return hl, theta

hl, theta = half_life(spread)
print(f'回归速度θ: {theta:.4f}')
print(f'半衰期: {hl:.2f} 天')
注意: 如果计算出的θ接近0或负数,说明价差没有均值回归特性,甚至可能是发散的。这种情况下,不要强行做均值回归策略。

4.3 Ornstein-Uhlenbeck过程简介

Ornstein-Uhlenbeck过程(简称OU过程),是描述均值回归最经典的数学模型。它的数学形式是:

dX(t) = θ(μ - X(t))dt + σdW(t)

别被公式吓到,我来拆解一下:

  • θ(μ - X(t))dt:这是“拉力”项。当X偏离均值μ时,θ会把X往回拉。θ越大,拉力越强。
  • σdW(t):这是随机扰动项。市场总有噪音,σ控制噪音的大小。

说白了,OU过程就是:价差像一个被弹簧拴住的球,弹簧想把它拉回中心,但市场噪音一直在推它

我在实盘中使用OU过程时,最关注三个参数:

参数 含义 对策略的影响
μ(均值) 长期均衡价差 决定开仓和平仓的参考线
θ(回归速度) 回归的强度 决定持仓周期和资金占用
σ(波动率) 随机噪音大小 决定止损宽度和仓位大小

拟合OU参数,除了前面提到的线性回归法,还可以用最大似然估计(MLE)。我个人更推荐MLE,因为它能同时估计所有参数,而且统计性质更好。

import numpy as np
from scipy.optimize import minimize

def ou_mle(spread):
    def neg_log_likelihood(params):
        mu, theta, sigma = params
        n = len(spread)
        dt = 1
        
        # 计算似然函数
        ll = 0
        for i in range(1, n):
            x_prev = spread[i-1]
            x_curr = spread[i]
            mean = x_prev * np.exp(-theta*dt) + mu * (1 - np.exp(-theta*dt))
            var = (sigma**2 / (2*theta)) * (1 - np.exp(-2*theta*dt))
            ll += -0.5 * np.log(2*np.pi*var) - (x_curr - mean)**2 / (2*var)
        
        return -ll
    
    # 初始值
    init_params = [np.mean(spread), 0.5, np.std(spread)]
    result = minimize(neg_log_likelihood, init_params, method='L-BFGS-B')
    
    mu, theta, sigma = result.x
    hl = np.log(2) / theta
    
    return {
        'mu': mu,
        'theta': theta,
        'sigma': sigma,
        'half_life': hl
    }
经验之谈: 我建议每3-6个月重新拟合一次OU参数。市场结构会变,价差的均值、回归速度都会漂移。用滚动窗口拟合,比一次拟合管用得多。

4.4 知识体系总览

下面这张图,把本章的核心逻辑串起来了:

价差建模与均值回归知识体系 价差序列 ADF平稳性检验 半衰期计算 OU过程建模 p值 / 临界值判断 回归速度θ / 半衰期 μ, θ, σ参数 均值回归策略参数

从这张图可以看得很清楚:价差序列进来后,经过ADF检验确认平稳性,再计算半衰期评估回归速度,最后用OU过程完整建模。三个步骤缺一不可。

嗯,这一章的内容就到这里。记住一句话:没有经过平稳性检验的均值回归策略,就是在赌博。下一章我们会聊如何用这些参数构建具体的交易信号,到时候见。


公众号:蓝海资料掘金营,微信deep3321