第十五章 多品种套利组合:组合构建方法、相关性矩阵分析、分散化效果评估、组合再平衡策略
各位同学,欢迎来到多品种套利的世界。
前面我们聊了单品种的基差交易,也讲了跨品种的配对策略。但说实话,真正在实盘里赚钱的,很少只做一两个品种。我个人的经验是——多品种组合才是量化套利的核心战场。为什么?因为单一策略总有失效的时候,但组合在一起,就能把风险摊薄,把收益做稳。
一、组合构建方法:从零到一搭框架
构建多品种套利组合,说白了就是选一批品种,然后决定每个品种配多少仓位。听起来简单,但坑不少。
我习惯把构建过程分成三步:
- 品种筛选:流动性是第一位的。我见过有人把冷门品种加进去,结果平仓时滑点吃掉所有利润。建议选日均成交额在5亿以上的品种。
- 信号生成:每个品种独立跑套利信号。比如螺纹钢看基差,豆粕看跨期价差,铜看内外盘比价。信号之间不要互相干扰。
- 仓位分配:这是关键。我常用等风险贡献法——让每个品种对组合总风险的贡献大致相等。
核心原则:组合不是简单的堆砌,而是让品种之间的风险互相抵消。你想想看,如果所有品种都在同一时间亏钱,那组合的意义就没了。
举个实际例子。我曾经做过一个组合,包含螺纹钢、铁矿石、焦炭、甲醇和PTA。每个品种都跑自己的基差套利信号。仓位分配时,我用了波动率倒数加权——波动大的品种少配,波动小的多配。这样组合的净值曲线就平滑多了。
二、相关性矩阵分析:看清品种间的“关系网”
相关性矩阵,是组合构建的“体检报告”。它能告诉你哪些品种会一起涨跌,哪些会反向运动。
我一般用滚动窗口计算相关性,窗口长度选60个交易日。为什么是60?太短了噪声大,太长了反应迟钝。嗯,这是我在实践中试出来的。
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
# 假设我们有5个品种的日收益率数据
# 数据格式:每列是一个品种,每行是一个交易日
returns = pd.DataFrame({
'RB': np.random.randn(500) * 0.02, # 螺纹钢
'I': np.random.randn(500) * 0.025, # 铁矿石
'J': np.random.randn(500) * 0.03, # 焦炭
'MA': np.random.randn(500) * 0.02, # 甲醇
'TA': np.random.randn(500) * 0.018 # PTA
})
# 计算60日滚动相关性矩阵
window = 60
rolling_corr = returns.rolling(window).corr()
# 取最近一个窗口的相关性矩阵
latest_corr = rolling_corr.iloc[-window:].mean()
print(latest_corr.unstack())
# 可视化
sns.heatmap(latest_corr, annot=True, cmap='coolwarm', center=0)
plt.title('多品种套利组合 - 相关性矩阵')
plt.show()
实战技巧:相关性不是一成不变的。我见过螺纹钢和铁矿石的相关性在0.3到0.8之间来回跳。所以别只看静态数据,要动态跟踪。我习惯每周更新一次相关性矩阵,看看有没有“叛变”的品种。
相关性矩阵能帮你做两件事:
- 发现冗余品种:如果两个品种相关性长期高于0.8,那它们其实在“说同一件事”。留一个就够了。
- 找到对冲机会:相关性为负的品种,天然就是好搭档。比如豆粕和豆油,一个涨另一个往往跌。
三、分散化效果评估:你的组合真的分散了吗?
很多人以为多放几个品种就是分散化。错。真正的分散化,是看组合的波动率有没有降下来。
我常用一个指标叫分散化比率:
# 计算分散化比率
# 分散化比率 = 各品种波动率加权平均 / 组合实际波动率
weights = np.array([0.2, 0.2, 0.2, 0.2, 0.2]) # 等权
cov_matrix = returns.cov() # 协方差矩阵
# 各品种波动率加权平均
indiv_vol = np.sqrt(np.diag(cov_matrix))
weighted_avg_vol = np.dot(weights, indiv_vol)
# 组合实际波动率
portfolio_vol = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
# 分散化比率
diversification_ratio = weighted_avg_vol / portfolio_vol
print(f'分散化比率: {diversification_ratio:.2f}')
这个比率越大,说明分散化效果越好。我一般要求这个值大于1.5。如果低于1.2,那你的组合其实没怎么分散,得重新审视品种选择。
注意:分散化比率不是越高越好。我曾经见过一个组合,比率高达2.8,但仔细一看,里面塞了太多低相关性的冷门品种。结果流动性差,平仓时滑点把收益全吃掉了。所以,分散化要兼顾流动性。
还有一个更直观的方法——看组合的最大回撤。如果组合的最大回撤明显小于各品种的最大回撤,那说明分散化起作用了。我习惯把组合的最大回撤控制在10%以内,单个品种可以到15%,但组合不能超过这个数。
四、组合再平衡策略:别让组合“跑偏”
组合建好后,不是一劳永逸的。市场在变,品种的相关性在变,波动率也在变。如果不做再平衡,组合的风险结构会慢慢走样。
我常用的再平衡策略有三种:
| 策略类型 | 触发条件 | 优缺点 |
|---|---|---|
| 定期再平衡 | 每周/每月固定时间 | 简单易行,但可能错过最佳时机 |
| 阈值再平衡 | 品种权重偏离目标超过一定比例(如5%) | 灵活,能及时纠偏,但交易频率可能较高 |
| 波动率再平衡 | 品种波动率变化超过阈值 | 能动态调整风险,但计算复杂 |
我个人最常用的是阈值再平衡+波动率调整。具体做法是:
- 设定每个品种的目标权重,比如等权20%。
- 每天检查实际权重,如果偏离超过5%,就触发再平衡。
- 再平衡时,不是简单回到目标权重,而是根据近期波动率微调——波动率上升的品种减仓,下降的加仓。
def rebalance_portfolio(positions, target_weights, vol_series, threshold=0.05):
"""
阈值再平衡 + 波动率调整
positions: 当前持仓市值
target_weights: 目标权重
vol_series: 各品种近期波动率
threshold: 偏离阈值
"""
total_value = sum(positions.values())
current_weights = {k: v/total_value for k, v in positions.items()}
# 检查是否需要再平衡
need_rebalance = any(
abs(current_weights[k] - target_weights[k]) > threshold
for k in target_weights
)
if not need_rebalance:
return positions
# 波动率调整:波动率高的品种降低权重
total_vol = sum(vol_series.values())
vol_adjusted_weights = {
k: (1 - v/total_vol) * target_weights[k]
for k, v in vol_series.items()
}
# 归一化
sum_weights = sum(vol_adjusted_weights.values())
final_weights = {k: v/sum_weights for k, v in vol_adjusted_weights.items()}
# 计算新持仓
new_positions = {k: total_value * final_weights[k] for k in final_weights}
return new_positions
避坑指南:再平衡不是越频繁越好。我曾经每两天就做一次再平衡,结果交易成本把收益吃掉了一大半。后来我改成每周检查一次,只在偏离超过阈值时才动手。记住,再平衡的目的是控制风险,不是追求完美。
最后说一句,再平衡时要考虑交易成本。如果某个品种的买卖价差很大,那再平衡的阈值可以设得宽一些。比如螺纹钢流动性好,我设3%的阈值;而一些冷门品种,我可能设到8%。
好了,以上就是多品种套利组合的核心内容。从构建到评估,再到再平衡,每一步都有讲究。记住,组合不是越多越好,而是越“稳”越好。下次你构建组合时,不妨先跑一遍相关性矩阵,看看你的品种是不是真的在“各司其职”。
总结一句话:多品种套利组合的本质,是用品种间的低相关性来换取更平滑的净值曲线。再平衡则是确保这条曲线不会走偏。