配对交易策略
配对交易策略(Pairs Trading)是一种统计套利方法,通过对具有历史协整关系的资产对进行反向操作,捕捉价格偏离与回归过程中的利润。
基本原理
配对交易的核心假设是:两个资产的价格虽然可能短期偏离,但长期会回归到一个稳定的关系。当两个资产的价差偏离历史均值时,做多价格相对偏低的资产,同时做空价格相对偏高的资产。
策略流程
1. 股票配对选择
常用方法包括:
- 最小距离法:寻找价格行为相近的股票
- 产业关联法:选择同行业、相似业务模型的股票
- 基本面匹配:基于公司规模、盈利能力等指标匹配
2. 协整性检验
通过统计检验确认两资产是否存在长期稳定关系:
- ADF检验:对价格差值序列进行单位根检验
- Johansen协整检验:用于多变量协整关系检验
- CADF检验:考虑交叉相关的ADF检验
3. 一元回归分析
构建两资产间的价格关系模型:
Y = β * X + α + ε
其中Y和X为两资产价格,β为对冲比例,α为常数项,ε为残差项。
4. 动态更新
为适应市场变化,需定期更新参数:
- 滚动窗口回归:使用固定历史窗口更新参数
- 卡尔曼滤波:实时动态调整模型参数
- 指数加权:对近期数据赋予更高权重
5. 交易信号生成
设定开仓与平仓触发条件:
- 标准差倍数:价差超过N个标准差时开仓
- 百分位:价差处于历史极端百分位时开仓
- 均值回归速度:考虑价差回归速度设定平仓时机
策略优势
- 市场中性:对冲了系统性风险,理论上不受大盘走势影响
- 波动率较低:相比单边策略,两资产对冲降低了投资组合波动
- 收益相对稳定:不依赖于市场方向判断
风险与挑战
- 协整关系破裂:历史关系可能因基本面变化而失效
- 执行风险:包括滑点、借券费用和做空限制等
- 资金占用:需要同时建立多头和空头仓位
- 长期偏离风险:价差可能在回归前进一步扩大
改进方向
- 多因子增强:结合其他量化因子增强信号质量
- 机器学习优化:使用AI方法预测回归概率和速度
- 多配对组合:构建多个低相关配对降低整体风险
- 自适应参数:根据市场波动率调整参数
实现代码示例
import pandas as pd
import numpy as np
import statsmodels.api as sm
from statsmodels.tsa.stattools import adfuller
# 1. 读取数据
stock1 = pd.Series(...) # 股票1价格序列
stock2 = pd.Series(...) # 股票2价格序列
# 2. 协整检验
def check_cointegration(s1, s2):
# 回归获取对冲比例
model = sm.OLS(s1, sm.add_constant(s2))
results = model.fit()
beta = results.params[1]
# 构建价差序列
spread = s1 - beta * s2
# ADF检验
adf_result = adfuller(spread)
return adf_result[0] < adf_result[4]['5%'], beta, spread
is_cointegrated, hedge_ratio, spread = check_cointegration(stock1, stock2)
# 3. 计算触发信号的阈值
mean = spread.mean()
std = spread.std()
upper_threshold = mean + 2 * std # 开仓阈值
lower_threshold = mean - 2 * std
exit_threshold = mean # 平仓阈值
# 4. 生成交易信号
def generate_signals(spread, hedge_ratio):
signals = pd.DataFrame(index=spread.index)
signals['stock1'] = 0
signals['stock2'] = 0
# 当价差高于上阈值时,做空stock1,做多stock2
signals.loc[spread > upper_threshold, 'stock1'] = -1
signals.loc[spread > upper_threshold, 'stock2'] = hedge_ratio
# 当价差低于下阈值时,做多stock1,做空stock2
signals.loc[spread < lower_threshold, 'stock1'] = 1
signals.loc[spread < lower_threshold, 'stock2'] = -hedge_ratio
# 当价差回归到均值时平仓
signals.loc[abs(spread - mean) < 0.1 * std, 'stock1'] = 0
signals.loc[abs(spread - mean) < 0.1 * std, 'stock2'] = 0
return signals