如何在PuLP目标函数中纳入决策变量绝对值以计算交易成本
投资组合优化:加入交易成本的线性规划实现
问题背景
现有5种可交易工具的投资组合优化需求,目标是确定各工具的买卖仓位以实现利润最大化,已知价格变动估算值及因子约束条件。需要在模型中加入双向交易成本(无论买卖都产生正向成本),但原代码中直接使用abs()函数会触发非线性问题,无法被PuLP的线性规划求解器处理。
核心问题
线性规划(LP)求解器不支持非线性的绝对值表达式abs(instr_avl[i]),必须将绝对值项线性化处理,把非线性问题转化为标准线性规划问题才能求解。
解决方案思路
将每个工具的持仓变量拆分为两个非负整数变量:
long_i:工具i的多头仓位(买入数量,≥0)short_i:工具i的空头仓位(卖出数量,≥0)
实际仓位满足:instr_avl[i] = long_i - short_i
交易成本对应的绝对值项可转化为:abs(instr_avl[i]) = long_i + short_i(最优解中自然不会同时出现正的多头和空头仓位,无需额外约束)
修改后的完整代码
from pulp import * import pandas as pd df = pd.DataFrame({ 'instrument':["A", "B", "C", "D", "E"], 'price_change':[-6.09, -3.15, 6.1 , 6.43, 6.48], 'transaction_cost':[0.6, 0.6, 3.0, 6.0, 3.0], 'factor_A':[ 0.28032, -0.20112, 0.55631, -0.73323, -0.54905], 'factor_B':[18.87091, 15.73831, 29.61791, 24.64536, 29.68997] }) prob = LpProblem("The portfolio Problem", LpMaximize) # 拆分持仓为多头和空头变量,均为非负整数 long_pos = LpVariable.dicts("Long", df['instrument'].values, lowBound=0, cat='Integer') short_pos = LpVariable.dicts("Short", df['instrument'].values, lowBound=0, cat='Integer') # 目标函数:利润 = 价格变动收益 - 交易成本 objective_terms = [] for i in df['instrument'].values: price_change = df.loc[df['instrument'] == i, 'price_change'].values[0] trans_cost = df.loc[df['instrument'] == i, 'transaction_cost'].values[0] # 多头收益:long_pos[i] * price_change;空头收益:-short_pos[i] * price_change(空头赚价格下跌的钱) profit = long_pos[i] * price_change - short_pos[i] * price_change # 交易成本:无论多空都要付,所以是(long + short)*单位成本 cost = (long_pos[i] + short_pos[i]) * trans_cost objective_terms.append(profit - cost) prob += lpSum(objective_terms), "Total Profit" # Factor约束:用拆分后的变量计算实际仓位 prob += lpSum([(long_pos[i] - short_pos[i]) * df.loc[df['instrument'] == i, 'factor_A'].values[0] for i in df['instrument'].values]) == 0, "factor_A Constraint" factor_B_total = lpSum([(long_pos[i] - short_pos[i]) * df.loc[df['instrument'] == i, 'factor_B'].values[0] for i in df['instrument'].values]) prob += factor_B_total <= 1000, "factor_B max Constraint" prob += factor_B_total >= -1000, "factor_B min Constraint" # 求解问题 prob.solve() # 输出结果:计算实际仓位 for i in df['instrument'].values: long_val = value(long_pos[i]) short_val = value(short_pos[i]) actual_pos = long_val - short_val print(f"Instrument {i}: 仓位={actual_pos} (多头={long_val}, 空头={short_val})") print(f"Total profit= {value(prob.objective):.2f}")
代码说明
- 变量拆分:将原单一仓位变量拆分为
long_pos和short_pos,确保均为非负整数,符合线性规划要求。 - 目标函数线性化:通过
long_pos + short_pos替代abs(instr_avl[i]),将交易成本转化为线性表达式。 - 约束适配:所有因子约束均使用
long_pos[i] - short_pos[i]计算实际仓位,保持原约束逻辑不变。 - 结果输出:直接计算并输出实际仓位(多头-空头),更直观展示交易决策。
内容的提问来源于stack exchange,提问作者Generalenthu
相关产品推荐
相关产品推荐

