简体   繁体   中英

How do I setup an objective function in CPLEX Python containing indicator functions?

The following is the objective function:

The idea is that a mean-variance optimization has already been done on a universe of securities. This gives us the weights for a target portfolio. Now suppose the investor already is holding a portfolio and does not want to change their entire portfolio to the target one.

Let w_0 = [w_0(1),w_0(2),...,w_0(N)] be the initial portfolio, where w_0(i) is the fraction of the portfolio invested in stock i = 1,...,N. Let w_t = [w_t(1), w_t(2),...,w_t(N)] be the target portfolio, ie, the portfolio that it is desirable to own after rebalancing. This target portfolio may be constructed using quadratic optimization techniques such as variance minimization.

The objective is to decide the final portfolio w_f = [w_f (1), w_f (2),..., w_f(N)] that satisfies the following characteristics:

  • (1) The final portfolio is close to our target portfolio
  • (2) The number of transactions from our initial portfolio is sufficiently small
  • (3) The return of the final portfolio is high
  • (4) The final portfolio does not hold many more securities that our initial portfolio

An objective function which is to be minimized is created by summing together the characteristic terms 1 through 4.

The first term is captured by summing the absolute difference in weights from the final and the target portfolio.

The second term is captured by the sum of an indicator function multiplied by a user specified penalty. The indicator function is y_{transactions}(i) where it is 1 if the weight of security i is different in the initial portfolio and the final portfolio, and 0 otherwise.

The third term is captured by the total final portfolio return multiplied by a negative user specified penalty since the objective is minimization.

The final term is the count of assets in the final portfolio (ie. sum of an indicator function counting the number of positive weights in the final portfolio), multiplied by a user specified penalty.

Assuming that we already have the target weights as target_w how do I setup this optimization problem in docplex python library? Or if anyone is familiar with mixed integer programming in NAG it would be helpful to know how to setup such a problem there as well.

`
final_w = [0.]*n
final_w = np.array(final_w)
obj1 = np.sum(np.absolute(final_w - target_w)) 

pen_trans = 1.2
def ind_trans(final,inital):
    list_trans = []
    for i in range(len(final)):
        if abs(final[i]-inital[i]) == 0:
            list_trans.append(0)
        else:
            list_trans.append(1)
    return list_trans
obj2 = pen_trans*sum(ind_trans(final_w,initial_w))

pen_returns = 0.6
returns_np = np.array(df_secs['Return'])
obj3 = (-1)*np.dot(returns_np,final_w)

pen_count = 1.
def ind_count(final):
    list_count = []
    for i in range(len(final)):
        if final[i] == 0:
            list_count.append(0)
        else:
            list_count.append(1)
    return list_count
obj4 = sum(ind_count(final_w))

objective = obj1 + obj2 + obj3 + obj4

The main issue in your code is that final_w is not a an array of variables but an array of data. So there will be nothing to optimize. To create an array of variables in docplex you have to do something like this:

from docplex.mp.model import Model
with Model() as m:
    final = m.continuous_var_list(n, 0.0, 1.0)

That creates n variables that can take values between 0 and 1. With that in hand you can start things. For example:

    obj1 = m.sum(m.abs(initial[i] - final[i]) for i in range(n))

For the next objective things become harder since you need indicator constraints. To simplify definition of these constraints first define a helper variable delta that gives the absolute difference between stocks:

    delta = m.continuous_var_list(n, 0.0, 1.0)
    m.add_constraints(delta[i] == m.abs(initial[i] - final[i]) for i in range(n))

Next you need an indicator variable that is 1 if a transaction is required to adjust stock i :

    needtrans = m.binary_var_list(n)
    for i in range(n):
        # If needtrans[i] is 0 then delta[i] must be 0.
        # Since needtrans[i] is penalized in the objective, the solver will
        # try hard to set it to 0. It will only set it to 1 if delta[i] != 0.
        # That is exactly what we want
        m.add_indicator(needtrans[i], delta[i] == 0, 0)

With that you can define the second objective:

    obj2 = pen_trans * m.sum(needtrans)

once all objectives have been defined, you can add their sum to the model: m.minimize(obj1 + obj2 + obj3 + obj4) and then solve the model and display its solution:

    m.solve()
    print(m.solution.get_values(final))

If any of the above is not (yet) clear to you then I suggest you take a look at the many examples that ship with docplex and also at the (reference) documentation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM