Python投资组合优化:如何设置份额为0或0.05-0.2的约束?
问题解答
scipy.optimize.minimize的常规Bounds只能定义连续数值区间,没法直接实现“每个资产份额要么为0,要么落在[0.05, 0.2]”这种离散选择的约束——因为这是非凸的分段约束,超出了普通边界或线性约束的处理能力。
可行解决方案:
混合整数优化工具(推荐):
这种“要么选要么不选,选了就有最低持仓比例”的需求,本质是混合整数规划问题(用二进制变量标记是否选中资产)。scipy 1.9及以上版本提供的scipy.optimize.milp函数专门处理这类问题,示例代码如下:from scipy.optimize import milp, LinearConstraint, Bounds import numpy as np num_assets = 5 # 示例资产数量 # 目标函数系数(以最小化方差为例,此处用随机值替代) c = np.random.rand(num_assets) # 二进制变量:x_bin[i]=1表示选中第i个资产,0表示不选 num_bin = num_assets # 连续变量:x_cont[i]表示第i个资产的持仓比例 num_cont = num_assets # 合并变量:前num_bin个是二进制,后num_cont个是连续 c_full = np.concatenate([np.zeros(num_bin), c]) # 约束1:选中资产时持仓在[0.05,0.2],未选中时持仓为0 # 转化为线性约束:0.05*x_bin[i] ≤ x_cont[i] ≤ 0.2*x_bin[i] constraints = [] for i in range(num_assets): # 下界约束:x_cont[i] ≥ 0.05*x_bin[i] row = np.zeros(num_bin + num_cont) row[i] = -0.05 row[num_bin + i] = 1 constraints.append(LinearConstraint(row, lb=0, ub=np.inf)) # 上界约束:x_cont[i] ≤ 0.2*x_bin[i] row = np.zeros(num_bin + num_cont) row[i] = -0.2 row[num_bin + i] = 1 constraints.append(LinearConstraint(row, lb=-np.inf, ub=0)) # 约束2:所有持仓比例之和为1(满仓) sum_row = np.zeros(num_bin + num_cont) sum_row[num_bin:] = 1 constraints.append(LinearConstraint(sum_row, lb=1, ub=1)) # 变量边界:二进制变量取0或1,连续变量≥0且≤0.2 bounds = Bounds( lb=np.concatenate([np.zeros(num_bin), np.zeros(num_cont)]), ub=np.concatenate([np.ones(num_bin), np.full(num_cont, 0.2)]) ) # 指定整数变量(前num_bin个为二进制变量) integrality = np.concatenate([np.ones(num_bin), np.zeros(num_cont)]) # 求解 res = milp(c=c_full, constraints=constraints, bounds=bounds, integrality=integrality) # 提取最优持仓比例 optimal_weights = res.x[num_bin:]minimize近似处理(不推荐):
如果非要用scipy.minimize,只能先按原Bounds(0, 0.2)优化,之后筛选掉持仓比例在(0, 0.05)之间的组合——但这种方法会丢失最优解,因为优化过程可能收敛到(0,0.05)区间内的点,筛选后需要手动调整,结果不一定最优。
另一种近似方法是给目标函数加惩罚项,逼优化器避开(0,0.05)区间,示例如下:from scipy.optimize import minimize, Bounds import numpy as np num_assets = 5 # 原目标函数(以最小化方差为例) def objective(weights): cov_matrix = np.random.rand(num_assets, num_assets) cov_matrix = cov_matrix + cov_matrix.T # 协方差矩阵对称化 return weights @ cov_matrix @ weights # 带惩罚的目标函数 def objective_with_penalty(weights): base_obj = objective(weights) penalty = 0 # 对(0,0.05)区间的权重加惩罚,力度可调整 for w in weights: if 0 < w < 0.05: penalty += 1000 * (0.05 - w) return base_obj + penalty # 边界与约束设置 bounds = Bounds([0]*num_assets, [0.2]*num_assets) constraints = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1}] x0 = np.ones(num_assets)/num_assets # 初始猜测 res = minimize(objective_with_penalty, x0, bounds=bounds, constraints=constraints) # 求解后需检查结果是否符合约束,必要时手动调整
总结
如果需要严格满足约束,优先用混合整数优化工具(如scipy.milp);如果暂时用minimize,只能做近似处理,之后必须筛选或调整结果,但无法保证最优性。
内容的提问来源于stack exchange,提问作者Olsen_I
相关产品推荐
相关产品推荐

