Как настроить целевую функцию в CPLEX Python, содержащую индикаторные функции? - PullRequest
0 голосов
/ 26 июня 2019

Ниже приводится целевая функция:

Идея состоит в том, что оптимизация среднего отклонения уже была проведена во вселенной ценных бумаг.Это дает нам вес для целевого портфеля.Теперь предположим, что инвестор уже держит портфель и не хочет менять весь свой портфель на целевой.

Пусть w_0 = [w_0 (1), w_0 (2), ..., w_0 (N)] - начальный портфель, где w_0 (i) - доля портфеля, вложенного в акции, i = 1, ..., N.Пусть w_t = [w_t (1), w_t (2), ..., w_t (N)] будет целевым портфелем, т. Е. Портфелем, который желательно иметь после перебалансировки.Этот целевой портфель может быть построен с использованием методов квадратичной оптимизации, таких как минимизация отклонений.

Цель состоит в том, чтобы определить окончательный портфель w_f = [w_f (1), w_f (2), ..., w_f (N)], который удовлетворяет следующим характеристикам:

  • (1) Конечный портфель близок к нашему целевому портфелю
  • (2) Количество транзакций из нашего первоначального портфеля достаточно мало
  • (3) Доходность конечного портфеля высока
  • (4) В конечном портфеле содержится не так много ценных бумаг, как в нашем первоначальном портфеле

Целевая функция, которую необходимо минимизировать, создается путем суммирования характерных членов с 1 по 4.

Первый член фиксируется суммированием абсолютной разницы в весах от конечного и целевого портфеля.

Второй член фиксируется суммой индикаторной функции, умноженной на указанный пользователемштраф.Функция индикатора - это y_ {транзакции} (i), где она равна 1, если вес ценной бумаги i отличается в начальном портфеле и в конечном портфеле, и 0 в противном случае.

Третий член фиксируется суммойитоговая доходность портфеля, умноженная на отрицательный штраф, указанный пользователем, поскольку целью является минимизация.

Последний термин - это количество активов в конечном портфеле (т. е. сумма функции индикатора, подсчитывающая количество положительных весов вконечный портфель), умноженный на штраф, указанный пользователем.

Предполагая, что у нас уже есть целевые веса как target_w, как мне настроить эту проблему оптимизации в библиотеке docplex python?Или, если кто-то знаком со смешанным целочисленным программированием в NAG, было бы полезно узнать, как настроить такую ​​проблему там.

`
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

1 Ответ

1 голос
/ 27 июня 2019

Основная проблема в вашем коде заключается в том, что final_w - это не массив переменных, а массив данных.Так что оптимизировать будет нечего.Чтобы создать массив переменных в docplex, вы должны сделать что-то вроде этого:

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

Это создает n переменных, которые могут принимать значения от 0 до 1. Имея это в виду, вы можете начинать все.Например:

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

Для следующей цели все становится сложнее, так как вам нужны ограничения индикатора.Чтобы упростить определение этих ограничений, сначала определите вспомогательную переменную delta, которая дает абсолютную разницу между акциями:

    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))

Затем вам нужна переменная индикатора, равная 1, если для корректировки запаса требуется транзакция 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)

С помощью этого вы можете определить вторую цель:

    obj2 = pen_trans * m.sum(needtrans)

после определения всех целей вы можете добавить их сумму в модель: m.minimize (obj1+ obj2 + obj3 + obj4), а затем решите модель и покажите ее решение:

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

Если что-то из перечисленного выше (пока) вам не ясно, я предлагаю вам взглянуть на множество примеровкоторый поставляется с docplex, а также по (справочной) документации.

...