4、距离方法与配对选择:欧氏距离、相关系数、最小距离法、配对筛选的实战流程
做配对交易这么多年,我最大的体会是:选对配对,策略就成功了一半。很多人一上来就写代码、跑回测,结果亏得莫名其妙。其实问题往往出在最开始——配对选错了。
这一章,我们就来聊聊怎么科学地选配对。说白了,就是怎么用数学工具,找到那些「天生就该一起走」的股票对。
4.1 为什么需要距离方法?
你想想看,A股市场几千只股票,两两组合有多少种可能?几百万对。我们不可能每一对都去测试。所以需要一种方法,快速筛选出「看起来有戏」的候选对。
距离方法,就是干这个的。它衡量两只股票价格走势的「相似度」。走势越像,越有可能存在稳定的价差关系。
核心思想:如果两只股票长期走势高度一致,那么它们之间的价差大概率会回归均值。这就是配对交易的基础。
4.2 欧氏距离:最直观的相似度度量
欧氏距离,说白了就是两只股票价格序列的「直线距离」。数学上很简单:
# 计算两只股票的欧氏距离
import numpy as np
import pandas as pd
def euclidean_distance(series1, series2):
"""
计算两只股票价格序列的欧氏距离
注意:需要先归一化,否则价格高的股票会主导距离
"""
# 归一化到[0,1]区间
s1_norm = (series1 - series1.min()) / (series1.max() - series1.min())
s2_norm = (series2 - series2.min()) / (series2.max() - series2.min())
# 计算欧氏距离
dist = np.sqrt(np.sum((s1_norm - s2_norm) ** 2))
return dist
# 示例:计算贵州茅台和五粮液的欧氏距离
maotai = pd.Series([1800, 1820, 1790, 1850, 1830])
wuliangye = pd.Series([220, 225, 218, 230, 228])
dist = euclidean_distance(maotai, wuliangye)
print(f"欧氏距离: {dist:.4f}")
我的经验:欧氏距离对价格水平很敏感。茅台1800块,五粮液200多块,直接算距离会被茅台主导。所以归一化是必须的。我在项目中吃过这个亏,一开始没归一化,选出来的配对全是高价股配高价股,毫无意义。
4.3 相关系数:衡量线性关系的强弱
相关系数大家应该很熟悉了。它衡量的是两只股票价格变动的同步性。值越接近1,说明它们涨跌越同步。
# 计算皮尔逊相关系数
def pearson_correlation(series1, series2):
"""
计算两只股票价格序列的皮尔逊相关系数
返回:相关系数(-1到1之间)
"""
return series1.corr(series2)
# 示例
corr = pearson_correlation(maotai, wuliangye)
print(f"相关系数: {corr:.4f}")
# 实际应用中,我们通常用滚动窗口计算动态相关系数
def rolling_correlation(series1, series2, window=60):
"""
计算滚动相关系数
window: 滚动窗口大小(交易日)
"""
return series1.rolling(window).corr(series2)
嗯,这里要注意:相关系数只捕捉线性关系。如果两只股票存在非线性关系(比如A涨1%,B涨2%),相关系数可能不高,但它们仍然可能适合配对。
我曾经踩过的坑:只看相关系数选配对。有一次选出了相关系数0.98的配对,结果回测时亏得一塌糊涂。后来发现,这两只股票虽然走势高度相关,但价差没有均值回归特性。相关系数高 ≠ 适合配对交易。
4.4 最小距离法:综合排序的利器
最小距离法,就是把欧氏距离和相关系数结合起来,给每对股票打分。我个人的习惯是:
- 计算欧氏距离:距离越小越好
- 计算相关系数:相关系数越大越好
- 综合打分:把两个指标标准化后加权求和
def min_distance_scoring(pairs_df):
"""
最小距离法打分
pairs_df: 包含 'euclidean_dist' 和 'correlation' 两列
返回:综合得分(越小越好)
"""
# 标准化欧氏距离(越小越好,所以取负)
dist_score = -pairs_df['euclidean_dist']
dist_score = (dist_score - dist_score.mean()) / dist_score.std()
# 标准化相关系数(越大越好)
corr_score = pairs_df['correlation']
corr_score = (corr_score - corr_score.mean()) / corr_score.std()
# 综合得分(等权重)
pairs_df['total_score'] = dist_score + corr_score
# 按综合得分排序
return pairs_df.sort_values('total_score', ascending=False)
# 示例数据
pairs_data = pd.DataFrame({
'pair': ['茅台-五粮液', '茅台-泸州老窖', '五粮液-泸州老窖'],
'euclidean_dist': [0.15, 0.22, 0.18],
'correlation': [0.95, 0.88, 0.92]
})
result = min_distance_scoring(pairs_data)
print(result)
实战建议:我一般会给相关系数更高的权重(比如0.6),欧氏距离0.4。因为相关系数更能反映同步性,而欧氏距离容易受短期波动影响。当然,这个权重不是固定的,你可以根据市场环境调整。
4.5 配对筛选的实战流程
好了,理论讲完了。我们来看看实际怎么做。下面是我在项目中常用的完整流程:
import yfinance as yf
import pandas as pd
import numpy as np
from itertools import combinations
def pair_screening_pipeline(tickers, start_date, end_date, top_n=20):
"""
配对筛选完整流程
tickers: 候选股票列表
start_date, end_date: 回测时间范围
top_n: 返回前N对候选
"""
# 1. 下载数据
data = yf.download(tickers, start=start_date, end=end_date)['Adj Close']
# 2. 计算日收益率(用收益率代替价格,避免价格水平影响)
returns = data.pct_change().dropna()
# 3. 生成所有可能的配对
pairs = list(combinations(tickers, 2))
results = []
for pair in pairs:
s1, s2 = pair
# 4. 计算欧氏距离(用收益率序列)
dist = np.sqrt(np.sum((returns[s1] - returns[s2]) ** 2))
# 5. 计算相关系数
corr = returns[s1].corr(returns[s2])
# 6. 计算协整检验p值(可选,但强烈推荐)
from statsmodels.tsa.stattools import coint
_, p_value, _ = coint(data[s1], data[s2])
results.append({
'pair': f'{s1}-{s2}',
'euclidean_dist': dist,
'correlation': corr,
'coint_pvalue': p_value
})
# 7. 转换为DataFrame并打分
results_df = pd.DataFrame(results)
# 过滤:相关系数 > 0.8,协整p值 < 0.05
filtered = results_df[
(results_df['correlation'] > 0.8) &
(results_df['coint_pvalue'] < 0.05)
]
# 8. 最小距离法排序
final = min_distance_scoring(filtered)
return final.head(top_n)
# 使用示例
tickers = ['600519.SS', '000858.SZ', '000568.SZ', '600809.SS', '000596.SZ']
top_pairs = pair_screening_pipeline(tickers, '2023-01-01', '2024-01-01')
print(top_pairs[['pair', 'correlation', 'coint_pvalue', 'total_score']])
4.6 知识体系总览
下面这张图,是我梳理的配对筛选核心逻辑。你看一遍,应该就能把握住整体脉络了。
4.7 实战中的避坑指南
最后,分享几个我这些年总结的经验:
- 不要只看历史:过去走势相似,不代表未来也会同步。我建议每3个月重新筛选一次配对。
- 行业要一致:茅台和五粮液是同行业,配对有意义。茅台和中国石油,就算相关系数再高,也别碰。
- 流动性要匹配:一只日成交额100亿,一只只有1000万,配对交易时滑点会让你亏到怀疑人生。
- 协整检验不是万能的:它只能告诉你过去有没有协整关系,不能保证未来。我一般会结合滚动协整检验,看关系是否稳定。
我的小技巧:筛选出候选配对后,我会先做一轮「目视检查」。把两只股票的价格走势画在一张图上,用肉眼看看价差是否真的在均值附近来回摆动。有时候,数学指标完美,但图形一看就不对劲。这种直觉,做久了自然就有了。
好了,这一章的内容就到这里。距离方法和配对筛选,是配对交易的基石。把这些搞扎实了,后面的策略构建才能站得住脚。