第三章 统计套利基础:协整与均值回归
各位好,欢迎来到第三章。
前两章我们聊了复杂系统的一些基本概念,以及市场里那些看似随机、实则有序的混沌现象。今天,我们终于要进入一个非常具体的实战领域——统计套利。
说白了,统计套利就是利用数学工具,找到两个或多个资产之间那种「长期稳定、短期偏离」的关系。然后,在它们偏离的时候下注,等它们回归的时候收钱。
嗯,听起来是不是有点像「捡漏」?没错,就是捡市场的漏。
3.1 协整:两个时间序列的「舞步」
要理解统计套利,首先得理解协整。
我记得刚入行那会儿,有个老交易员跟我说:「你看这两只股票,走势就像一对舞伴,虽然各自跳得乱七八糟,但步调总是一致的。」我当时觉得这比喻挺玄乎,后来学了协整,才发现人家说的就是数学。
协整的定义:如果两个非平稳的时间序列(比如股价),它们的某个线性组合是平稳的,那我们就说这两个序列是协整的。
举个例子:
- 序列A:随机游走(非平稳)
- 序列B:随机游走(非平稳)
- 但 A - 2*B 是平稳的(围绕一个均值波动)
那A和B就是协整的。
为什么会这样?因为这两个序列背后有一个共同的「隐藏因子」在驱动它们。比如两家同行业的公司,受同样的宏观经济影响,长期来看它们的价差就会保持稳定。
关键点:协整 ≠ 相关性。
相关性高不代表协整。两只股票可能高度相关,但价差却越走越远,那就不适合做配对交易。我见过不少新手上来就找相关系数高的股票,结果亏得一塌糊涂。原因就是——它们不协整。
3.2 均值回归:市场的「橡皮筋」效应
协整给了我们一个「长期关系」,而均值回归给了我们「交易信号」。
想象一下,你把两个协整的资产绑在一起,中间连一根橡皮筋。当它们偏离太远时,橡皮筋就会把它们拉回来。这个「拉回来」的过程,就是均值回归。
我个人习惯用「价差」来观察这个现象。价差 = 资产A的价格 - β * 资产B的价格。这里的β,就是协整系数。
当价差偏离均值超过某个阈值(比如2倍标准差),我们就认为出现了交易机会:
- 价差过高 → 做空价差(卖A买B)
- 价差过低 → 做多价差(买A卖B)
等它回归到均值附近,平仓获利。
避坑指南:我曾经在2015年股灾期间做过一个配对交易,价差偏离到了4倍标准差,我以为机会来了,重仓杀入。结果呢?价差继续偏离,直接爆仓。
后来我才明白:均值回归不是必然发生的。极端行情下,橡皮筋可能会断。所以,止损和仓位管理永远是第一位的。
3.3 配对交易策略的数学框架
好了,理论讲完了,我们来搭一个完整的数学框架。
步骤1:选择配对
通常在同行业、同板块中寻找。比如:可口可乐 vs 百事可乐,或者工商银行 vs 建设银行。
步骤2:检验协整
用Engle-Granger两步法或Johansen检验。我个人常用的是Engle-Granger:
- 对两个序列做回归:Y = α + βX + ε
- 对残差ε做单位根检验(ADF检验)
- 如果残差平稳,则协整成立
步骤3:确定交易阈值
计算价差的均值和标准差。通常设置:
- 开仓阈值:均值 ± 2倍标准差
- 平仓阈值:均值 ± 0.5倍标准差
- 止损阈值:均值 ± 3倍标准差
步骤4:执行交易
当价差触及开仓阈值时,建立头寸。回归到平仓阈值时,平仓。
数学表达:
设价差序列为 S_t = P_A_t - β * P_B_t
交易信号:
- 如果 S_t > μ + 2σ → 做空价差
- 如果 S_t < μ - 2σ → 做多价差
- 如果 |S_t - μ| < 0.5σ → 平仓
3.4 用Python实现一个简单的配对交易回测
纸上谈兵没意思,我们来写点真家伙。
下面这个例子,我用的是招商银行(600036)和兴业银行(601166)的历史数据。这两家都是股份制银行,业务结构相似,理论上应该协整。
import pandas as pd
import numpy as np
import statsmodels.api as sm
from statsmodels.tsa.stattools import adfuller
import matplotlib.pyplot as plt
# 1. 加载数据(假设你已经有了两个DataFrame)
# 这里用模拟数据演示
np.random.seed(42)
dates = pd.date_range('2020-01-01', periods=500, freq='D')
# 模拟两个协整序列
common_trend = np.cumsum(np.random.randn(500)) * 0.01
stock_a = 100 + common_trend + np.random.randn(500) * 0.5
stock_b = 100 + common_trend * 0.8 + np.random.randn(500) * 0.3
df = pd.DataFrame({'A': stock_a, 'B': stock_b}, index=dates)
# 2. 检验协整
def check_cointegration(series1, series2):
# 回归
X = sm.add_constant(series2)
model = sm.OLS(series1, X).fit()
beta = model.params[1]
residuals = model.resid
# ADF检验
adf_result = adfuller(residuals)
p_value = adf_result[1]
return beta, p_value
beta, p_val = check_cointegration(df['A'], df['B'])
print(f'协整系数 β: {beta:.4f}')
print(f'ADF检验 p值: {p_val:.4f}')
if p_val < 0.05:
print('协整关系成立')
else:
print('协整关系不成立')
# 3. 计算价差
df['spread'] = df['A'] - beta * df['B']
mean_spread = df['spread'].mean()
std_spread = df['spread'].std()
# 4. 生成交易信号
df['z_score'] = (df['spread'] - mean_spread) / std_spread
df['signal'] = 0
df.loc[df['z_score'] > 2, 'signal'] = -1 # 做空价差
df.loc[df['z_score'] < -2, 'signal'] = 1 # 做多价差
df.loc[abs(df['z_score']) < 0.5, 'signal'] = 0 # 平仓
# 5. 回测(简化版:假设每次交易1单位)
df['position'] = df['signal'].shift(1) # 第二天开盘执行
df['return'] = df['position'] * df['spread'].diff()
df['cum_return'] = (1 + df['return']).cumprod()
# 6. 结果
print(f'累计收益率: {df["cum_return"].iloc[-1]:.4f}')
print(f'夏普比率: {df["return"].mean() / df["return"].std() * np.sqrt(252):.4f}')
# 画图
plt.figure(figsize=(12, 8))
plt.subplot(2, 1, 1)
plt.plot(df.index, df['spread'], label='价差')
plt.axhline(mean_spread, color='black', linestyle='--')
plt.axhline(mean_spread + 2*std_spread, color='red', linestyle='--')
plt.axhline(mean_spread - 2*std_spread, color='green', linestyle='--')
plt.legend()
plt.subplot(2, 1, 2)
plt.plot(df.index, df['cum_return'], label='累计收益', color='orange')
plt.legend()
plt.show()
注意:上面的代码是简化版,实际回测要考虑:
- 交易成本(佣金、滑点)
- 资金管理(每次交易多少仓位)
- 动态更新协整系数(滚动窗口)
- 多品种配对(不止一对)
我曾经在实盘中发现,如果不考虑滑点,回测曲线漂亮得像假的一样。一上实盘,直接打回原形。所以,回测一定要保守再保守。
3.5 本章知识体系图
下面这张图,是我自己画的一个知识结构,帮你理清思路:
3.6 一些实战中的坑
最后,分享几个我踩过的坑,希望能帮你省点学费:
| 坑 | 表现 | 解决办法 |
|---|---|---|
| 协整关系失效 | 价差不再回归,越走越远 | 定期重新检验协整,设置止损 |
| 过度拟合 | 回测漂亮,实盘拉胯 | 使用样本外数据验证,减少参数 |
| 交易成本 | 频繁交易吃掉利润 | 提高阈值,降低交易频率 |
| 流动性不足 | 无法按预期价格成交 | 选择高流动性品种,避免小盘股 |
我的建议:刚开始做配对交易,别想着赚大钱。先用模拟盘跑三个月,把各种坑都踩一遍。等你对策略的「脾性」摸透了,再上小资金实盘。
记住:统计套利赚的是「概率」的钱,不是「确定性」的钱。你可能会连续亏损10次,但只要第11次赚回来,整体就是盈利的。关键是——你得活到第11次。
公众号:蓝海资料掘金营,微信deep3321