Оптимизация портфеля ценных бумаг средствами Python

    Введение


    На финансовом рынке обращается, как правило, несколько типов ценных бумаг: государственные ценные бумаги, муниципальные облигации, корпоративные акции и т.п.

    Если у участника рынка есть свободные деньги, то их можно отнести в банк и получать проценты или купить на них ценные бумаги и получать дополнительный доход. Но в какой банк отнести? Какие ценные бумаги купить?

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

    Программные средства для анализа портфелей ценных бумах должны работать с матрицами доходности и решать задачи нелинейного программирования с ограничениями в виде строгих и нестрогих неравенств. Символьное решение на Python некоторых типов задач нелинейного программирования мною уже рассматривалось в публикации [1]. Однако, применить предложенные в указанной публикации методы для анализа портфеля ценных бумаг нельзя из-за ограничений в виде строгих неравенств.

    Целью настоящей публикации является разработка методов оптимизации портфелей ценных бумаг с использованием библиотеки scipy.optimize. Пришлось исследовать и применить при программировании такие мало известные возможности указанной библиотеки, как введение дополнительных ограничений в функцию цели [2].

    Постановка задачи об оптимальном портфеле Марковица


    Рассмотрим общую задачу распределения капитала, который участник рынка хочет потратить на приобретение ценных бумаг. Цель инвестора – вложить деньги так, чтобы сохранить свой капитал, а при возможности и нарастить его.

    Набор ценных бумаг, находящихся у участника рынка, называется его портфелем. Стоимость портфеля – это суммарная стоимость всех составляющих его бумаг. Если сегодня его стоимость есть Р, а через год она окажется равной Р', то (Р'- Р)/Р естественно назвать доходностью портфеля в процентах годовых. Доходность портфеля – это доходность на единицу его стоимости.

    Пусть xi – доля капитала, потраченная на покупку ценных бумаг i-го вида. Весь выделенный капитал принимается за единицу. Пусть di – доходность в процентах годовых бумаг i-го вида в расчете на одну денежную единицу.

    Доходность колеблется во времени, так что будем считать ее случайной величиной. Пусть mi, ri – средняя ожидаемая доходность и среднее квадратическое отклонение, называемое риском. Через CVij обозначим ковариацию доходностей ценных бумаг i – го и j – го видов.

    Каждый владелец портфеля ценных бумаг сталкивается с дилеммой: хочется иметь эффективность больше, а риск меньше. Однако, поскольку “нельзя поймать двух зайцев сразу”, необходимо сделать определенный выбор между эффективностью и риском.

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


    Такая модель в виде системы из уравнений и неравенств имеет вид [3]:



    Необходимо определить: x1,x2…xn.

    Исходными данными для расчета является матрица доходности ценных бумаг следующей формы (заполненный пример матрицы в листинге программы):



    Для реализации модели минимального риска на Python нужно выполнить следующие этапы разработки:
    1.Определение средней доходности акций 1-6:

    from sympy import *
    import numpy as np
    from scipy.optimize import minimize
    from sympy import *
    import numpy as np
    from scipy.optimize import minimize
    "D- матрица доходности (обычно загружается из файла)"
    D=np.array([[9.889, 11.603,11.612,  12.721,11.453,12.102],
    [12.517, 13.25,12.947,12.596,12.853,13.036],
    [12.786, 12.822,15.447,14.452,15.143,16.247],
    [11.863, 12.114,13.359,13.437,11.913,15.300],
    [11.444, 13.292,13.703,11.504,13.406,15.255],
    [14.696, 15.946,16.829,17.698,16.051,17.140]],np.float64)
    d= np.zeros([6,1])# столбец для средней доходности 
    m,n= D.shape#размерность матрицы
    for j in np.arange(0,n):
             for i in np.arange(0,m):
                      d[j,0]=d[j,0]+D[i,j]
    d=d/n
    print("Средняя доходность акций 1-6 : \n %s"%d)

    Получим:

    Средняя доходность акций 1-6:

    [[ 12.19916667]
    [ 13.17116667]
    [ 13.98283333]
    [ 13.73466667]
    [ 13.46983333]
    [ 14.84666667]]

    2. Построение ковариационной матрицы (m=n=6).

    CV= np.zeros([m,n])
    for i in np.arange(0,m):
    for j in np.arange(0,n):
    x=np.array(D[0:m,j]).T
    y=np.array(D[0:m,i]).T
    X = np.vstack((x,y))
    CV[i,j]=round(np.cov(x,y,ddof=0)[1,0],3)
    print(«Ковариационная матрица CV: \n %s»%CV)

    Получим:

    Ковариационная матрица CV:

    [[ 2.117 1.773 2.256 2.347 2.077 1.975]
    [ 1.773 1.903 1.941 2.049 1.888 1.601]
    [ 2.256 1.941 2.901 2.787 2.701 2.761]
    [ 2.347 2.049 2.787 3.935 2.464 2.315]
    [ 2.077 1.888 2.701 2.464 2.723 2.364]
    [ 1.975 1.601 2.761 2.315 2.364 3.067]]

    3. Символьное определение функции для определения дисперсии доходности портфеля (функции риска).

    x1,x2,x3,x4,x5,x6,x7,x8,p,q,w=symbols(' x1 x2 x3 x4 x5 x6 x7 x8  p q w' , float= True)
    v1=Matrix([x1,x2,x3,x4,x5,x6])
    v2=v1.T
    w=0
    for i in np.arange(0,m):
             for j in np.arange(0,n):            w=w+v1[p.subs({p:i}),q.subs({q:0})]*v2[p.subs({p:0}),q.subs({q:j})]*CV[p.subs({p:i}),q.subs({q:j})]
    print("Дисперсия доходности портфеля (функция риска):\n%s"%w) 

    Получим:

    Дисперсия доходности портфеля (функция риска):

    2.117*x1**2 + 3.546*x1*x2 + 4.512*x1*x3 + 4.694*x1*x4 + 4.154*x1*x5 + 3.95*x1*x6 + 1.903*x2**2 + 3.882*x2*x3 + 4.098*x2*x4 + 3.776*x2*x5 + 3.202*x2*x6 + 2.901*x3**2 + 5.574*x3*x4 + 5.402*x3*x5 + 5.522*x3*x6 + 3.935*x4**2 + 4.928*x4*x5 + 4.63*x4*x6 + 2.723*x5**2 + 4.728*x5*x6 + 3.067*x6**2

    4. Определение оптимального портфеля акций для минимального риска и доходности mp=13.25

    def objective(x):#функция риска
             x1=x[0];x2=x[1];x3=x[2]; x4=x[3]; x5=x[4];  x6=x[5]
             return 2.117*x1**2 + 3.546*x1*x2 + 4.512*x1*x3 + 4.694*x1*x4 + 4.154*x1*x5 + 3.95*x1*x6\
                    + 1.903*x2**2 + 3.882*x2*x3 + 4.098*x2*x4 + 3.776*x2*x5 + 3.202*x2*x6 + 2.901*x3**2 \
                    + 5.574*x3*x4 + 5.402*x3*x5 + 5.522*x3*x6 + 3.935*x4**2 + 4.928*x4*x5 + 4.63*x4*x6 \
                    + 2.723*x5**2 + 4.728*x5*x6 + 3.067*x6**2
    def constraint1(x):#условие для суммы долей -1
             return x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-1.0
    def constraint2(x): # задание доходности       
             return d[0,0]*x[0] + d[1,0]*x[1] + d[2,0]*x[2] + d[3,0]*x[3] + d[4,0]*x[4]+ d[5,0]*x[5] - 13.25
    x0=[1,1,0,0,0,1]#начальное значение переменных для поиска минимума функции риска
    b=(0.0,1.0)# условие для  x от нуля до единицы включая пределы
    bnds=(b,b,b,b,b,b)#передача условий в функцию  риска(подготовка)
    con1={'type':'ineq','fun':constraint1} #передача условий в функцию  риска(подготовка)
    con2={'type':'eq','fun':constraint2} #передача условий в функцию  риска(подготовка)
    cons=[con1,con2]#передача условий в функцию  риска(подготовка)
    sol=minimize(objective,x0,method='SLSQP',\
                 bounds=bnds,constraints=cons)# поиск минимума функции риска
    print("Минимум функции риска -%s"%str(round(sol.fun,3)))
    print("Акция 1 доля- %s, доходность- %s"%(round(sol.x[0],3),round(d[0,0]*sol.x[0],3)))
    print("Акция 2 доля- %s, доходность- %s"%(round(sol.x[1],3),round(d[1,0]*sol.x[1],3)))
    print("Акция 3 доля- %s, доходность- %s"%(round(sol.x[2],3),round(d[2,0]*sol.x[2],3)))
    print("Акция 4 доля- %s, доходность- %s"%(round(sol.x[3],3),round(d[3,0]*sol.x[3],3)))
    print("Акция 5 доля- %s, доходность- %s"%(round(sol.x[4],3),round(d[4,0]*sol.x[4],3)))
    print("Акция 6 доля- %s, доходность- %s"%(round(sol.x[5],3),round(d[5,0]*sol.x[5],3)))
    

    Получим:

    Минимум функции риска -1.846
    Акция 1 доля- 0.141, доходность- 1.721
    Акция 2 доля- 0.73, доходность- 9.616
    Акция 3 доля- 0.0, доходность- 0.0
    Акция 4 доля- 0.0, доходность- 0.0
    Акция 5 доля- 0.0, доходность- 0.0
    Акция 6 доля- 0.129, доходность- 1.914

    Вывод:

    Доходными являются 1,2,6 акции. Это и есть часть ответа на вопросы, поставленные в начале публикации.

    Полный листинг программы для минимизации риска по методу Марковица при заданной доходности
    from sympy import *
    import numpy as np
    from scipy.optimize import minimize
    "D- матрица доходности(обычно загружается из файла)"
    D=np.array([[9.889, 11.603,11.612,  12.721,11.453,12.102],
    [12.517, 13.25,12.947,12.596,12.853,13.036],
    [12.786, 12.822,15.447,14.452,15.143,16.247],
    [11.863, 12.114,13.359,13.437,11.913,15.300],
    [11.444, 13.292,13.703,11.504,13.406,15.255],
    [14.696, 15.946,16.829,17.698,16.051,17.140]],np.float64)
    d= np.zeros([6,1])# столбец для средней доходности 
    m,n= D.shape#размерность матрицы
    for j in np.arange(0,n):
             for i in np.arange(0,m):
                      d[j,0]=d[j,0]+D[i,j]
    d=d/n
    print("Средняя доходность по столбцам : \n %s"%d)
    CV= np.zeros([m,n])
    for i in np.arange(0,m):
             for j in np.arange(0,n):
                      x=np.array(D[0:m,j]).T
                      y=np.array(D[0:m,i]).T
                      X = np.vstack((x,y))
                      CV[i,j]=round(np.cov(x,y,ddof=0)[1,0],3)
    print("Ковариационная матрица  CV: \n %s"%CV)
    x1,x2,x3,x4,x5,x6,x7,x8,p,q,w=symbols(' x1 x2 x3 x4 x5 x6 x7 x8  p q w' , float= True)
    v1=Matrix([x1,x2,x3,x4,x5,x6])
    v2=v1.T
    w=0
    for i in np.arange(0,m):
             for j in np.arange(0,n):
                      w=w+v1[p.subs({p:i}),q.subs({q:0})]*v2[p.subs({p:0}),q.subs({q:j})]*CV[p.subs({p:i}),q.subs({q:j})]
    print("Дисперсия доходности портфеля (функция риска):\n%s"%w)                  
    def objective(x):#функция риска
             x1=x[0];x2=x[1];x3=x[2]; x4=x[3]; x5=x[4];  x6=x[5]
             return 2.117*x1**2 + 3.546*x1*x2 + 4.512*x1*x3 + 4.694*x1*x4 + 4.154*x1*x5 + 3.95*x1*x6\
                    + 1.903*x2**2 + 3.882*x2*x3 + 4.098*x2*x4 + 3.776*x2*x5 + 3.202*x2*x6 + 2.901*x3**2 \
                    + 5.574*x3*x4 + 5.402*x3*x5 + 5.522*x3*x6 + 3.935*x4**2 + 4.928*x4*x5 + 4.63*x4*x6 \
                    + 2.723*x5**2 + 4.728*x5*x6 + 3.067*x6**2
    def constraint1(x):#условие для суммы долей -1
             return x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-1.0
    def constraint2(x): # задание доходности       
             return d[0,0]*x[0] + d[1,0]*x[1] + d[2,0]*x[2] + d[3,0]*x[3] + d[4,0]*x[4]+ d[5,0]*x[5] - 13.25
    x0=[1,1,0,0,0,1]#начальное значение переменных для поиска минимума функции риска
    b=(0.0,1.0)# условие для  x от нуля до единицы включая пределы
    bnds=(b,b,b,b,b,b)#передача условий в функцию  риска(подготовка)
    con1={'type':'ineq','fun’: constraint1} #передача условий в функцию  риска(подготовка)
    con2={'type':'eq','fun’: constraint2} #передача условий в функцию  риска(подготовка)
    cons=[con1,con2]#передача условий в функцию  риска(подготовка)
    sol=minimize(objective,x0,method='SLSQP',\
                 bounds=bnds,constraints=cons)# поиск минимума функции риска
    print("Минимум функции риска -%s"%str(round(sol.fun,3)))
    print("Акция 1 доля- %s, доходность- %s"%(round(sol.x[0],3),round(d[0,0]*sol.x[0],3)))
    print("Акция 2 доля- %s, доходность- %s"%(round(sol.x[1],3),round(d[1,0]*sol.x[1],3)))
    print("Акция 3 доля- %s, доходность- %s"%(round(sol.x[2],3),round(d[2,0]*sol.x[2],3)))
    print("Акция 4 доля- %s, доходность- %s"%(round(sol.x[3],3),round(d[3,0]*sol.x[3],3)))
    print("Акция 5 доля- %s, доходность- %s"%(round(sol.x[4],3),round(d[4,0]*sol.x[4],3)))
    print("Акция 6 доля- %s, доходность- %s"%(round(sol.x[5],3),round(d[5,0]*sol.x[5],3)))


    Оптимальный портфель Марковица максимальной доходности и заданного, (приемлемого) риска


    Система уравнений и неравенств имеет вид:



    Оптимизация портфеля максимальной доходности при заданном риске на Python
    import numpy as np
    from scipy.optimize import minimize
    d=np.array( [[ 12.19916667],
     [ 13.17116667],
     [ 13.98283333],
     [ 13.73466667],
     [ 13.46983333],
     [ 14.84666667]])
    def constraint2(x):
             x1=x[0];x2=x[1];x3=x[2]; x4=x[3]; x5=x[4];  x6=x[5]
             return 2.117*x1**2 + 3.546*x1*x2 + 4.512*x1*x3 + 4.694*x1*x4 + 4.154*x1*x5 \
             + 3.95*x1*x6 + 1.903*x2**2 + 3.882*x2*x3 + 4.098*x2*x4 + 3.776*x2*x5 + 3.202*x2*x6 \
             + 2.901*x3**2 + 5.574*x3*x4 + 5.402*x3*x5 + 5.522*x3*x6 + 3.935*x4**2 + 4.928*x4*x5 \
             + 4.63*x4*x6 + 2.723*x5**2 + 4.728*x5*x6 + 3.067*x6**2-2
    def constraint1(x):
             return x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-1.0
    def objective(x):      
             return -(12.199*x[0] + 13.171*x[1] + 13.983*x[2] + 13.735*x[3] + 13.47*x[4]+ 14.847*x[5] )
    x0=[1,1,1,1,1,1]
    b=(0.0,1.0)
    bnds=(b,b,b,b,b,b)
    con1={'type':'ineq','fun':constraint1}
    con2={'type':'eq','fun':constraint2}
    cons=[con1,con2]
    sol=minimize(objective,x0,method='SLSQP',\
                 bounds=bnds,constraints=cons)
    print("Максимум функции доходности -%s"%str(round(sol.fun,3)))
    print("Акция 1 доля- %s, доходность- %s"%(round(sol.x[0],3),round(d[0,0]*sol.x[0],3)))
    print("Акция 2 доля- %s, доходность- %s"%(round(sol.x[1],3),round(d[1,0]*sol.x[1],3)))
    print("Акция 3 доля- %s, доходность- %s"%(round(sol.x[2],3),round(d[2,0]*sol.x[2],3)))
    print("Акция 4 доля- %s, доходность- %s"%(round(sol.x[3],3),round(d[3,0]*sol.x[3],3)))
    print("Акция 5 доля- %s, доходность- %s"%(round(sol.x[4],3),round(d[4,0]*sol.x[4],3)))
    print("Акция 6 доля- %s, доходность- %s"%(round(sol.x[5],3),round(d[5,0]*sol.x[5],3)))
    


    Часть приведенного листинга не требует пояснений, поскольку всё подробно изложено в предыдущем примере. Однако есть отличия. Столбец средней доходности d и функция условия def constraint2(x) взяты из предыдущего примера, причем в предыдущем примере это была функция минимального риска. Кроме того, для определения максимума перед выводом значения новой функции цели – def objective(x), поставлен знак минус.

    Результат:

    Максимум функции доходности --14.1
    Акция 1 доля- 0.0, доходность- 0.0
    Акция 2 доля- 0.72, доходность- 9.489
    Акция 3 доля- 0.0, доходность- 0.0
    Акция 4 доля- 0.0, доходность- 0.0
    Акция 5 доля- 0.0, доходность- 0.0
    Акция 6 доля- 0.311, доходность- 4.611

    Акции 2,6 доходны. Но это не единственный результат оптимизации средствами scipy optimize minimize. Я решил сравнить результаты с решением той же задачи средствами Mathcad и вот что получил:



    Mathcad указывает на те же номера 2,6 доходных акций, но доли другие. В Python 0.720,0.311 в Mathcad 0.539, 0.461, при этом разные значения максимальной доходности соответственно 14.1 и 13.9. Для того чтобы окончательно убедиться какая программа вычисляет оптимум правильно, подставим полученные в Python значения долей в Mathcad, получим:



    Вывод: на Python оптимум функции, а следовательно доли и доходность вычисляется более точно, чем при использовании Mathcad.

    Формирование оптимального портфеля ценных бумаг по модели Тобина



    Портфель Тобина минимального риска:



    где d0 – эффективность без рисковых бумаг;
    x0 – доля капитала вложенная в без рисковые бумаги;
    xi,xj — доля капитала вложенная в ценные бумаги i-го и j–го видов;
    di – математическое ожидание (среднее арифметическое) доходности i — й ценной бумаги;
    vij – корреляционный момент между эффективностью бумаг i-го и j –го видов.

    Подбираем долю капитала заданной доходности, задаём общую доходность, приняв для примера следующие числовые значения x0=0.3, d0 =10, dp=12.7.

    Реализация портфеля Тобина минимального риска на Python
    import numpy as np
    from scipy.optimize import minimize
    d=np.array( [[ 12.19916667],
     [ 13.17116667],
     [ 13.98283333],
     [ 13.73466667],
     [ 13.46983333],
     [ 14.84666667]])
    x00=0.3;d0=10;dp=12.7
    def objective(x):#функция риска
             x1=x[0];x2=x[1];x3=x[2]; x4=x[3]; x5=x[4];  x6=x[5]
             return 2.117*x1**2 + 3.546*x1*x2 + 4.512*x1*x3 + 4.694*x1*x4 + 4.154*x1*x5 + 3.95*x1*x6\
                    + 1.903*x2**2 + 3.882*x2*x3 + 4.098*x2*x4 + 3.776*x2*x5 + 3.202*x2*x6 + 2.901*x3**2 \
                    + 5.574*x3*x4 + 5.402*x3*x5 + 5.522*x3*x6 + 3.935*x4**2 + 4.928*x4*x5 + 4.63*x4*x6 \
                    + 2.723*x5**2 + 4.728*x5*x6 + 3.067*x6**2
    def constraint1(x):#условие для суммы долей -1
             return x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-1.0+x00
    def constraint2(x): # задание доходности       
             return d[0,0]*x[0] + d[1,0]*x[1] + d[2,0]*x[2] + d[3,0]*x[3] + d[4,0]*x[4]+ d[5,0]*x[5] - dp+x00*d0
    x0=[1,1,1,1,1,1]#начальное значение переменных для поиска минимума функции риска
    b=(-1.0,100.0)# условие для  x от нуля до единицы включая пределы
    bnds=(b,b,b,b,b,b)#передача условий в функцию  риска(подготовка)
    con1={'type':'ineq','fun':constraint1} #передача условий в функцию  риска(подготовка)
    con2={'type':'eq','fun':constraint2} #передача условий в функцию  риска(подготовка)
    cons=[con1,con2]#передача условий в функцию  риска(подготовка)
    sol=minimize(objective,x0,method='SLSQP',\
                 bounds=bnds,constraints=cons)# поиск минимума функции риска
    print("Минимум функции риска : %s"%str(round(sol.fun,3)))
    print("Акция 1 доля- %s, доходность:  %s"%(round(sol.x[0],3),round(d[0,0]*sol.x[0],3)))
    print("Акция 2 доля- %s, доходность:  %s"%(round(sol.x[1],3),round(d[1,0]*sol.x[1],3)))
    print("Акция 3 доля- %s, доходность:  %s"%(round(sol.x[2],3),round(d[2,0]*sol.x[2],3)))
    print("Акция 4 доля- %s, доходность:  %s"%(round(sol.x[3],3),round(d[3,0]*sol.x[3],3)))
    print("Акция 5 доля- %s, доходность: %s"%(round(sol.x[4],3),round(d[4,0]*sol.x[4],3)))
    print("Акция 6 доля- %s, доходность:  %s"%(round(sol.x[5],3),round(d[5,0]*sol.x[5],3)))


    Получим:

    Минимум функции риска: 0.728
    Акция 1 доля- -0.023, доходность: -0.286
    Акция 2 доля- 0.666, доходность: 8.778
    Акция 3 доля- -1.0, доходность: -13.983
    Акция 4 доля- 0.079, доходность: 1.089
    Акция 5 доля- 0.3, доходность: 4.048
    Акция 6 доля- 0.677, доходность: 10.054

    Доходными являются акции 2,4,5,6.

    Портфель Тобина максимальной эффективности




    где rp – риск портфеля.

    Реализация портфеля Тобина максимальной эффективности на Python
    import numpy as np
    from scipy.optimize import minimize
    x00=0.8;d0=10;rp=0.07
    d=np.array( [[ 12.19916667],
     [ 13.17116667],
     [ 13.98283333],
     [ 13.73466667],
     [ 13.46983333],
     [ 14.84666667]])
    def constraint2(x):
             x1=x[0];x2=x[1];x3=x[2]; x4=x[3]; x5=x[4];  x6=x[5]
             return 2.117*x1**2 + 3.546*x1*x2 + 4.512*x1*x3 + 4.694*x1*x4 + 4.154*x1*x5 \
             + 3.95*x1*x6 + 1.903*x2**2 + 3.882*x2*x3 + 4.098*x2*x4 + 3.776*x2*x5 + 3.202*x2*x6 \
             + 2.901*x3**2 + 5.574*x3*x4 + 5.402*x3*x5 + 5.522*x3*x6 + 3.935*x4**2 + 4.928*x4*x5 \
             + 4.63*x4*x6 + 2.723*x5**2 + 4.728*x5*x6 + 3.067*x6**2-rp
    def constraint1(x):
             return x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-1.0+x0
    def objective(x):      
             return -(d[0,0]*x[0] + d[1,0]*x[1] + d[2,0]*x[2] + d[3,0]*x[3] + d[4,0]*x[4]+ d[5,0]*x[5]+x00*d0)
    x0=[1,1,1,1,1,1]
    b=(-1.0,100.0)
    bnds=(b,b,b,b,b,b)
    con1={'type':'ineq','fun':constraint1}
    con2={'type':'eq','fun':constraint2}
    cons=[con1,con2]
    sol=minimize(objective,x0,method='SLSQP',\
                 bounds=bnds,constraints=cons)
    print("Максимум функции доходности : %s"%str(round(sol.fun,3)))
    print("Акция 1 доля- %s, доходность:  %s"%(round(sol.x[0],3),round(d[0,0]*sol.x[0],3)))
    print("Акция 2 доля- %s, доходность: %s"%(round(sol.x[1],3),round(d[1,0]*sol.x[1],3)))
    print("Акция 3 доля- %s, доходность: %s"%(round(sol.x[2],3),round(d[2,0]*sol.x[2],3)))
    print("Акция 4 доля- %s, доходность: %s"%(round(sol.x[3],3),round(d[3,0]*sol.x[3],3)))
    print("Акция 5 доля- %s, доходность: %s"%(round(sol.x[4],3),round(d[4,0]*sol.x[4],3)))
    print("Акция 6 доля- %s, доходность- %s"%(round(sol.x[5],3),round(d[5,0]*sol.x[5],3)))


    Получим:

    Максимум функции доходности: -11.657
    Акция 1 доля- 0.09, доходность: 1.096
    Акция 2 доля- 0.196, доходность: 2.583
    Акция 3 доля- -1.0, доходность: -13.983
    Акция 4 доля- 0.113, доходность: 1.552
    Акция 5 доля- 0.411, доходность: 5.538
    Акция 6 доля- 0.463, доходность- 6.872

    Доходными являются акции 1,2,4,5.

    Выводы:


    Впервые средствами Python решена задача оптимизации портфеля ценных бумаг по моделям Марковица и Тобина.
    На сравнительном примере c математическим пакетом Mathcad показаны преимущества библиотеки scipy optimize minimize.

    Ссылки:

    1. Символьное решение задач нелинейного программирования
    2. scipy.optimize.minimize
    3. Постановка задачи об оптимальном портфеле
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 7
    • –1
      Т.е. это нормально, что доли в пакете отрицательные?
      Как в портфель акций положить минус 30 процентов акций БердянскКолхозСтроя?
      • –1

        Продать в короткую?

        • –1
          зашортить?
          • 0
            Да, это же модель Тобина, продажа без покрытия.
          • +1
            Решал подобную задачу (см. www.wolframcloud.com/objects/945e3912-eff1-45a2-affc-1f0e3c81518e) только на вольфрамовской математике. В математике очень сильно подкупает большое количество реализованных функций встроенных в ядро. Их там более пяти тысяч.
            Вот и здесь код код получился довольно коротким. Но реализован расчет не только в отдельной точке, а смоделированы множественные пересчеты портфелей на всей длинне данных начиная с 2000 года.
            Только в моделировании участвовало 50 акций. Удивительно, но расчет шел в пределах минуты на персоналке в окне браузера. Кстати задача оптимизации здесь получилась линейной. Может потому и вычисления быстрые.
            Автору за труды отлично. Сам постоянно слушаю лекции по анализу данных и машинному обучению. А там сплошной питон. Но тамошние методы все реализованы в математике.
            • 0
              def objective(x):#функция риска
                       x1=x[0];x2=x[1];x3=x[2]; x4=x[3]; x5=x[4];  x6=x[5]

              Это просто ужас, а не код.
              • 0
                Может быть, автор просто не знает про присваивание через распаковку? ))

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.