← 返回量化与 AI 投研概念库

beta 与 alpha:跟大盘的部分,和不跟的部分

最后更新 2026-08-18
R2 · 注意风险

有人给你看一份成绩单:一年下来赚了 32%,同期大盘涨了 20%。他说,多出来的 12 个点是我的本事。

这句话听起来天经地义,可它悄悄用了一个假设——他这套组合,在市场涨 20% 的年份里"本来应该"涨 20%。如果他满仓的都是那种大盘动一格、自己动一格半的高弹性品种,那么"本来应该"是 30%,他真正挣出来的只有 2 个点,剩下 10 个点是市场推的,只不过被他的持仓风格放大了一遍。反过来,如果他一半仓位常年是现金,"本来应该"只有 10%,那这 12 个点其实被低估了。

同一份收益率,同一个大盘,结论能差出五倍。差别全在那条看不见的分界线上。这篇讲的就是这条线:它叫什么、怎么划、以及为什么它划歪一点点,就会有一大笔不存在的"本事"凭空冒出来。

先说人话:河水与划桨

你在一条向下游流的河上划船。到了终点,测速仪告诉你:全程平均每小时走了 8 公里。

这 8 公里里,有多少是你划出来的?没法直接回答,因为水自己在流。你得先知道两件事:水流有多快,以及这条船被水流带得有多厉害

第二件事是很多人不会想到的。同一条河上,一只空载的橡皮艇和一艘满载的货船,被同一股水流带走的速度不一样——橡皮艇吃水浅、迎水面大,水流一冲就跑;货船沉、阻力大,水推它推得费劲。所以"水流每小时 5 公里"这个数字,不能直接从每条船的成绩里减掉。你得先量出这条船对水流的敏感倍数,用倍数乘上水速,才知道该扣多少。

投资里,那个"水流速度"就是市场整体的涨跌;那个"敏感倍数"有个名字,叫贝塔(beta)——你的组合相对市场的放大系数。市场涨一个点,你大致涨几个点,这个"几"就是贝塔。扣掉"贝塔 × 市场"这一整份之后,还剩下的那一截,才叫阿尔法(alpha)。

关键的一句话,请先记住:阿尔法不是算出来的,是减剩下的。 它是一个余数。余数的性质决定了它后面所有的麻烦。

投资层:这条线划在哪,直接改变你下一步该干什么

贝塔和阿尔法不是两个用来装点报告的希腊字母,它们对应两种性质完全不同的东西,也对应两种完全不同的行动。

贝塔那一份,是可以花很少的钱买到的。 你想要一个贝塔为 1 的敞口,买一只低成本宽基指数基金就行,不用研究,不用盯盘。你想要贝塔 1.5,理论上把仓位调高、或者去持有那些天然弹性更大的品种也能凑出来(这里只讲原理,不是操作建议,加杠杆的风险另说)。既然它便宜到几乎是免费的,那么为贝塔支付高昂的管理费、或者为它熬夜看研报,就是浪费

阿尔法那一份,才是你的研究、你的判断、你那套方法真正贡献的东西。 它稀缺、不稳定、而且很难持续。

于是分界线一歪,你的行动就全歪了。把贝塔算成阿尔法,你会得出"我这套方法有效"的结论,接着做三件事:加仓、集中、上杠杆。等水流转向,你才发现自己配的仓位是按一个不存在的能力配的。反过来把阿尔法算成贝塔,你会在一套确实有效的方法上失去信心,往往还是在最难受的时候放弃。

这一层和基准与超额收益那篇是连着的:那篇讲的是"跟谁比",这篇讲的是"比之前先按什么倍数折算"。选错基准和估错倍数,都会让最后那个余数变成一句空话。

贝塔怎么估:一道除法,四个能改结论的旋钮

贝塔的算法说穿了很朴素:你和市场一起动的程度,除以市场自己动的程度。前者叫协方差(两条序列同涨同跌的共同幅度),后者叫方差(市场自身波动的平方)。

这道除法还能拆成一个更有用的形态:

贝塔 = 你与市场的相关系数 × (你的波动率 ÷ 市场的波动率)

这个拆法值得多看两眼,因为它一次说清了两件常被混为一谈的事。相关系数只管方向是否同步,贝塔还要乘上一个波动比。 两个组合可以跟大盘的同步程度一模一样,但因为一个持仓颠得厉害、一个稳如老狗,贝塔差出三倍。所以"我跟大盘相关性很高"和"我的贝塔很大"完全是两句话——这一层的细节在相关性那篇里展开。

麻烦在于,这道除法的每一个输入都不是天上掉下来的,全靠你从历史数据里估。至少有四个旋钮,每拧一个,贝塔就变一次:

旋钮一,拿谁当"市场"。 换一个指数,协方差和方差同时变。一个只买中小市值的组合,拿大盘蓝筹指数当市场,估出来的贝塔会偏低——因为它的涨跌里有一大块跟那个指数根本不同步,被算进了"不跟市场的部分",也就是阿尔法。

旋钮二,用什么频率的数据。 日收益、周收益、月收益估出来的贝塔通常不一样。低频数据会把短期的不同步抹平,往往给出更高的贝塔;高频数据则容易被交易不连续拖累——一只成交清淡的品种,今天市场大涨它没动,第二天才补上,日频协方差就被摊薄了,贝塔被系统性低估。

旋钮三,取多长的窗口、从哪天开始。 三年的贝塔和一年的贝塔是两个数。窗口里包不包含那次暴跌,结论能差很多。

旋钮四,静态还是滚动。 用整段历史估一个固定的贝塔,等于假设这个敏感度从头到尾没变过。现实中它一直在变——尤其在市场剧烈下跌的时候,各类资产会一起被抛售,相关性齐刷刷往上跑,贝塔跟着集体抬升。你最需要"我的暴露没那么大"这个结论的时刻,恰恰是它最不成立的时刻。 用平静期估出来的贝塔去衡量崩盘期的暴露,是这个指标最经典的失手方式。

还有一个更细的地方:估计本身是有不确定性的。如果你的组合跟市场的共动关系本来就弱(回归的解释力低),那么估出来的贝塔虽然是个具体的数字,它周围的不确定区间却很宽。一个不确定区间很宽的贝塔,减出来的阿尔法只会更不确定。 报告上那个精确到小数点后两位的数字,容易让人忘记这一点。

阿尔法为什么那么容易是假的

现在把两件事合起来看。

第一,阿尔法是余数。 它的公式大致是:你的收益,减去无风险利率(那个不用承担风险就能拿到的基准回报,细节见风险溢价与无风险利率),再减去贝塔乘上市场的超额部分,剩下的就是它。凡是没被"市场"这一项解释掉的东西,全都会掉进这个余数里——不管它是什么。

第二,贝塔是估的。 上面四个旋钮随便动一个,减掉的那一份就变。

两条合起来的结论很硬:贝塔估错的那部分,会一比一地变成阿尔法。

举个数看得更清楚(下面是为了说明机制编的示例数字,不是任何真实产品)。假设一年里市场涨了 20%,无风险利率 3%,某个组合的真实贝塔是 1.5,真实阿尔法是 0——也就是说这套方法完全没有超出市场的贡献,只是把市场的波动放大了一倍半。那么它的收益应该是 3% + 1.5 × (20% - 3%) = 28.5%。

现在,如果评价它的人用了一个把贝塔估成 1.0 的口径(比如换了个不匹配的基准,或者窗口取在一段同步性偏弱的时间),算出来的阿尔法是:

28.5% - 3% - 1.0 × (20% - 3%) = 8.5%

凭空多出 8.5 个百分点的"超额本事",而这套方法什么都没做。

更要命的是,这个错误不是随机的,它有方向。牛市里,市场超额是正的,贝塔估低就会高估阿尔法;而牛市里表现最扎眼、最容易被拿出来展示的,恰恰是高贝塔的那批组合。"被高估的可能性"和"被展示的可能性"高度重合——这就是为什么行情好的年份,市面上"有阿尔法"的东西会突然变多。熊市里同一个偏差反向作用,会把一些其实还行的方法算成负阿尔法,于是它们在最低点被放弃。

还有一层更隐蔽的假阿尔法,跟估计精度无关,而是模型里少了一列。只扣掉"市场"这一项,意味着你默认收益里除了市场之外没有别的系统性来源。可现实里有的是:你可能长期偏向小市值、偏向低估值、重仓在某个行业上、或者在做一件"平时稳定小赚、偶尔巨亏"的事。这些暴露都是有系统性的、可以被复制的,但因为模型里没给它们留位置,它们全部堆进了余数栏,看起来就像本事。

所以看到"阿尔法显著",第一反应不该是"厉害",而应该是两个问题:贝塔是怎么估的?除了市场,还有什么系统性来源没被扣掉? 后一个问题正是业绩归因要处理的事——多加几列解释变量,往往能把大半个"阿尔法"解释没了。另一条路是在建组合的时候就把不想要的暴露剔干净,那属于中性化处理的范畴,本库另有一篇专门讲。

系统层:三段真实实现,三种对"算不出来"的态度

概念讲完,看看它在真实系统里长什么样。有意思的不是公式(公式都一样),而是边角情况的处理方式——那才是把一个概念落成代码时真正要做的决定。

qlib:教科书式的一行除法,和藏在默认值里的口径问题

qlib 的组合评价模块里,贝塔和阿尔法各是一个短函数。贝塔就是那道除法:

def get_beta(r, b):
    cov_r_b = np.cov(r, b)
    var_b = np.var(b)
    return cov_r_b / var_b

阿尔法紧跟着它:

def get_alpha(r, b, risk_free_rate=0.03):
    beta = get_beta(r, b)
    annaul_r = get_annaul_return_from_return_series(r)
    annaul_b = get_annaul_return_from_return_series(b)
    alpha = annaul_r - risk_free_rate - beta * (annaul_b - risk_free_rate)

这就是前面那段公式的逐字翻译。但把它当标本读,有三处细节比公式本身更有价值。

第一处,分子分母的自由度约定不一样。 np.cov 默认按"样本"口径算(分母是 n-1),np.var 默认按"总体"口径算(分母是 n)。长序列上这点差别可以忽略,短序列上不能。这不是谁对谁错的问题——而是提醒你:同一个贝塔,两个人用两个库、各自取默认参数,就能算出不同的数,然后各自都觉得自己算的是"那个"贝塔。

第二处,返回的东西不一定是你以为的形状。 np.cov 传入两条一维序列时,返回的是一个 2×2 的协方差矩阵,而不是一个标量。抄这类公式的时候,值得先确认自己拿到的到底是数字还是矩阵——这类形状问题不会报错,只会让后面的结果变成一个你没打算要的东西。

第三处,无风险利率的默认值在同一份文件里就不统一。 快照里,算夏普比率的那个函数默认无风险利率是 0.00,算阿尔法的这个默认是 0.03(都是快照中的默认值,可配置)。同一条收益序列,在同一个文件里,能得出两套"无风险"口径。这不算 bug,但它精确地说明了一件事:这类指标的数值高度依赖调用者传了什么,而调用者往往什么都没传。 另外它把日收益折算成年化时用了一个固定的每年交易日数,这是个折算假设——任何年化数字背后都有这么一个假设,换一个,贝塔不变但阿尔法会变。

顺带澄清一个常见的混淆:qlib 里有两组很有名的特征集,名字叫 Alpha158 和 Alpha360。那里的 "alpha" 是行业里的另一层用法,指信号、因子(一条用来预测未来涨跌的计算式),跟本篇讲的这个"扣掉市场之后的余数"不是一回事。看到 alpha 这个词,先看它在哪个语境里。

Vibe-Trading 的指标层:算不出来的时候,它填了 0

另一个系统在算完信息比率和跟踪误差之后,顺手算贝塔:

bench_var = float(aligned_bench.var()) if len(aligned_bench) > 1 else 0.0
if returns_finite and bench_var > 0:
    covariance = float(port_ret.cov(aligned_bench))
    bench_beta = covariance / bench_var
    if not np.isfinite(bench_beta):
        bench_beta = 0.0

这段的防守做得很仔细:基准序列先跟组合的日期对齐过(对不齐的话后面所有差值都是错的),只有基准方差真的大于零、收益序列有限时才做除法,算出无穷或非数时兜回去。

但请注意这个函数开头的初始值:bench_beta = 0.0。也就是说,当根本没有基准、或者数据不足以估计时,输出里的贝塔是 0

0 是一个有含义的值——它意味着"这个组合的涨跌跟市场完全无关",也就是市场中性,那是一个相当强的判断。而"我没算"和"我算出来是 0"在这份输出里长得一模一样。下游任何一个读这份指标的人(或者模型),都没有办法区分这两种情况。

把"没测到"填成 0,是数据分析里最常见的一种自欺,在这里它的后果尤其具体:一个缺基准的回测,会被读成一个完美对冲了市场风险的策略。

同一个仓库的另一处:算不出来时返回"没有"

有意思的是,同一份代码库里的风险透视模块,面对同一类边角情况给出了相反的处理:

market = returns.mean(axis=1)
market_var = float(market.var(ddof=1)) if len(market) > 1 else 0.0
if market_var > 0 and math.isfinite(market_var):
    beta = float(port.cov(market) / market_var)
else:
    beta = None

算不出来时它给的是 None——明确的"没有",而不是一个看起来很正常的 0。下游一眼就知道这一栏无从谈起。同一个仓库里两种态度并存,这本身就是个好教材:边角情况怎么填,是个需要专门做的决定,不是随手写个初始值就完事。

这段还有一个更值得琢磨的设计。它的"市场"不是外部指数,而是 returns.mean(axis=1)——你自己持有的这些标的的等权平均。所以这个字段叫 beta_to_equal_weight,它回答的问题是"我这套加权方式,相对于把这些票平均买一遍,敏感度是多少"。

这跟对市场的贝塔完全是两码事。它衡量的是权重集中度带来的放大效应,不是市场暴露。如果一个组合把大半仓位压在其中一两只上,这个值就会明显偏离 1。贝塔永远是"相对于某个东西"的贝塔,脱离参照物谈贝塔高低毫无意义——报告上写着 1.2,你得先问一句:相对谁的 1.2。

🚧 避坑

阿尔法不是一个发现,是一个余数。 它等于"我没能解释掉的部分",而不是"我确认存在的能力"。模型里少一个解释来源,余数就大一分;贝塔估低一点,余数就厚一层。所以看到一个漂亮的阿尔法,正确的动作是继续往里减——多加几列风险来源、换几个窗口、换个基准重算一遍——看它还剩多少。能被减没的阿尔法,从来就不是阿尔法。

失效边界与常见误用

低贝塔不等于安全。 贝塔只覆盖"跟市场一起动"的那部分风险。一只贝塔只有 0.2 的品种,完全可以因为自身的经营问题独自腰斩,而这跟大盘一点关系都没有。把贝塔当成风险的全部,等于只锁了前门。

贝塔不是历史涨幅的倍数。 它是波动敏感度,不是收益倍数。贝塔 1.5 不代表市场涨 10% 你就一定涨 15%,那只是一个平均意义上的期望关系,单个时段的偏离可以很大——那些偏离,恰恰就是阿尔法所在的地方。

别把估计值当常数用。 拿一个从三年数据里估出来的贝塔,去推算下个月的暴露,中间隔着"它不会变"这个假设。前面说过,它一定会变,而且在最坏的时候变得最快。

不同软件算出来的贝塔不一样,是常态不是异常。 频率、窗口、自由度、基准、对齐方式,任何一处不同都会改数。跨报告比较贝塔之前,先确认口径一致,否则比的是口径不是组合。

短样本的阿尔法基本是噪音。 收益序列本身随机成分就大,减完之后剩下的余数随机成分更大。几个月的阿尔法说明不了任何事情,把它当成能力证明是最常见的过度解读。

用阿尔法给自己发证书。 就算这个数字扛住了各种口径的检验,它说明的也是"过去这段时间里,有一部分收益没被这些因素解释掉"。至于那部分是技能还是运气,是不是下一段时间还在,这个数字回答不了。

小结

  • 贝塔 = 你对市场波动的敏感倍数(协方差 ÷ 市场方差,也等于相关系数 × 波动比);阿尔法 = 扣掉"贝塔 × 市场"之后剩下的余数。
  • 贝塔那一份可以很便宜地买到,阿尔法那一份才稀缺——分界线划歪,你的仓位、杠杆、去留全会跟着歪。
  • 贝塔是估出来的,至少四个旋钮能改结论:拿谁当市场、数据频率、窗口长度与起止、静态还是滚动。它还会随市场剧烈下跌而集体抬升,在你最需要它准的时候最不准。
  • 贝塔估低多少,阿尔法就凭空多出多少,而且这个错误在牛市里有明确方向——高贝塔组合最容易被高估,也最容易被拿出来展示。
  • 阿尔法是余数,模型里少的每一个系统性来源(小市值暴露、行业押注、卖尾部保险……)都会伪装成它。看到显著的阿尔法,先往里多减几列。
  • 三段实现给出三种边角处理:教科书式的一行除法(默认参数和自由度约定就能改数)、算不出来填 0(把"没测到"说成了"市场中性")、算不出来给"没有"。另外,贝塔永远是相对某个参照物的贝塔,脱离参照物谈高低没有意义。

一句话记住这篇:先说清楚有多少是水推着走的,剩下的那点,才轮得到谈本事。

本文所引源码与文档均来自上述快照版本,是阅读代码得出的机制说明,不是运行结果;具体行为请以你手上那个版本为准。文中数字例子为说明机制而设,不对应任何真实产品。回 量化与 AI 投研概念库 看其它概念各自在说什么;本站内容为投资教育,不构成任何投资建议,边界见 免责声明

内容有错、看不懂、或想看下一篇?告诉我们 →

本文为投资教育整理,不构成任何投资建议;不荐个股/基金、不预测点位、不承诺收益。关键数据与结论请结合权威来源自行验证,并见风险免责声明