第 4 章 · 05 二次效用 本节摘要:本节详解 的第五个目标—— 。它最大化一个二次效用函数 ,用单一参数 δ(风险厌恶系数) 平衡收益与风险:δ 越大越厌恶风险(趋向最小方差组合),δ 越小越追求收益(趋向最大收益组合)。这种「用一个连续参数扫描整条前沿」的方式比 / 的「离散目标」更优雅,且不需要预先指定目标值——你只需表达「我有多怕风险」。本节拆解效用函数的数学形式、 参数的语义、源码实现、δ 的实务取值范围,以及 Black-Litterman 模块给出的「市场隐含 δ」如何给 δ 一个客观参考。最后指出它支持 且与额外目标兼容。 内容来源:原项目源码 、 、文档 ,汉化并套用体系化模板。 学习目标 阅读完本节,你应当能够: 写出二次效用函数 。
本节摘要:本节详解
EfficientFrontier的第五个目标——max_quadratic_utility。它最大化一个二次效用函数U = wᵀ μ - ½ δ wᵀ Σ w,用单一参数 δ(风险厌恶系数) 平衡收益与风险:δ 越大越厌恶风险(趋向最小方差组合),δ 越小越追求收益(趋向最大收益组合)。这种「用一个连续参数扫描整条前沿」的方式比efficient_risk/efficient_return的「离散目标」更优雅,且不需要预先指定目标值——你只需表达「我有多怕风险」。本节拆解效用函数的数学形式、risk_aversion参数的语义、源码实现、δ 的实务取值范围,以及 Black-Litterman 模块给出的「市场隐含 δ」如何给 δ 一个客观参考。最后指出它支持market_neutral且与额外目标兼容。
内容来源:原项目源码
pypfopt/efficient_frontier/efficient_frontier.py、pypfopt/objective_functions.py、文档docs/MeanVariance.rst,汉化并套用体系化模板。
阅读完本节,你应当能够:
U = wᵀ μ - ½ δ wᵀ Σ w。max_quadratic_utility(risk_aversion=...) 求最优组合。经济学里「效用」是对偏好程度的数学刻画。二次效用是金融里最常用的一种:
U(w) = wᵀ μ - ½ · δ · wᵀ Σ w
wᵀ μ:组合期望收益(越多越好)。wᵀ Σ w:组合方差(风险,越少越好)。δ:风险厌恶系数(risk aversion),衡量「方差每增加 1 单位,要扣多少效用」。½:数学惯例(让 δ 直接等于相对风险厌恶系数,与一阶条件对应)。💡 核心心法:效用函数把「收益」与「风险」统一到一个标量里,用一个参数 δ 表达「你多怕风险」。δ 大 = 宁可少赚也不要波动;δ 小 = 只要能赚,波动无所谓。这比
efficient_risk的「我要 20% 波动」更贴近真实偏好——很少有人能精确说出「我能承受的波动是 20.3%」,但多数人能回答「我比较保守 / 比较激进」。
δ 是个正实数,它把优化结果在「最小方差」与「最大收益」之间拉:
| δ 取值 | 行为 | 极限 |
|---|---|---|
| δ → 0 | 几乎忽略风险,只追收益 | 趋向「最大收益组合」(权重全押最高 μ 的资产) |
| δ = 1(默认) | 中等平衡 | 在前沿中间某点 |
| δ → ∞ | 极度厌恶风险 | 趋向「全局最小方差组合」(GMV) |
源码里 if risk_aversion <= 0: raise ValueError,δ 必须为正——因为负 δ 意味着「喜欢风险」,会无界增大方差,数学上发散。
打开 efficient_frontier.py,定位 max_quadratic_utility(源码 317-357 行):
def max_quadratic_utility(self, risk_aversion=1, market_neutral=False): if risk_aversion <= 0: raise ValueError("risk aversion coefficient must be greater than zero") update_existing_parameter = self.is_parameter_defined("risk_aversion") if update_existing_parameter: self._validate_market_neutral(market_neutral) self.update_parameter_value("risk_aversion", risk_aversion) else: self._objective = objective_functions.quadratic_utility( self._w, self.expected_returns, self.cov_matrix, risk_aversion=risk_aversion, ) for obj in self._additional_objectives: self._objective += obj self._make_weight_sum_constraint(market_neutral) return self._solve_cvxpy_opt_problem()
关键点:
risk_aversion <= 0 抛错。is_parameter_defined("risk_aversion") 检查是否已建过 risk_aversion 参数。若是,直接 update_parameter_value 改值重解——这让「扫不同 δ」更高效(不重建实例)。若否,首次构造完整目标。objective_functions.quadratic_utility(w, μ, Σ, risk_aversion)(详见下节)。min_volatility 一样,+= 到主目标——与额外目标完全兼容(对比 max_sharpe 的警告)。_make_weight_sum_constraint(market_neutral),与 efficient_risk/efficient_return 一致——支持市场中性。quadratic_utility 目标函数打开 objective_functions.py,定位 quadratic_utility(源码 165-195 行):
def quadratic_utility(w, expected_returns, cov_matrix, risk_aversion, negative=True): sign = -1 if negative else 1 mu = w @ expected_returns variance = cp.quad_form(w, cov_matrix, assume_PSD=True) risk_aversion_par = cp.Parameter( value=risk_aversion, name="risk_aversion", nonneg=True ) utility = mu - 0.5 * risk_aversion_par * variance return _objective_value(w, sign * utility)
关键点:
negative=True(默认):返回负效用。因为 PyPortfolioOpt 的优化器是最小化框架,要「最大化效用」等价于「最小化负效用」——这是库内统一的设计(README 模块注释明确:「明显应最大化的目标(夏普、收益),函数返回负值,因最小化负值 = 最大化正值」)。mu = w @ μ:组合收益,线性项。variance = cp.quad_form(w, Σ):组合方差,二次项。risk_aversion_par:把 δ 包成 cp.Parameter(name="risk_aversion", nonneg=True)——这正是 max_quadratic_utility 用 is_parameter_defined 检测的那个参数,让 δ 可重解。效用公式严格对应 μ - 0.5 × δ × variance:
U = wᵀ μ - ½ δ wᵀ Σ w
| 参数 | 默认 | 含义 | 备注 |
|---|---|---|---|
risk_aversion |
1 |
风险厌恶系数 δ | 必须 > 0 |
market_neutral |
False |
是否市场中性(权重和为 0) | 需负权重边界 |
⚠️ δ 不能 ≤ 0:源码
if risk_aversion <= 0: raise ValueError。负 δ 意味「喜欢风险」,数学上无界。
δ 该取多少?这是 max_quadratic_utility 最常被问的问题。几个参考:
docs/MeanVariance.rst 的 note 提到——Black-Litterman 模块提供了一种计算「市场隐含风险厌恶参数」的方法,在没有其他信息时给出客观估计。💡 实务建议:先从 δ=1 起步,看权重分布是否合理;若太集中(某资产占 50%+),说明风险约束不够,调大 δ;若太分散(几乎等权),说明风险约束过强,调小 δ。也可扫 δ ∈ [0.5, 5] 看权重如何平滑变化,选一个「直觉舒服」的点。若做严肃配置,用 Black-Litterman 的市场隐含 δ(第 7 章)作为客观锚点。
ef = EfficientFrontier(mu, S) ef.max_quadratic_utility() ef.clean_weights() ef.portfolio_performance(verbose=True)
ef = EfficientFrontier(mu, S) ef.max_quadratic_utility(risk_aversion=5) ef.portfolio_performance(verbose=True)
波动率会比 δ=1 时更低,收益也相应降低。
ef = EfficientFrontier(mu, S, weight_bounds=(-1, 1)) ef.max_quadratic_utility(risk_aversion=2, market_neutral=True)
deltas = [0.5, 1, 2, 3, 5, 10] curve = [] for d in deltas: ef = EfficientFrontier(mu, S) ef.max_quadratic_utility(risk_aversion=d) ret, vol, _ = ef.portfolio_performance() curve.append((vol, ret, d)) # 画 (vol, ret),每点标 δ——这条曲线就是有效前沿的另一种参数化
注意:扫不同 δ 时,如果用 update_parameter_value 重解(而非重建实例),需要保证 market_neutral 不变——源码里 _validate_market_neutral 会检查,改了 market_neutral 必须重建。
from pypfopt import objective_functions ef = EfficientFrontier(mu, S) ef.add_objective(objective_functions.L2_reg, gamma=1) ef.max_quadratic_utility(risk_aversion=2)
无 max_sharpe 的变量替换问题,L2 正则正常工作。
| 维度 | max_sharpe | min_volatility | efficient_risk/return | max_quadratic_utility |
|---|---|---|---|---|
| 需要 μ | 是 | 否 | 是 | 是 |
| 需要预设目标 | 否 | 否 | 是(目标值) | 否(只给 δ) |
| 参数连续性 | — | — | 离散扫目标 | δ 连续扫 |
| 与额外目标兼容 | 否 | 是 | 是 | 是 |
| 支持市场中性 | 否 | 否 | 是 | 是 |
| 直觉 | 风险调整收益 | 最保守 | 精确目标 | 偏好强度 |
💡 何时用 quadratic_utility:如果你不想精确指定「目标波动 20%」或「目标收益 25%」,而是想表达「我比较保守/激进」的模糊偏好,
max_quadratic_utility是最自然的选择。δ 是一个连续参数,扫它就能描出整条前沿,且每次结果都是前沿上的有效点(不像 max_sharpe 只给一个点)。
U = wᵀ μ - ½ δ wᵀ Σ w,δ 平衡收益与风险。quadratic_utility(negative=True 返负效用),与额外目标兼容、支持市场中性。cp.Parameter,is_parameter_defined + update_parameter_value 高效扫不同 δ。至此,均值-方差优化的五个目标讲完。下一章我们进入进阶——加约束、做市场中性、用 L2 正则、自定义目标,把标准模型推向工程实战。