配对交易策略(一):配对交易的核心思想、如何选择配对标的、价差序列的构建与标准化
大家好,欢迎来到统计套利的实战环节。
今天咱们聊一个非常经典、也非常实用的策略——配对交易。说白了,它就是统计套利里最接地气的一种玩法。我刚开始做量化那会儿,第一个实盘跑的策略就是配对交易。嗯,虽然中间踩过不少坑,但回过头来看,它确实是理解市场微观结构最好的入门砖。
一、配对交易的核心思想
配对交易的逻辑其实很简单:找到两只走势高度相关的股票,当它们的价差偏离正常范围时,做多一只、做空另一只,赌价差回归。
你想想看,同行业的两个公司,比如茅台和五粮液,它们的基本面、行业周期、政策影响都差不多。正常情况下,它们的股价走势应该像双胞胎一样同步。但偶尔也会闹别扭——一个涨得多,一个跌得多。这时候,价差就拉大了。
我们的机会就来了:买入被低估的那只,卖出被高估的那只。等它们重新步调一致,我们就平仓获利。
我个人习惯把这种策略叫做「均值回归的亲戚」。它不预测大盘涨跌,只赌两个标的之间的相对关系会修复。说白了,这是一种市场中性策略——大盘涨跌跟你关系不大,你赚的是价差收敛的钱。
我在项目中遇到过一种情况:两只股票基本面完全一样,但就因为某只被游资炒了一波,价差瞬间拉大。我当时按模型信号进场,结果游资撤退后价差果然回归,三天赚了2.3%。但反过来,如果价差拉大是因为基本面真的变了(比如一家公司出了利空),那你就惨了。所以,选对配对标的,比什么都重要。
二、如何选择配对标的
选标的,是配对交易的第一步,也是最关键的一步。我见过太多人随便拉两只股票就开始跑模型,结果亏得莫名其妙。嗯,这里要注意:不是任何两只股票都能配对的。
2.1 同行业、同板块
这是最基本的要求。为什么?因为同行业的公司受相同的宏观因素、政策、原材料价格影响。比如:
- 白酒板块:贵州茅台 vs 五粮液
- 银行板块:招商银行 vs 兴业银行
- 新能源车:比亚迪 vs 宁德时代(虽然不完全一样,但产业链关联度高)
我建议你从申万一级行业分类入手,先锁定一个行业,再挑市值相近、流动性好的两只股票。市值差太大的话,资金容量和波动率都不匹配,配对效果会很差。
2.2 相关性检验
光看行业还不够,还得算算它们的历史相关性。我一般用皮尔逊相关系数,阈值设在0.8以上。低于0.8的,基本不用考虑。
举个例子,我拿茅台和五粮液过去一年的日收益率算了一下:
| 标的 | 茅台 | 五粮液 |
|---|---|---|
| 茅台 | 1.00 | 0.87 |
| 五粮液 | 0.87 | 1.00 |
0.87,不错,可以继续往下走。
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(防止价差不回归)
代码实现:
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 # 价差回归,平仓
四、知识体系总览
为了让你更直观地理解整个流程,我画了一张图:
五、避坑指南
最后,分享几个我亲身踩过的坑:
- 不要用全量历史数据做协整检验。我建议用最近1-2年的数据,太老的数据没有参考价值。
- 注意交易成本。配对交易是双边交易,手续费和滑点加起来很可观。我一般会在Z-score阈值上再加0.5个标准差作为缓冲,避免频繁交易。
- 动态调整对冲比率。对冲比率不是一成不变的。我习惯每20个交易日重新回归一次,更新β值。
- 警惕「伪回归」。有些价差序列虽然通过了ADF检验,但实际交易中就是不回归。这时候要检查一下样本外表现,别被过拟合骗了。
好了,这一章的内容就到这里。配对交易的核心思想、选标的方法、价差构建与标准化,你都应该心里有数了。下一章我们会深入讲具体的交易信号设计和仓位管理,到时候再聊。