MATLAB均值方差投资组合模型实战:从数据到有效前沿

发布时间:2026/10/29 16:37:29
MATLAB均值方差投资组合模型实战:从数据到有效前沿 简介这份资源面向金融工程、量化投资方向的学生与研究者以及希望用MATLAB落地资产配置策略的从业者围绕马科维茨均值方差模型展开解决从历史行情数据到最优权重求解的完整实现问题。压缩包共12个文件约108KB包含8个m脚本、2个csv行情数据、1个mat数据文件与1个xlsx表格脚本覆盖数据抓取、清洗、聚合与建模全流程数据文件则提供标普500等标的的历史收益率样本。已有1917人学习下载说明其在教学与实操场景中具备一定参考价值。读者可借助这些代码理解期望回报、方差与协方差矩阵的计算方式掌握用quadprog求解二次规划、构建有效前沿并确定最优投资组合权重的思路同时通过回测观察组合在历史数据上的表现为后续策略调整与风险偏好匹配提供可复用的脚本框架与数据基础。1. 均值方差投资组合模型从一组收益率数据到有效前沿的最小闭环很多人第一次接触量化投资都是从马科维茨的均值方差模型开始的。它解决的问题很朴素给定一堆资产的日收益率序列怎么分配权重才能在目标收益下把波动压到最低。标题里这个.rar包本质就是一份「数据 MATLAB 代码」的案例集把教科书上的二次规划公式变成能跑出有效前沿的脚本。它适合两类人一类是金融工程、数量经济方向的学生需要交一份能复现的课程作业另一类是刚转量化的工程师想用 MATLAB 把组合优化的整条链路走通一遍。这篇笔记就顺着这个案例把数据怎么读、协方差怎么估、权重怎么解、前沿怎么画一步步拆开讲清楚顺带把那些一跑就翻车的地方标出来。2. 均值方差模型的数学骨架与 MATLAB 落地选型2.1 目标函数与约束到底在优化什么均值方差模型的核心是一个带约束的二次规划。设资产权重向量为 $w$期望收益向量为 $\mu$协方差矩阵为 $\Sigma$模型写成$$\min_w \frac{1}{2} w^T \Sigma w \quad \text{s.t.} \quad \mu^T w r,\ \sum_i w_i 1,\ w_i \ge 0$$第一行是目标最小化组合方差也就是 $w^T \Sigma w$。第二行是约束组合期望收益等于目标收益 $r$权重之和为 1且不允许做空。这里有个容易被忽略的点——目标函数前面的 $\frac{1}{2}$ 只是为了求导后系数干净不影响最优解位置。真正决定结果的是 $\mu$ 和 $\Sigma$ 的估计质量而不是求解器本身。MATLAB 里解这个问题的标准入口是quadprog它属于 Optimization Toolbox。如果你的环境没装这个工具箱quadprog会直接报未定义函数这是新手最常见的第一个拦路虎。替代方案是用fmincon但速度慢很多或者自己写拉格朗日乘子法硬解不推荐。2.2 为什么用 quadprog 而不是 fminconquadprog专门针对二次型目标加线性约束内部用的是内点法或信赖域反射法收敛快且稳定。fmincon是通用非线性求解器把二次问题丢给它属于杀鸡用牛刀还容易因为初值选得不好掉进局部解。案例代码里如果用的是quadprog说明作者走的是标准路线值得信任。调用格式大致是这样% H 是目标函数的二次项矩阵f 是一次项A/b 是不等式约束Aeq/beq 是等式约束 % lb/ub 是权重上下界options 控制求解器行为 [w_opt, fval, exitflag] quadprog(H, f, A, b, Aeq, beq, lb, ub, [], options);对应到均值方差模型H Sigma因为目标里是 $w^T \Sigma w$二次项系数矩阵就是 $\Sigma$f zeros(n,1)没有一次项Aeq [mu; ones(1,n)]beq [r; 1]lb zeros(n,1)ub []。exitflag必须检查等于 1 才代表正常收敛其他值都要回头看数据或约束。2.3 数据读取与收益率矩阵的构造案例包里的数据通常是.mat或.xlsx格式存的是若干资产的收盘价或收益率。如果是价格数据第一步要转成对数收益率或简单收益率。我一般用对数收益率因为可加性更好统计性质也更接近正态。% 假设 data.mat 里有个变量 prices是 T×N 的价格矩阵 load(data.mat); % 载入数据 returns diff(log(prices)); % 对数收益率T-1 行 N 列 mu mean(returns); % 期望收益转成列向量 Sigma cov(returns); % 协方差矩阵N×Ndiff默认做一阶前向差分log是自然对数。注意mean默认按列求均值得到 1×N 行向量转置成列向量才能和Sigma维度对齐。cov默认除以 T-1 做无偏估计样本量小的时候这个选择会影响结果但差异不大。提示如果数据里有 NaNmean和cov会直接返回 NaN必须先做缺失值处理。常见做法是用前值填充或直接删掉含 NaN 的行。3. 有效前沿的求解流程与参数调优3.1 从单点最优到整条前沿的循环结构有效前沿不是一条解析曲线而是一系列目标收益 $r$ 对应的最优组合点连成的线。做法是在最小期望收益和最大期望收益之间取一串 $r$对每个 $r$ 解一次quadprog把得到的组合波动和收益存下来。n size(Sigma, 1); r_min min(mu); % 前沿左端点收益 r_max max(mu); % 前沿右端点收益 N_points 50; % 前沿采样点数 target_returns linspace(r_min, r_max, N_points); frontier_risk zeros(N_points, 1); frontier_weights zeros(n, N_points); options optimoptions(quadprog, Display, off, Algorithm, interior-point-convex); for k 1:N_points r target_returns(k); Aeq [mu; ones(1, n)]; beq [r; 1]; [w, fval, exitflag] quadprog(Sigma, zeros(n,1), [], [], Aeq, beq, ... zeros(n,1), [], [], options); if exitflag 1 frontier_risk(k) sqrt(2 * fval); % 波动率 标准差 frontier_weights(:, k) w; else frontier_risk(k) NaN; % 标记失败点 end end这里fval返回的是 $\frac{1}{2} w^T \Sigma w$所以组合方差是 $2 \times fval$波动率再开根号。exitflag不等于 1 的点直接标 NaN画图时会自动断开不会污染曲线。Algorithm显式指定interior-point-convex避免 MATLAB 在不同版本里默认算法变化导致结果漂移。3.2 关键参数采样点数、收益上下界与权重约束N_points取 50 是经验值太少前沿看起来是折线太多计算时间线性增长。一般 30 到 100 之间都合理。r_min和r_max直接用min(mu)和max(mu)有个问题当某个资产期望收益特别高但波动也极大时前沿右端会拉得很长图形比例失调。更稳的做法是取r_min min(mu)r_max max(mu) * 0.95或者用分位数截断。权重约束lb zeros(n,1)表示不允许做空。如果案例允许卖空lb要设成-inf或具体负值。ub一般设[]表示无上界但实务里常加单资产上限比如ub 0.3 * ones(n,1)防止组合过度集中。这个改动会显著改变前沿形状值得单独跑一组对比。参数典型取值影响N_points30~100前沿平滑度与计算耗时lb0 或 -inf是否允许做空ub[] 或 0.2~0.4单资产集中度上限Algorithminterior-point-convex求解稳定性3.3 协方差估计的坑样本协方差不是唯一选择用cov(returns)得到的是样本协方差矩阵。当资产数量 N 接近甚至超过时间长度 T 时这个矩阵会接近奇异quadprog可能报「矩阵接近奇异」或者给出极端权重。这是均值方差模型最著名的血泪经验之一。常见补救有三种。第一种是收缩估计把样本协方差向对角矩阵或常数相关矩阵收缩MATLAB 里可以手写 Ledoit-Wolf 收缩。第二种是用因子模型降维先做主成分分析保留前几个主成分再重构协方差。第三种最简单加一点对角扰动Sigma Sigma 1e-6 * eye(n)让矩阵正定。案例代码如果没做这一步资产一多就容易翻车。% 简单的对角加载缓解协方差矩阵病态 Sigma cov(returns); Sigma Sigma 1e-6 * eye(size(Sigma, 1));1e-6这个量级要视数据尺度而定。如果收益率是小数形式比如 0.01 表示 1%1e-6足够小不影响结果如果收益率是百分数形式可能要调到1e-4。判断标准是加载后cond(Sigma)明显下降且最优权重不再出现极端值。4. 结果可视化与组合权重的解读4.1 画出有效前沿和资本市场线有效前沿画出来是波动率做横轴、期望收益做纵轴的曲线。再叠加一条从无风险利率出发的切线就是资本市场线。MATLAB 里用plot和scatter组合即可。figure; plot(frontier_risk, target_returns, b-, LineWidth, 1.5); hold on; scatter(sqrt(diag(Sigma)), mu, 60, r, filled); % 单个资产 xlabel(组合波动率 \sigma); ylabel(期望收益 \mu); title(均值方差有效前沿); grid on; legend(有效前沿, 单个资产, Location, best);sqrt(diag(Sigma))是每个资产自身的波动率mu是各自期望收益散点图能直观看出哪些资产在前沿内部、哪些靠近前沿。如果某个资产点落在前沿右侧很远说明它被支配了理性投资者不会单独持有它。4.2 权重向量的可读性处理frontier_weights是一个 N×N_points 的矩阵直接看数字没意义。我一般挑几个关键点看最小方差组合前沿最左端、最大夏普组合、以及目标收益等于某个基准的点。最大夏普组合需要额外算夏普比率再找最大值。rf 0.02; % 无风险利率年化 sharpe (target_returns - rf) ./ frontier_risk; [~, idx_max_sharpe] max(sharpe); w_max_sharpe frontier_weights(:, idx_max_sharpe); disp(array2table(w_max_sharpe, RowNames, asset_names, VariableNames, {Weight}));asset_names是资产名称的 cell 数组从数据文件里读出来。用array2table输出比裸矩阵可读性好得多。注意sharpe计算里如果frontier_risk有 NaNmax会忽略 NaN但最好先过滤掉失败点。注意无风险利率rf要和收益率的时间频率匹配。如果收益率是日频rf也要用日频不能直接填年化的 2%。5. 避坑与排查均值方差模型最常见的五类翻车5.1 quadprog 报「矩阵接近奇异或缩放不良」现象运行到某个目标收益时MATLAB 警告Matrix is close to singular or badly scaledexitflag返回负值。原因协方差矩阵条件数过大通常因为资产数量多、样本时间短或者某些资产高度相关。解决先做对角加载Sigma Sigma 1e-6 * eye(n)再检查cond(Sigma)。如果条件数仍大于 1e10考虑减少资产数量或改用收缩估计。另外把options里的TolFun和TolX适当放宽也有帮助但治标不治本。5.2 有效前沿出现「折返」或负波动率现象画出来的前沿曲线在某些段往回折或者frontier_risk出现负值。原因fval理论上非负但如果求解失败返回了垃圾值sqrt(2*fval)可能得到虚数或负数。另外exitflag没检查就全部存下来失败点混进了曲线。解决循环里严格判断exitflag 1才存结果否则存 NaN。画图前用frontier_risk(frontier_risk 0) NaN兜底。负波动率在数学上不可能出现就是求解器没收敛。5.3 权重全是零或集中在一个资产上现象最优权重向量里大部分是 0或者某个资产权重接近 1。原因约束设置有问题比如Aeq维度不对导致等式约束没生效或者某个资产期望收益远高于其他资产在无上界约束下求解器把所有权重都给了它。解决先检查size(Aeq)是否为 2×Nbeq是否为 2×1。然后加单资产上限ub 0.4 * ones(n,1)强制分散。如果加了上限还是集中说明数据本身有问题回头看收益率序列是不是有异常值。5.4 数据频率不一致导致结果失真现象组合波动率算出来特别小或特别大和直觉不符。原因有的资产是日频数据有的是周频或月频混在一起算协方差时间尺度对不上。解决统一到同一频率。日频转周频可以用resample或手动按周聚合。检查方法很简单size(returns, 1)对所有资产应该一致不一致就说明频率或缺失值处理有问题。5.5 前沿曲线和单个资产散点对不上现象散点图里某个资产明明在有效前沿左侧但前沿曲线却从它右边穿过。原因散点用的是单个资产的mu和sqrt(diag(Sigma))而前沿是组合优化结果。单个资产本来就不一定在有效前沿上落在右侧是正常的。如果落在左侧说明前沿求解有问题大概率是r_min设得比该资产收益还高导致前沿左端被截断。解决把r_min设成min(mu)或更低确保前沿覆盖所有单个资产收益范围。另外确认frontier_risk和target_returns长度一致画图时没有错位。6. 从案例代码到可复用函数封装与敏感性验证把上面这套流程封装成一个函数是让案例真正变成自己工具的关键一步。我一般会写成mean_variance_frontier(returns, n_points, lb, ub, rf)输入收益率矩阵和参数输出前沿风险、收益、权重和最大夏普组合。这样换一组数据只要改输入不用动逻辑。function [risk, ret, weights, w_sharpe] mean_variance_frontier(returns, n_points, lb, ub, rf) % returns: T×N 收益率矩阵 % n_points: 前沿采样点数 % lb, ub: 权重下界和上界 % rf: 无风险利率与收益率同频率 mu mean(returns); Sigma cov(returns); Sigma Sigma 1e-6 * eye(size(Sigma, 1)); % 对角加载 n size(Sigma, 1); ret linspace(min(mu), max(mu), n_points); risk nan(n_points, 1); weights nan(n, n_points); options optimoptions(quadprog, Display, off, ... Algorithm, interior-point-convex); for k 1:n_points Aeq [mu; ones(1, n)]; beq [ret(k); 1]; [w, fval, exitflag] quadprog(Sigma, zeros(n,1), [], [], Aeq, beq, ... lb, ub, [], options); if exitflag 1 risk(k) sqrt(2 * fval); weights(:, k) w; end end sharpe (ret - rf) ./ risk; [~, idx] max(sharpe); w_sharpe weights(:, idx); end封装好之后做敏感性验证就方便了。换不同的n_points看前沿是否稳定换lb从 0 到 -0.2 看做空对前沿的改善换ub从 1 到 0.3 看分散化约束的代价。这些对比跑一遍比读十篇理论文章都管用。我自己的习惯是每次拿到新数据先跑三组无约束、仅做多、做多加上限。三组前沿叠在一张图上如果差异巨大说明数据里存在极端资产需要先做异常值处理再谈优化。均值方差模型对输入极其敏感这不是它的缺陷而是它的本性——它忠实地把你对收益和风险的估计翻译成权重估计错了结果一定错。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询