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

Gurobi绝对值处理报错解决:投资组合优化模型问题

投资组合优化LP模型:Gurobi中绝对值约束的错误修复

问题场景

用Gurobi实现多期投资组合线性规划优化,基于48只股票120天的预期收益率计算每日股价,目标是最大化第120天的投资组合总价值。模型定义了bought(每日买卖数量)、holding(每日持仓)等变量,核心约束为单只股票价值占组合总价值至少1%。运行时触发错误:

AttributeError: 'GenExprAbs' object has no attribute 'getValue'

错误根源

  1. getValue()调用时机错误:该方法仅能在模型求解完成后,用于获取变量/表达式的最优结果,建模阶段不能将其嵌入约束表达式中。
  2. 绝对值的非线性问题:gp.abs_()生成的是非线性表达式,而当前模型是线性规划(LP),求解器无法直接处理非线性约束,必须将绝对值逻辑线性化。

解决方案

将bought变量拆分为买入量和卖出量两个非负整数变量,彻底避免绝对值的使用:

  • 新增buy[i,j]:第i天买入第j只股票的数量,非负整数
  • 新增sell[i,j]:第i天卖出第j只股票的数量,非负整数
  • 关联关系:当日持仓 = 前日持仓 + 买入 - 卖出,交易总价值为买入价值与卖出价值之和,直接计算交易成本即可。

额外说明:若需严格禁止同一股票同时买卖,可引入0-1变量添加互斥约束,但由于同时买卖会额外增加交易成本,对最大化收益的目标不利,求解器通常会自动规避这类无意义操作,可根据模型规模决定是否添加。

修正后的完整代码

# data
num_periods = 121  # first period no trade
num_stocks = 48
starting_capital = 100000
diversification_constraint = 0.01
transaction_cost = 0.02
estimated_prices = price  # 假设price是预先计算好的121×48股价矩阵

# Create model
m = gp.Model('Portfolio Optimization')

# Create variables
# 拆分买入/卖出变量,替代原bought的绝对值逻辑
buy = m.addVars(num_periods, num_stocks, vtype=gp.GRB.INTEGER, lb=0, name='buy')
sell = m.addVars(num_periods, num_stocks, vtype=gp.GRB.INTEGER, lb=0, name='sell')
holding = m.addVars(num_periods, num_stocks, vtype=gp.GRB.INTEGER, lb=0, name='holding')  # 持仓量非负
transaction = m.addVars(num_periods, name='transaction')
portfolio_value = m.addVars(num_periods, name='portfolio_value')
cash = m.addVars(num_periods, name='cash', lb=0)  # 现金非负

# Set objective: 最大化最后一期组合价值
m.setObjective(portfolio_value[num_periods-1], gp.GRB.MAXIMIZE)

# Set constraints
# 初始现金约束
m.addConstr(cash[0] == starting_capital)

# 初始无交易、无持仓
m.addConstrs((buy[0, j] == 0 for j in range(num_stocks)))
m.addConstrs((sell[0, j] == 0 for j in range(num_stocks)))
m.addConstrs((holding[0, j] == 0 for j in range(num_stocks)))

# 持仓变化约束:当日持仓 = 前日持仓 + 买入 - 卖出
m.addConstrs((holding[i, j] == holding[i-1, j] + buy[i,j] - sell[i,j] 
              for i in range(1, num_periods) for j in range(num_stocks)))

# 交易成本约束:按当日交易总价值的2%计算
m.addConstrs((transaction[i] == transaction_cost * gp.quicksum(
    buy[i,j]*estimated_prices[i,j] + sell[i,j]*estimated_prices[i,j] 
    for j in range(num_stocks)) for i in range(1, num_periods)))

# 组合价值约束:组合价值 = 持仓总价值 + 现金
m.addConstrs((portfolio_value[i] == gp.quicksum(holding[i, j] * estimated_prices[i,j] 
                                                for j in range(num_stocks)) + cash[i] 
              for i in range(num_periods)))

# 现金平衡约束:当日现金 = 前日现金 - 交易成本 - 买入支出 + 卖出收入
m.addConstrs((cash[i] == cash[i-1] - transaction[i] 
              - gp.quicksum(buy[i,j]*estimated_prices[i,j] for j in range(num_stocks))
              + gp.quicksum(sell[i,j]*estimated_prices[i,j] for j in range(num_stocks))
              for i in range(1, num_periods)))

# 分散化约束:单只股票价值至少占组合总价值的1%
m.addConstrs((holding[i, j] * estimated_prices[i,j] >= diversification_constraint * portfolio_value[i]
              for i in range(num_periods) for j in range(num_stocks)))

# Solve model
m.optimize()

# Print solution
if m.status == gp.GRB.OPTIMAL:
    print('Optimal solution found!')
    print('Final portfolio value:', portfolio_value[num_periods-1].x)
else:
    print('No solution found.')

额外优化点

  • 给holding和cash添加非负约束(lb=0),符合实际投资逻辑(不能持有负股票、不能透支现金)
  • 修正现金平衡约束:卖出股票会增加现金,原代码中遗漏了这部分收入

内容的提问来源于stack exchange,提问作者Jing Heng Lim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:09:58