Python Scipy最小均方误差优化无迭代、循环异常问题排查
投资组合Beta零目标优化代码问题定位
核心问题根因拆解
1. for循环执行次数不符合预期的原因
- 你写的
range(1,2)本身就只会执行1次:Python的range(start, end)是左闭右开区间,仅包含start值、不包含end值,所以range(1,2)只会取到i=1这1个值,本身就达不到你预期跑2次的效果。如果要遍历所有日期列,range范围应该设为range(1, numDates+1)。 - 你看到的N次重复执行,根本不是外层for循环触发的:你在目标函数
minSqError里写了print(a,b),scipy的minimize每次迭代试探参数时都会调用一次目标函数,每调用一次就触发一次打印,大量迭代打印让你误以为是外层循环在重复运行。
2. 求解器只返回初始值、不迭代的原因
这个问题是3处代码逻辑错误叠加导致的:
- 权重约束函数完全失效
你写的Weight_constraint逻辑是在循环里反复执行TotalWeight = PortWeight - x[i],每次循环都会覆盖上一次的计算结果,最终返回值是1 - x[-1](即1减去最后一个资产的权重),根本不是「所有权重之和等于1」的约束。正确的约束返回值应该是np.sum(x) - 1,代表权重和与1的差值为0。错误的约束会让求解器判定初始点已经满足可行条件,梯度计算异常直接终止迭代。 - 目标函数传参逻辑错误,优化变量被完全固定
scipyminimize的调用规则是:第一个传入的位置参数是待优化的自由度变量x,后面args里传的是全程固定不变的常量参数。
你现在的写法是把初始权重tempBetas['Weight']既作为初始猜测值传入,又放在args里当固定参数传给目标函数;且目标函数计算加权Beta时,用的是args里传进来的固定初始权重,完全没用到求解器迭代更新的优化变量(也就是你定义的第一个形参df,全程没参与目标值计算)。相当于不管求解器怎么调整权重参数,你算出来的误差值永远是初始等权重对应的结果,求解器判定初始点梯度为0,自然不会迭代。 - 未指定适配约束的求解器方法
你没有给minimize指定method参数,默认调用的BFGS算法本身不支持等式/不等式约束,即使约束写对了也会出现求解异常。带约束的优化场景应该指定method='SLSQP'或trust-constr。
修正后可运行代码
import pandas as pd import numpy as np from scipy.optimize import minimize # 基础配置 SolverTarget = 0 numDates = len(BetaResults.iloc[0,1:]) numEquities = len(BetaResults.iloc[:,0]) equalWeight = 1 / numEquities allocationResult = [] # 目标函数:第一个参数固定为待优化的权重数组x,后面接固定常量 def minSqError(x, beta_arr, target): port_beta = np.sum(x * beta_arr) return (target - port_beta) ** 2 # 权重和为1的等式约束 def weight_sum_constraint(x): return np.sum(x) - 1 cons = {'type':'eq', 'fun': weight_sum_constraint} # 可选:添加权重非负约束,不允许做空 bounds = tuple((0,1) for _ in range(numEquities)) # 外层循环遍历所有日期 for date_col_idx in range(1, numDates+1): current_beta = BetaResults.iloc[:, date_col_idx].values x0 = np.array([equalWeight]*numEquities) # 等权重作为初始猜测 # 初始权重校验 if not np.isclose(x0.sum(), 1): print(f"列索引{date_col_idx}初始权重和异常,当前值{x0.sum()}") break # 执行优化 opt_result = minimize( minSqError, x0=x0, args=(current_beta, SolverTarget), constraints=cons, bounds=bounds, method='SLSQP' ) # 存储结果 allocationResult.append({ 'date': BetaResults.columns[date_col_idx], 'opt_weights': opt_result.x, 'min_port_beta': np.sum(opt_result.x * current_beta), 'converge_success': opt_result.success })
额外说明
如果你的Beta值全为正(比如示例里的0.3、0.42这类值),在权重非负、和为1的约束下,加权Beta永远不可能等于0,求解器会自动把尽可能多的权重分配给Beta最小的资产,得到约束条件下的最小Beta值。
内容的提问来源于stack exchange,提问作者dabrows
相关产品推荐
相关产品推荐

