1
$\begingroup$

I want to solve the following portfolio optimization problem by means of the Python API of Gurobi: \begin{align}\max_w&\quad w^\top\Pi\\\text{s.t.}&\quad w^\top\Sigma w=\sigma^{\rm target}\\&\quad\sum_{i=1}^nw_i=1-{\rm cash}\\&\quad l_i\le w_i\le u_i\quad\forall i\in\{1,\ldots,n\}\quad\small\text{(absolute constraints})\\&\quad w_j=0\quad\forall j\in{\cal J}\quad\small\text{(certain assets are excluded from the portfolio)}\\&\quad L_j\le\sum_{s\in G_j}w_s\le U_j\quad\forall j\in\{1,\ldots,t\}\quad\small\text{(group constraints)}\\&\quad|w_i-w_i^{\rm start}|\Bbb I_{\{|w_i-w_i^{\rm start}|>0\}}\ge\textsf{rebalancing_threshold}\quad{\small\text{(rebalancing must be}\\\small\text{above a threshold)}}\\&\quad\|w-w^{\rm start}\|_1\le\textsf{max_rebalancing}\quad\small\text{(total portfolio rebalancing is bounded)}\\&\quad\sum_{i=1}^n\Bbb I_{\{|w_i-w_i^{\rm start}|>0\}}\le\textsf{max_number_rebalancing}\quad{\small\text{(maximum number of}\\\small\text{rebalances is bounded)}}\end{align}

I have implemented the problem in the following code:

subassets = ptf_optimization.ptf.index
max_rebalancing = 0.50
max_number_rebalancing = 10
rebalancing_threshold = 0.02
J = np.array(Chosen_new.tolist())

# Define model and set parameters
m2 = gp.Model('eff_ret_portfolio')
m2.setParam("NonConvex", 2)
m2.setParam("MIPGapAbs", 5e-3)
m2.setParam("TimeLimit", 100.0)

# Define variables
vars = pd.Series(m2.addVars(subassets), index=subassets)
vars_diff = pd.Series(m2.addVars(subassets, lb=(-1)*np.ones(len(subassets)), ub=2*np.ones(len(subassets))), index=subassets)
vars_abs = pd.Series(m2.addVars(subassets, lb=np.zeros(len(subassets)), ub=2*np.ones(len(subassets))), index=subassets)
vars_ind = pd.Series(m2.addVars(subassets, vtype=GRB.BINARY), index=subassets)
m2.update()

# Set objective
m2.setObjective(vars.T.dot(BL_returns), GRB.MAXIMIZE)

## Set constraints
portfolio_variance = vars.T.dot(ptf_optimization.cov.dot(vars))
m2.addConstr(portfolio_variance == target_std**2)
m2.addConstr(vars.sum() == 1-cash, 'budget')
m2.addConstrs((vars[j] == 0 for j in range(len(J)) if J[j] == True), name='c1')     #J defined in min vol optimization
m2.addConstrs((vars_diff[item] == vars[item]-w_start[item] for item in subassets), name='diff')
m2.addConstrs((vars_abs[item] == gp.abs_(vars_diff[item]) for item in subassets), name='abs')
for item in subassets:
    m2.addConstr((vars_ind[item]==1) >> (vars_abs[item] >= 1e-5))
    m2.addConstr((vars_ind[item]==0) >> (vars_abs[item] <= 1e-5))
m2.addConstr(quicksum(vars_ind) <= max_number_rebalancing, 'max_number_rebalancing')
m2.addConstrs((vars_abs[item]*vars_ind[item] >= rebalancing_threshold for item in subassets), name='rebalancing_threshold')
m2.addConstr(quicksum(vars_abs) <= max_rebalancing, 'max_rebalancing')

# Add absolute constraints
for item in subassets:
    l = ptf_optimization.absolute_constraints[item]['Min']
    u = ptf_optimization.absolute_constraints[item]['Max']
    m2.addConstr(l <= vars[item], f'constraint_abs_{item}_lower')
    m2.addConstr(vars[item] <= u, f'constraint_abs_{item}_upper')
# Add group constraints
for j, item in enumerate(ptf_optimization.group_constraints):
    constraint = ptf_optimization.group_constraints[item]
    l = constraint['Min']
    u = constraint['Max']
    vars_list = (vars[item] for item in constraint['Group'])
    m2.addConstr(l <= quicksum(vars_list), f'constraint_group_{j}_lower')
    m2.addConstr(quicksum(vars_list) <= u, f'constraint_group_{j}_upper')
m2.setParam('OutputFlag', 1)    
m2.optimize()

However, by inserting the constraint that rebalancing must be above a certain threshold, I obtained that the problem becomes infeasible. I computed an IIS and obtain the following .ilp file:

\ Model eff_ret_portfolio_copy
\ LP format - for model browsing. Use MPS format to capture full model detail.
Maximize

Subject To
max_number_rebalancing: C96 + C97 + C98 + C99 + C100 + C101 + C102 + C103
+ C104 + C105 + C106 + C107 + C108 + C109 + C110 + C111 + C112 + C113
+ C114 + C115 + C116 + C117 + C118 + C119 + C120 + C121 + C122 + C123
+ C124 + C125 + C126 + C127 <= 10
rebalancing_threshold[Obbligazionari_Governativi_Dollari]: [ C64 * C96 ]
>= 0.02
rebalancing_threshold[Obbligazionari_High_Yield_Euro]: [ C70 * C102 ]
>= 0.02
rebalancing_threshold[Obbligazionari_Emergenti_Hard_Currency]: [
C71 * C103 ] >= 0.02
rebalancing_threshold[Azionari_Euro]: [ C73 * C105 ] >= 0.02
rebalancing_threshold[Obbligazionari_Governativi_Breve_Termine_Europe_ex_Euro]:
[ C84 * C116 ] >= 0.02
rebalancing_threshold[Obbligazionari_Governativi_Breve_Termine_Yen]: [
C85 * C117 ] >= 0.02
rebalancing_threshold[Obbligazionari_Inflation_Linked_Dollari]: [
C87 * C119 ] >= 0.02
rebalancing_threshold[Obbligazionari_Corporate_Finanziari]: [ C89 * C121 ]
>= 0.02
rebalancing_threshold[Obbligazionari_Corporate_Dollari_Breve_Termine]: [
C91 * C123 ] >= 0.02
rebalancing_threshold[Obbligazionari_ABS]: [ C92 * C124 ] >= 0.02
rebalancing_threshold[Obbligazionari_Convertible_Euro]: [ C94 * C126 ]
>= 0.02
Bounds
C64 free
C70 free
C71 free
C73 free
C84 free
C85 free
C87 free
C89 free
C91 free
C92 free
C94 free
Binaries
C96 C97 C98 C99 C100 C101 C102 C103 C104 C105 C106 C107 C108 C109 C110
C111 C112 C113 C114 C115 C116 C117 C118 C119 C120 C121 C122 C123 C124 C125
C126 C127
End

Can you please tell me if I coded the model correctly and where the source of infeasibility is?

$\endgroup$

1 Answer 1

2
$\begingroup$

There are 11 constraints of the form

x * y >= 0.02

with y being a binary variable, effectively forcing all these binaries to 1 to satisfy the constraints. Then there is the first constraint:

y_1 + ... + y_11 <= 10

This can never be satisfied if all 11 y variables have to be 1.

crosspost in Gurobi community forum

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.