配对交易 · 协整
直觉
配对交易是统计套利的经典:找两只「长期绑在一起」的股票(比如同一行业的龙头),它们的价差虽然时大时小,但总会回归均值。价差拉得太开就「做空强的、做多弱的」,赌它收敛。关键前提是两只股票协整(cointegrated)——各自乱走,但差值平稳。
协整与价差
两只股票价格 各自是非平稳的,但若存在 使
平稳(均值与方差恒定),则称二者协整, 是对冲比率。
「人话」解释:协整 vs 相关,区别在哪?
相关是「同涨同跌」——两个都涨的序列相关性可能很高。 协整更强:要求「差值」稳定在某个水平附近,不会越走越远。 两条同时上涨的线可能高度相关、但差值发散(不协整);只有差值「像橡皮筋拉住」的,才能做配对交易——因为你可以确信「价差终会回来」。
交易规则(z-score 进出场)
用价差的滚动 z-score:
- → 价差过低,做多价差(多 、空 );
- → 价差过高,做空价差;
- → 回归,平仓。
可运行案例:AAPL / MSFT 配对(合成数据)
读取 aapl_msft_daily.csv,OLS 估计 ,构造价差与 z-score,按规则交易。价差组合的收益 = 。
:::caution 合成数据 本数据刻意协整(真值 ,价差 ADF )。你估计出的 会偏离真值——这正是「估计误差」的教学点,并非套利 bug。 :::
import quant
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('/data/aapl_msft_daily.csv', parse_dates=['date']).set_index('date')
pa, pm = df['aapl_close'], df['msft_close']
la, lm = np.log(pa), np.log(pm)
# OLS: la ~ a + b*lm
x, y = lm.values, la.values
b = np.cov(x, y, ddof=0)[0,1] / np.var(x)
a = y.mean() - b * x.mean()
spread = la - (a + b*lm) # 残差(近似平稳)
print(f"估计 β = {b:.3f} (合成数据真值 ≈ 1.30)")
z = (spread - spread.rolling(60).mean()) / spread.rolling(60).std()
raw = pd.Series(np.nan, index=pa.index)
raw[z < -2.0] = 1.0
raw[z > 2.0] = -1.0
raw[z.abs() < 0.5] = 0.0
signal = raw.ffill().fillna(0.0)
# 价差组合收益 = aapl 对数收益 − β*msft 对数收益
ra = np.log(pa / pa.shift(1)); rm = np.log(pm / pm.shift(1))
spread_ret = (ra - b*rm).fillna(0.0)
position = signal.shift(1).fillna(0.0) # 防前视(同引擎约定)
strat_ret = (position * spread_ret).fillna(0.0)
equity = (1 + strat_ret).cumprod()
print(quant.performance_summary(equity, strat_ret, freq=252).round(3))
fig, ax = plt.subplots(2, 1, figsize=(9, 5), sharex=True,
gridspec_kw={'height_ratios':[2,1]})
ax[0].plot(z, color='#2563eb', lw=0.9); ax[0].axhline(2, color='r', ls='--', lw=0.8)
ax[0].axhline(-2, color='g', ls='--', lw=0.8); ax[0].axhline(0, color='gray', lw=0.8)
ax[0].set_title('价差 z-score(红=做空价差, 绿=做多价差)')
ax[1].plot(equity, color='crimson'); ax[1].set_title('配对策略资金曲线')
plt.tight_layout(); plt.show()
动手改一改:拖动阈值即时看回测
拖动进场阈值 与平仓阈值——阈值越小交易越频繁、成本越敏感;越大越挑剔、信号越少。
# ParamSlider(SSR 预览)
参数: ENTRY, EXIT
import quant
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('/data/aapl_msft_daily.csv', parse_dates=['date']).set_index('date')
pa, pm = df['aapl_close'], df['msft_close']
la, lm = np.log(pa), np.log(pm)
x, y = lm.values, la.values
b = np.cov(x, y, ddof=0)[0, 1] / np.var(x)
a = y.mean() - b * x.mean()
spread = la - (a + b * lm)
z = (spread - spread.rolling(60).mean()) / spread.rolling(60).std()
raw = pd.Series(np.nan, index=pa.index)
raw[z < -ENTRY] = 1.0
raw[z > ENTRY] = -1.0
raw[z.abs() < EXIT] = 0.0
signal = raw.ffill().fillna(0.0)
ra = np.log(pa / pa.shift(1)); rm = np.log(pm / pm.shift(1))
spread_ret = (ra - b * rm).fillna(0.0)
strat = (signal.shift(1).fillna(0.0) * spread_ret).fillna(0.0)
eq = (1 + strat).cumprod()
print(quant.performance_summary(eq, strat, freq=252).round(3))
plt.figure(figsize=(9, 3.4))
plt.plot(eq, color='crimson')
plt.title(f'配对策略 进场 |z|>{ENTRY} 平仓 |z|<{EXIT}'); plt.ylabel('净值')
plt.tight_layout(); plt.show()
进一步:把代码里的对冲比率
b手动改成1.30(真值)再跑——用真值时价差更平稳、回撤往往更小,凸显精确估计 β 的价值。
小结
- 配对交易赌的是协整价差的均值回归;
- 由 OLS 估计,价差 ,z-score 决定进出场;
- 协整 相关——只有差值平稳的「橡皮筋」关系才能套利。