You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 16:25:12