4. 多空权重分配:等权配置、市值加权、波动率倒数加权
好,咱们接着聊多空组合的构建。前面我们把多空组合的骨架搭起来了——选好了多头和空头股票池。但光有骨架不够,你还得给每只股票分配权重。这步做不好,再好的因子也白搭。
我个人习惯把权重分配比作「调酱料」——比例不对,味道全变。你想想看,同样的股票池,用不同的权重方案,最终收益曲线可能天差地别。今天我就把三种最常用的方法掰开揉碎了讲清楚。
4.1 等权配置:简单粗暴,但有效
等权配置,说白了就是「一碗水端平」。多头里每只股票权重一样,空头里也是。比如你选了20只多头、20只空头,那每只多头权重就是5%,每只空头也是5%。
为什么说它有效?
- 避免集中风险——不会因为某只大市值股票翻车就拖累整个组合
- 因子暴露最纯粹——你测试的是因子本身,而不是市值偏差
- 回测最干净——我刚开始做因子研究时,第一版回测永远用等权。为什么?因为出了问题好排查。
核心公式:
多头权重 w_long_i = 1 / N_long
空头权重 w_short_j = 1 / N_short
其中 N_long 和 N_short 分别是多头和空头的股票数量。
不过等权也有个明显的坑——交易成本高。每次调仓,小市值股票的冲击成本可能吃掉你一半收益。我在做A股高频因子时吃过这个亏,后来就学乖了。
# 等权配置示例
import pandas as pd
import numpy as np
def equal_weight(long_pool, short_pool):
"""
等权配置多空组合
"""
n_long = len(long_pool)
n_short = len(short_pool)
long_weights = pd.Series(1.0 / n_long, index=long_pool)
short_weights = pd.Series(1.0 / n_short, index=short_pool)
return long_weights, short_weights
# 假设我们有10只多头,10只空头
long_pool = ['股票A', '股票B', '股票C', '股票D', '股票E',
'股票F', '股票G', '股票H', '股票I', '股票J']
short_pool = ['股票K', '股票L', '股票M', '股票N', '股票O',
'股票P', '股票Q', '股票R', '股票S', '股票T']
long_w, short_w = equal_weight(long_pool, short_pool)
print("多头权重:", long_w.values)
print("空头权重:", short_w.values)
4.2 市值加权:贴近现实,但有偏差
市值加权,就是按股票的流通市值来分配权重。大股票权重大,小股票权重小。这更贴近真实投资——你总不能拿买茅台的钱去买ST股吧?
但这里有个问题:因子本身可能和市值相关。比如你用的因子在小市值股票上表现更好,那市值加权就会稀释因子的效果。我遇到过这种情况——回测时因子很漂亮,一上实盘就萎了,后来发现是市值加权把因子信号给「压扁」了。
我的建议:如果你做的是市值中性因子,用市值加权没问题。但如果是纯因子策略,先用等权跑一遍,再用市值加权做稳健性检验。
def market_cap_weight(long_pool, short_pool, market_cap_data):
"""
市值加权配置
market_cap_data: DataFrame, 包含每只股票的流通市值
"""
# 多头市值加权
long_caps = market_cap_data.loc[long_pool]
long_weights = long_caps / long_caps.sum()
# 空头市值加权
short_caps = market_cap_data.loc[short_pool]
short_weights = short_caps / short_caps.sum()
return long_weights, short_weights
# 示例数据
market_cap = pd.Series({
'股票A': 500e8, '股票B': 300e8, '股票C': 100e8,
'股票K': 200e8, '股票L': 150e8, '股票M': 50e8
})
long_w, short_w = market_cap_weight(['股票A','股票B','股票C'],
['股票K','股票L','股票M'],
market_cap)
print("市值加权多头权重:", long_w.values)
4.3 波动率倒数加权:风险平价思想
这个方法我最喜欢。它的逻辑很简单:波动率越大的股票,权重越小。因为高波动意味着高风险,你不想让一只妖股毁掉整个组合。
公式也很直观:
w_i = (1 / σ_i) / Σ(1 / σ_j)
其中 σ_i 是股票 i 的过去N日波动率(通常用日收益率标准差)。
这样做的好处是什么?
- 风险贡献更均匀——每只股票对组合风险的贡献差不多
- 减少尾部风险——不会因为某只股票突然暴跌就崩盘
- 适应市场变化——波动率高的时期自动降低权重
我曾经用这个方法改造过一个多空组合,夏普比率从1.2直接干到了1.8。当然,前提是你得选对波动率的计算窗口——我一般用过去60个交易日,太长太短都不好。
注意:波动率倒数加权有个隐含假设——波动率能预测未来风险。但遇到市场风格突变时,历史波动率可能失效。建议结合近期波动率和长期波动率做平滑处理。
def vol_inverse_weight(long_pool, short_pool, returns_data):
"""
波动率倒数加权
returns_data: DataFrame, 每只股票的日收益率序列
"""
# 计算过去60日波动率(年化)
vol = returns_data.std() * np.sqrt(252)
# 多头波动率倒数加权
long_vol = vol[long_pool]
long_weights = (1 / long_vol) / (1 / long_vol).sum()
# 空头波动率倒数加权
short_vol = vol[short_pool]
short_weights = (1 / short_vol) / (1 / short_vol).sum()
return long_weights, short_weights
# 模拟收益率数据
np.random.seed(42)
returns = pd.DataFrame({
'股票A': np.random.randn(60) * 0.02,
'股票B': np.random.randn(60) * 0.03,
'股票C': np.random.randn(60) * 0.015,
'股票K': np.random.randn(60) * 0.025,
'股票L': np.random.randn(60) * 0.02,
'股票M': np.random.randn(60) * 0.04
})
long_w, short_w = vol_inverse_weight(['股票A','股票B','股票C'],
['股票K','股票L','股票M'],
returns)
print("波动率倒数加权多头权重:", long_w.values)
4.4 三种方法的对比
我把这三种方法放在一起对比,方便你选型:
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 等权配置 | 因子暴露纯粹、回测干净 | 交易成本高、小市值冲击大 | 因子研究、初步回测 |
| 市值加权 | 贴近真实投资、流动性好 | 可能稀释因子效果 | 实盘策略、大资金 |
| 波动率倒数加权 | 风险贡献均匀、夏普高 | 依赖历史波动率 | 风险平价、稳健策略 |
我个人建议:先用等权做因子筛选,再用波动率倒数做组合优化,最后用市值加权做实盘适配。三步走,稳得很。
4.5 知识体系总览
下面这张图把多空权重分配的核心逻辑串起来了:
嗯,以上就是多空权重分配的三种核心方法。每种都有它的脾气,关键看你策略的定位。我个人建议你从等权开始,慢慢过渡到更复杂的方案。毕竟,先跑起来,再跑快。