halo 的技术博客

返回

再平衡(Rebalancing)几乎是每个组合管理员的日常:战略权重定好了,市场一涨一跌,实际持仓就漂移了,你得把它拉回目标

但「拉回」这件事,绝大多数人用的是最朴素的做法:

把超配的资产卖掉 → 变成现金 → 再用现金买入低配的资产。

这一卖一买之间,你付了两道交易成本,而且中间现金裸露,还承担了一段「半仓」的跟踪误差。当你管理的是 8 个、20 个甚至上百个资产的组合时,这种「全走现金」的再平衡每年吃掉的真实摩擦,常被严重低估。

最优传输(Optimal Transport, OT) 给了我们一个更聪明的视角:

再平衡本质上是一个「把当前权重分布,变形为目标权重分布」的问题——而变形(运输)是有单位成本的。让「该减的资产」直接换给「该增的资产」,而不是一律先回现金,就能省掉一半以上的摩擦。

本文用 8 资产组合,把再平衡重写成一个最小成本运输问题,scipy 线性规划从零求解,量化不同再平衡方式的真实成本差异。

1. 把再平衡重写成「运输问题」#

设当前权重 wcurw^{\text{cur}}(源分布),目标权重 wtgtw^{\text{tgt}}(汇分布),两者都和为 1。我们要找一个运输矩阵 TRN×NT \in \mathbb{R}^{N\times N},其中 TijT_{ij} 表示「从资产 ii 转移多少质量到资产 jj」。

约束很自然:

  • ii 行和 = wicurw^{\text{cur}}_i(流出的总量等于 ii 当前的权重)
  • jj 列和 = wjtgtw^{\text{tgt}}_j(流入的总量等于 jj 的目标权重)
  • Tij0T_{ij} \ge 0

目标:最小化总运输成本 ijCijTij\sum_{ij} C_{ij} T_{ij}

成本矩阵怎么设?这是 OT 再平衡的精髓:

  • 对角线 Cii=0C_{ii} = 0:留在原地不动,零成本。
  • 非对角 Cij=κC_{ij} = \kappa(基础比例成本,比如 10bps):现货对现货的换仓成本。
  • 特定折价配对 CijC_{ij} 极低(如 1bps):如果 iijj 可以通过实物申赎、篮子交易、或同一做市商内部对冲来近乎零成本交换,就把这对成本压到地板。

这个设定本身就是一种「领域知识注入」:你告诉优化器哪些转换是便宜的,它就会优先走这些便宜路径。

2. 用 scipy 从零求解#

注意一个干净的数学事实:当所有非对角成本都等于同一个 κ\kappa 时,最优解会让「需要减仓的资产」的质量直接流向「需要加仓的资产」,总非对角运输量恰好等于换手率 T=12ΔwT = \frac12\sum|\Delta w|。所以均匀成本下的总运输成本 = κT\kappa \cdot T

当前 vs 目标权重:沪深300 超配至 38%、国债低配至 16%

上图就是一次典型的漂移:股票涨多了、债券跌少了,你需要把股票的钱挪去债券。朴素做法会先卖股票成现金、再买债券——两道成本。

最优传输方案:非对角格=实际换仓流量

热力图展示了 OT 给出的真实换仓结构:沪深300 直接流向国债、美股流向可转债……绝大部分流量走的是非对角格,也就是现货对现货的直换,而不是回现金。对角线(原地持有)保留了 90% 的质量——不需要动的资产,一处都不动。

3. 三种再平衡方式的真实成本#

把三种方式摆在一起PK:

方式机制成本公式本次实测
朴素现金中枢卖→现金→买2κ2\kappa\cdot 换手率2.0 bps
OT 直接换仓(均匀)现货对现货κ\kappa\cdot 换手率1.0 bps
OT 含配对折价优先走便宜配对<κ<\kappa\cdot 换手率0.7 bps

三种再平衡成本对比

数字很直观:OT 直接换仓比现金中枢省 50%,因为你把两道成本压成了一道;再叠上折价配对(实物申赎/篮子交易),再降一截到 66%。这省下的不是小数点后的幻觉——对一个年化换手率 200%、规模 10 亿的组合,0.7bps vs 2.0bps 的差异,就是每年 130 万 vs 200 万的摩擦之差。

4. 用 W1 距离作再平衡触发器#

再平衡还有个老问题:什么时候调? 固定周期(每月)简单,但市场在平静期白白付摩擦;阈值触发能省,但「阈值」用什么度量?

答案是 1-Wasserstein 距离 W1=12ΔwW_1 = \frac12\sum|\Delta w|——它恰好等于「把当前分布推成目标分布的最小直线运输成本」。漂移越大,W1W_1 越大,自然就是再平衡的触发器。

eps_thr = 0.04  # W1 触发阈值 4%
drift = 0.5 * np.sum(np.abs(w - w_tgt))
if drift > eps_thr:
    # 触发 OT 再平衡, 本次运输成本 = drift(而非 2*drift)
    ...
python

我们做了一次 2 年日度回测:每月固定再平衡 vs W1>4%W_1>4\% 阈值触发(两种都用 OT 换仓)。

W1 触发 vs 固定周期的累计成本与漂移

回测结果:阈值触发累计交易成本 2.9 bps,比固定周期的 4.8 bps 省 41%,同时组合偏离目标权重始终被压在 4% 以内(固定周期在调仓日瞬时归零,但阈值法在非调仓日也只漂到 ~2.6%,绝不放飞)。这就是「该调才调、且调得便宜」。

这里有个反直觉的工程细节值得停下来想:固定周期法的单笔运输成本其实也是用 OT 算的(现货对现货,省一半)。也就是说,41% 的节省纯粹来自「少触发」,而非「换仓方式」——OT 负责把每次调仓的成本压到地板,W1 阈值负责决定「哪天值得花这笔钱」。两者是乘法关系:OT 降低单次成本,阈值降低触发频率,叠起来才是完整的摩擦最小化。如果你的触发条件本身就很勤(比如每天检查),那 OT 省下的单次成本就是主要贡献;如果触发已经很稀疏,OT 的边际价值就在「让那少数几次调仓更便宜」。把这两层分开看,你才能知道该在哪一层继续优化。

5. 五类真实陷阱(必读)#

陷阱一:成本矩阵拍脑袋,优化器就给你拍脑袋的结果。 κ\kappa 和折价配对必须来自真实交易成本(佣金+冲击+价差),不能随便设。把折价配对设得比真实可行还低,优化器会疯狂走这些路径,回测漂亮、实盘踩雷。

陷阱二:资产数量大时线性规划变慢。 变量是 N2N^2 个。N=8 是 64 个变量,秒解;N=200 是 4 万个变量,scipy 的通用 LP 会吃力。真实组合规模大时,要么用专门的 OT 求解器(如 POT 库的 linprog/network_simplex),要么只在「需要调的少数资产」子空间里求解。

陷阱三:折价配对的「便宜」会消失。 篮子交易、实物申赎的折价依赖特定市场结构和对手方,流动性枯竭时折价配对可能变贵甚至不可用。回测里永远可用的折价,实盘未必。建议把折价配对的成本设成「正常市况下的保守值」,而非最优值。

陷阱四:W1 阈值不是越低越好。 阈值太低(如 1%),几乎每次都触发,退化成高频再平衡,摩擦反而高;阈值太高(如 15%),漂移长期不修,跟踪误差失控。4%~6% 是常见甜区,但必须按你的组合波动率和风险预算校准。

陷阱五:OT 省的是「执行摩擦」,不创造「alpha」。 本文所有数字都是成本侧改善。如果你的目标权重本身烂(比如追涨杀跌式动态权重),OT 只会让你「更便宜地犯错」。OT 是再平衡的执行层优化,不是策略层优化。

陷阱六(延伸):多周期与税务视角。 本文把再平衡当成「一次性的当前→目标」。真实组合里还有分红再投资、申购赎回现金流、以及资本利得税——这些都会改变源分布 wcurw^{\text{cur}} 的形状。扩展做法是在源端先叠加「现金流入预定用途」「税后净调仓」等约束,再跑 OT;税务敏感账户里,「不卖浮盈资产、只调浮亏资产」可以直接编码成某些 CijC_{ij} 的无穷大。OT 框架的优雅之处正在于此:任何你对「什么转换更便宜/更合法」的先验,都能写进成本矩阵,而不必改算法本身。

6. 结语#

再平衡的本质,是一个最小成本变形问题:把当前权重分布,用最低代价推成目标分布。朴素现金中枢是「所有质量先回原点再出发」,最优传输是「点对点直送、原地不动的绝不挪」。两者在成本上差一倍,而实现成本只是 scipy 里几十行线性规划。

但请记住铁律:OT 优化的是执行摩擦,不是投资观点。它让你的好权重更便宜地落地,却不会替你决定什么权重是对的。先把目标权重想清楚,再用 OT 把它以最低代价实现——这才是工程上正确的顺序。

数据说明:本文所有图表与关键数字(省 50%/66% 摩擦、W1 阈值回测省 41% 交易成本)均由文中 Python 代码真实计算生成;收益与协方差由带结构的合成数据自洽生成,仅用于演示方法论,不构成任何投资建议。

最优传输再平衡:用Wasserstein距离做最小摩擦调仓
https://blog.halo26812.eu.org/blog/optimal-transport-rebalance
Author halo
Published at 2026年7月19日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨