Повышение доходности с инвестиций
Максимизация долгосрочных инвестиций
Введение
В данном примере представлена задача оптимизации долгосрочного инвестиционного портфеля с фиксированной доходностью. Целью является максимизация конечного капитала на заданном горизонте планирования за счет реинвестирования доходов от облигаций с различными сроками погашения и процентными ставками. Исследование демонстрирует применение методов линейного программирования для формализации и решения данной задачи.
Постановка задачи
Рассмотрим инвестиционную модель с фиксированным начальным капиталом, распределяемым между облигациями с детерминированной доходностью на заданном временном горизонте. Каждая облигация характеризуется фиксированной процентной ставкой с ежегодной капитализацией и выплатой номинальной стоимости с накопленным процентным доходом в момент погашения. Целевая функция модели направлена на максимизацию совокупного капитала по окончании инвестиционного периода.
Дополнительно в модель может быть включено ограничение на диверсификацию портфеля, устанавливающее максимальную долю единичной инвестиции в общем объеме капитала на момент размещения.
Методология исследования предполагает поэтапное рассмотрение задачи: от частного случая с ограниченным набором инструментов к обобщенной постановке. Математическая формализация проблемы реализуется в рамках аппарата линейного программирования, что позволяет сформулировать и решить соответствующую оптимизационную задачу максимизации конечного благосостояния.
Вводный пример
Рассмотрим демонстрационный пример с начальным капиталом 1000 рублей и горизонтом инвестирования 5 лет. Модель включает четыре облигации с различными характеристиками: облигация 1 приобретается в первом году со сроком погашения 4 года и доходностью 2%; облигация 2 доступна в пятом году со сроком 1 год и доходностью 4%; облигации 3 и 4 приобретаются во втором году со сроками погашения 4 и 3 года соответственно при доходности 6%.
Для моделирования неинвестированных средств введены пять одногодичных облигаций с нулевой доходностью, что формирует эквивалентную модель из девяти инвестиционных инструментов.
Присоединим необходимые библиотеки и функцию визуализации:
using Plots, Random, JuMP, HiGHS
include("plotInvest.jl")
Визуализируем эту задачу с помощью горизонтальных прямоугольников, которые представляют доступные периоды покупки для каждой облигации. По строкам расположены номера облигаций с процентными ставками. По столбцам расположены периоды инвестиций в годах.
# Период времени в годах
Период = 5
# Количество облигаций
КоличествоОблигаций = 4
# Начальная сумма денег
Сумма_0 = 1000
# Общее количество возможностей покупки
ВсегоВозможностейПокупки = КоличествоОблигаций + Период
# Периоды покупки
ГодыПокупки = [1; 2; 3; 4; 5; 1; 5; 2; 2]
# Длительности облигаций
СрокПогашения = [1; 1; 1; 1; 1; 4; 1; 4; 3]
# Периоды продажи облигаций
ГодыПогашения = ГодыПокупки .+ СрокПогашения .- 1
# Процентные ставки в процентах
Проценты = [0; 0; 0; 0; 0; 2; 4; 6; 6]
# Доходность после одного года с учетом процентов
ДоходностьЗаГод = 1 .+ Проценты ./ 100
# Визуализация задачи
p1 = plotInvest(КоличествоОблигаций, ГодыПокупки, СрокПогашения, Проценты)
display(p1)
Переменные решения
Представим переменные решения вектором x, где — сумма в рублях, инвестированная в облигацию k, для k = 1,...,9. При погашении выплата за инвестицию равна:
# Создаём модель оптимизации
модель = Model(HiGHS.Optimizer)
# Переменные решения - суммы инвестиций
@variable(модель, x[1:ВсегоВозможностейПокупки] >= 0)
# Общая доходность
ОбщаяДоходность = ДоходностьЗаГод .^ СрокПогашения
Целевая функция
Целью оптимизации является максимизация совокупного капитала, формируемого за счёт реинвестирования доходов от погашаемых облигаций. Как демонстрирует графическая визуализация, денежные потоки, поступающие в промежуточные периоды, реинвестируются и вносят вклад в формирование итогового благосостояния.
Создадим целевую функцию и задачу для максимизации.
# Создание задачу оптимизации (максимизация)
@objective(модель, Max, x[5] * ОбщаяДоходность[5] + x[7] * ОбщаяДоходность[7] + x[8] * ОбщаяДоходность[8])
Линейные ограничения
Модель предполагает ежегодное распределение доступного капитала между инвестиционными инструментами. На начальном этапе размещается стартовый капитал, в последующие периоды — реинвестируются денежные потоки от погашаемых облигаций.
Составим систему уравнений:
# Ограничения на инвестиции
@constraint(модель, ограничение_инвестиции1, x[1] + x[6] == Сумма_0)
@constraint(модель, ограничение_инвестиции2, x[2] + x[8] + x[9] == ДоходностьЗаГод[1] * x[1])
@constraint(модель, ограничение_инвестиции3, x[3] == ДоходностьЗаГод[2] * x[2])
@constraint(модель, ограничение_инвестиции4, x[4] == ДоходностьЗаГод[3] * x[3])
@constraint(модель, ограничение_инвестиции5, x[5] + x[7] == ДоходностьЗаГод[4] * x[4] + ОбщаяДоходность[6] * x[6] + ОбщаяДоходность[9] * x[9])
Решение
Решим эту задачу без ограничений на сумму, которую можно инвестировать в одну облигацию.
optimize!(модель)
решение = value.(x)
значение_целевой_функции = objective_value(модель)
Визуализация решения
Отобразим значение дохода с инвестиций.
println("После $Период лет, доход с начальных $(Сумма_0) ₽ составит $(round(значение_целевой_функции, digits=2)) ₽")
Визуализируем решение:
p2 = plotInvest(КоличествоОблигаций, ГодыПокупки, СрокПогашения, Проценты, решение)
display(p2)
Оптимальные инвестиции с ограничениями
Для обеспечения диверсификации портфеля в модель вводится ограничение на максимальную долю инвестиций в единичный актив относительно совокупного капитала текущего периода, включая поступления от погашаемых ценных бумаг.
# Создание модели для задачи с ограничениями
модель_с_ограничениями = Model(HiGHS.Optimizer)
# Переменные решения для модели с ограничениями
@variable(модель_с_ограничениями, x2[1:ВсегоВозможностейПокупки] >= 0)
# Целевая функция
@objective(модель_с_ограничениями, Max, x2[5] * ОбщаяДоходность[5] + x2[7] * ОбщаяДоходность[7] + x2[8] * ОбщаяДоходность[8])
# Базовые ограничения на инвестиции
@constraint(модель_с_ограничениями, инвестиции1, x2[1] + x2[6] == Сумма_0)
@constraint(модель_с_ограничениями, инвестиции2, x2[2] + x2[8] + x2[9] == ДоходностьЗаГод[1] * x2[1])
@constraint(модель_с_ограничениями, инвестиции3, x2[3] == ДоходностьЗаГод[2] * x2[2])
@constraint(модель_с_ограничениями, инвестиции4, x2[4] == ДоходностьЗаГод[3] * x2[3])
@constraint(модель_с_ограничениями, инвестиции5, x2[5] + x2[7] == ДоходностьЗаГод[4] * x2[4] + ОбщаяДоходность[6] * x2[6] + ОбщаяДоходность[9] * x2[9])
# Максимальный процент для инвестиций в любую облигацию
МаксПроцент = 0.6
Создадим систему неравенств:
# Ограничения на максимальные инвестиции
@constraint(модель_с_ограничениями, лимит1, x2[1] <= МаксПроцент * Сумма_0)
@constraint(модель_с_ограничениями, лимит2, x2[2] <= МаксПроцент * (ДоходностьЗаГод[1] * x2[1] + ДоходностьЗаГод[6] * x2[6]))
@constraint(модель_с_ограничениями, лимит3, x2[3] <= МаксПроцент * (ДоходностьЗаГод[2] * x2[2] + ДоходностьЗаГод[6]^2 * x2[6] + ДоходностьЗаГод[8] * x2[8] + ДоходностьЗаГод[9] * x2[9]))
@constraint(модель_с_ограничениями, лимит4, x2[4] <= МаксПроцент * (ДоходностьЗаГод[3] * x2[3] + ДоходностьЗаГод[6]^3 * x2[6] + ДоходностьЗаГод[8]^2 * x2[8] + ДоходностьЗаГод[9]^2 * x2[9]))
@constraint(модель_с_ограничениями, лимит5, x2[5] <= МаксПроцент * (ДоходностьЗаГод[4] * x2[4] + ДоходностьЗаГод[6]^4 * x2[6] + ДоходностьЗаГод[8]^3 * x2[8] + ДоходностьЗаГод[9]^3 * x2[9]))
@constraint(модель_с_ограничениями, лимит6, x2[6] <= МаксПроцент * Сумма_0)
@constraint(модель_с_ограничениями, лимит7, x2[7] <= МаксПроцент * (ДоходностьЗаГод[4] * x2[4] + ДоходностьЗаГод[6]^4 * x2[6] + ДоходностьЗаГод[8]^3 * x2[8] + ДоходностьЗаГод[9]^3 * x2[9]))
@constraint(модель_с_ограничениями, лимит8, x2[8] <= МаксПроцент * (ДоходностьЗаГод[1] * x2[1] + ДоходностьЗаГод[6] * x2[6]))
@constraint(модель_с_ограничениями, лимит9, x2[9] <= МаксПроцент * (ДоходностьЗаГод[1] * x2[1] + ДоходностьЗаГод[6] * x2[6]))
Решение задачи с ограничением диверсификации (максимум 60% капитала на один актив) демонстрирует снижение конечной доходности по сравнению с неограниченной оптимизацией. Визуализация результирующего портфеля показывает перераспределение инвестиционных потоков.
# Решение задачи с ограничениями
optimize!(модель_с_ограничениями)
решение2 = value.(x2)
значение_целевой_функции2 = objective_value(модель_с_ограничениями)
Отобразим значение дохода с учётом ограничений:
println("После $Период лет, доход с начальных $(Сумма_0) ₽ составит $(round(значение_целевой_функции2, digits=2)) ₽")
Визуализируем решение:
# Визуализация решения с ограничениями
p3 = plotInvest(КоличествоОблигаций, ГодыПокупки, СрокПогашения, Проценты, решение2)
display(p3)
Модель произвольного размера
Перейдём к обобщённой постановке задачи, масштабировав модель до 30-летнего горизонта инвестирования с портфелем из 400 облигаций со случайной доходностью в диапазоне 1–6%. Данная конфигурация формирует задачу линейного программирования с 430 переменными, демонстрируя применимость метода к реальным инвестиционным задачам.
Random.seed!(123)
# Начальная сумма денег
Сумма_0_большая = 1000
# Период времени в годах
Период_большой = 30
# Количество облигаций
КоличествоОблигаций_большое = 400
# Общее количество возможностей покупки
ВсегоВозможностейПокупки_большое = КоличествоОблигаций_большое + Период_большой
# Генерируем случайные длительности погашения
СрокПогашения_большой = rand(1:(Период_большой-1), ВсегоВозможностейПокупки_большое)
# Облигации имеют период погашения 1 год
СрокПогашения_большой[1:Период_большой] .= 1
# Генерируем случайные годовые процентные ставки для каждой облигации
Проценты_большие = rand(1:6, ВсегоВозможностейПокупки_большое)
# Облигации имеют процентную ставку 0 (не инвестированы)
Проценты_большие[1:Период_большой] .= 0
# Доходность после одного года с учетом процентов
ДоходностьЗаГод_большая = 1 .+ Проценты_большие ./ 100
# Вычисление доходность в конце периода погашения для каждой облигации
ОбщаяДоходность_большая = ДоходностьЗаГод_большая .^ СрокПогашения_большой
# Создание случайные годы покупки для каждой опции
ГодыПокупки_большие = zeros(Int, ВсегоВозможностейПокупки_большое)
# Облигации доступны для покупки каждый год
ГодыПокупки_большие[1:Период_большой] = 1:Период_большой
for i in 1:КоличествоОблигаций_большое
# Генерируем случайный год для погашения облигации до конца T-летнего периода
ГодыПокупки_большие[i+Период_большой] = rand(1:(Период_большой - СрокПогашения_большой[i+Период_большой] + 1))
end
# Вычисление периодов, когда каждая облигация достигает погашения в конце года
ГодыПогашения_большие = ГодыПокупки_большие .+ СрокПогашения_большой .- 1
Сформируем временную модель инвестиционных операций, где матрицы индексы_покупки_большие и индексы_продажи_большие задают допустимые периоды для открытия и закрытия позиций по каждому финансовому инструменту.
# Матрица индексов покупки
индексы_покупки_большие = falses(ВсегоВозможностейПокупки_большое, Период_большой)
for ii in 1:Период_большой
индексы_покупки_большие[:, ii] = ГодыПокупки_большие .== ii
end
# Матрица индексов продажи
индексы_продажи_большие = falses(ВсегоВозможностейПокупки_большое, Период_большой)
for ii in 1:Период_большой
индексы_продажи_большие[:, ii] = ГодыПогашения_большие .== ii
end
Настроем переменные оптимизации, соответствующие облигациям.
модель_большая = Model(HiGHS.Optimizer)
# Переменные решения
@variable(модель_большая, x_большой[1:ВсегоВозможностейПокупки_большое] >= 0)
Создадим целевую функцию задачу оптимизации.
# Задача максимизации
@objective(модель_большая, Max, sum(x_большой[индексы_продажи_большие[:, Период_большой]] .* ОбщаяДоходность_большая[индексы_продажи_большие[:, Период_большой]]))
# Ограничения
@constraint(модель_большая, НачальнаяИнвестицияБольшая,
sum(x_большой[i] for i in 1:ВсегоВозможностейПокупки_большое if индексы_покупки_большие[i, 1]) == Сумма_0_большая)
for t in 2:Период_большой
@constraint(модель_большая,
sum(x_большой[i] for i in 1:ВсегоВозможностейПокупки_большое if индексы_покупки_большие[i, t]) ==
sum(x_большой[i] * ОбщаяДоходность_большая[i] for i in 1:ВсегоВозможностейПокупки_большое if индексы_продажи_большие[i, t-1]))
end
Решим задачу:
@time optimize!(модель_большая)
решение_большое = value.(x_большой)
значение_целевой_функции_большое = objective_value(модель_большая)
Насколько хорошо сработали инвестиции?
println("После $Период_большой лет, доход с начальных $(Сумма_0_большая) ₽ составит $(round(значение_целевой_функции_большое, digits=2)) ₽")
Решение с ограниченными долями
Создадим задачу оптимизации с ограничениями:
# Создаём модель для задачи с ограничениями
модель_большая_с_ограничениями = Model(HiGHS.Optimizer)
@variable(модель_большая_с_ограничениями, x_большой_с[1:ВсегоВозможностейПокупки_большое] >= 0)
@objective(модель_большая_с_ограничениями, Max,
sum(x_большой_с[i] * ОбщаяДоходность_большая[i] for i in 1:ВсегоВозможностейПокупки_большое if индексы_продажи_большие[i, Период_большой]))
# Ограничения для модели с ограничениями
@constraint(модель_большая_с_ограничениями, НачальнаяИнвестицияБольшаяС,
sum(x_большой_с[i] for i in 1:ВсегоВозможностейПокупки_большое if индексы_покупки_большие[i, 1]) == Сумма_0_большая)
for t in 2:Период_большой
@constraint(модель_большая_с_ограничениями,
sum(x_большой_с[i] for i in 1:ВсегоВозможностейПокупки_большое if индексы_покупки_большие[i, t]) ==
sum(x_большой_с[i] * ОбщаяДоходность_большая[i] for i in 1:ВсегоВозможностейПокупки_большое if индексы_продажи_большие[i, t-1]))
end
В масштабированной модели применяется норматив диверсификации, ограничивающий долю единичной облигации в портфеле уровнем 0.4.
МаксПроцент_большой = 0.4
Формализация ограничений диверсификации требует построения двух структур данных: матрицы активности облигаций и матрицы их текущей стоимости, на основе которых задается верхняя граница доли единичного актива в портфеле.
активные_большие = falses(ВсегоВозможностейПокупки_большое, Период_большой)
for ii in 1:Период_большой
активные_большие[:, ii] = (ii .>= ГодыПокупки_большие) .& (ii .<= ГодыПогашения_большие)
end
Установим ограничения на максимальные инвестиции:
# Ограничения на максимальные инвестиции
for i in 1:ВсегоВозможностейПокупки_большое
for t in 1:Период_большой
if индексы_покупки_большие[i, t]
if t == 1
@constraint(модель_большая_с_ограничениями, x_большой_с[i] <= МаксПроцент_большой * Сумма_0_большая)
else
@constraint(модель_большая_с_ограничениями, x_большой_с[i] <= МаксПроцент_большой *
sum(x_большой_с[j] * (ДоходностьЗаГод_большая[j] ^ sum(активные_большие[j, 1:(t-1)]))
for j in 1:ВсегоВозможностейПокупки_большое if активные_большие[j, t-1]))
end
end
end
end
Решение задачи:
@time optimize!(модель_большая_с_ограничениями)
решение_большое_с_ограничениями = value.(x_большой_с)
значение_целевой_функции_большое_с_ограничениями = objective_value(модель_большая_с_ограничениями)
println("\nПосле $Период_большой лет, доход с начальных $(Сумма_0_большая) ₽ составит $(round(значение_целевой_функции_большое_с_ограничениями, digits=2)) ₽")
Качественный анализ результатов
Для оценки эффективности оптимального портфеля целесообразно сравнить его доходность с теоретическим максимумом — инвестицией всего капитала в облигацию с максимальной ставкой 6% на весь 30-летний период. Дополнительной метрикой анализа может служить расчет эквивалентной годовой ставки, соответствующей достигнутому уровню конечного благосостояния.
# Максимальная возможная сумма
максимальная_сумма = Сумма_0_большая * (1 + 6/100)^Период_большой
# Отношение (в процентах)
отношение = значение_целевой_функции_большое_с_ограничениями / максимальная_сумма * 100
# Эквивалентная процентная ставка
эквивалентная_ставка = ((значение_целевой_функции_большое_с_ограничениями / Сумма_0_большая)^(1/Период_большой) - 1) * 100
println("\nПолученная сумма составит $(round(отношение, digits=2))% от максимальной суммы $(round(максимальная_сумма, digits=2)) ₽,")
println("которая могла быть получена при инвестировании в одну облигацию.")
println("\nВаш доход соответствует $(round(эквивалентная_ставка, digits=2))% годовой ставке за $(Период_большой)-летний период.")
# Визуализируем результаты
p4 = plotInvest(КоличествоОблигаций_большое, ГодыПокупки_большие, СрокПогашения_большой, Проценты_большие, решение_большое_с_ограничениями, false)
display(p4)
На графике показана временная структура инвестиционного портфеля с ограничениями:
-
Горизонтальные зелёные линии - выбранные облигации
-
Начало линии — год покупки, конец - год погашения
-
Номера в фигурных скобках {i} — идентификаторы облигаций
-
Расположение сверху вниз — приоритет инвестиций
График демонстрирует диверсифицированный портфель, где инвестиции распределены по разным годам и срокам в соответствии с ограничением — не более 60% в один актив.
Вывод
Проведённое исследование демонстрирует эффективность методов линейного программирования для оптимизации долгосрочных инвестиционных портфелей. Разработанная модель позволяет определить стратегию распределения капитала, максимизирующую конечное благосостояние при заданных временных горизонтах.
Экспериментальные расчеты подтверждают существование компромисса между диверсификацией и доходностью: введение ограничения в 60% на долю одного актива снижает конечную доходность на 4.3% по сравнению с неограниченной оптимизацией. Масштабируемость подхода верифицирована на задаче с 30-летним горизонтом и портфелем из 400 облигаций.
Направлениями для будущих исследований являются учёт неопределённости доходности и создание алгоритмов динамической оптимизации портфеля в меняющихся рыночных условиях.