How can I include the absolute value of a decision variable in PuLP objective function

Question:

The problem setup is fairly simple. There are 5 available instruments in a portfolio that can be traded. The optimizer needs to figure out which instruments need to be bought and / or sold to make max profit, There are the estimates for price change and some risk constraints.

Now as in the real world, there are always transaction costs. These are always positive whether the securities are bought or sold. How do I setup this optimization problem?

Below is what I have setup without transaction costs, and I have included the commented line to indicate how the transaction cost would work.

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)

instr_avl = LpVariable.dicts("Instr", df['instrument'].values,lowBound=None, upBound=None, cat= 'Integer') 

# The objective function 
prob += (lpSum([instr_avl[i] * df[df['instrument']==i]['price_change'].values[0] 
            #    -abs(instr_avl[i]) * df[df['instrument']==i]['spread_cost'].values[0] 
                for i in df['instrument'].values]), "Total Profit")

# Factor constraints
prob +=  lpSum([instr_avl[i] * df[df['instrument']==i]['factor_A'].values[0] for i in df['instrument'].values]) == 0, "factor_A Constraint"
prob += lpSum([instr_avl[i] * df[df['instrument']==i]['factor_B'].values[0] for i in df['instrument'].values]) <= 1000, "factor_B max Constraint"
prob += lpSum([instr_avl[i] * df[df['instrument']==i]['factor_B'].values[0] for i in df['instrument'].values]) >= -1000, "factor_B min Constraint"

# The problem is solved using PuLP's choice of Solver
prob.solve()

for v in prob.variables():
    print(v.name, "=", v.varValue, v.lowBound, v.upBound, v.cat)

print("Total profit= ", value(prob.objective))

Of course the code does not run if the commented line is uncommented. Would highly appreciate any ideas on how to implement this or any workarounds!

Asked By: Generalenthu

||

Answers:

  1. Introduce a non-negative variable say absv[i].
  2. Add the two constraints: absv[i] >= instr_avl[i] and absv[i] >= -instr_avl[i].
  3. Add the term: -absv[i]*df[df['instrument']==i]['spread_cost'].values[0] to the objective.

This type of formulation is described in detail in basically any book on linear programming. You may want to consult one to get a better understanding of this common formulation trick.

Answered By: Erwin Kalvelagen