第十六章 多品种与多策略组合:分散化投资、资金分配、相关性矩阵
各位同学,欢迎来到第十六讲。
前面我们讲了单品种的基差套利,也讲了如何构建一个策略。但说实话,在实盘里只靠一个品种、一个策略吃饭,风险太大了。我见过不少交易员,一个策略回测漂亮得不行,一上实盘就翻车。为什么?因为市场环境一变,那个策略就失效了。
所以这一章,我们来聊聊怎么把多个品种、多个策略组合起来。说白了,就是不要把鸡蛋放在一个篮子里。
16.1 为什么要做多品种多策略组合?
先问大家一个问题:你辛辛苦苦开发了一个螺纹钢的跨期套利策略,回测年化收益30%,最大回撤5%。你兴冲冲地上了实盘,结果呢?
结果可能是:螺纹钢突然来了个政策限产,价差直接崩了。你的策略瞬间失效。
这就是单一品种的风险。我个人的习惯是,至少同时跑5-8个不相关的品种。比如黑色系、能化、农产品各选几个。这样即使一个品种出问题,整体组合也不会太难看。
核心逻辑:多品种组合的核心不是增加收益,而是降低波动。收益是多个策略的平均,但风险却不是简单的相加——只要品种间相关性够低,组合的风险会显著下降。
16.2 相关性矩阵:你的组合体检报告
做组合之前,第一件事就是算相关性矩阵。我刚开始做组合时,犯过一个错误:选了螺纹钢、热卷、铁矿石三个品种。结果发现它们相关性高达0.9以上,说白了就是同一个东西。组合了个寂寞。
相关性矩阵长什么样?我们来看一个例子:
| 品种 | 螺纹钢 | 热卷 | 铁矿石 | PTA | 甲醇 |
|---|---|---|---|---|---|
| 螺纹钢 | 1.00 | 0.92 | 0.85 | 0.12 | 0.08 |
| 热卷 | 0.92 | 1.00 | 0.80 | 0.10 | 0.05 |
| 铁矿石 | 0.85 | 0.80 | 1.00 | 0.15 | 0.11 |
| PTA | 0.12 | 0.10 | 0.15 | 1.00 | 0.45 |
| 甲醇 | 0.08 | 0.05 | 0.11 | 0.45 | 1.00 |
你看,螺纹钢、热卷、铁矿石之间相关性极高。而它们跟PTA、甲醇的相关性就很低。这才是我们想要的组合。
计算相关性矩阵的代码很简单:
import pandas as pd
import numpy as np
# 假设df是各品种的日收益率数据
# 列名是品种名称
corr_matrix = df.corr()
# 可视化
import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', center=0)
plt.title('品种间相关性矩阵')
plt.show()
我的经验:选择组合品种时,我一般要求两两相关性不超过0.6。如果超过0.7,我会考虑只保留其中一个。另外,记得用滚动窗口计算相关性——静态的相关性矩阵会骗人,市场结构是会变的。
16.3 资金分配:怎么分才科学?
选好了品种,接下来就是资金分配。这里有几个常见的方法:
16.3.1 等权分配
最简单粗暴的方法:每个品种分配相同的资金。比如你有100万,选了5个品种,每个分20万。
优点:简单,容易执行。缺点:没有考虑品种的风险差异。波动大的品种会主导组合表现。
16.3.2 风险平价
这是我个人比较推崇的方法。核心思想:让每个品种对组合的风险贡献相等。
def risk_parity_weights(cov_matrix):
"""
计算风险平价权重
cov_matrix: 协方差矩阵
"""
n = cov_matrix.shape[0]
# 初始化权重
w = np.ones(n) / n
# 迭代求解
for _ in range(100):
# 计算边际风险贡献
portfolio_var = w @ cov_matrix @ w
marginal_contrib = cov_matrix @ w / np.sqrt(portfolio_var)
# 计算风险贡献
risk_contrib = w * marginal_contrib
# 调整权重使风险贡献相等
target_risk = np.mean(risk_contrib)
w = w * (target_risk / risk_contrib)
w = w / w.sum()
return w
# 使用示例
cov = df.cov()
weights = risk_parity_weights(cov)
print('风险平价权重:', weights)
你想想看,风险平价的好处是什么?它自动给波动大的品种分配更少的资金,给波动小的品种分配更多资金。这样组合的整体波动会更稳定。
16.3.3 波动率倒数加权
一个更简单的变体:权重与波动率成反比。
vol = df.std()
weights = (1 / vol) / (1 / vol).sum()
print('波动率倒数权重:', weights)
注意:我曾经吃过一次亏。用历史波动率计算权重,结果市场突然变盘,波动率飙升,权重没来得及调整,组合回撤很大。所以建议用滚动窗口计算,并且设置权重调整的阈值——比如偏离超过20%就重新平衡。
16.4 多策略组合:不只是品种
除了多品种,我们还可以做多策略组合。比如同一个品种上,同时跑趋势跟踪、均值回归、基差套利三个策略。
为什么?因为不同策略在不同市场环境下表现不同。趋势跟踪在单边行情中赚钱,均值回归在震荡行情中赚钱,基差套利在期限结构稳定时赚钱。组合起来,就能适应更多市场环境。
我个人的做法是:先给每个策略分配一个基础权重,然后根据近期表现动态调整。比如过去20个交易日,哪个策略表现好,就给它多加一点权重。
def dynamic_strategy_allocation(strategy_returns, lookback=20):
"""
动态策略权重分配
strategy_returns: 各策略的日收益率DataFrame
"""
# 计算滚动夏普比率
rolling_sharpe = strategy_returns.rolling(lookback).mean() / \
strategy_returns.rolling(lookback).std()
# 权重与夏普比率成正比
weights = rolling_sharpe / rolling_sharpe.sum(axis=1).values.reshape(-1, 1)
# 限制最大权重,防止过度集中
weights = weights.clip(upper=0.5)
weights = weights.div(weights.sum(axis=1), axis=0)
return weights
16.5 组合的再平衡
权重不是一成不变的。需要定期再平衡。我一般每周五收盘后做一次再平衡。
再平衡的频率怎么定?
- 太频繁:交易成本高,磨损大
- 太少:权重偏离目标太远,风险暴露失控
我建议设置一个阈值。比如当某个品种的权重偏离目标超过20%时,触发再平衡。这样既控制了成本,又控制了风险。
避坑指南:我曾经设置过每日再平衡,结果一个月下来,手续费吃掉了一半收益。后来改成周频+阈值触发,效果好多了。记住,再平衡不是越频繁越好。
16.6 本章知识体系
下面这张图总结了多品种多策略组合的核心逻辑:
16.7 实战中的几个要点
最后,分享几个我在实战中总结的要点:
- 不要过度优化:组合的权重计算越复杂,越容易过拟合。有时候等权分配就挺好。
- 关注尾部风险:相关性在极端行情下会趋同。2008年金融危机时,所有资产的相关性都接近1。所以即使组合看起来很分散,也要留足安全边际。
- 交易成本不可忽视:多品种多策略意味着更多的交易。一定要把滑点和手续费算进去,否则回测漂亮,实盘亏钱。
- 心理准备:组合的收益曲线会比单策略平滑,但也会出现所有策略同时亏损的时候。这时候不要慌,坚持纪律。
我的一个小技巧:我会在组合里留5%的现金仓位。平时不动,遇到极端行情时用来抄底。这个现金仓位就像消防员,平时看着没用,关键时刻能救命。
好了,这一章的内容就到这里。多品种多策略组合是一个系统工程,需要不断迭代优化。希望你们能把这些方法用起来,构建属于自己的稳健组合。