3. 相关性分析实战:外汇与黄金、原油、铜等大宗商品的统计相关性、协整检验与套利窗口识别
做跨市场套利,说白了就是找「亲戚」。外汇和黄金、原油、铜这些大宗商品,它们之间到底有没有血缘关系?关系有多铁?什么时候会翻脸?这就是我们今天要啃的硬骨头。
我个人习惯,拿到任何品种对,第一件事不是跑模型,而是先画一张相关性热力图。就像医生看病人,先量体温,再查血常规。你想想看,如果两个品种长期走势像双胞胎,突然有一天闹别扭了,那不就是套利机会吗?
核心逻辑:统计相关性 ≠ 套利可行性。高相关是入场的前提,协整关系才是套利的保障。
3.1 外汇与大宗商品的「亲戚关系」图谱
先说说我这些年观察到的几个铁律。当然,市场没有绝对,但这些规律在80%的时间里是有效的。
| 品种对 | 典型相关性 | 逻辑解释 | 我的实战经验 |
|---|---|---|---|
| EUR/USD vs 黄金 | 正相关(0.6~0.8) | 欧元和黄金都是美元的对手盘 | 2015年希腊危机时,这对关系一度破裂,我吃过亏 |
| USD/CAD vs 原油 | 负相关(-0.7~-0.9) | 加拿大是石油出口国,油价涨加元强 | 这个关系最稳定,我拿它做过主力策略 |
| AUD/USD vs 铜 | 正相关(0.5~0.7) | 澳大利亚是铜矿出口大国 | 注意:铁矿石比铜更敏感,但铜的数据更干净 |
| USD/JPY vs 黄金 | 负相关(-0.3~-0.5) | 避险逻辑,但日元有时也避险 | 这个关系最不稳定,我建议新手别碰 |
避坑指南:我曾经以为USD/CHF和黄金也是强相关,结果回测亏了三个月。后来发现,瑞士央行干预市场时,什么相关性都是浮云。
3.2 统计相关性计算:别只看皮尔逊
很多人一上来就用皮尔逊相关系数。嗯,这里要注意,皮尔逊假设数据是正态分布且线性相关。但金融数据哪有那么乖?
我一般会同时算三种相关性:
- 皮尔逊相关系数:衡量线性关系,适合快速筛查
- 斯皮尔曼秩相关系数:衡量单调关系,对异常值不敏感
- 肯德尔相关系数:更稳健,适合小样本
举个例子,我拿EUR/USD和黄金的日线数据跑一下:
import pandas as pd
import numpy as np
from scipy.stats import pearsonr, spearmanr, kendalltau
# 假设df包含两列:'EURUSD' 和 'GOLD'
# 先算收益率,别用价格直接算
df['ret_EUR'] = df['EURUSD'].pct_change()
df['ret_GOLD'] = df['GOLD'].pct_change()
# 去掉NaN
clean_df = df[['ret_EUR', 'ret_GOLD']].dropna()
# 三种相关性
pearson_r, p_pearson = pearsonr(clean_df['ret_EUR'], clean_df['ret_GOLD'])
spearman_r, p_spearman = spearmanr(clean_df['ret_EUR'], clean_df['ret_GOLD'])
kendall_r, p_kendall = kendalltau(clean_df['ret_EUR'], clean_df['ret_GOLD'])
print(f"皮尔逊: {pearson_r:.3f} (p值: {p_pearson:.4f})")
print(f"斯皮尔曼: {spearman_r:.3f} (p值: {p_spearman:.4f})")
print(f"肯德尔: {kendall_r:.3f} (p值: {p_kendall:.4f})")
你看,如果三种相关性方向一致,那这个关系就比较靠谱。如果皮尔逊和斯皮尔曼符号相反,说明存在非线性关系,这时候要小心。
重要提醒:千万别用价格序列直接算相关性!价格是随机游走,两个随机游走算出来的相关性是伪相关。一定要用收益率或者对数差分。
3.3 协整检验:找到真正的「长期伴侣」
相关性高不代表能套利。为什么?因为相关性是短期的,协整才是长期的。我打个比方:相关性就像两个人今天心情都很好,协整就像两个人这辈子注定要在一起。
协整检验的核心思想:两个非平稳序列的线性组合可能是平稳的。说白了,就是它们虽然各自乱跑,但差距不会无限扩大。
我常用的方法是Engle-Granger两步法:
import statsmodels.api as sm
from statsmodels.tsa.stattools import adfuller
# 第一步:回归
X = df['GOLD'] # 自变量
y = df['EURUSD'] # 因变量
X = sm.add_constant(X)
model = sm.OLS(y, X).fit()
residuals = model.resid
# 第二步:对残差做ADF检验
adf_result = adfuller(residuals, maxlag=1)
print(f"ADF统计量: {adf_result[0]:.4f}")
print(f"p值: {adf_result[1]:.4f}")
# p值小于0.05,说明残差平稳,存在协整关系
if adf_result[1] < 0.05:
print("存在协整关系,可以构建套利策略")
else:
print("不存在协整关系,别浪费时间")
我在项目中遇到过最坑的一次:用日线数据做协整检验,p值0.03,看起来很美。结果换成周线数据,p值变成0.15。后来发现是数据频率的问题。我的建议是:至少用两种频率验证,日线和周线都跑一遍。
3.4 套利窗口识别:什么时候动手?
找到协整关系后,下一步就是找入场点。我习惯用「Z-score」方法,说白了就是看当前价差偏离均值几个标准差。
具体步骤:
- 计算价差:spread = y - β * x(β是回归系数)
- 计算价差的均值和标准差(用滚动窗口,我一般用60天)
- 计算Z-score = (当前价差 - 均值) / 标准差
- 设定阈值:|Z-score| > 2 时开仓,回归到0附近平仓
# 计算滚动Z-score
window = 60
spread = y - model.params['GOLD'] * X['GOLD']
rolling_mean = spread.rolling(window=window).mean()
rolling_std = spread.rolling(window=window).std()
z_score = (spread - rolling_mean) / rolling_std
# 生成交易信号
entry_threshold = 2.0
exit_threshold = 0.5
# 做空价差(卖EURUSD,买黄金)
short_signal = (z_score > entry_threshold) & (z_score.shift(1) <= entry_threshold)
# 做多价差(买EURUSD,卖黄金)
long_signal = (z_score < -entry_threshold) & (z_score.shift(1) >= -entry_threshold)
# 平仓信号
exit_signal = (abs(z_score) < exit_threshold)
我的小技巧:阈值不是死的。波动率大的时候,我会把阈值调到2.5;波动率小的时候,1.8就够了。你可以用ATR(平均真实波幅)来动态调整。
3.5 实战案例:USD/CAD vs 原油
这个组合是我最拿手的。加拿大是石油出口国,USD/CAD和原油价格长期负相关。我记得2020年4月,原油期货跌到负值那天,USD/CAD瞬间暴涨,但第二天就回归了。这就是典型的套利窗口。
具体操作流程:
- 数据准备:取USD/CAD和WTI原油的日线数据,时间跨度至少2年
- 相关性验证:收益率相关性应该在-0.7以下
- 协整检验:p值小于0.05
- 参数优化:滚动窗口用30天、60天、90天分别测试
- 回测验证:至少跑3年数据,看夏普比率是否大于1.5
我曾经犯过一个错误:只用了2018-2019年的数据做回测,结果2020年3月流动性危机时,策略直接爆仓。后来我学乖了,一定要包含极端行情的数据。
3.6 本章知识体系
下面这张图是我自己总结的,每次做新策略前都会过一遍:
这张图我用了好几年,每次做新策略都按这个流程走一遍。你会发现,大部分品种对在「协整检验」那一步就被淘汰了。没关系,宁缺毋滥。
最后说一句:相关性分析不是一劳永逸的。市场结构会变,我每个月都会重新跑一遍相关性矩阵。如果发现某个品种对的关系变了,我会立刻暂停策略,重新评估。
公众号:蓝海资料掘金营,微信deep3321