4. 价差计算与建模:价差公式推导、协整关系检验、统计套利模型(均值回归)

好,咱们进入价差套利最核心的部分。

前面几章我们聊了数据清洗、订单簿对齐,那都是准备工作。真正决定你能不能赚钱的,是价差怎么算、怎么建模。说白了,你得先搞清楚两个品种之间到底有没有“亲戚关系”,然后才能去套利。

4.1 价差公式推导:别小看这个减法

价差公式看起来简单,就是价格相减。但我在实战中吃过亏,所以得跟你仔细说说。

最基本的价差公式:

Spread = P_A - P_B

其中 P_A 和 P_B 是同一时刻两个品种的价格。嗯,这里要注意,价格必须是对齐的,时间戳差1毫秒都不行。我见过有人用1秒K线算价差,结果回测曲线漂亮得很,实盘一跑就亏——因为1秒内的价格波动已经把价差吃掉了。

带系数的价差公式:

现实中,两个品种的波动幅度往往不一样。比如螺纹钢和热卷,波动率可能差30%。这时候直接用减法就不合适了。我个人习惯用回归系数来调整:

Spread = P_A - β * P_B - α

这里的 β 和 α 是通过历史数据回归出来的。β 代表两个品种的“弹性关系”,α 是常数偏移。

避坑指南: 我曾经在股指期货和ETF套利中,直接用1:1的价差公式,结果发现价差序列根本不平稳。后来加上β系数,协整检验才通过。所以,别偷懒,该算系数就算。

对数价差:

有些品种价格跨度大,比如比特币和以太坊,一个几万刀,一个几千刀。直接减法会导致价差被高价品种主导。这时候用对数价差更合理:

Spread = ln(P_A) - ln(P_B)

对数价差的好处是,它把价格尺度归一化了,相当于在比“相对变化”。我在加密货币套利中,几乎只用对数价差。

4.2 协整关系检验:两个品种是不是“一家人”

价差公式有了,但你怎么知道这个价差会回归?万一它越走越远呢?

这就是协整检验要干的事。协整,说白了就是两个不平稳的时间序列,它们的线性组合是平稳的。你想想看,如果两个品种长期来看会“手拉手”一起走,那价差就会围绕某个均值波动——这就是套利的基础。

常用的检验方法:Engle-Granger两步法

我一般用这个方法,简单直接。步骤是这样的:

  1. 第一步: 用OLS回归估计 β 和 α,得到价差序列。
  2. 第二步: 对价差序列做ADF检验,看它是否平稳。

代码实现也不复杂:

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()
beta = model.params[1]
alpha = model.params[0]

# 计算价差
spread = price_a - beta * price_b - alpha

# ADF检验
adf_result = adfuller(spread)
p_value = adf_result[1]

if p_value < 0.05:
    print("协整关系成立,可以套利")
else:
    print("不协整,别碰")

个人经验: p值我一般取0.01,更严格一些。因为实盘中,0.05的显著性水平容易出假信号。我吃过这个亏,回测时协整通过,实盘价差就是不回归,后来发现是伪回归。

Johansen检验:

如果你同时看三个以上品种,Engle-Granger就不够用了。这时候用Johansen检验,它可以一次性检验多个序列之间是否存在多个协整关系。不过,高频交易中我很少用超过两个品种,因为流动性是个大问题。

4.3 统计套利模型:均值回归的实战玩法

协整检验通过了,接下来就是建模型。统计套利的核心假设就是:价差会回归均值。所以,我们得找到价差偏离均值到什么程度时开仓,回归到什么程度时平仓。

经典的均值回归模型:

我把它拆成三步:

  1. 计算价差的均值和标准差: 用滚动窗口,比如过去60分钟的数据。
  2. 设定阈值: 一般用 ±2 倍标准差作为开仓信号,±0.5 倍标准差作为平仓信号。
  3. 执行交易: 价差上穿上轨,做空价差(卖A买B);价差下穿下轨,做多价差(买A卖B)。

代码示例:

import numpy as np

window = 60  # 滚动窗口
spread_mean = spread.rolling(window=window).mean()
spread_std = spread.rolling(window=window).std()

upper_band = spread_mean + 2 * spread_std
lower_band = spread_mean - 2 * spread_std

# 生成信号
signal = np.zeros(len(spread))
signal[spread > upper_band] = -1  # 做空价差
signal[spread < lower_band] = 1   # 做多价差

# 平仓条件:价差回归到均值附近
exit_long = (spread >= spread_mean - 0.5 * spread_std) & (signal.shift(1) == 1)
exit_short = (spread <= spread_mean + 0.5 * spread_std) & (signal.shift(1) == -1)

注意: 滚动窗口的大小很关键。窗口太小,均值和标准差不稳定;窗口太大,反应太慢。我一般用60-120个数据点,具体要看品种的交易频率。比如股指期货1分钟数据,我用120;加密货币1秒数据,我用60。

模型优化:

实战中,光靠固定阈值不够。我习惯加入以下调整:

  • 动态阈值: 根据市场波动率调整阈值倍数。波动率大时,阈值放大到2.5倍;波动率小时,缩到1.5倍。
  • 止损机制: 如果价差偏离超过3倍标准差还不回归,强制平仓。别死扛,我见过有人扛到4倍标准差,结果爆仓了。
  • 持仓时间限制: 如果开仓后30秒内没回归,平仓。高频交易讲究快进快出,持仓太久容易出幺蛾子。

回测与实盘的差异:

嗯,这里要特别提醒你。回测时,价差序列是静态的,你看着它回归得很漂亮。但实盘里,价差可能因为流动性不足、滑点、或者突发事件而“卡住”。我建议你在回测中加入滑点模拟,至少加1个tick的成本。否则,回测收益曲线再好看,也是纸上谈兵。

知识体系图:价差套利建模流程

价差套利建模流程 价差计算 Spread = P_A - β*P_B - α 协整检验 ADF / Johansen 检验 统计套利模型 均值回归策略 线性价差 / 对数价差 回归系数 β、α 估计 Engle-Granger 两步法 p值 < 0.01 通过检验 滚动均值 ± 2σ 阈值 动态阈值 / 止损机制 核心逻辑:价差偏离 → 开仓 → 回归 → 平仓 协整保证长期回归,阈值控制开平仓时机 ⚠ 避坑:伪回归、滑点、流动性不足 回测必须加滑点模拟,实盘必须设止损

这张图把整个流程串起来了。从价差计算开始,到协整检验,再到模型构建,每一步都有坑。你按这个流程走,至少能避开80%的常见错误。

最后说一句: 统计套利不是万能药。它只在市场相对有效、流动性充足的时候好用。遇到极端行情,比如2020年3月的原油暴跌,价差可能彻底失效。所以,永远给自己留一手——仓位管理比模型本身更重要。

蓝海数据掘金营,专注资料整理