什么是协整配对交易?#
协整(Cointegration)是Engle & Granger在1987年提出的概念,指两个非平稳时间序列的线性组合是平稳的。在量化交易中,这意味着两只股票的价差会均值回归。
与相关性交易的区别#
| 维度 | 相关性交易 | 协整交易 |
|---|---|---|
| 理论基础 | 价格同涨同跌 | 长期均衡关系 |
| 平稳性 | 不需要 | 价差必须平稳 |
| 持仓逻辑 | 趋势跟随 | 均值回归 |
| 适用场景 | 牛市/熊市 | 震荡市 |

协整检验的实战流程#
Step 1: 平稳性检验(ADF Test)#
from statsmodels.tsa.stattools import adfuller
def adf_test(series, title=''):
"""Augmented Dickey-Fuller检验"""
result = adfuller(series, autolag='AIC')
print(f'ADF Statistic: {result[0]:.4f}')
print(f'p-value: {result[1]:.4f}')
if result[1] <= 0.05:
print("→ 平稳(拒绝原假设)")
return True
else:
print("→ 非平稳(接受原假设)")
return False
# 检验两只股票的价差
spread = np.log(stock_a) - np.log(stock_b) * hedge_ratio
is_stationary = adf_test(spread, 'Spread')pythonStep 2: 协整检验(Johansen Test)#
from statsmodels.tsa.vector_ar.vecm import coint_johansen
def johansen_test(stock_a, stock_b):
"""Johansen协整检验"""
data = pd.DataFrame({
'stock_a': np.log(stock_a),
'stock_b': np.log(stock_b)
})
result = coint_johansen(data, det_order=0, k_ar_diff=1)
# 读取迹统计量
trace_stat = result.lr1
critical_value = result.cvt[:, 1] # 95%置信度
if trace_stat[0] > critical_value[0]:
print("→ 存在协整关系(拒绝原假设)")
return True
else:
print("→ 不存在协整关系")
return FalsepythonStep 3: 计算对冲比率(Hedge Ratio)#
使用动态滚动窗口OLS估计对冲比率:
def rolling_hedge_ratio(stock_a, stock_b, window=60):
"""滚动窗口估计对冲比率"""
hedge_ratios = []
for i in range(window, len(stock_a)):
y = np.log(stock_a.iloc[i-window:i])
x = np.log(stock_b.iloc[i-window:i])
model = OLS(y, x).fit()
hedge_ratios.append(model.params[0])
return pd.Series(hedge_ratios, index=stock_a.index[window:])pythonA股10个经典协整案例#
案例1:中国平安 vs 中国太保(保险双雄)#
协整关系:2010-2025年,对冲比率1.15,价差Z-score均值回归
交易规则:
- 做多平安 + 做空太保(当Z-score < -2)
- 平仓条件:Z-score回归到±0.5以内
回测绩效(2015-2025):
- 年化收益:12.3%
- 夏普比率:1.85
- 最大回撤:-8.7%
- 胜率:68%

案例2:贵州茅台 vs 五粮液(白酒CP)#
协整关系:高端白酒双龙头,对冲比率0.85
特点:
- 价差在春节前扩大(茅台更强)
- 6-8月价差收窄(白酒淡季)
季节性策略:
# 添加月份哑变量
df['month'] = df.index.month
seasonal_spread = spread - df['month'].map(monthly_mean_spread)python案例3:招商银行 vs 兴业银行(股份制银行)#
协整关系:2012-2025年,对冲比率1.08
风险事件:
- 2019年包商银行事件 → 兴业跌幅更大(同业负债占比高)
- 2020年疫情 → 招商零售优势显现,价差扩大
改进:加入宏观因子作为协变量
X = sm.add_constant(df[['spread', 'interbank_rate', 'lending_spread']])
model = sm.OLS(spread, X).fit()python案例4:海天味业 vs 中炬高新(酱油双雄)#
协整关系:调味品赛道,对冲比率0.92
断裂事件:
- 2021年海天添加剂事件 → 协整关系暂时断裂
- 2022年恢复(消费者回归理性)
启示:基本面突变会导致协整断裂,需要协整稳定性检验
案例5:宁德时代 vs 比亚迪(新能源双子星)#
协整关系:2020-2023年强协整,2024年后减弱
原因:
- 宁德专注动力电池,比亚迪全产业链布局
- 2024年比亚迪出海加速,与宁德业务逻辑分化
动态协整:使用BEKK-GARCH模型捕捉时变协整关系
案例6:三一重工 vs 中联重科(工程机械)#
协整关系:基建周期驱动,对冲比率1.25
宏观敏感度:
- 房地产开发投资增速 ↑ → 价差收窄(三一弹性更大)
- 专项债发行 ↑ → 价差扩大
策略增强:加入PMI、挖掘机销量作为外生变量
案例7:美的集团 vs 格力电器(家电双霸)#
协整关系:白电双龙头,但对冲比率不稳定(0.8-1.2波动)
结构性变化:
- 2016年格力造车失败 → 价差扩大
- 2020年美的数字化转型 → 估值重塑
应对:使用**门槛协整(Threshold Cointegration)**模型
案例8:恒瑞医药 vs 复星医药(医药双雄)#
协整关系:2015-2020年强协整,2021年集采冲击后断裂
政策风险:
- 仿制药集采 → 恒瑞受冲击更大(仿制药占比高)
- 创新药谈判 → 两家都面临降价压力
启示:强监管行业慎用统计套利
案例9:中信证券 vs 华泰证券(券商双雄)#
协整关系:牛市中协整增强,熊市中协整减弱
市场机制:
- 2015年牛市 → 两家 beta 高度相关
- 2018年熊市 → 差异化战略(中信机构业务 vs 华泰零售业务)
时变对冲比率:
# GARCH(1,1) 估计时变对冲比率
from arch import arch_model
model = arch_model(spread, vol='Garch', p=1, q=1)
result = model.fit()
conditional_vol = result.conditional_volatilitypython案例10:长江电力 vs 中国核电(电力双雄)#
协整关系:公用事业属性,对冲比率1.05,最稳定
低风险低收益:
- 年化收益:6-8%
- 夏普比率:2.5+
- 最大回撤:-4%以内
适合场景:资金成本高(如保险资金)、风险偏好低的投资者
实战中的10个坑#
坑1:伪回归(Spurious Regression)#
现象:两个独立随机游走,回归R²却很高
解决:必须做ADF检验 + 协整检验
坑2:结构断裂(Structural Break)#
现象:协整关系突然消失
解决:使用Zivot-Andrews检验检测断裂点
坑3:幸存者偏差#
现象:只看到成功的配对,失败的配对被遗忘
解决:样本外测试 + 前视偏差检查
坑4:交易成本侵蚀收益#
现象:高频调仓,手续费吃掉利润
解决:加入交易成本约束,降低调仓频率
坑5:卖空限制#
现象:A股做空成本高(融券费率8-12%)
解决:使用股指期货对冲或期权对冲
坑6:流动性风险#
现象:小盘股配对,冲击成本高
解决:限制度小(市值>100亿)+ 换手率筛选
坑7:过拟合#
现象:样本内完美,样本外拉胯
解决:Walk-Forward分析 + 样本外验证
坑8:黑天鹅事件#
现象:2020年疫情,所有配对同时失效
解决:多策略组合 + 止损机制
坑9:参数敏感性#
现象:Z-score阈值从2.0改成2.5,绩效大幅变化
解决:参数鲁棒性测试
坑10:数据挖掘偏差#
现象:试了1000对股票,只报告10对有效的
解决:Benjamini-Hochberg程序控制False Discovery Rate
实盘部署要点#
1. 实时监控面板#
import dash
import plotly.graph_objs as go
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Graph(id='spread-zscore'),
dcc.Graph(id='cumulative-pnl'),
html.Table(id='open-positions')
])
# 实时更新价差Z-score
@app.callback(
Output('spread-zscore', 'figure'),
[Input('interval-component', 'n_intervals')]
)
def update_spread():
# 从数据库读取最新价差
spread = fetch_latest_spread()
zscore = (spread - spread.mean()) / spread.std()
fig = go.Figure()
fig.add_trace(go.Scatter(x=spread.index, y=zscore))
fig.add_hline(y=2, line_dash='dash', line_color='red')
fig.add_hline(y=-2, line_dash='dash', line_color='green')
return figpython2. 自动交易执行#
from qclaw_trade_api import TradeAPI # 假设的量化交易API
class PairsTradingBot:
def __init__(self, pair, zscore_threshold=2.0):
self.stock_a, self.stock_b = pair
self.threshold = zscore_threshold
self.api = TradeAPI()
def check_signal(self):
zscore = self.calculate_zscore()
if zscore < -self.threshold:
# 做多A,做空B
self.api.place_order(self.stock_a, 'BUY', size=10000)
self.api.place_order(self.stock_b, 'SELL', size=10000*self.hedge_ratio)
elif zscore > self.threshold:
# 做空A,做多B
self.api.place_order(self.stock_a, 'SELL', size=10000)
self.api.place_order(self.stock_b, 'BUY', size=10000*self.hedge_ratio)
elif abs(zscore) < 0.5:
# 平仓
self.api.close_all_positions()python3. 风险管理#
# 止损规则
if unrealized_pnl < -0.02: # 亏损超过2%
self.api.close_all_positions()
send_alert("Pairs trading stop loss triggered")
# 最大持仓时间
if holding_days > 20: # 持仓超过20天
self.api.close_all_positions()
send_alert("Pairs trading max holding period reached")
# 协整关系断裂检测
if adf_pvalue > 0.05: # 价差不再平稳
self.api.close_all_positions()
send_alert("Cointegration broken, closing positions")python总结:协整配对交易的精髓#
- 严谨的统计检验:ADF + Johansen + 稳定性检验
- 动态调整:时变对冲比率 + 门槛模型
- 风险管理:止损 + 最大持仓时间 + 协整断裂检测
- 成本控制:降低换手 + 优化执行
“统计套利不是印钞机,而是需要持续监控和适应的生态系统。” —— 量化交易的残酷真相
参考文献#
- Engle & Granger (1987), “Co-integration and Error Correction”
- Vidyamurthy (2004), “Pairs Trading: Quantitative Methods and Analysis”
- Ganapathy (2004), “Statistical Arbitrage in the U.S. Equities Market”
- Pole (2007), “Statistical Arbitrage: Algorithmic Trading Insights”