价差序列构建:协整检验、价差计算、Z-score标准化

好,咱们接着往下聊。上一章我们把配对选出来了,但选出来只是第一步。你想想看,两个品种的价格走势再像,也不可能完全同步。它们之间的差值——也就是价差——才是我们真正要交易的东西。

我个人习惯把价差序列的构建分成三步走:先做协整检验确认关系,再算价差,最后做Z-score标准化。每一步都有坑,我一个个说。

第一步:协整检验——确认关系不是假的

很多人上来就直接算价差,这是大忌。你想想看,两个品种可能只是碰巧在某段时间走势相似,实际上根本没有稳定关系。这种伪回归会让你亏到怀疑人生。

协整检验,说白了就是验证两个时间序列是否存在长期稳定的线性关系。我常用的方法是Engle-Granger两步法,简单粗暴有效。

核心逻辑:如果两个序列都是非平稳的(比如价格序列),但它们的线性组合是平稳的,那它们就存在协整关系。

具体怎么做?先对两个价格序列做回归,然后检验残差的平稳性。代码长这样:

import statsmodels.api as sm
from statsmodels.tsa.stattools import adfuller

# 假设 price_a 和 price_b 是两个价格序列
# 第一步:回归
X = sm.add_constant(price_b)
model = sm.OLS(price_a, X).fit()
residuals = model.resid

# 第二步:对残差做ADF检验
adf_result = adfuller(residuals)
p_value = adf_result[1]

if p_value < 0.05:
    print("协整关系成立,可以继续")
else:
    print("协整关系不成立,换对儿吧")

这里有个坑,我必须要说。ADF检验的临界值在协整检验里其实需要调整,因为残差是估计出来的。不过在实际项目中,直接用标准临界值做初步筛选问题不大。我一般会把p值阈值设到0.02甚至0.01,宁可漏掉一些机会,也别把假关系放进来。

注意:协整检验对数据长度敏感。太短的数据(比如少于100根K线)检验结果不可靠。我建议至少用半年的日线数据,或者3个月的1小时数据。

第二步:价差计算——选对公式很重要

协整关系确认了,接下来就是算价差。价差的计算方式其实有好几种,我列一下常见的:

方法 公式 适用场景
简单价差 price_a - price_b 两个品种价格接近,比如同品种不同月份
比率价差 price_a / price_b 价格差异大,比如股票和ETF
回归残差 price_a - (α + β * price_b) 协整系数不为1的情况,最常用
对数价差 log(price_a) - log(price_b) 处理波动率随时间变化的情况

我个人最常用的是回归残差法。为什么?因为协整检验时我们已经算出了回归系数β,直接用这个β来构建价差,理论上是最合理的。

# 用协整回归的系数计算价差
beta = model.params['price_b']  # 回归系数
alpha = model.params['const']   # 截距项

spread = price_a - (alpha + beta * price_b)

这里有个细节:β代表两个品种之间的对冲比例。比如β=1.5,意味着每做多1份品种A,就要做空1.5份品种B才能保持中性。这个比例在回测中要固定住,还是动态更新?嗯,这是个好问题。我建议在样本内固定,但定期(比如每周)重新校准一次。

小技巧:如果你发现价差序列有明显的趋势或季节性,可以考虑用对数价差。我在做商品期货套利时经常用这个方法,能有效消除价格水平变化带来的影响。

第三步:Z-score标准化——让价差变得可比较

价差算出来了,但直接看绝对值没意义。比如价差现在是5,这个5算大还是小?你得知道它的历史分布才行。

Z-score标准化就是干这个的。它把价差转换成以0为中心、以标准差为单位的数值。公式很简单:

z_score = (spread - spread_mean) / spread_std

标准化之后,Z-score=2意味着当前价差偏离均值2个标准差。这时候你就可以设定统一的交易阈值了,比如Z-score超过2就开仓,回到0就平仓。

代码实现:

import numpy as np

# 计算滚动均值和滚动标准差
window = 60  # 回看窗口,我一般用60期
spread_mean = spread.rolling(window=window).mean()
spread_std = spread.rolling(window=window).std()

# 计算Z-score
z_score = (spread - spread_mean) / spread_std

避坑指南:滚动窗口的选择很关键。窗口太小,均值和标准差波动太大,信号不稳定;窗口太大,反应迟钝,跟不上市场变化。我一般用20-60期,具体取决于你的交易频率。日线级别用60,小时级别用40,分钟级别用20。

还有一个容易忽略的问题:Z-score的计算要用滚动窗口,而不是全样本。为什么?因为全样本的均值和标准差包含了未来信息,回测时会过拟合。我刚开始做回测时就犯过这个错,回测曲线漂亮得不行,实盘一跑就崩。后来才意识到是未来函数的问题。

完整流程:一张图说清楚

说了这么多,我画个流程图帮你理一下思路:

价差序列构建流程 1. 协整检验 2. 价差计算 3. Z-score标准化 输出:标准化价差序列 关键参数 • ADF检验p值 < 0.05 • 数据长度 ≥ 100期 • 回归残差法最常用 • β系数定期校准 • 滚动窗口20-60期 • 避免全样本计算 • 阈值:±2标准差 • 平仓:回到0附近 —— 实战经验总结 ——

你看,整个流程其实不复杂。但每一步都有细节,忽略任何一个都可能让策略失效。我见过太多人把协整检验当成走过场,随便跑个p值就完事,结果实盘亏得莫名其妙。

最后说一句:价差序列构建好了,下一步就是设定交易规则。但那是后面章节的内容了,咱们先把基础打牢。记住,地基不稳,楼盖得再高也得塌。


专注资料整理