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

Scipy Optimize:如何控制解数组规模,指定10只基金分配?

如何给Scipy Optimize添加「恰好选中10只基金」的约束

当然可以添加这类约束!针对你从25只基金里选10只分配资金的需求,我帮你梳理两种适配Scipy Optimize的可行方案,精准实现选中数量的控制:

方案一:混合整数线性规划(MILP)—— 精准硬约束(适合线性/二次型问题)

如果你的投资组合优化是线性或二次型问题(比如最小方差、线性近似的夏普比率最大化),Scipy 1.9+版本的scipy.optimize.milp是完美选择,它支持整数变量标记基金是否被选中,直接实现「恰好10只」的硬约束。

核心思路

  • 用0/1整数变量x_i标记第i只基金是否被选中(1=选中,0=不选)
  • 用连续变量w_i表示对应基金的仓位权重,约束w_i ≤ x_i(确保未选中的基金仓位为0)
  • 添加关键约束:sum(x_i) = 10,强制恰好选中10只基金
  • 保留常规投资组合约束:比如sum(w_i) = 1(总资金全部分配)、w_i ≥ 0(非负仓位)

代码示例

from scipy.optimize import milp, LinearConstraint, Bounds
import numpy as np

# 替换为你的真实数据
n_funds = 25
cov_matrix = np.random.rand(n_funds, n_funds)
cov_matrix = (cov_matrix + cov_matrix.T) / 2  # 协方差矩阵对称化
returns = np.random.rand(n_funds)  # 基金历史收益率
target_return = 0.06  # 期望最低收益率

# 变量定义:前25个是整数变量x_i(0/1),后25个是连续变量w_i(仓位)
n_vars = n_funds * 2
integer_vars = list(range(n_funds))  # 指定整数变量范围

# 目标函数:最小化投资组合方差(二次型适配milp的简化处理)
objective = 0.5 * cov_matrix.flatten() @ np.kron(np.eye(n_funds), np.eye(n_funds)).flatten()

# 约束1:恰好选中10只基金
constraint_select = LinearConstraint(
    A=np.hstack([np.ones((1, n_funds)), np.zeros((1, n_funds))]),
    lb=10, ub=10
)

# 约束2:总仓位和为1
constraint_total = LinearConstraint(
    A=np.hstack([np.zeros((1, n_funds)), np.ones((1, n_funds))]),
    lb=1, ub=1
)

# 约束3:未选中的基金仓位必须为0(w_i ≤ x_i)
constraint_w_x = []
for i in range(n_funds):
    row = np.zeros(n_vars)
    row[i] = 1
    row[n_funds + i] = -1
    constraint_w_x.append(LinearConstraint(A=row, lb=0, ub=np.inf))

# 约束4:组合收益率不低于目标值
constraint_return = LinearConstraint(
    A=np.hstack([np.zeros((1, n_funds)), returns]),
    lb=target_return, ub=np.inf
)

# 变量边界:x_i∈{0,1},w_i∈[0,1]
bounds = Bounds(
    lb=np.hstack([np.zeros(n_funds), np.zeros(n_funds)]),
    ub=np.hstack([np.ones(n_funds), np.ones(n_funds)])
)

# 求解优化问题
result = milp(
    c=objective,
    constraints=[constraint_select, constraint_total, constraint_return] + constraint_w_x,
    bounds=bounds,
    integrality=integer_vars
)

# 提取结果
selected_idx = np.where(result.x[:n_funds] == 1)[0]
weights = result.x[n_funds:][selected_idx]
print(f"选中的基金索引:{selected_idx}")
print(f"对应仓位权重:{np.round(weights, 4)}")

方案二:L1正则化+阈值调整(适合非线性问题)

如果你的目标函数是非线性的(比如直接最大化原始夏普比率),Scipy的常规优化器不支持整数变量,这时候可以用L1正则化诱导稀疏性,通过调整正则化强度,让最终非零仓位的基金数量恰好为10。

核心思路

  • 在目标函数中加入L1惩罚项:原目标函数 + λ * sum(|w_i|),λ越大,非零仓位的基金数量越少
  • 逐步调整λ的值,直到优化后非零仓位的基金数量等于10(用小阈值判断「非零」,比如w_i > 1e-6)

代码示例

from scipy.optimize import minimize
import numpy as np

# 替换为你的真实数据
n_funds = 25
cov_matrix = np.random.rand(n_funds, n_funds)
cov_matrix = (cov_matrix + cov_matrix.T) / 2
returns = np.random.rand(n_funds)
risk_free_rate = 0.02  # 无风险利率

# 定义带L1正则的目标函数(最大化夏普比率等价于最小化负夏普比率)
def negative_sharpe(w, lambda_reg):
    port_return = w @ returns
    port_vol = np.sqrt(w.T @ cov_matrix @ w)
    sharpe = (port_return - risk_free_rate) / port_vol
    # 加入L1惩罚项,最小化负夏普+L1惩罚
    return -sharpe + lambda_reg * np.sum(np.abs(w))

# 约束:总仓位=1,仓位非负
constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}]
bounds = [(0, 1) for _ in range(n_funds)]

# 调整正则化系数λ,直到非零仓位数量为10
lambda_reg = 0.001
target_count = 10
non_zero_count = 0

while non_zero_count != target_count:
    # 初始值设为平均分配
    result = minimize(negative_sharpe, x0=np.ones(n_funds)/n_funds, args=(lambda_reg,),
                     constraints=constraints, bounds=bounds, method='SLSQP')
    # 用小阈值过滤极小仓位
    non_zero_weights = result.x[result.x > 1e-6]
    non_zero_count = len(non_zero_weights)
    
    # 动态调整λ
    if non_zero_count > target_count:
        lambda_reg *= 1.2  # 增大正则化,减少非零数量
    else:
        lambda_reg *= 0.8  # 减小正则化,增加非零数量

# 提取结果
selected_idx = np.where(result.x > 1e-6)[0]
weights = result.x[selected_idx]
print(f"选中的基金索引:{selected_idx}")
print(f"对应仓位权重:{np.round(weights, 4)}")
print(f"最终夏普比率:{-(result.fun - lambda_reg*np.sum(np.abs(result.x)))}")

注意事项

  • 方案一的MILP要求目标和约束是线性/二次型的,适合规则清晰的优化场景;方案二更灵活,可处理复杂非线性问题,但需要耐心调整正则化系数。
  • 实际使用时,记得替换示例中的随机数据为你的真实基金收益率、协方差矩阵等业务数据。

内容的提问来源于stack exchange,提问作者Naveen Srinivas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:35:10