第 01 章 优化问题与建模
学习目标
读完本章,你应当能够:
- 把一个实际决策问题拆成目标、变量、约束三要素,并写成标准形式 \(\min f(x)\) s.t. \(c_i(x)=0,\ c_i(x)\ge 0\)。
- 按连续/离散、有约束/无约束、局部/全局、确定/随机、线性/非线性几个维度给优化问题分类,并知道每一类大致该找什么工具。
- 准确说出凸集、凸函数、凹函数、凸规划的定义,理解"凸问题的局部解就是全局解"为什么是实务中最重要的一条性质。
- 用稳健性、效率、精确性三条标准评价一个优化算法,理解它们之间的权衡。
- 能把均值–方差组合优化写成标准形式,判断它是凸规划,并看清"先解连续问题再取整"在整手约束下会出什么问题。
读前导读
这一章在解决什么问题
先从你熟悉的场景说起。在 CFA 课程或工作里,你多半用过 Excel Solver 做均值–方差优化:在一张表里放好预期收益和协方差矩阵,把"权重"那几格设为可变单元格,把"组合效用"或"组合方差"那一格设为目标单元格,再在"约束"框里加上"权重和 = 1""每个权重 ≥ 0",选好"GRG 非线性",点"求解"。几秒钟后权重就填好了。
这一册要讲的,就是你点下"求解"之后 Solver 在背后做了什么。可变单元格就是本章说的变量,目标单元格就是目标,约束框里那几行就是约束——这三样东西合起来叫"建模"。Solver 拿到模型后,从你表里现有的数字(初始点)出发,反复做一件事:估计"往哪个方向改权重能让目标变好、改多少",然后改一点,再估计,再改,直到改不动为止。GRG 是"广义既约梯度法"的缩写,它用到的梯度、步长、约束处理,正是本册第 2–18 章的内容。
本章本身不讲算法,只做三件准备工作。第一,把问题写成统一的"标准形式",就像把各种债券的现金流都写成"贴现因子乘现金流再求和"一样,统一了写法,后面的理论才能通用。第二,给问题分类,因为不同类型的问题要用不同的求解器(Solver 里"单纯形 LP""GRG 非线性""演化"三个选项,就是按问题类型分的)。第三,引出凸性:它决定了 Solver 报告的"找到一个解"到底是不是真正的最优解。你可能遇到过 Solver 换个初始值就给出不同答案的情况,那就是问题不凸、只找到了局部解。均值–方差问题是凸的,所以你过去用 Solver 得到的答案是可信的;但加上"最多持有 10 只股票"之类的约束,情况就变了。
需要先想起来的数学
- 向量与 \(\mathbb{R}^n\):\(x\in\mathbb{R}^n\) 读作"\(x\) 是一个由 \(n\) 个实数排成的列向量",例如 8 只股票的权重 \(w=(w_1,\dots,w_8)^T\)。\(w^T\Sigma w\) 是"行向量 × 矩阵 × 列向量",结果是一个数,就是你在 CFA 里用 \(\sum_i\sum_j w_iw_j\sigma_{ij}\) 算的组合方差。见 第 00 册第 06 章 线性代数速成。
- 集合记号:\(\in\) 表示"属于",\(\subseteq\) 表示"是……的子集",\(\emptyset\) 是空集,\(\forall\) 读作"对所有的",\(\mathbb{Z}\) 是整数集。见 第 00 册第 08 章 读懂数学证明与符号。
- 二阶导数与凸性(一元):\(f''(x)\ge 0\) 表示斜率在增加,图像"向上弯",像碗。例如 \(f(x)=x^2\),\(f''=2>0\),是凸的。你熟悉的凸度(convexity)就是价格对收益率的二阶导数为正,名字同源。见 第 00 册第 02 章 导数与泰勒展开。
- 梯度与 Hessian(多元):多元函数对每个变量分别求导,排成向量叫梯度 \(\nabla f\);二阶偏导排成矩阵叫 Hessian \(\nabla^2 f\)。一元的"\(f''\ge0\) 则凸"推广到多元,就是"Hessian 半正定则凸"。见 第 00 册第 05 章 多元微积分与优化。
- 半正定矩阵:对称矩阵 \(A\) 满足"对任意向量 \(v\),\(v^TAv\ge 0\)"。协方差矩阵天然半正定,因为 \(v^T\Sigma v\) 是某个组合的方差,方差不会是负数。见第 00 册第 06 章。
怎么读这一章
1.2(三要素与标准形式)和 1.5(凸性)是核心,必须读懂,后面每一章都会用到。1.3 的分类和 1.4 的算法三性质读一遍、知道有哪些类别即可。1.6 的量化实战建议认真看输出和解读,它直接回答了"Solver 算出 0.39 手怎么办"这个实盘问题;代码第一次可以只看注释。练习 2、4、5 最能检验你是否真懂了凸性。
1.1 为什么要学数值优化
先说结论:量化交易里几乎每一个"求最好"的步骤,底层都是一个数值优化问题。估计一个 GARCH 模型是在最大化似然函数;拟合收益率曲线是在最小化残差平方和;构建组合是在风险约束下最大化期望收益;把大单拆成小单执行是在冲击成本与时间风险之间求最优权衡;训练一个神经网络因子模型是在最小化损失函数。会调用 scipy.optimize.minimize 不难,难的是在它报 "line search failed"、迭代几千步不收敛、或者给出一个明显荒谬的解时,知道问题出在哪里。这正是本册要解决的问题。
原书(Nocedal & Wright《Numerical Optimization》第 1 版,1999)专讲连续优化(continuous optimization):变量取实数值、函数光滑的问题。作者在前言中列出的典型应用里,就有"在可接受的风险水平下最大化期望收益的投资组合设计"。全书的写法是:先用不严格的讨论和图形建立直觉,再正式给出算法,最后严格陈述并(多数情况下)证明理论结果——证明可以跳读,但结论必须掌握。
原书第 1 版共 18 章:第 2–9 章是无约束优化(基础、线搜索、信赖域、共轭梯度、实用牛顿法、导数计算、拟牛顿法、大规模拟牛顿),第 10–11 章是非线性最小二乘与非线性方程,第 12 章是约束优化理论,第 13–14 章是线性规划(单纯形法与内点法),第 15–18 章是非线性约束优化(二次规划、罚函数与增广拉格朗日、序列二次规划),附录 A 汇总了所需的分析与线性代数背景。原书不涉及网络优化、整数规划、随机规划、非光滑优化和全局优化——这几类问题在量化中也会遇到,本章会说明它们与本书内容的关系。
本章对应原书第 1 章,内容不难,但"建模"和"凸性"这两个概念会贯穿全册。
1.2 优化的三要素与建模
优化问题都由三部分组成:
- 目标(objective):一个可以用单个数字衡量的性能指标,例如利润、时间、能量、组合的风险调整收益。
- 变量(variables,也叫 unknowns):目标所依赖的、我们可以选择的量,例如各资产的持仓权重。
- 约束(constraints):变量必须满足的限制,例如权重和为 1、不允许卖空、单只股票不超过 10%。
找出这三者的过程叫做建模(modeling)。原书强调,建模往往是整个过程中最重要的一步:模型太简单,得不到对实际问题有用的洞见;模型太复杂,又可能根本解不出来。量化研究里这种权衡随处可见——忽略交易成本的组合优化很好解,但结果换手率高得离谱;加入非凸的市场冲击成本更真实,但求解器可能只能给出局部解。
建好模型之后,还要回答两个问题:
- 用什么算法? 不存在通用的优化算法,算法要针对问题类型挑选。选得好,几秒钟解完;选得不好,可能永远解不出来。
- 怎么知道解对了? 这要靠最优性条件(optimality conditions)。它既用来检验当前点是不是解,没满足时还能提示如何改进(第 2 章、原书第 12 章)。此外还可以做敏感性分析(sensitivity analysis),看解对模型参数和数据的变化有多敏感——对量化来说,这一步往往比求出解本身更重要,因为输入的期望收益和协方差都是带噪声的估计。
1.2.1 标准形式
记 \(x\in\mathbb{R}^n\) 为变量向量,\(f\) 为目标函数,\(c_i\) 为约束函数。原书采用的标准形式是
其中 \(\mathcal{E}\)、\(\mathcal{I}\) 分别是等式约束和不等式约束的指标集。注意本书约定不等式写成 "\(\ge 0\)" 的形式,这和某些教材(以及 CVXPY 等软件)用 "\(\le 0\)" 的习惯相反,读文献时要留心。
白话解释:(1.1) 逐个符号读一遍。\(\min_{x\in\mathbb{R}^n} f(x)\):在所有 \(n\) 维实向量 \(x\) 里找让 \(f(x)\) 最小的那个;s.t. 是 subject to("满足")的缩写。\(\mathcal{E}\) 和 \(\mathcal{I}\) 只是"编号清单":比如有 3 个约束,第 1 个是等式、第 2、3 个是不等式,就写 \(\mathcal{E}=\{1\}\),\(\mathcal{I}=\{2,3\}\)。这样写的好处是所有约束都变成"某个函数 \(=0\)"或"某个函数 \(\ge 0\)"两种格式。 拿 Excel Solver 对照:\(x\) 是可变单元格,\(f(x)\) 是目标单元格,\(c_i(x)\) 是约束框里每一行"左边减右边"的那个算式。例如约束"\(\sum_i w_i = 1\)"写成 \(c_1(w)=\sum_i w_i-1=0\);约束"\(w_3\le 0.1\)"写成 \(c_2(w)=0.1-w_3\ge 0\)。
把实际问题写成 (1.1) 时常需要几个小变换:
- 最大化 \(f\) 等价于最小化 \(-f\)。
- "\(\le\)" 约束两边乘 \(-1\) 变成 "\(\ge\)"。
- 对变量重新编号,把所有决策量排成一个向量。
好的优化软件会对用户透明地完成这些转换。
例 1.1(原书例 (1.2)) 考虑
写成标准形式:\(c_1(x)=-x_1^2+x_2\ge 0\),\(c_2(x)=-x_1-x_2+2\ge 0\),于是 \(\mathcal{I}=\{1,2\}\),\(\mathcal{E}=\emptyset\)。原书图 1.1 画出了目标函数的等高线(contours,即 \(f\) 取常值的点集)、可行域(feasible region,满足全部约束的点集)以及最优点 \(x^*\):最优点落在可行域边界上,恰好是某条等高线与可行域相切的位置。这个几何图像——"最优点是等高线与约束边界相切之处"——就是后面拉格朗日乘子理论的直观来源。
金融直觉:这个"相切"你在 CFA 里见过。画有效前沿时,最优风险组合是资本配置线与有效前沿相切的点;选最终组合时,是投资者的无差异曲线与资本配置线相切的点。无差异曲线就是效用函数的等高线,资本配置线就是可行域的边界。例 1.1 的图是同一件事的一般版本:在可行域里不断把等高线往"更好"的方向推,推到最后一刻还碰着可行域的那一点,就是最优点。此时等高线和边界在这一点方向一致,这就是第 12 章拉格朗日条件"目标梯度与约束梯度平行"的几何含义。
1.2.2 例:运输问题
例 1.2(原书运输问题) 一家化工公司有 2 个工厂 \(F_1,F_2\) 和 12 个零售店 \(R_1,\dots,R_{12}\)。工厂 \(F_i\) 每周产能 \(a_i\) 吨,零售店 \(R_j\) 每周需求 \(b_j\) 吨,从 \(F_i\) 运到 \(R_j\) 每吨运费 \(c_{ij}\)。设 \(x_{ij}\) 为从 \(F_i\) 运往 \(R_j\) 的吨数,问题是
目标和约束都是线性函数,这类问题叫线性规划(linear programming, LP)。原书提醒,真实模型还应包括生产成本和库存成本。量化中的 LP 也很多:最小化 CVaR 的组合优化(Rockafellar–Uryasev 表述)、带 \(\ell_1\) 换手约束的组合调整、指数复制中最小化绝对跟踪误差,都可以写成 LP。线性规划的解法在原书第 13–14 章。
1.3 优化问题的分类
1.3.1 连续优化与离散优化
如果运输问题里工厂生产的是拖拉机,\(x_{ij}\) 就必须是整数,要加上 \(x_{ij}\in\mathbb{Z}\),问题变成整数规划(integer programming)。同时含连续变量和整数变量的叫混合整数规划(mixed integer programming)。
- 离散优化(discrete optimization)的解取自有限(或可数)集合;连续优化的解取自不可数集合,通常是实向量空间。
- 连续问题通常更容易。原因是光滑性:在某一点算出的函数值和导数,可以用来推断它附近点的行为。离散问题中"挨得很近"的两个点函数值可能差别很大,而可行解又多到无法穷举。
- 连续优化算法在离散优化中同样重要:求解整数规划的分支定界(branch-and-bound)法,大部分时间都花在求解线性规划松弛(relaxation,即去掉整数约束后的问题)上。
原书特别指出一个常见误区:先忽略整数约束求出实数解,再四舍五入,并不能保证得到接近最优的整数解。这在交易中非常现实:A 股以 100 股为一手、持仓股票数量有上限(基数约束)、期货合约只能整张交易。资金量小或股价很高时,四舍五入会严重偏离最优,本章的量化实战会给出具体数字。
1.3.2 有约束与无约束
可以按目标与约束的性质(线性、非线性、凸)、变量多少(大规模/小规模)、光滑性(可微/不可微)给问题分类,但最重要的区分是有没有约束,原书正是据此分成前后两大部分。
- 无约束问题有时直接来自应用,例如极大似然估计(参数可以先做变换使其不受限,如用 \(\log\sigma\) 代替 \(\sigma>0\))。
- 有时自然约束可以安全忽略,因为它们不影响最优解。
- 约束问题也可以改写为无约束问题:用罚项(penalization terms)代替约束,这是原书第 17 章的主题。
约束有三种常见形式:简单界约束(如 \(0\le x_1\le 100\))、一般线性约束(如 \(\sum_i x_i\le 1\)——这正是组合权重的预算约束)、非线性不等式(如组合波动率 \(\sqrt{w^T\Sigma w}\le \sigma_{\max}\))。目标与约束全是线性的叫线性规划;至少有一个是非线性的叫非线性规划(nonlinear programming)。
1.3.3 全局优化与局部优化
最快的算法一般只能找到局部解(local solution):在它的某个邻域内目标值最小的可行点。它未必是全局解(global solution)——整个可行域上目标值最小的点。全局解既难识别也难找到。
一个极其重要的特例是凸规划(convex programming):它的每个局部解都是全局解。线性规划是凸规划;标准的均值–方差组合优化也是凸规划。一般非线性问题则可能有许多不是全局解的局部解。原书只顺带讨论全局优化;许多全局优化算法本身就是通过求解一系列局部优化问题来实现的(例如多起点法)。
1.3.4 随机优化与确定性优化
模型可能依赖建模时未知的量:运输问题里的需求 \(b_j\),或者原书特别提到的——金融规划模型依赖未来利率走势和经济表现。建模者可以给出若干情景(scenarios)并赋予概率,随机优化(stochastic optimization)利用这些不确定性信息去优化模型的期望表现。
原书只讨论确定性优化(deterministic optimization),即模型完全给定。但许多随机优化算法正是通过求解一组确定性子问题来实现的。量化中基于情景的组合优化、CVaR 优化、资产负债管理都属于随机规划,它们的子问题多半就是本书讲的 LP 或 QP。
1.4 优化算法应具备的性质
优化算法都是迭代的:从一个初始猜测出发,生成一列逐步改进的估计,直到满足停止条件。不同算法的区别在于"怎样从当前点走到下一点"——用不用函数值、一阶导数、二阶导数,是否累积历史信息。原书提出好算法应具备三个性质:
- 稳健性(robustness):在它所针对的那类问题上,从任何合理的初始点出发都能表现良好。
- 效率(efficiency):不需要过多的计算时间和存储。
- 精确性(accuracy):能精确地找到解,对数据误差和计算机舍入误差不过分敏感。
这三者经常冲突。收敛快的方法(如牛顿法)在大规模问题上可能需要存储过多(\(n\times n\) 的 Hessian 矩阵);最稳健的方法可能最慢。收敛速度与存储、稳健性与速度之间的权衡,是贯穿本册的主线。例如第 3 章会看到,最速下降法几乎总能保证梯度趋于零,但在病态问题上慢得无法接受;牛顿法在解附近极快,但离解较远时可能连下降方向都不是。
1.5 凸性
凸性是本章最重要的数学概念。
定义(凸集) 集合 \(S\subseteq\mathbb{R}^n\) 称为凸集(convex set),如果其中任意两点的连线段都在 \(S\) 内:
定义(凸函数) 函数 \(f\) 称为凸函数(convex function),如果其定义域是凸集,且
几何意义:函数图像位于连接 \((x,f(x))\) 与 \((y,f(y))\) 的弦的下方。光滑凸函数在一维、二维时呈"碗状",等高线围成凸集。原书图 1.3 的例子是 \(f(x)=(x_1-6)^2+\tfrac{1}{25}(x_2-4.5)^4\)。若 \(-f\) 是凸函数,则称 \(f\) 为凹函数(concave function)。
白话解释:两个定义里的 \(\alpha x+(1-\alpha)y\) 就是"按 \(\alpha\) 和 \(1-\alpha\) 的比例把 \(x\) 和 \(y\) 混合"。\(\alpha\) 从 1 走到 0,这个点就从 \(x\) 沿直线走到 \(y\),所以它代表"\(x\) 与 \(y\) 连线上的任意一点"。 用组合来理解最直观:\(x\) 和 \(y\) 是两个组合的权重向量,\(\alpha x+(1-\alpha)y\) 就是"把 \(\alpha\) 的资金按 \(x\) 配、\(1-\alpha\) 按 \(y\) 配"得到的混合组合。凸集说的是:两个可行组合的任何混合仍然可行(例如两个都满足"权重和为 1、不卖空"的组合,混合后仍满足)。凸函数说的是:混合组合的目标值不超过两者目标值的同比例加权平均。组合标准差就是凸函数——这正是分散化:两个组合混合后的波动率 \(\le\) 两者波动率的加权平均,只有相关系数为 1 时才取等号。
为什么凸性重要? 无约束优化算法一般只能保证收敛到驻点(stationary point,梯度为零的点),而驻点可能是极小点、极大点或鞍点。但如果 \(f\) 是凸的,驻点就是全局极小点(第 2 章定理 2.5 给出证明)。
定义(凸规划) 问题 (1.1) 称为凸规划,如果:
- 目标函数 \(f\) 是凸函数;
- 等式约束函数 \(c_i,\ i\in\mathcal{E}\) 是线性(仿射)函数;
- 不等式约束函数 \(c_i,\ i\in\mathcal{I}\) 是凹函数。
第 3 条初看奇怪,原因在于本书约定写成 \(c_i(x)\ge 0\):凹函数的上水平集 \(\{x: c_i(x)\ge 0\}\) 是凸集,于是可行域是一族凸集的交,仍是凸集。等式约束必须是线性的,因为非线性等式约束 \(c(x)=0\) 一般定义的是一个弯曲的曲面,不是凸集。
推导拆解:为什么"\(c\) 凹"能推出"\(\{x: c(x)\ge 0\}\) 是凸集"?只需三步。 第一步,任取集合里两点 \(x,y\),即 \(c(x)\ge 0\),\(c(y)\ge 0\)。 第二步,凹函数的定义是凸函数不等式反过来:\(c(\alpha x+(1-\alpha)y)\ge \alpha c(x)+(1-\alpha)c(y)\)。 第三步,右边是两个非负数的非负加权和,所以 \(\ge 0\)。于是 \(c(\alpha x+(1-\alpha)y)\ge 0\),混合点也在集合里。 再用"多个凸集的交仍是凸集"(练习 3),就得到整个可行域是凸集。换成 "\(\le 0\)" 的软件约定时,要求就变成"\(c\) 凸",本质一样。
判断凸性的实用方法 对二阶可微函数,\(f\) 凸当且仅当其 Hessian 在定义域内处处半正定(这一事实的线性代数背景见第 01 册第 07a 章「正定矩阵的刻画、平方根与 Cholesky 分解」)。例如二次函数 \(f(w)=\frac{\gamma}{2}w^T\Sigma w-\mu^Tw\) 的 Hessian 是 \(\gamma\Sigma\),协方差矩阵 \(\Sigma\) 半正定,所以它是凸函数。约束方面:\(\sum_i w_i=1\) 是线性等式,\(w_i\ge 0\)、\(w_i\le u_i\) 是线性不等式(线性函数既凸又凹),而波动率约束 \(\sigma_{\max}-\sqrt{w^T\Sigma w}\ge 0\) 中 \(\sqrt{w^T\Sigma w}=\|\Sigma^{1/2}w\|\) 是凸函数,取负后为凹,符合凸规划的要求。
推导拆解:\(f(w)=\frac{\gamma}{2}w^T\Sigma w-\mu^Tw\) 的 Hessian 为什么是 \(\gamma\Sigma\)? 先求梯度。\(w^T\Sigma w=\sum_i\sum_j\sigma_{ij}w_iw_j\),对 \(w_k\) 求偏导,含 \(w_k\) 的项有 \(j=k\) 和 \(i=k\) 两组,得 \(\sum_j\sigma_{kj}w_j+\sum_i\sigma_{ik}w_i=2(\Sigma w)_k\)(用了 \(\Sigma\) 对称)。所以 \(\nabla(w^T\Sigma w)=2\Sigma w\),\(\nabla f=\gamma\Sigma w-\mu\)。这和一元的 \((ax^2)'=2ax\) 完全对应。 再求一次导:\(\gamma\Sigma w\) 对 \(w\) 是线性的,导数就是系数矩阵 \(\gamma\Sigma\);\(\mu\) 是常数,导数为 0。所以 \(\nabla^2 f=\gamma\Sigma\)。 半正定的含义也很直观:对任意方向 \(v\),\(v^T(\gamma\Sigma)v=\gamma\cdot\text{Var}(v^Tr)\ge 0\),即沿任何方向调仓,目标函数都"向上弯"。
金融直觉:把 \(f\) 取负,\(-f=\mu^Tw-\frac{\gamma}{2}w^T\Sigma w\),这正是 CFA 里的效用公式 \(U=E(R)-\tfrac12 A\sigma^2\),\(\gamma\) 就是风险厌恶系数 \(A\)。最小化 \(f\) 等于最大化效用,也就是最大化"确定性等价收益"。下面这些在量化中常见的问题不是凸的,要警惕局部解:
- 带基数约束(最多持有 \(K\) 只股票)或整手约束的组合问题——可行域是离散的;
- 以夏普比率 \(\mu^Tw/\sqrt{w^T\Sigma w}\) 为目标且允许多空的问题(可以变换为凸问题,但直接优化时不凸);
- 某些风险平价的表述(例如直接最小化 \(\sum_{i,j}(w_i(\Sigma w)_i-w_j(\Sigma w)_j)^2\));
- 神经网络因子模型的训练、带非线性冲击成本的执行优化。
白话解释:基数约束为什么破坏凸性?取两个可行组合:\(x\) 只持有股票 1–5,\(y\) 只持有股票 6–10,在"最多持有 5 只"的约束下两者都可行。但它们的 50/50 混合持有 10 只,不可行。连线上的点跑出了可行域,所以可行域不是凸集。 夏普比率的问题在于它是一个比值。分子线性、分母是凸函数,比值一般既不凸也不凹。"变换为凸问题"的常见做法是:固定 \(\mu^Tw=1\) 去最小化 \(w^T\Sigma w\),再把解按比例缩放回权重和为 1——在 \(\mu^Tw>0\) 可达的前提下,这样求出的就是切点组合(最大夏普组合)。
1.6 量化实战:把均值–方差组合写成标准形式
场景 一个 20 万元的账户在 8 只 A 股上做均值–方差配置。我们要:(1) 把问题写成标准形式 (1.1) 并验证它是凸规划;(2) 用 SciPy 求连续最优解;(3) 加上"100 股一手"的整手约束,检验原书"先解连续再取整不可靠"的警告。
设股票权重为 \(w\in\mathbb{R}^8\),剩余资金 \(1-\sum_i w_i\) 放现金(收益率 \(r_f\))。问题为
目标的 Hessian 是 \(\gamma\Sigma\)(正定),约束全部线性,所以这是凸规划——任何局部算法找到的解都是全局最优。
import numpy as np
from scipy.optimize import minimize
from itertools import product
rng = np.random.default_rng(0)
n, T = 8, 750 # 8 只股票,750 个交易日的模拟日收益
F = rng.normal(0, 0.012, (T, 1)) # 市场因子
beta = rng.uniform(0.6, 1.4, n)
R = F @ beta[None, :] + rng.normal(0, 0.015, (T, n))
Sigma = np.cov(R.T) * 252 # 年化协方差(样本估计)
mu = np.array([0.06, 0.08, 0.05, 0.09, 0.16, 0.07, 0.10, 0.06]) # 假设的年化预期收益
rf, gamma = 0.02, 3.0 # 现金收益率、风险厌恶系数
# 1) 凸性检查:目标的 Hessian = gamma*Sigma 正定,约束全线性 => 凸规划
print("Sigma 特征值范围: [%.4f, %.4f]" % tuple(np.linalg.eigvalsh(Sigma)[[0, -1]]))
# 2) 标准形式 (1.1):股票权重 w;现金 1 - sum(w) >= 0;0 <= w_i <= 0.3
def f(w):
return 0.5 * gamma * w @ Sigma @ w - (mu @ w + rf * (1 - w.sum()))
grad = lambda w: gamma * Sigma @ w - (mu - rf)
def solve(ub):
cons = [{"type": "ineq", "fun": lambda w: 1.0 - w.sum()},
{"type": "ineq", "fun": lambda w: w},
{"type": "ineq", "fun": lambda w: ub - w}]
r = minimize(f, np.full(n, 0.05), jac=grad, constraints=cons, method="SLSQP")
return np.clip(r.x, 0, None)
w_star = solve(np.full(n, 0.30))
print("连续最优权重:", np.round(w_star, 3), "现金 %.3f f* = %.5f" % (1 - w_star.sum(), f(w_star)))
# 3) 整手约束:资金 20 万元,A 股 100 股一手;第 5 只股票股价 1520 元,一手 15.2 万
C = 200_000
price = np.array([12.5, 38.0, 8.2, 165.0, 1520.0, 27.3, 64.0, 5.6])
lot_value = 100 * price
def report(name, lots):
w = lots * lot_value / C
ok = (w.sum() <= 1) and (w <= 0.30 + 1e-12).all()
print("%-10s 手数 %s 权重和 %.3f 可行=%s f = %.5f" % (name, lots.astype(int), w.sum(), ok, f(w)))
lots_cont = w_star * C / lot_value
print("连续解对应手数:", np.round(lots_cont, 2))
report("四舍五入", np.round(lots_cont))
# 第 5 只股票哪怕 1 手也占 76% 资金,违反 30% 上限,只能取 0 手。
# 正确做法:把这一事实作为约束重新求解连续问题,再取整
ub = np.full(n, 0.30); ub[4] = 0.0
w_re = solve(ub)
report("重优化后取整", np.floor(w_re * C / lot_value))
# 4) 在“重优化后取整”解的每只股票 ±2 手邻域内枚举(第 5 只固定 0 手),找可行整数解中的最优者
base = np.floor(w_re * C / lot_value)
idx = [i for i in range(n) if i != 4]
best, best_val = None, np.inf
for d in product(range(-2, 3), repeat=len(idx)):
lots = base.copy(); lots[idx] += d
if (lots < 0).any(): continue
w = lots * lot_value / C
if w.sum() > 1 or (w > 0.30).any(): continue
v = f(w)
if v < best_val: best, best_val = lots, v
report("邻域枚举", best)
运行输出:
Sigma 特征值范围: [0.0468, 0.3250]
连续最优权重: [0. 0.052 0. 0.059 0.3 0. 0.175 0. ] 现金 0.414 f* = -0.05488
连续解对应手数: [0. 2.71 0. 0.71 0.39 0. 5.48 0. ]
四舍五入 手数 [0 3 0 1 0 0 5 0] 权重和 0.299 可行=True f = -0.03547
重优化后取整 手数 [1 6 0 1 0 0 7 0] 权重和 0.427 可行=True f = -0.03802
邻域枚举 手数 [1 7 0 2 0 0 7 0] 权重和 0.528 可行=True f = -0.03865
解读
- 协方差矩阵最小特征值为正,目标严格凸,SLSQP 给出的连续解就是全局最优,目标值 \(-0.0549\)。
- 连续解要求高价股(第 5 只)持有 0.39 手。四舍五入后它变成 0 手,其余股票也只是机械取整,目标值跌到 \(-0.0355\),损失了约 35%。原因是:高价股"消失"后,原来分给它的 30% 风险预算本应重新分配给其他股票,单纯取整完全没有做这件事。
- 把"第 5 只股票只能为 0"作为约束重新求解连续问题再取整,目标值改善到 \(-0.0380\);在其邻域内枚举又能找到 \(-0.0387\) 的整数解。真正求解这类问题要用混合整数二次规划(MIQP)求解器,它的核心正是分支定界加上反复求解连续松弛——也就是本书讲的连续优化算法。
实务要点:资金量越小、股价越高、组合越集中,取整误差越大。实盘系统中常见的做法是"连续优化 → 取整 → 对剩余现金做一次小规模的贪心或整数修正",本例说明至少要在取整后检查约束并重新分配。
本章小结
优化 = 目标 + 变量 + 约束,建模的好坏常常比算法更决定成败。本书只处理连续、确定、(大多)光滑的问题,并以求局部解为主;整数、随机、非光滑、全局优化是相邻的领域,它们的算法大量调用连续优化作为子程序。凸性是最重要的结构性质:凸规划的局部解即全局解,因此判断问题是否为凸是动手求解前的第一步。好的算法要在稳健性、效率、精确性之间取得平衡。
| 概念 | 要点 |
|---|---|
| 标准形式 | \(\min f(x)\),s.t. \(c_i(x)=0\ (i\in\mathcal{E})\),\(c_i(x)\ge 0\ (i\in\mathcal{I})\) |
| 线性规划 LP | 目标与约束全线性;凸规划的特例 |
| 非线性规划 | 目标或约束中至少一个非线性 |
| 整数/混合整数规划 | 部分变量取整数;"连续解取整"不保证接近最优 |
| 凸集 | \(\alpha x+(1-\alpha)y\in S\),\(\alpha\in[0,1]\) |
| 凸函数 | \(f(\alpha x+(1-\alpha)y)\le\alpha f(x)+(1-\alpha)f(y)\);二阶可微时等价于 Hessian 半正定 |
| 凸规划 | \(f\) 凸;等式约束线性;不等式约束 \(c_i\ge0\) 中 \(c_i\) 凹 |
| 凸性的回报 | 局部解 = 全局解;可微时驻点 = 全局极小点 |
| 算法三性质 | 稳健性、效率、精确性,三者常需权衡 |
练习
基础
- 把问题"\(\max\ x_1x_2\) s.t. \(x_1+2x_2\le 4\),\(x_1\ge 0\),\(x_2\ge 0\)"写成标准形式 (1.1),写出 \(\mathcal{E}\)、\(\mathcal{I}\) 和每个 \(c_i\)。 提示:目标取 \(-x_1x_2\);\(c_1=4-x_1-2x_2\),\(c_2=x_1\),\(c_3=x_2\),\(\mathcal{E}=\emptyset\)。
- 判断下列函数是否为凸函数:(a) \(f(x)=e^x\);(b) \(f(x)=x^3\)(在 \(\mathbb{R}\) 上);(c) \(f(w)=w^T\Sigma w\),\(\Sigma\) 为协方差矩阵;(d) \(f(x)=\|x\|_1\)。 提示:(a) 凸,\(f''>0\);(b) 不凸,\(x<0\) 时 \(f''<0\);(c) 凸,Hessian \(2\Sigma\) 半正定;(d) 凸(范数都是凸函数,由三角不等式证明),但不可微。
- 证明两个凸集的交仍是凸集。由此说明:由线性等式和凹函数不等式 \(c_i\ge 0\) 定义的可行域是凸集。
- 说明为什么非线性等式约束 \(x_1^2+x_2^2=1\) 定义的集合不是凸集。如果把它换成 \(x_1^2+x_2^2\le 1\) 呢? 提示:单位圆周上两点的中点不在圆周上;单位圆盘是凸集,对应 \(c(x)=1-x_1^2-x_2^2\ge0\),\(c\) 是凹函数。
进阶
- 均值–方差问题加上"组合年化波动率不超过 15%"的约束 \(\sqrt{w^T\Sigma w}\le 0.15\)。证明加约束后仍是凸规划。如果改为"组合波动率不低于 10%",还是凸规划吗? 提示:\(\|\Sigma^{1/2}w\|\) 是凸函数;"不低于"对应 \(\sqrt{w^T\Sigma w}-0.10\ge 0\),左边是凸函数而不是凹函数,可行域是一个凸集的补集,不是凸规划。
- 证明:若 \(f\) 是凸函数,则任意水平集 \(\{x: f(x)\le a\}\) 是凸集。反之不成立,请举一个水平集都是凸集但函数不凸的一维例子。 提示:反例 \(f(x)=\sqrt{|x|}\),水平集是区间,但函数不凸。
- 在本章量化实战中把资金改为 200 万元,重新运行,比较"四舍五入"与连续解的差距,并解释资金规模的影响。 提示:资金越大,每手占资金比例越小,取整误差越小;但高价股一手 15.2 万元仍占 7.6%,仍有可见误差。
- 指数复制问题:用 \(n\) 只股票复制一个指数,最小化跟踪误差的绝对值之和 \(\sum_t|r_t^Tw-r_t^{\text{idx}}|\)。说明如何引入辅助变量把它写成线性规划。 提示:令 \(u_t\ge r_t^Tw-r_t^{\text{idx}}\),\(u_t\ge -(r_t^Tw-r_t^{\text{idx}})\),最小化 \(\sum_t u_t\)。
原书推荐习题:原书第 1 章没有习题。
原书对照
| 本章内容 | 原书位置 | PDF 页码 |
|---|---|---|
| 前言:全书目标、覆盖范围、未覆盖内容 | Preface | PDF p.7–10 |
| 全书目录 | Contents | PDF p.11–20 |
| 1.2 三要素与建模、标准形式、例 1.1 | §1 引言,Mathematical Formulation,式 (1.1)(1.2),图 1.1 | PDF p.22–24(原书 p.1–3) |
| 1.2.2 运输问题 | Example: A Transportation Problem,式 (1.3)(1.4) | PDF p.25–26 |
| 1.3 分类:连续/离散、有/无约束、全局/局部、随机/确定 | Continuous versus Discrete … Stochastic and Deterministic Optimization | PDF p.25–28 |
| 1.4 算法的三个性质 | Optimization Algorithms | PDF p.28–29 |
| 1.5 凸性 | Convexity,图 1.3 | PDF p.29–30 |
| 历史注释("数学规划"一词的来历等) | Notes and References | PDF p.30 |
页码换算:原书正文页码约等于 PDF 页码减 21。