Оптимизация портфеля
Это руководство было изначально предоставлено Арпитом Бхатией (Arpit Bhatia).
Модели оптимизации играют все более важную роль в принятии финансовых решений. Многие вычислительные финансовые задачи можно эффективно решать с помощью современных методов оптимизации.
В этом руководстве решается известная задача оптимизации портфеля Марковица с использованием данных из конспектов лекций, которые читал в Технологическом институте Джорджии Шаббир Ахмед (Shabbir Ahmed).
Формулировка
Предположим, мы рассматриваем возможность инвестирования 1000 долларов в три акции, по которым не выплачиваются дивиденды: IBM (IBM), Walmart (WMT) и Southern Electric (SEHI), сроком на один месяц.
Мы покупаем акции трех компаний по текущим рыночным ценам на первоначальную сумму, удерживаем их в течение одного месяца, а затем в конце месяца продаем по преобладающим рыночным ценам.
Как рациональные инвесторы мы надеемся получить некоторую прибыль от этого начинания, то есть доходность инвестиций должна быть положительной.
Предположим, мы купили акции по цене долларов за акцию в начале месяца и продали их по цене долларов за акцию в конце месяца. Тогда месячная доходность акций составит .
Поскольку цены на акции весьма неопределенны, то же самое касается и доходности инвестиций на конец месяца. Наша цель — инвестировать таким образом, чтобы ожидаемая доходность на конец месяца составляла не менее $50 или 5 %. Кроме того, мы хотим, чтобы «риск» недостижения желаемой доходности был минимален.
Обратите внимание, что задача решается при следующих допущениях:
-
Торговать можно любым набором акций.
-
Продажа без покрытия не допускается.
-
Транзакционных издержек нет.
Эта задача моделируется с переменными решения , которые означают суммы, инвестированные в каждую из трех акций.
Обозначим как случайную величину, соответствующую ежемесячной доходности (приросту цены акций) на доллар для акции .
Тогда доход (или прибыль) от суммы долларов, вложенной в акции , составит , а общий (случайный) доход от инвестиций равен Ожидаемый доход от инвестиций равен =\sum_{i=1}^{3} \overline{r}_{i} x_{i},], где — ожидаемое значение
Теперь необходимо количественно оценить понятие «риска» инвестиций.
Марковиц в своей удостоенной Нобелевской премии работе показал, что рациональное представление инвестора о минимизации риска можно близко аппроксимировать путем минимизации дисперсии доходности инвестиционного портфеля. Дисперсия выражается следующей формулой:
где — ковариация доходности акции с доходностью акции .
Обратите внимание, что правая часть уравнения представляет собой максимально сокращенную форму выражения; мы не стали показывать промежуточные шаги, необходимые для ее вывода. Это уравнение можно также записать в следующей форме:
где — ковариационная матрица для случайного вектора .
Наконец, модель можно записать так:
Данные
В качестве данных для задачи мы используем приведенные ниже месячные цены акций с ноября 2000 года по ноябрь 2001 года.
df = DataFrames.DataFrame(
[
93.043 51.826 1.063
84.585 52.823 0.938
111.453 56.477 1.000
99.525 49.805 0.938
95.819 50.287 1.438
114.708 51.521 1.700
111.515 51.531 2.540
113.211 48.664 2.390
104.942 55.744 3.120
99.827 47.916 2.980
91.607 49.438 1.900
107.937 51.336 1.750
115.590 55.081 1.800
],
[:IBM, :WMT, :SEHI],
)
julia
Row | IBM | WMT | SEHI |
---|---|---|---|
Float64 | Float64 | Float64 | |
1 | 93.043 | 51.826 | 1.063 |
2 | 84.585 | 52.823 | 0.938 |
3 | 111.453 | 56.477 | 1.0 |
4 | 99.525 | 49.805 | 0.938 |
5 | 95.819 | 50.287 | 1.438 |
6 | 114.708 | 51.521 | 1.7 |
7 | 111.515 | 51.531 | 2.54 |
8 | 113.211 | 48.664 | 2.39 |
9 | 104.942 | 55.744 | 3.12 |
10 | 99.827 | 47.916 | 2.98 |
11 | 91.607 | 49.438 | 1.9 |
12 | 107.937 | 51.336 | 1.75 |
13 | 115.59 | 55.081 | 1.8 |
Далее вычислим процентную доходность акций за каждый месяц:
returns = diff(Matrix(df); dims = 1) ./ Matrix(df[1:end-1, :])
julia
12×3 Matrix{Float64}: -0.0909042 0.0192374 -0.117592 0.317645 0.0691744 0.0660981 -0.107023 -0.118137 -0.062 -0.0372369 0.00967774 0.533049 0.197132 0.0245391 0.182197 -0.0278359 0.000194096 0.494118 0.0152087 -0.0556364 -0.0590551 -0.0730406 0.145487 0.305439 -0.0487412 -0.140428 -0.0448718 -0.0823425 0.0317639 -0.362416 0.178261 0.0383915 -0.0789474 0.0709025 0.0729508 0.0285714
output
Ожидаемая ежемесячная доходность составляет:
r = vec(Statistics.mean(returns; dims = 1))
julia
3-element Vector{Float64}: 0.026002150277777348 0.008101316405671459 0.07371590949198982
output
Ковариационная матрица имеет следующий вид:
Q = Statistics.cov(returns)
julia
3×3 Matrix{Float64}: 0.018641 0.00359853 0.00130976 0.00359853 0.00643694 0.00488727 0.00130976 0.00488727 0.0686828
output
Формулировка на языке JuMP
model = Model(Ipopt.Optimizer)
set_silent(model)
@variable(model, x[1:3] >= 0)
@objective(model, Min, x' * Q * x)
@constraint(model, sum(x) <= 1000)
@constraint(model, r' * x >= 50)
optimize!(model)
@assert is_solved_and_feasible(model)
solution_summary(model)
julia
* Solver : Ipopt * Status Result count : 1 Termination status : LOCALLY_SOLVED Message from the solver: "Solve_Succeeded" * Candidate solution (result #1) Primal status : FEASIBLE_POINT Dual status : FEASIBLE_POINT Objective value : 2.26344e+04 Dual objective value : 4.52688e+04 * Work counters Solve time (sec) : 5.02205e-03 Barrier iterations : 11
output
Оптимальное распределение наших активов:
value.(x)
julia
3-element Vector{Float64}: 497.04552984986407 -9.670578501816873e-9 502.9544801594808
output
Поэтому мы тратим $497 на акции IBM и $503 на акции SEHI. В результате получается такая дисперсия:
scalar_variance = value(x' * Q * x)
julia
22634.41784988414
output
и ожидаемая доходность:
scalar_return = value(r' * x)
julia
49.99999950000236
output
Многоцелевая оптимизация портфеля
Предыдущая модель дала единственное решение с минимизацией дисперсии, гарантирующее ожидаемую прибыль не менее 50.0 На практике может быть допустима немного более высокая дисперсия при условии значительного увеличения ожидаемой доходности. Чтобы исследовать эту область решения задачи, мы можем сформулировать задачу оптимизации портфеля с двумя целями:
-
минимизация дисперсии;
-
максимизация ожидаемой доходности.
Решением этой двухцелевой задачи является граница эффективности из современной теории инвестиционного портфеля, а каждая точка решения — это точка с наилучшей доходностью при фиксированном уровне риска.
model = Model(() -> MOA.Optimizer(Ipopt.Optimizer)) set_silent(model)
julia
Необходимо также выбрать алгоритм решения для MOA
. Для нашей задачи граница эффективности будет иметь бесконечное число решений. Так как мы не можем найти все решения, выберем алгоритм аппроксимации и ограничим количество возвращаемых точек решения:
set_optimizer_attribute(model, MOA.Algorithm(), MOA.EpsilonConstraint())
set_optimizer_attribute(model, MOA.SolutionLimit(), 50)
julia
Теперь можно определить остальную модель:
@variable(model, x[1:3] >= 0)
@constraint(model, sum(x) <= 1000)
@expression(model, variance, x' * Q * x)
@expression(model, expected_return, r' * x)
# Мы хотим минимизировать дисперсию и максимизировать ожидаемую доходность, но должны выбрать
# единственное целевое назначение `Min` и поменять знак целевых функций с назначением `Max`:
@objective(model, Min, [variance, -expected_return])
optimize!(model)
@assert termination_status(model) == OPTIMAL
solution_summary(model)
julia
* Solver : MOA[algorithm=MultiObjectiveAlgorithms.EpsilonConstraint, optimizer=Ipopt] * Status Result count : 50 Termination status : OPTIMAL Message from the solver: "Solve complete. Found 50 solution(s)" * Candidate solution (result #1) Primal status : FEASIBLE_POINT Dual status : NO_SOLUTION Objective value : [2.58170e-08,-5.36777e-05] Objective bound : [5.78303e-09,-7.37159e+01] * Work counters Solve time (sec) : 1.23696e+00 Barrier iterations : 0
output
Алгоритм нашел 50 различных решений. Давайте построим графики и посмотрим, чем они отличаются:
objective_space = Plots.hline(
[scalar_return];
label = "Single-objective solution",
linecolor = :red,
)
Plots.vline!(objective_space, [scalar_variance]; label = "", linecolor = :red)
Plots.scatter!(
objective_space,
[value(variance; result = i) for i in 1:result_count(model)],
[value(expected_return; result = i) for i in 1:result_count(model)];
xlabel = "Variance",
ylabel = "Expected Return",
label = "",
title = "Objective space",
markercolor = "white",
markersize = 5,
legend = :bottomright,
)
for i in 1:result_count(model)
y = objective_value(model; result = i)
Plots.annotate!(objective_space, y[1], -y[2], (i, 3))
end
decision_space = StatsPlots.groupedbar(
vcat([value.(x; result = i)' for i in 1:result_count(model)]...);
bar_position = :stack,
label = ["IBM" "WMT" "SEHI"],
xlabel = "Solution #",
ylabel = "Investment (\$)",
title = "Decision space",
)
Plots.plot(objective_space, decision_space; layout = (2, 1), size = (600, 600))
julia
Возможно, компромисс оказался не таким уж и плохим. Наше первоначальное решение соответствует выбору решения № 17. Если купить больше акций SEHI, можно увеличить доходность, но при этом вырастет и дисперсия. Если купить меньше акций SEHI, как в решении № 5 или № 6, можно получить аналогичную прибыль, не вкладывая весь капитал. Также следует отметить, что ни в коем случае не следует покупать акции WMT.