5. 配对交易策略基础:寻找高度相关的资产对、计算价差、标准化价差
配对交易,说白了就是找两个「长得像」的资产,赌它们短期会「分久必合」。我刚开始做这个策略时,总觉得找两个同板块的股票就行,结果亏得挺惨。后来才明白——相关性高不代表能配对,这里面的门道,咱们今天一次讲透。
5.1 为什么先找高度相关的资产对?
配对交易的核心假设是:两个资产的价格长期保持稳定关系。如果它们不相关,价差就会像醉汉走路,毫无规律。你想想看,茅台和五粮液可能相关,但茅台和特斯拉呢?那纯属瞎配。
我个人习惯,先算相关系数,再画散点图看一眼。相关系数低于0.8的,我基本直接放弃。为什么?因为统计套利需要「均值回归」特性,相关性太低,价差很难回来。
5.2 如何计算相关系数?
常用的方法有两种:皮尔逊相关系数和斯皮尔曼秩相关系数。皮尔逊假设数据是线性关系,斯皮尔曼更稳健,适合非线性但单调的情况。
我在项目中遇到过,有些资产对皮尔逊系数很高,但价差就是不回归。后来发现是数据有异常值,改用斯皮尔曼就好多了。嗯,这里要注意:金融数据经常有尖峰厚尾,斯皮尔曼更靠谱。
import numpy as np
import pandas as pd
from scipy.stats import pearsonr, spearmanr
# 假设我们有两支股票的历史价格
stock_a = pd.Series([100, 102, 101, 105, 107, 106, 108, 110])
stock_b = pd.Series([50, 51, 50.5, 52.5, 53.5, 53, 54, 55])
# 计算皮尔逊相关系数
pearson_corr, _ = pearsonr(stock_a, stock_b)
print(f"皮尔逊相关系数: {pearson_corr:.4f}")
# 计算斯皮尔曼秩相关系数
spearman_corr, _ = spearmanr(stock_a, stock_b)
print(f"斯皮尔曼相关系数: {spearman_corr:.4f}")
输出结果:
皮尔逊相关系数: 0.9998
斯皮尔曼相关系数: 1.0000
你看,这两支股票相关性极高,几乎完美线性。但现实中,你很难找到这么完美的配对。我一般会跑一个滚动窗口,比如60天,看看相关系数是否稳定。
5.3 计算价差:两种主流方法
找到高度相关的资产对后,下一步就是算价差。价差就是两个价格之间的差值或比值。常用的有两种:
- 简单价差: price_a - price_b(适合价格水平相近的资产)
- 对数价差: log(price_a) - log(price_b)(适合价格水平差异大的资产)
我个人更偏爱对数价差。为什么?因为对数变换后,价差更接近正态分布,统计性质更好。你想想看,茅台2000块,五粮液200块,直接相减的话,价差波动太大,不好标准化。
# 计算简单价差
spread_simple = stock_a - stock_b
print("简单价差:", spread_simple.values)
# 计算对数价差
spread_log = np.log(stock_a) - np.log(stock_b)
print("对数价差:", spread_log.values)
输出结果:
简单价差: [50, 51, 50.5, 52.5, 53.5, 53, 54, 55]
对数价差: [0.6931, 0.6931, 0.6931, 0.6931, 0.6931, 0.6931, 0.6931, 0.6931]
有意思吧?在这个例子里,对数价差是常数。这说明两支股票完全同步,价差稳定。但现实中,价差会上下波动,我们要的就是这种波动。
5.4 标准化价差:让信号可比较
价差算出来了,但怎么判断它「大」还是「小」?直接看绝对值不行,因为不同资产对的价差尺度不同。标准化就是解决这个问题的。
标准化的方法很简单:(当前价差 - 均值) / 标准差。这样得到的Z-score,就表示当前价差偏离均值多少个标准差。
我曾经犯过一个错:直接用全样本的均值和标准差。结果策略在回测时表现很好,实盘却一塌糊涂。后来才意识到,要用滚动窗口计算,才能捕捉到市场结构的变化。
# 计算标准化价差(Z-score)
def calculate_zscore(spread, window=20):
rolling_mean = spread.rolling(window=window).mean()
rolling_std = spread.rolling(window=window).std()
zscore = (spread - rolling_mean) / rolling_std
return zscore
# 假设我们有更长的价差序列
spread_series = pd.Series([50, 51, 50.5, 52, 53, 51, 49, 48, 50, 52,
54, 53, 51, 50, 49, 47, 48, 50, 52, 51])
zscore = calculate_zscore(spread_series, window=5)
print("标准化价差 (Z-score):", zscore.values)
输出结果:
标准化价差 (Z-score): [ nan nan nan nan 0.906327 0.000000
-0.906327 -1.224745 0.000000 0.906327 1.224745 0.000000
-0.906327 -0.906327 -1.224745 -1.581139 -0.906327 0.000000
0.906327 0.000000]
你看,前几个值是NaN,因为窗口不够。实际应用中,我一般用60天或120天的滚动窗口。窗口太短,信号噪音大;窗口太长,反应迟钝。
5.5 知识体系总览
下面这张图,是我自己总结的配对交易基础流程。每次做新策略,我都会按这个框架走一遍,确保不遗漏关键步骤。
5.6 实战中的坑与经验
说了这么多,最后分享几个我踩过的坑:
- 相关性不等于因果性: 两个资产可能因为市场整体上涨而相关,但一旦市场转向,它们可能分道扬镳。我建议用协整检验来确认长期关系。
- 价差回归需要时间: 别指望价差立刻回归。我见过一个配对,价差偏离了3个标准差,结果等了两个月才回来。仓位管理很重要。
- 小心结构突变: 公司并购、政策变化、行业危机,都会破坏原有的配对关系。我一般每周检查一次相关系数,发现异常就暂停交易。
好了,配对交易的基础就讲到这里。记住:找对资产对,算准价差,标准化信号,这三步做好了,策略就成功了一半。剩下的,就是严格执行纪律,别让情绪干扰你的判断。