5. 配对交易策略基础:寻找高度相关的资产对、计算价差、标准化价差

配对交易,说白了就是找两个「长得像」的资产,赌它们短期会「分久必合」。我刚开始做这个策略时,总觉得找两个同板块的股票就行,结果亏得挺惨。后来才明白——相关性高不代表能配对,这里面的门道,咱们今天一次讲透。

5.1 为什么先找高度相关的资产对?

配对交易的核心假设是:两个资产的价格长期保持稳定关系。如果它们不相关,价差就会像醉汉走路,毫无规律。你想想看,茅台和五粮液可能相关,但茅台和特斯拉呢?那纯属瞎配。

我个人习惯,先算相关系数,再画散点图看一眼。相关系数低于0.8的,我基本直接放弃。为什么?因为统计套利需要「均值回归」特性,相关性太低,价差很难回来。

核心要点: 相关系数 > 0.8 是入门门槛,> 0.9 才值得认真考虑。

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天的滚动窗口。窗口太短,信号噪音大;窗口太长,反应迟钝。

避坑指南: 我曾经用30天窗口做螺纹钢和热卷的配对,结果价差频繁触发信号,手续费都亏光了。后来改成60天,效果才好。窗口选择没有标准答案,得根据资产特性和交易频率来调。

5.5 知识体系总览

下面这张图,是我自己总结的配对交易基础流程。每次做新策略,我都会按这个框架走一遍,确保不遗漏关键步骤。

配对交易策略基础流程 1. 寻找资产对 同行业/同板块/相关性高 2. 计算相关系数 皮尔逊/斯皮尔曼 > 0.8 3. 计算价差 简单价差/对数价差 4. 标准化价差 Z-score = (价差-均值)/标准差 5. 设置交易阈值 Z-score > 2 做空,< -2 做多 关键注意事项 • 滚动窗口计算均值和标准差 • 窗口大小:60-120天较常见 • 检查价差是否平稳(单位根检验) • 避免过度拟合,用样本外数据验证 • 考虑交易成本,别频繁开平仓 • 多资产对组合,分散风险

5.6 实战中的坑与经验

说了这么多,最后分享几个我踩过的坑:

  • 相关性不等于因果性: 两个资产可能因为市场整体上涨而相关,但一旦市场转向,它们可能分道扬镳。我建议用协整检验来确认长期关系。
  • 价差回归需要时间: 别指望价差立刻回归。我见过一个配对,价差偏离了3个标准差,结果等了两个月才回来。仓位管理很重要。
  • 小心结构突变: 公司并购、政策变化、行业危机,都会破坏原有的配对关系。我一般每周检查一次相关系数,发现异常就暂停交易。
我的习惯: 每次新配对上线前,我会先用模拟账户跑一个月。看看实盘环境下的滑点和成交情况,再决定是否投入真金白银。

好了,配对交易的基础就讲到这里。记住:找对资产对,算准价差,标准化信号,这三步做好了,策略就成功了一半。剩下的,就是严格执行纪律,别让情绪干扰你的判断。

公众号:蓝海资料掘金营,微信deep3321