配对交易策略(一):配对交易的核心思想、如何选择配对标的、价差序列的构建与标准化

大家好,欢迎来到统计套利的实战环节。

今天咱们聊一个非常经典、也非常实用的策略——配对交易。说白了,它就是统计套利里最接地气的一种玩法。我刚开始做量化那会儿,第一个实盘跑的策略就是配对交易。嗯,虽然中间踩过不少坑,但回过头来看,它确实是理解市场微观结构最好的入门砖。

一、配对交易的核心思想

配对交易的逻辑其实很简单:找到两只走势高度相关的股票,当它们的价差偏离正常范围时,做多一只、做空另一只,赌价差回归。

你想想看,同行业的两个公司,比如茅台和五粮液,它们的基本面、行业周期、政策影响都差不多。正常情况下,它们的股价走势应该像双胞胎一样同步。但偶尔也会闹别扭——一个涨得多,一个跌得多。这时候,价差就拉大了。

我们的机会就来了:买入被低估的那只,卖出被高估的那只。等它们重新步调一致,我们就平仓获利。

我个人习惯把这种策略叫做「均值回归的亲戚」。它不预测大盘涨跌,只赌两个标的之间的相对关系会修复。说白了,这是一种市场中性策略——大盘涨跌跟你关系不大,你赚的是价差收敛的钱。

核心要点:配对交易不是预测价格方向,而是赌「价差」会回到历史均值附近。

我在项目中遇到过一种情况:两只股票基本面完全一样,但就因为某只被游资炒了一波,价差瞬间拉大。我当时按模型信号进场,结果游资撤退后价差果然回归,三天赚了2.3%。但反过来,如果价差拉大是因为基本面真的变了(比如一家公司出了利空),那你就惨了。所以,选对配对标的,比什么都重要

二、如何选择配对标的

选标的,是配对交易的第一步,也是最关键的一步。我见过太多人随便拉两只股票就开始跑模型,结果亏得莫名其妙。嗯,这里要注意:不是任何两只股票都能配对的

2.1 同行业、同板块

这是最基本的要求。为什么?因为同行业的公司受相同的宏观因素、政策、原材料价格影响。比如:

  • 白酒板块:贵州茅台 vs 五粮液
  • 银行板块:招商银行 vs 兴业银行
  • 新能源车:比亚迪 vs 宁德时代(虽然不完全一样,但产业链关联度高)

我建议你从申万一级行业分类入手,先锁定一个行业,再挑市值相近、流动性好的两只股票。市值差太大的话,资金容量和波动率都不匹配,配对效果会很差。

2.2 相关性检验

光看行业还不够,还得算算它们的历史相关性。我一般用皮尔逊相关系数,阈值设在0.8以上。低于0.8的,基本不用考虑。

举个例子,我拿茅台和五粮液过去一年的日收益率算了一下:

标的 茅台 五粮液
茅台 1.00 0.87
五粮液 0.87 1.00

0.87,不错,可以继续往下走。

小技巧:我一般会滚动计算60天的相关性,而不是用全量数据。因为相关性会随时间变化,用太久远的数据反而会误导你。

2.3 协整性检验

相关性高不等于协整。你可能听过一句话:「相关性不代表因果性」。在配对交易里,协整性才是硬道理

协整性检验,说白了就是看两只股票的价差序列是否平稳。如果价差是平稳的,那它就会围绕均值来回摆动,我们就能做均值回归。如果价差不平稳,那它可能越走越远,你就等着爆仓吧。

我常用的方法是Engle-Granger两步法,或者直接用ADF检验。代码实现很简单:

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

# 假设 price_a 和 price_b 是两只股票的价格序列
# 第一步:回归
price_a = sm.add_constant(price_a)
model = sm.OLS(price_b, price_a).fit()
hedge_ratio = model.params[1]  # 对冲比率

# 第二步:计算价差
spread = price_b - hedge_ratio * price_a['close']

# 第三步:ADF检验
adf_result = adfuller(spread)
print(f'ADF统计量: {adf_result[0]}, p值: {adf_result[1]}')

# p值小于0.05,说明价差平稳,可以配对
if adf_result[1] < 0.05:
    print('价差平稳,适合配对交易')
else:
    print('价差不平稳,换一对试试')

我曾经踩过一个坑:两只股票相关性高达0.92,但协整检验死活过不了。后来发现是因为其中一只股票在样本期内有增发,导致价格结构变了。所以,数据预处理一定要做干净,复权、除权、停牌处理都不能少。

三、价差序列的构建与标准化

选好配对标的之后,下一步就是构建价差序列。价差,就是两只股票价格之间的差值。但直接拿价格相减是不行的,因为价格量级不同。茅台2000块,五粮液150块,直接相减的话,茅台波动一下,价差就炸了。

3.1 构建价差

正确的做法是:先确定对冲比率,再用对冲比率构建价差

对冲比率(Hedge Ratio)就是回归出来的系数。比如上面代码里的 hedge_ratio,它告诉你:买1股股票B,需要卖多少股股票A来对冲。

价差公式:

spread = price_b - hedge_ratio * price_a

这样构建出来的价差,理论上是一个平稳序列。它的均值就是0(如果回归时加了截距项,均值就是截距项)。

3.2 标准化价差

有了价差序列,我们还需要把它标准化,变成Z-score。为什么?因为价差的绝对值没有意义,我们需要知道它偏离均值多少个标准差,才能判断「偏离程度」。

Z-score公式:

z_score = (spread - spread.mean()) / spread.std()

标准化之后,Z-score的均值是0,标准差是1。这样我们就可以设定统一的交易阈值:

  • 开仓阈值:Z-score > 2 或 < -2(偏离2个标准差)
  • 平仓阈值:Z-score 回归到 0 附近
  • 止损阈值:Z-score > 3 或 < -3(防止价差不回归)
注意:阈值不是固定的。我建议你用滚动窗口计算Z-score,比如用过去60天的数据。因为市场环境在变,价差的均值和标准差也会变。用固定参数的话,策略会慢慢失效。

代码实现:

import pandas as pd
import numpy as np

# 假设 spread 是价差序列
window = 60
spread_rolling_mean = spread.rolling(window=window).mean()
spread_rolling_std = spread.rolling(window=window).std()

z_score = (spread - spread_rolling_mean) / spread_rolling_std

# 生成交易信号
z_score = z_score.dropna()

long_entry = z_score < -2   # 价差被低估,做多价差
short_entry = z_score > 2   # 价差被高估,做空价差
exit_signal = abs(z_score) < 0.5  # 价差回归,平仓

四、知识体系总览

为了让你更直观地理解整个流程,我画了一张图:

配对交易策略知识体系 选择配对标的 同行业、同板块 相关性检验 皮尔逊相关系数 > 0.8 协整检验 ADF检验 p < 0.05 构建价差序列 spread = price_b - β·price_a 标准化价差 Z-score = (spread - μ) / σ 生成交易信号 |Z-score| > 2 开仓,回归平仓 核心逻辑 1. 两只股票走势高度相关 2. 价差围绕均值上下波动 3. 价差偏离均值时开仓 4. 价差回归均值时平仓 5. 市场中性,不赌方向 6. 赚的是价差收敛的钱 7. 风险:价差不回归(基本面变化) 8. 对策:严格止损,动态调整参数

五、避坑指南

最后,分享几个我亲身踩过的坑:

  • 不要用全量历史数据做协整检验。我建议用最近1-2年的数据,太老的数据没有参考价值。
  • 注意交易成本。配对交易是双边交易,手续费和滑点加起来很可观。我一般会在Z-score阈值上再加0.5个标准差作为缓冲,避免频繁交易。
  • 动态调整对冲比率。对冲比率不是一成不变的。我习惯每20个交易日重新回归一次,更新β值。
  • 警惕「伪回归」。有些价差序列虽然通过了ADF检验,但实际交易中就是不回归。这时候要检查一下样本外表现,别被过拟合骗了。
我的习惯:每次跑配对交易之前,我都会先做一遍「样本外回测」。用前80%的数据训练模型,后20%的数据验证。如果样本外表现不行,这个配对我就直接放弃。

好了,这一章的内容就到这里。配对交易的核心思想、选标的方法、价差构建与标准化,你都应该心里有数了。下一章我们会深入讲具体的交易信号设计和仓位管理,到时候再聊。

专注资料整理