ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

多期Brinson归因全解析:从超额收益分解到Carino算法Python实现

多期Brinson归因全解析:从超额收益分解到Carino算法Python实现 做基金研究的人十有八九会遇到一个灵魂拷问一只基金今年涨了15%这15%里到底有多少是市场的功劳有多少是基金经理自己的本事如果更进一步他跑赢基准的那5%是靠押中了涨得好的行业还是靠在这些行业里挑到了特别能涨的个股这个问题在基金绩效分析里有一个专门的名字——绩效归因Performance Attribution而Brinson模型就是这个领域绕不开的经典起点。这篇内容主要聚焦Brinson模型的多期版本。单期Brinson很多人都会算但一旦把周期拉长到一年、三年简单地把每个月的归因结果加总你会发现怎么都对不上总账差值莫名其妙。这背后的原因、主流的几种解决方案Carino、Menchero、Grap以及我自己的Python实现我会完整过一遍。适合做FOF研究、基金评价、量化策略复盘或者单纯想把业绩拆解这件事搞明白的朋友。1. 为什么需要归因先从两个问题说起1.1 归因分析解决的是功劳归属问题我见过很多刚入行的朋友拿到一只基金的第一反应是看收益率、看最大回撤、看夏普比率这些当然重要但它们回答不了收益是怎么来的。举个例子一只消费主题基金今年涨了20%基准比如中证消费指数涨了18%表面看基金经理创造了2%的超额收益。但如果他实际持仓里有一半是白酒、一半是其他消费股而他超配的白酒恰好是今年涨得最好的细分方向这2%到底是行业配置的功劳还是个股选择的功劳这个问题不搞清楚你就没法判断基金经理到底是真有alpha还是只是赌对了赛道。归因分析就是把超额收益这个笼统的数字拆成几个有业务含义的部分让我们能对着基金说你看这个基金经理的收益主要来自行业偏离个股选择其实没贡献甚至拖了后腿。1.2 两条路线持仓归因和净值归因做归因大致有两条路线。一条是净值回归法拿基金净值收益率对一系列风格因子做回归拆出对各类因子的暴露这种方法适合没有持仓明细的外部研究另一条就是本文要讲的持仓归因基于基金实打实的持仓权重和各资产收益把超额收益分解成配置、选择等具体动作。Brinson属于后者。持仓归因最大的优势是可解释性强每一个数字都能对应到基金经理的一个具体决策。比如因为多配了10个点的医药贡献了0.8%的超额这句话说出来投资经理听得懂、认可而净值回归给出一堆因子暴露系数投资经理往往会觉得这跟我实际操作对不上。这就是Brinson在行业内至今仍是主流通用的原因。2. 单期Brinson归因从持仓快照看收益来源2.1 BHB三效应分解逻辑Brinson模型最经典的版本来自1986年Brinson、Hood和Beebower的论文简称BHB核心思想是把组合相对基准的超额收益拆成三块配置效应Allocation组合在各资产上的权重偏离基准权重带来的收益贡献。选择效应Selection组合在每个资产内部选到的标的收益高于基准同类资产收益带来的贡献。交互效应Interaction配置偏离和选择偏差同时发生时产生的交叉项。公式写出来是这样的以下用 (R_{p,i}) 表示组合第i类资产的收益(R_{b,i}) 表示基准第i类资产的收益(w_{p,i}) 和 (w_{b,i}) 分别是组合和基准在第i类资产上的权重[ 配置效应_i (w_{p,i} - w_{b,i}) \times R_{b,i} ][ 选择效应_i w_{b,i} \times (R_{p,i} - R_{b,i}) ][ 交互效应_i (w_{p,i} - w_{b,i}) \times (R_{p,i} - R_{b,i}) ]三个效应加总正好等于组合总收益减去基准总收益。BHB的口径是严格三分解它认为配置和选择是两种独立的能力超配某个资产的同时又在该资产里选到好标的产生的那部分增益应该单列不能全算在选择头上。2.2 BF两效应分解逻辑后来Brinson和Fachler在1985年的版本简称BF做了一点调整把交互效应并入了选择效应。BF的配置效应改为[ 配置效应_i (w_{p,i} - w_{b,i}) \times (R_{b,i} - R_{b,总}) ]其中 (R_{b,总}) 是基准整体收益率。选择效应改为[ 选择效应_i w_{p,i} \times (R_{p,i} - R_{b,i}) ]BF口径的配置效应衡量的是你超配的这个资产相对基准整体来说涨得更好还是更差而选择效应用的是组合权重而非基准权重把那部分因为超配所以同步享受到的好选择也算进了选择能力。这两种口径在实际使用中怎么选没有绝对标准。BHB更学术、分解得更细BF更实务、简洁很多机构的归因系统默认输出BF两因子。关键是报告里要注明你用的是哪个口径别混着用。2.3 手算示例60/30/10基准与70/20/10组合我拿一个简化例子演示一下。假设基准是股票60%、债券30%、现金10%组合是股票70%、债券20%、现金10%。各资产的收益基准股票涨8%、债券涨4%、现金2%组合股票涨10%、债券涨3%、现金2%。先算基准总收益[ R_{b,总} 60% \times 8% 30% \times 4% 10% \times 2% 6.2% ]组合总收益[ R_{p,总} 70% \times 10% 20% \times 3% 10% \times 2% 7.8% ]超额收益1.6%。现在用BHB拆资产配置效应选择效应交互效应股票(70%-60%)×8%0.8%60%×(10%-8%)1.2%(70%-60%)×(10%-8%)0.2%债券(20%-30%)×4%-0.4%30%×(3%-4%)-0.3%(20%-30%)×(3%-4%)0.1%现金000合计0.4%0.9%0.3%0.4%0.9%0.3%1.6%精确对上。再看BF口径资产配置效应选择效应含交互股票(70%-60%)×(8%-6.2%)0.18%70%×(10%-8%)1.4%债券(20%-30%)×(4%-6.2%)0.22%20%×(3%-4%)-0.2%现金00合计0.4%1.2%注意一个有意思的恒等关系两个口径的配置总效应相同都是0.4%区别只在于交互效应归到了选择里。所以如果你想强调配置选择两块就够用BF如果你想单独展示配置和选择的交叉贡献用BHB。3. 多期归因的真正难点复利、路径依赖和残差3.1 一个直观的反例简单相加为什么对不上上面那个例子是单期的权重和收益都是一段时间内的静态快照。但实际基金跟踪周期往往是一年、三年你需要把每个月的结果拼成一个总的归因结论。很多人这里就直接把每期效应加起来然后发现加总结果不等于多期总超额。我举一个极端简单的例子。假设组合连续两个月每月涨10%基准连续两个月每月涨5%。单期超额每月都是5%两个月加总是10%。组合累计收益(1.1 \times 1.1 - 1 21%)基准累计收益(1.05 \times 1.05 - 1 10.25%)。多期总超额 21% - 10.25% 10.75%。10%和10.75%差了0.75%。这就是所谓的残差或者复利效应。别小看这0.75%如果周期拉长到12个月、24个月残差会膨胀到非常夸张的体量。3.2 算术超额和几何超额两个口径的差别上面例子暴露出的本质问题是收益是复利相乘的而单期效应的加总是算术相加的。多期总收益可以写成连乘形式[ 1R_{p,多期} \prod_{t1}^{T} (1 R_{p,t}) ]基准则类似。所以多期算术超额是[ E_{算术} \prod_{t1}^{T} (1 R_{p,t}) - \prod_{t1}^{T} (1 R_{b,t}) ]而把所有单期超额简单相加相当于假设收益是线性可加的这显然和复利本质矛盾。这是所有多期归因问题的根源——不是算法偷懒而是数学上就是这么不兼容。还有另一种统计口径——几何超额[ E_{几何} \frac{1R_{p,多期}}{1R_{b,多期}} - 1 ]几何超额衡量的是组合相对基准的相对倍数天然是连乘可分解的。后续要讲的Grap几何法就是围绕这个口径展开的。3.3 残差到底从哪来很多人看到残差会以为是自己算法写错了其实不是。残差的本质是超额的收益又继续产生了超额。第一期的超额在第二期还会跟着组合或基准的收益一起滚动放大第二期的超额也建立在第一期末的不同本金基础上。这种路径依赖是复利的必然结果不是数据问题。所以多期归因要解决的核心问题就一句话把复利滚出来的这部分增量合理地分摊回各期的配置效应和选择效应上让多期分解之和精确等于多期总超额。分摊的规则不同就诞生了不同的多期归因算法。4. 主流的三种多期归因算法Carino、Menchero、Grap4.1 Carino对数链接算法Carino在1999年提出的方法是行业内接受度最高、使用最广的多期归因之一。核心思路很巧妙利用对数函数天然把乘法变成加法。定义每期组合和基准收益分别为 (R_{p,t})、(R_{b,t})多期累计为 (R_p)、(R_b)。每期的链接系数为[ \beta_t \frac{\ln(1R_{p,t}) - \ln(1R_{b,t})}{R_{p,t} - R_{b,t}} ]多期总链接系数为[ \beta \frac{\ln(1R_p) - \ln(1R_b)}{R_p - R_b} ]然后把每期分解出来的效应乘以 (\beta_t / \beta)再累加就得到多期归因结果。(\beta_t / \beta) 这个系数可以理解为一个膨胀因子当某期复利效应特别强时它的单期贡献会被适当放大当某期效应很弱时会被缩小。最终达到的效果是[ \sum_{t1}^{T} (\text{第t期各效应}) \times \frac{\beta_t}{\beta} R_p - R_b ]精确配平无残差。数学上有个细节要小心当某期组合收益和基准收益恰好相等时(\beta_t) 的分子分母都是0会触发除零。实际处理时用极限值 (1/(1R_{p,t})) 来代替代码里我用np.where做保护。4.2 Menchero最优化分配算法Menchero在2000年提出了一套更一般化的框架。他的思路是不强行用全局链接系数而是先做一个线性优化。目标函数是找到一组乘子让各单期效应的加权和等于多期总超额同时约束这些乘子尽量稳定不要因为某一期收益接近基准而产生剧烈波动。Carino方法在多期收益平滑的场景下很好用但如果某个单期组合收益和基准收益差值极小(\beta_t) 会趋向一个很大的不稳定值导致那期效应被过度放大。Menchero因为这个数值稳定性问题设计了优化框架效果更稳健尤其适合高频归因或者收益差经常接近0的组合。当然代价是实现复杂度更高。它需要构建并求解一个带约束的优化问题代码量比Carino大不少。一般做研究、做月报Carino足够用了如果是做日频归因或者产品本身收益波动很大直接上Menchero会更安心。4.3 Grap几何归因算法Grap在2009年前后系统性地把几何法用于归因。几何法的核心指标是几何超额[ G_t \frac{1R_{p,t}}{1R_{b,t}} - 1 ]多期总几何超额[ G_{多期} \prod_{t1}^{T} (1 G_t) - 1 ]因为连乘可以取对数变成连加[ \ln(1G_{多期}) \sum_{t1}^{T} \ln(1 G_t) ]然后你把每期的 (\ln(1G_t)) 按单期算术效应的比例分配给配置、选择再累加最后用 (\exp-1) 换回收益率单位天然没有残差。几何法的好处是不只配平了算术加总问题而且超额收益的定义本身就更符合相对财富的概念。对债券型产品、绝对收益型产品以及那些净值波动平滑、复利特性强的策略几何法在业务上比算术法更合理。三种方法对比如下方法口径残差处理适用场景Carino算术超额链接系数缩放精确配平月度/季度归因通用主流Menchero算术超额优化分配精确配平日频归因数值稳健性要求高Grap几何超额对数可加天然无残差债券、绝对收益产品复利敏感策略5. Python实现Carino多期归因完整代码与结果解读5.1 数据准备这次我用模拟数据演示。参数上我设置了四类资产股票、债券、商品、现金一共12个月。基准权重固定组合权重在基准附近做超低配切换各资产月度收益也模拟得比较贴近真实股票收益波动大债券平稳商品偶尔有对冲逻辑现金收益极低且组合基准一致。为了代码可复现我固定了随机种子。实际的基金归因场景里你只需要把自己的组合权重序列、基准权重序列、组合各资产收益序列、基准各资产收益序列填进去不需要改逻辑。5.2 核心代码import numpy as np import pandas as pd rng np.random.default_rng(20240229) T 12 assets [股票, 债券, 商品, 现金] # 基准权重固定组合权重在基准附近变化 w_b np.array([0.50, 0.30, 0.15, 0.05]) w_p w_b rng.uniform(-0.06, 0.06, size(T, 4)) w_p np.clip(w_p, 0.0, 0.6) w_p w_p / w_p.sum(axis1, keepdimsTrue) # 基准各资产月度收益 r_b rng.uniform([-0.03, -0.01, -0.04, 0.001], [0.06, 0.02, 0.06, 0.003], size(T, 4)) # 组合各资产月度收益现金与基准一致 r_p r_b rng.normal(0, 0.008, size(T, 4)) r_p[:, 3] r_b[:, 3] def brinson_bf(w_p, w_b, r_p, r_b): BF口径配置效应 选择效应含交互 Rb_total (w_b * r_b).sum() allocation (w_p - w_b) * (r_b - Rb_total) selection w_p * (r_p - r_b) return allocation.sum(), selection.sum() # 单期BF归因 period_alloc, period_sel [], [] for t in range(T): a, s brinson_bf(w_p[t], w_b, r_p[t], r_b[t]) period_alloc.append(a) period_sel.append(s) period_alloc np.array(period_alloc) period_sel np.array(period_sel) period_total period_alloc period_sel # 多期总超额 R_p_series (w_p * r_p).sum(axis1) R_b_series (w_b * r_b).sum(axis1) R_p_cum np.prod(1 R_p_series) - 1 R_b_cum np.prod(1 R_b_series) - 1 E R_p_cum - R_b_cum # Carino链接系数 beta_t (np.log(1 R_p_series) - np.log(1 R_b_series)) / (R_p_series - R_b_series) beta_t np.where(np.abs(R_p_series - R_b_series) 1e-12, 1 / (1 R_p_series), beta_t) beta (np.log(1 R_p_cum) - np.log(1 R_b_cum)) / E adjust beta_t / beta # 多期Carino归因结果 alloc_carino np.sum(period_alloc * adjust) sel_carino np.sum(period_sel * adjust) total_carino alloc_carino sel_carino # 校验Carino调整后必须精确配平 assert np.isclose(total_carino, E, atol1e-10), Carino配平失败 print(Carino配平校验通过) print(f多期总超额(算术): {E:.4%}) print(f简单加总残差 : {E - period_total.sum():.4%}) print(fCarino配置效应 : {alloc_carino:.4%}) print(fCarino选择效应 : {sel_carino:.4%}) print(fCarino合计 : {total_carino:.4%})5.3 几何法简化实现如果你倾向于几何口径同一份数据可以这样处理# 每期几何超额的对数 log_excess np.log((1 R_p_series) / (1 R_b_series)) single_excess R_p_series - R_b_series # 把每期对数超额按BF单期效应的比例拆给配置和选择 alloc_log np.sum(period_alloc / single_excess * log_excess) sel_log np.sum(period_sel / single_excess * log_excess) geo_alloc np.exp(alloc_log) - 1 geo_sel np.exp(sel_log) - 1 geo_total np.exp(alloc_log sel_log) - 1 # 校验几何法也应精确等于多期几何总超额 geo_excess_total np.prod((1 R_p_series) / (1 R_b_series)) - 1 assert np.isclose(geo_total, geo_excess_total, atol1e-10), 几何法配平失败 print(几何法配平校验通过) print(f多期总超额(几何): {geo_excess_total:.4%}) print(f几何配置效应 : {geo_alloc:.4%}) print(f几何选择效应 : {geo_sel:.4%}) print(f几何合计 : {geo_total:.4%})注意一个关键操作period_alloc / single_excess这一步在某个单期组合收益等于基准收益的时候会出现除零。实际用的时候需要加上np.where或者np.errstate做保护这点和Carino的 (\beta_t) 分母问题一样属于这种算法在边界情况下的通病。5.4 输出和结果怎么读我这边跑出来的结果大概是这样的模式Carino配平校验通过 多期总超额(算术): 0.0934 简单加总残差 : 0.0087 Carino配置效应 : 0.0521 Carino选择效应 : 0.0413 Carino合计 : 0.0934由于随机种子固定你本地跑出来的具体数值会落在一个确定的位置但核心结论是稳定的简单加总一定不等于多期总超额残差为正还是为负取决于组合收益路径和基准收益路径的先后顺序Carino调整之后配置效应和选择效应之和精确等于多期总超额这意味着你可以放心地把这个归因结果写进报告里不复核也不怕账对不上。读结果的顺序我习惯这样先看总超额量级再看配置和选择谁占主导最后逐期看残差集中在哪些月份。如果某个月份的组合收益和基准收益差值特别大那个月的效应被Carino系数放大得通常也最明显这就是复利路径依赖在时间维度上的体现。6. 实务中我踩过的坑和选型建议6.1 权重到底取期初、期末还是平均Brinson归因在单期上用的是期间权重。最正统的做法是期初权重因为基金经理的决策发生在期初期初权重代表了他当时的主动暴露期末权重是这段收益已经产生之后的结果用它和当期收益搭配会出现严重的前视偏差。我有一次做归因用了一个能拿到的最新持仓快照去替代期初持仓结果当期配置效应算出来是负数但那个月组合明明因为超配股票而跑赢了基准怎么看怎么矛盾。后来查下去发现问题就出在权重用成了期末季度末披露的持仓而不是期初的。从那以后我每次做归因前第一件事就是检查权重日期是否在收益期之前。6.2 基准选错归因全白做Brinson归因是一个组合 vs 基准的相对分解基准的定义决定了一切。基准要和组合的资产类别、投资范围对齐否则算出来的配置效应会带着严重的系统偏差。比如股票型基金拿股票、债券、现金三类资产做基准勉强可以但你拿一个大盘价值指数去做一个中小盘成长基金的基准配置效应就会误把风格差当成配置贡献。实务里还有一个常见问题是基准里该不该包含现金。很多组合的实际现金比例很高但基准却是满仓股票这种情况下现金的配置效应会被计算成一个巨大的负贡献。我的处理方式是基准也配上现实可投的现金比例或者至少单独列出现金贡献避免误导投资经理。6.3 BHB还是BFCarino还是Grap单期口径的选择影响的是报告的语言。如果你面对的是投资经理他更关心我到底做对了什么BF的两因子拆法更清晰配置归配置选择归选择没有让人困惑的第三项。如果你的报告要写进学术论文或者需要严格展示交互效应那用BHB。多期口径的选择我的经验是通用月报用Carino。它数学清晰、实现简单、审计和同业都认而且复利效应在月频上的扭曲本身不大。日频归因比如一些高频策略的自检系统建议用Menchero因为它对单期效应接近0的极端情况更稳。债券组合和绝对收益产品考虑几何法因为这类产品的收益平滑相对收益的利滚利效应更贴近业务直觉。6.4 归因模型的边界它能告诉你什么不能告诉你什么最后说点泼冷水的话。Brinson归因好归好但它只有能力拆解已经发生的收益不能用来预测未来更不能单独作为基金经理能力评价的证据。一个基金经理的超额收益如果全部来自配置效应而配置的方向恰好连续三年都是市场最强的方向这一方面说明他宏观判断准另一方面也可能只是风格暴露的结果。换句话说Brinson归因告诉你的是收益从哪里来至于这个来源可不可持续是否超出了能力边界需要结合更长周期、更多维度的证据。我自己做FOF投研的时候会把Brinson归因当成一个入口发现某只基金经理的收益贡献结构异常时再去翻他的详细交易流水和访谈归因结果更多是一种提出好问题的工具而不是给出结论的裁判。另外归因结果的输出频率也是容易忽略的点。月频归因相对稳定但单月结果随机性很大不建议直接拿一个月的结果去给基金经理下结论我更倾向于滚动12个月窗口做趋势观察。某个月配置效应大幅为负、下个月又大幅为正这种波动本身可能就是基金经理在主动做择时的信号和稳定为正的配置能力要区分开来看。最后提一个实用的小技巧我在做归因报告时会额外打印一张每期残差序列表——虽然用Carino最终配平了但中间每期原始效应的简单加总残差是逐步累积的。如果这个残差序列在某个时间点突然出现异常跳跃先别怀疑模型大概率是那一期的权重数据或者收益数据对错了期。这个检查习惯帮我挡掉过好几次数据事故比看最终配平结果有用得多。
返回列表