Beta 中性组合:用市场对冲把方向风险剥离干净只留 alpha
多因子/多资产选股最尴尬的事:你以为在赚 alpha,其实 90% 的波动都来自大盘。Beta 中性(市场中性)用一篮子多头 + 一份指数空头,把组合的总市场暴露精确压到 0——系统性涨跌被对冲掉,剩下的只有个股特质收益(也就是真正的 alpha)+ 特质噪声。本文从收益分解讲起,用真实模拟演示:中性化前组合与市场的相关系数 0.97、回撤 −16%;中性化后相关系数降到 −0.07、回撤只剩 −1.5%;并诚实给出对冲倍数 k、做空成本、净敞口坍缩、beta 漂移四类实战陷阱(中阶)。
做量化的人大多经历过一个幻灭时刻:花三个月写了一个「选股因子」,回测年化 30%,实盘一跑,大盘跌 5% 它跌 4.5%。
这时候你才反应过来——你那 30% 里,可能 25% 都是「大盘涨了」的功劳,真正属于你这个因子的 alpha 没多少。更糟的是,你以为自己在做选股,其实在裸多一个指数。
Beta 中性(Beta-neutral / 市场中性)要解决的,就是这个「裸多指数」的尴尬:它用一份空头把组合的市场暴露精确对冲掉,让你剩下的收益只来自个股之间的相对强弱——也就是真正的 alpha。
一、先把收益拆开:你赚的到底是哪一块#
任何一只股票的日收益,都可以写成三部分的叠加:
- :个股的特质漂移(长期里你希望靠选股赚到的部分);
- :对市场的敏感度(大盘涨 1%,它平均涨 );
- :市场收益;
- :特质噪声(不可预测的抖动)。
把 只股票等权拼成一个组合,权重 ,组合收益就是:
注意中间那项——组合的系统性暴露 直接乘上市场收益。如果你的十只股票 beta 平均是 0.97,那不管你怎么调权重,组合都会像一个 0.97 倍杠杆的指数。这就是前面那个幻灭时刻的数学本质。
二、核心操作:用指数空头把 归零#
Beta 中性的解法极简:再拿一份市场指数做空头,空头权重 = (因为指数自身的 beta 是 1)。
对冲后的组合总 beta:
于是中性化组合日收益变成:
系统性项 完全消失了。剩下的只有(alpha + 特质噪声),和市场脱钩。
关键认知:Beta 中性不是「消灭风险」,而是把「方向风险」换成「选股风险」。你不再赌大盘涨跌,改赌「我选的这篮子股票相对大盘能跑赢多少」。
下面用模拟把这件事跑实。我们造 10 只股票,各自有不同真实 beta(0.6~1.35)和不同 alpha,再用前半段数据估计 beta(避免前视)、后半段实盘对冲:
import numpy as np
rng = np.random.default_rng(20260717)
T = 504 # 约 2 个交易年
n = 10
market = 0.0003 + 0.012 * rng.standard_normal(T) # 市场日收益
betas_true = np.array([0.6,0.8,0.9,1.1,1.2,0.7,1.0,1.35,0.85,1.15])
alphas = np.array([0.0004,0.0002,0.0006,0.0001,0.0003,
0.0005,0.0002,0.0007,0.0001,0.0004])
idio = 0.009 * rng.standard_normal((T, n))
R = alphas[None,:] + betas_true[None,:]*market[:,None] + idio # 资产收益矩阵
split = T // 2
# 用前半段对每只资产回归估 beta(OLS)
betas_est = np.array([np.linalg.lstsq(
np.column_stack([np.ones(split), market[:split]]),
R[:split, i], rcond=None)[0][1] for i in range(n)])
w = np.ones(n) / n
beta_P = float(np.dot(w, betas_est)) # 组合 beta ≈ 0.97
R_te, mkt_te = R[split:], market[split:]
port_ret = R_te @ w # 等权多头组合收益(测试段)
neut_ret = port_ret - beta_P * mkt_te # Beta 中性: 空头指数 = -beta_Ppython
三、中性化前后:暴露与脱钩程度#
跑完测试段(252 天),三个组合的对照非常锋利:
| 指标 | 市场指数 | 等权多头 | Beta 中性 |
|---|---|---|---|
| 与市场相关系数 | 1.00 | 0.97 | −0.07 |
| 最大回撤 | −19.4% | −16.1% | −1.5% |
| 年化 Sharpe | 1.16 | 1.65 | 2.21 |
等权多头 Sharpe 看着比市场高,但那是因为它有一篮子正 alpha;问题是它和市场相关系数 0.97——大盘一崩,它也崩。Beta 中性把相关系数打到 −0.07(统计上等于不相关),回撤从 −16% 砍到 −1.5%。这就是「把方向风险剥离」的实打实效果。
暴露口径上,中性化前组合只有 +1.0 多头、净敞口 +1.0;中性化后多头 +1.0、空头 −0.97、毛敞口 1.97、净敞口 0.03——几乎完全中性。

四、只有 k=1 才真正脱钩:对冲倍数的敏感性#
上面我们用了精确的空头权重 。但现实里 beta 是估出来的,万一估偏了呢?我们扫一遍对冲倍数 (空头权重 = ),看残差和市场的相关系数怎么变:
ks = np.linspace(0.0, 1.5, 31)
corrs_k, vols_k = [], []
for k in ks:
resid = port_ret - k * beta_P * mkt_te
corrs_k.append(np.corrcoef(resid, mkt_te)[0, 1])
vols_k.append(np.std(resid) * np.sqrt(252))
corrs_k, vols_k = np.array(corrs_k), np.array(vols_k)python
曲线很有教育意义:残差与市场的相关系数在 处精确穿过 0,只要偏离,无论是高估还是低估 beta,系统性暴露都会回来。而且 (对冲不足)时残差波动反而更高——因为你留了没对冲干净的方向风险。这告诉你:Beta 中性对 beta 估计精度极度敏感,这是它最大的工程难点(见下文陷阱)。
五、回撤对比:系统性下跌被吃掉了#
把多头和中性组合的回撤画在一起,差异一目了然:
def dd_series(eq):
peak = np.maximum.accumulate(eq)
return (eq - peak) / peak * 100.0
# eq_long / eq_neut 为各自累计净值python
多头的回撤图上能清楚看到几段「大盘带着一起跌」的深谷;中性组合的回撤曲线几乎贴着 0——因为那些系统性下跌全被空头吃掉了,它只暴露个股特质层面的小幅波动。
六、四类真实陷阱(实战必看)#
-
Beta 会漂移,一次性估计迟早失效:本文用前半段估 beta、后半段对冲,已经算「不前视」的规范做法。但实盘里 beta 本身随时间变(高波动期个股 beta 普遍飙升),静态 beta 会慢慢失准。正解是用滚动窗口/指数加权持续重估,或干脆上 Kalman 滤波(见本专栏卡尔曼配对交易)。对冲倍数图告诉我们:beta 估错一点,系统性暴露就回来一点。
-
做空有真实成本,模型里没有:空头要吃融券利息、可能遇「卖空禁限」、还得交保证金。本文中性组合的 Sharpe 2.21 是「假设零成本做空」下的上限;加上 3%~5% 年化融券成本 + 冲击成本,净 alpha 会打折。尤其小市值/低流动性标的,做空成本能吞掉大部分 alpha。
-
净敞口坍缩 ≠ 风险消失:中性化后净敞口 0.03、毛敞口 1.97——你其实是「1 倍多 + 0.97 倍空」的双边持仓。特质风险(个股 idio)完全没被对冲,10 只股票的特质噪声叠加后,中性组合仍有波动。更糟的是如果多头和空头撞上同一个特质因子(比如同行业),对冲会同时失效。
-
alpha 必须真的存在且稳定:Beta 中性把所有鸡蛋都押在「多头篮子跑赢空头指数」这件事上。如果你的选股因子是伪 alpha(只是换个方式暴露了某个已知风险因子),那中性化之后连那点虚假收益都没了,只剩噪声。中性化是 alpha 的照妖镜:能过 beta 中性还赚钱的,才是真 alpha。
七、小结:Beta 中性是 alpha 的「提纯器」#
Beta 中性干的事非常单纯,也非常重要:
- 它不改变你选股的逻辑,只把「大盘涨跌」这件与你选股无关的事对冲掉;
- 中性化前:组合 = 大盘 β×市场 + alpha,相关系数 0.97、回撤 −16%;
- 中性化后:组合 ≈ 纯 alpha + 特质噪声,相关系数 −0.07、回撤 −1.5%;
- 想要自动跟踪漂移的 beta → 上 Kalman 滤波(状态空间动态 β);
- 想要连行业/风格暴露也中性 → 进 多因子中性化(对市值、估值等因子回归取残差)。
对你做实盘的意义:任何一个选股策略,先跑一遍 Beta 中性版本。如果中性化之后收益塌了,说明你之前赚的是 beta 不是 alpha——这比回测虚高 30% 更有价值。先把收益分解吃透、把空头成本和 beta 漂移这两道坎算清楚,你就拿到了「区分真 alpha 和伪 beta」的钥匙。
代码与图表均由自包含 Python(numpy/scipy/matplotlib)真实计算,随机种子固定为 20260717,可完整复现。所有统计数字(组合 beta 0.97、多头-市场相关 0.97、中性-市场相关 −0.07、回撤 −16.1%/-1.5%、Sharpe 1.65/2.21、净敞口 0.03)来自文中脚本输出。模拟参数(beta/alpha/波动)为演示设定,实盘需用真实历史收益校准。