3、价差计算与平稳性检验:价差序列构建、ADF检验、协整检验、半衰期计算
做跨品种套利,核心就一句话:找到两个价格会「分久必合」的品种。
怎么找?靠的就是价差。说白了,价差就是两个品种之间的价格差。但这里有个坑——不是所有价差都能用。你得先确认这个价差是「平稳」的,否则你做的套利就是在赌大小,不是在套利。
核心逻辑:价差平稳 → 均值回归 → 套利机会
3.1 价差序列构建
我个人习惯用两种方式构建价差。第一种最简单,直接相减:
# 直接价差
spread = price_A - price_B
第二种是带权重的,适合两个品种波动率差异大的情况:
# 标准化价差
spread = price_A - hedge_ratio * price_B
这个 hedge_ratio 怎么来?我一般用 OLS 回归去算。嗯,这里要注意:回归前最好把数据对齐,别用不同时间频率的数据硬凑。
我在项目中遇到过最坑的事——两个品种明明走势很像,但价差就是不平稳。后来发现,是因为我没考虑分红除权的影响。你想想看,一个品种除权了,另一个没除,价差肯定跳变。
小技巧:构建价差前,先检查两个品种的交易时间是否一致。有些品种夜盘时间不同,会导致价差出现「假突破」。
3.2 ADF检验
ADF检验,全称是 Augmented Dickey-Fuller 检验。名字挺唬人,其实就干一件事:判断你的价差序列是不是平稳的。
什么叫平稳?简单说就是:均值稳定、方差稳定、自协方差只跟时间间隔有关。说白了,就是价差不会越跑越偏。
ADF检验的原假设是「存在单位根」,也就是不平稳。我们想要的是 p 值小于 0.05,拒绝原假设,说明价差平稳。
from statsmodels.tsa.stattools import adfuller
result = adfuller(spread, autolag='AIC')
p_value = result[1]
if p_value < 0.05:
print("价差平稳,可以继续")
else:
print("价差不平稳,需要调整")
我曾经犯过一个错:直接用原始价差跑 ADF,结果 p 值 0.3,以为没机会了。后来同事提醒我,试试对数价差。结果一测,p 值 0.01。为什么?因为对数变换能稳定方差。
避坑指南:ADF检验对滞后阶数敏感。我建议用 AIC 自动选择滞后阶数,别手动设。手动设容易「过拟合」——为了得到平稳结果而故意选参数。
3.3 协整检验
协整检验,说白了就是看两个品种之间有没有长期均衡关系。它比 ADF 更严格——ADF 只看单个序列,协整看的是两个序列的组合。
我常用的方法是 Engle-Granger 两步法:
- 第一步:用 OLS 回归估计 hedge ratio
- 第二步:对残差做 ADF 检验
import statsmodels.api as sm
# 第一步:回归
X = sm.add_constant(price_B)
model = sm.OLS(price_A, X).fit()
hedge_ratio = model.params[1]
# 第二步:残差检验
residual = price_A - hedge_ratio * price_B
result = adfuller(residual)
print(f"协整检验 p值: {result[1]:.4f}")
这里有个细节:Engle-Granger 的临界值跟普通 ADF 不一样。因为残差是从回归中估计出来的,自由度会损失。我建议直接用 statsmodels 里的 coint 函数,它自动处理了临界值问题。
经验之谈:协整检验通过,不代表一定能赚钱。它只说明价差会回归,但没说多久回归。有的品种半年才回归一次,你等得起吗?
3.4 半衰期计算
半衰期,就是价差偏离均值后,回到均值一半所需的时间。这个指标太重要了——它直接决定了你的持仓周期。
怎么算?我一般用 Ornstein-Uhlenbeck 过程来估计:
import numpy as np
def half_life(spread):
spread_lag = spread.shift(1).dropna()
spread_diff = spread.diff().dropna()
# 对齐数据
spread_lag = spread_lag.iloc[1:]
# 回归:Δspread = θ * (μ - spread_lag) + ε
X = sm.add_constant(spread_lag)
model = sm.OLS(spread_diff, X).fit()
theta = -model.params[1]
# 半衰期 = ln(2) / θ
hl = np.log(2) / theta
return hl
hl = half_life(spread)
print(f"半衰期: {hl:.2f} 天")
半衰期短(比如 1-3 天),适合高频套利。半衰期长(比如 20 天以上),你得有耐心,仓位也别太重。
我记得有一次,我测螺纹钢和热卷的价差,半衰期只有 2.5 天。当时觉得太好了,结果实盘发现——滑点吃掉了一半利润。为什么?因为这两个品种流动性太好,价差波动极快,我的策略根本来不及反应。
建议:半衰期 < 5 天的品种,用 tick 级别数据回测。半衰期 > 20 天的,日线就够了。别用错频率,否则回测结果全是假的。
3.5 知识体系总览
下面这张图,是我自己总结的价差分析流程。每次做新策略前,我都会过一遍:
这张图我贴在了办公桌旁边。每次做新策略,先看数据准备有没有坑,再看价差构建是否合理。最后才跑检验。顺序别搞反了——我见过有人先跑协整,再回头处理数据,结果白忙活。
最后提醒:统计检验只是工具,不是圣杯。ADF 通过了、协整也通过了,不代表策略一定能赚钱。你还要考虑交易成本、滑点、流动性。我见过太多人死在「回测完美,实盘崩溃」的路上。
公众号:蓝海资料掘金营,微信deep3321