Engee documentation
Notebook

Maximizing long-term investments

Introduction

This example presents the task of optimizing a long-term fixed-income investment portfolio. The goal is to maximize the final capital over a given planning horizon by reinvesting income from bonds with different maturities and interest rates. The study demonstrates the use of linear programming methods to formalize and solve this problem.

Setting the task

Let's consider an investment model with a fixed initial capital distributed among bonds with deterministic returns over a given time horizon. Each bond is characterized by a fixed interest rate with annual capitalization and the payment of a nominal value with accumulated interest income at maturity. The objective function of the model is aimed at maximizing total capital at the end of the investment period.

Additionally, the model may include a restriction on portfolio diversification, which sets the maximum share of a single investment in the total amount of capital at the time of placement.

The research methodology involves a step-by-step consideration of the problem: from a special case with a limited set of tools to a generalized formulation. The mathematical formalization of the problem is implemented within the framework of the linear programming apparatus, which makes it possible to formulate and solve the corresponding optimization problem of maximizing the final well-being.

Introductory example

Let's look at a demo example with an initial capital of 1,000 rubles and an investment horizon of 5 years. The model includes four bonds with different characteristics: bond 1 is purchased in the first year with a maturity of 4 years and a yield of 2%; bond 2 is available in the fifth year with a maturity of 1 year and a yield of 4%; bonds 3 and 4 are purchased in the second year with maturities of 4 and 3 years, respectively, at a yield of 6%.

To model the non-invested funds, five one-year zero-yield bonds were introduced, which forms an equivalent model of nine investment instruments.

Add the necessary libraries and visualization function:

In [ ]:
import Pkg 
Pkg.add(["JuMP", "HiGHS"])
In [ ]:
using Plots, Random, JuMP, HiGHS
include("plotInvest.jl")
Out[0]:
plotInvest (generic function with 1 method)

We visualize this task using horizontal rectangles that represent the available purchase periods for each bond. The numbers of the bonds with interest rates are located along the lines. The investment periods in years are shown in the columns.

In [ ]:
# Time period in years
Период = 5
# Number of bonds
КоличествоОблигаций = 4
# The initial amount of money
Сумма_0 = 1000
# Total number of purchase options
ВсегоВозможностейПокупки = КоличествоОблигаций + Период
# Purchase Periods
ГодыПокупки = [1; 2; 3; 4; 5; 1; 5; 2; 2]
# Bond durations
СрокПогашения = [1; 1; 1; 1; 1; 4; 1; 4; 3]
# Bond sale periods
ГодыПогашения = ГодыПокупки .+ СрокПогашения .- 1
# Interest rates as a percentage
Проценты = [0; 0; 0; 0; 0; 2; 4; 6; 6]
# Yield after one year, including interest
ДоходностьЗаГод = 1 .+ Проценты ./ 100
# Task visualization
p1 = plotInvest(КоличествоОблигаций, ГодыПокупки, СрокПогашения, Проценты)
display(p1)
No description has been provided for this image
GKS: could not find font middle.ttf

Variable solutions

Let's represent the solution variables by the vector x, where — the amount in rubles invested in bond k, for k = 1, ...,9. Upon repayment, the payment for the investment equal to:

In [ ]:
# Creating an optimization model
модель = Model(HiGHS.Optimizer)
# Variable solutions - investment amounts
@variable(модель, x[1:ВсегоВозможностейПокупки] >= 0)
# Total return
ОбщаяДоходность = ДоходностьЗаГод .^ СрокПогашения
Out[0]:
9-element Vector{Float64}:
 1.0
 1.0
 1.0
 1.0
 1.0
 1.08243216
 1.04
 1.2624769600000003
 1.1910160000000003

Target function

The purpose of optimization is to maximize the total capital generated by reinvesting income from redeemable bonds. As the graphical visualization demonstrates, the cash flows received in the interim periods are reinvested and contribute to the formation of the final well-being.

Let's create a target function and a task to maximize.

In [ ]:
# Creating an optimization task (maximizing)
@objective(модель, Max, x[5] * ОбщаяДоходность[5] + x[7] * ОбщаяДоходность[7] + x[8] * ОбщаяДоходность[8])
Out[0]:
$ x_{5} + 1.04 x_{7} + 1.2624769600000003 x_{8} $

Linear constraints

The model assumes an annual allocation of available capital between investment instruments. At the initial stage, seed capital is placed, and in subsequent periods, cash flows from redeemable bonds are reinvested.

Let's make a system of equations:

In [ ]:
# Restrictions on investments
@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])
Out[0]:
$$ -x_{4} + x_{5} - 1.08243216 x_{6} + x_{7} - 1.1910160000000003 x_{9} = 0 $$

Decision

We will solve this problem without restrictions on the amount that can be invested in one bond.

In [ ]:
optimize!(модель)
решение = value.(x)
значение_целевой_функции = objective_value(модель)
Running HiGHS 1.12.0 (git hash: 755a8e027a): Copyright (c) 2025 HiGHS under MIT licence terms
LP has 5 rows; 9 cols; 15 nonzeros
Coefficient ranges:
  Matrix  [1e+00, 1e+00]
  Cost    [1e+00, 1e+00]
  Bound   [0e+00, 0e+00]
  RHS     [1e+03, 1e+03]
Presolving model
2 rows, 6 cols, 9 nonzeros  0s
1 rows, 4 cols, 4 nonzeros  0s
0 rows, 0 cols, 0 nonzeros  0s
Presolve reductions: rows 0(-5); columns 0(-9); nonzeros 0(-15) - Reduced to empty
Performed postsolve
Solving the original LP from the solution after postsolve

Model status        : Optimal
Objective value     :  1.2624769600e+03
P-D objective error :  0.0000000000e+00
HiGHS run time      :          0.04
Out[0]:
1262.4769600000004

Visualization of the solution

Let's display the value of the return on investment.

In [ ]:
println("После $Период лет, доход с начальных $(Сумма_0) ₽ составит $(round(значение_целевой_функции, digits=2)) ₽")
После 5 лет, доход с начальных 1000 ₽ составит 1262.48 ₽

Visualize the solution:

In [ ]:
p2 = plotInvest(КоличествоОблигаций, ГодыПокупки, СрокПогашения, Проценты, решение)
display(p2)
No description has been provided for this image

Optimal investments with limitations

To ensure portfolio diversification, the model introduces a limit on the maximum share of investments in a single asset relative to the total capital of the current period, including proceeds from redeemable securities.

In [ ]:
# Creating a model for a problem with constraints
модель_с_ограничениями = Model(HiGHS.Optimizer)
# Decision variables for a constrained model
@variable(модель_с_ограничениями, x2[1:ВсегоВозможностейПокупки] >= 0)
# Target function
@objective(модель_с_ограничениями, Max, x2[5] * ОбщаяДоходность[5] + x2[7] * ОбщаяДоходность[7] + x2[8] * ОбщаяДоходность[8])
# Basic investment restrictions
@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])
# The maximum interest rate for investments in any bond
МаксПроцент = 0.6
Out[0]:
0.6

Let's create a system of inequalities:

In [ ]:
# Restrictions on maximum investments
@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]))
Out[0]:
$$ -0.6 x2_{1} - 0.612 x2_{6} + x2_{9} \leq 0 $$

Solving the problem with limited diversification (maximum 60% of capital per asset) demonstrates a decrease in final profitability compared to unlimited optimization. Visualization of the resulting portfolio shows the redistribution of investment flows.

In [ ]:
# Solving a problem with constraints
optimize!(модель_с_ограничениями)
решение2 = value.(x2)
значение_целевой_функции2 = objective_value(модель_с_ограничениями)
Running HiGHS 1.12.0 (git hash: 755a8e027a): Copyright (c) 2025 HiGHS under MIT licence terms
LP has 14 rows; 9 cols; 46 nonzeros
Coefficient ranges:
  Matrix  [6e-01, 1e+00]
  Cost    [1e+00, 1e+00]
  Bound   [0e+00, 0e+00]
  RHS     [6e+02, 1e+03]
Presolving model
7 rows, 6 cols, 25 nonzeros  0s
Dependent equations search running on 2 equations with time limit of 1000.00s
Dependent equations search removed 0 rows and 0 nonzeros in 0.00s (limit = 1000.00s)
4 rows, 6 cols, 19 nonzeros  0s
Presolve reductions: rows 4(-10); columns 6(-3); nonzeros 19(-27) 
Solving the presolved LP
Using EKK dual simplex solver - serial
  Iteration        Objective     Infeasibilities num(sum)
          0    -3.3024705065e+00 Ph1: 4(3.57078); Du: 3(3.30247) 0s
          3    -1.2077779546e+03 Pr: 0(0) 0s

Performed postsolve
Solving the original LP from the solution after postsolve

Model status        : Optimal
Simplex   iterations: 3
Objective value     :  1.2077779546e+03
P-D objective error :  9.4089971014e-17
HiGHS run time      :          0.00
Out[0]:
1207.7779545600001

Let's display the income value taking into account the restrictions:

In [ ]:
println("После $Период лет, доход с начальных $(Сумма_0) ₽ составит $(round(значение_целевой_функции2, digits=2)) ₽")
После 5 лет, доход с начальных 1000 ₽ составит 1207.78 ₽

Visualize the solution:

In [ ]:
# Visualization of a solution with constraints
p3 = plotInvest(КоличествоОблигаций, ГодыПокупки, СрокПогашения, Проценты, решение2)
display(p3)
No description has been provided for this image

A custom-sized model

Let's move on to a generalized formulation of the problem, scaling the model to a 30-year investment horizon with a portfolio of 400 bonds with random returns in the range of 1-6%. This configuration forms a linear programming problem with 430 variables, demonstrating the applicability of the method to real investment problems.

In [ ]:
Random.seed!(123)
# The initial amount of money
Сумма_0_большая = 1000
# Time period in years
Период_большой = 30
# Number of bonds
КоличествоОблигаций_большое = 400
# Total number of purchase options
ВсегоВозможностейПокупки_большое = КоличествоОблигаций_большое + Период_большой
# Generating random repayment durations
СрокПогашения_большой = rand(1:(Период_большой-1), ВсегоВозможностейПокупки_большое)
# The bonds have a maturity period of 1 year
СрокПогашения_большой[1:Период_большой] .= 1
# We generate random annual interest rates for each bond
Проценты_большие = rand(1:6, ВсегоВозможностейПокупки_большое)
# The bonds have an interest rate of 0 (not invested)
Проценты_большие[1:Период_большой] .= 0
# Yield after one year, including interest
ДоходностьЗаГод_большая = 1 .+ Проценты_большие ./ 100
# Calculating the yield at the end of the maturity period for each bond
ОбщаяДоходность_большая = ДоходностьЗаГод_большая .^ СрокПогашения_большой
# Create random purchase years for each option
ГодыПокупки_большие = zeros(Int, ВсегоВозможностейПокупки_большое)
# The bonds are available for purchase every year
ГодыПокупки_большие[1:Период_большой] = 1:Период_большой
for i in 1:КоличествоОблигаций_большое
    # Generating a random year to repay the bond before the end of the T-year period
    ГодыПокупки_большие[i+Период_большой] = rand(1:(Период_большой - СрокПогашения_большой[i+Период_большой] + 1))
end
# Calculating the periods when each bond reaches maturity at the end of the year
ГодыПогашения_большие = ГодыПокупки_большие .+ СрокПогашения_большой .- 1
Out[0]:
430-element Vector{Int64}:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
  ⋮
 21
 24
 12
 21
 30
 21
 23
  6
 27
 22
 26
  4

Let's form a temporary model of investment operations, where the matrices индексы_покупки_большие and индексы_продажи_большие The acceptable periods for opening and closing positions on each financial instrument are set.

In [ ]:
# The matrix of purchase indices
индексы_покупки_большие = falses(ВсегоВозможностейПокупки_большое, Период_большой)
for ii in 1:Период_большой
    индексы_покупки_большие[:, ii] = ГодыПокупки_большие .== ii
end

# The matrix of sales indices
индексы_продажи_большие = falses(ВсегоВозможностейПокупки_большое, Период_большой)
for ii in 1:Период_большой
    индексы_продажи_большие[:, ii] = ГодыПогашения_большие .== ii
end

We set up optimization variables corresponding to bonds.

In [ ]:
модель_большая = Model(HiGHS.Optimizer)
# Variable solutions
@variable(модель_большая, x_большой[1:ВсегоВозможностейПокупки_большое] >= 0)
Out[0]:
430-element Vector{VariableRef}:
 x_большой[1]
 x_большой[2]
 x_большой[3]
 x_большой[4]
 x_большой[5]
 x_большой[6]
 x_большой[7]
 x_большой[8]
 x_большой[9]
 x_большой[10]
 x_большой[11]
 x_большой[12]
 x_большой[13]
 ⋮
 x_большой[419]
 x_большой[420]
 x_большой[421]
 x_большой[422]
 x_большой[423]
 x_большой[424]
 x_большой[425]
 x_большой[426]
 x_большой[427]
 x_большой[428]
 x_большой[429]
 x_большой[430]

Let's create an optimization objective function.

In [ ]:
# The task of maximizing
@objective(модель_большая, Max, sum(x_большой[индексы_продажи_большие[:, Период_большой]] .* ОбщаяДоходность_большая[индексы_продажи_большие[:, Период_большой]]))
# Limitations
@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

Let's solve the problem:

In [ ]:
@time optimize!(модель_большая)
решение_большое = value.(x_большой)
значение_целевой_функции_большое = objective_value(модель_большая)
Running HiGHS 1.12.0 (git hash: 755a8e027a): Copyright (c) 2025 HiGHS under MIT licence terms
LP has 30 rows; 430 cols; 823 nonzeros
Coefficient ranges:
  Matrix  [1e+00, 5e+00]
  Cost    [1e+00, 5e+00]
  Bound   [0e+00, 0e+00]
  RHS     [1e+03, 1e+03]
Presolving model
29 rows, 381 cols, 727 nonzeros  0s
24 rows, 304 cols, 545 nonzeros  0s
10 rows, 64 cols, 109 nonzeros  0s
8 rows, 47 cols, 77 nonzeros  0s
5 rows, 22 cols, 36 nonzeros  0s
4 rows, 18 cols, 28 nonzeros  0s
0 rows, 0 cols, 0 nonzeros  0s
Presolve reductions: rows 0(-30); columns 0(-430); nonzeros 0(-823) - Reduced to empty
Performed postsolve
Solving the original LP from the solution after postsolve

Model status        : Optimal
Objective value     :  5.7434911729e+03
P-D objective error :  0.0000000000e+00
HiGHS run time      :          0.00
  0.010847 seconds (3.72 k allocations: 326.055 KiB)
Out[0]:
5743.491172913259

How well did the investment work?

In [ ]:
println("После $Период_большой лет, доход с начальных $(Сумма_0_большая) ₽ составит $(round(значение_целевой_функции_большое, digits=2)) ₽")
После 30 лет, доход с начальных 1000 ₽ составит 5743.49 ₽

A solution with limited shares

Let's create an optimization problem with constraints:

In [ ]:
# Creating a model for a problem with constraints
модель_большая_с_ограничениями = Model(HiGHS.Optimizer)

@variable(модель_большая_с_ограничениями, x_большой_с[1:ВсегоВозможностейПокупки_большое] >= 0)
@objective(модель_большая_с_ограничениями, Max, 
    sum(x_большой_с[i] * ОбщаяДоходность_большая[i] for i in 1:ВсегоВозможностейПокупки_большое if индексы_продажи_большие[i, Период_большой]))

# Constraints for a model with constraints
@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

The scaled model uses a diversification standard that limits the share of a single bond in the portfolio to 0.4.

In [ ]:
МаксПроцент_большой = 0.4
Out[0]:
0.4

Formalization of diversification constraints requires the construction of two data structures: a matrix of bond activity and a matrix of their current value, based on which the upper bound of the share of a single asset in the portfolio is set.

In [ ]:
активные_большие = falses(ВсегоВозможностейПокупки_большое, Период_большой)
for ii in 1:Период_большой
    активные_большие[:, ii] = (ii .>= ГодыПокупки_большие) .& (ii .<= ГодыПогашения_большие)
end

We will set limits on maximum investments.:

In [ ]:
# Restrictions on maximum investments
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

Problem solving:

In [ ]:
@time optimize!(модель_большая_с_ограничениями)
решение_большое_с_ограничениями = value.(x_большой_с)
значение_целевой_функции_большое_с_ограничениями = objective_value(модель_большая_с_ограничениями)
println("\nПосле $Период_большой лет, доход с начальных $(Сумма_0_большая) ₽ составит $(round(значение_целевой_функции_большое_с_ограничениями, digits=2)) ₽")
Running HiGHS 1.12.0 (git hash: 755a8e027a): Copyright (c) 2025 HiGHS under MIT licence terms
LP has 460 rows; 430 cols; 72421 nonzeros
Coefficient ranges:
  Matrix  [4e-01, 5e+00]
  Cost    [1e+00, 5e+00]
  Bound   [0e+00, 0e+00]
  RHS     [4e+02, 1e+03]
Presolving model
419 rows, 430 cols, 72380 nonzeros  0s
Dependent equations search running on 28 equations with time limit of 1000.00s
Dependent equations search removed 0 rows and 0 nonzeros in 0.00s (limit = 1000.00s)
417 rows, 422 cols, 70114 nonzeros  0s
Presolve reductions: rows 417(-43); columns 422(-8); nonzeros 70114(-2307) 
Solving the presolved LP
Using EKK dual simplex solver - serial
  Iteration        Objective     Infeasibilities num(sum)
          0    -2.3043600253e+02 Ph1: 380(10201.3); Du: 102(230.436) 0s
        117    -5.7109808455e+03 Pr: 0(0) 0s

Performed postsolve
Solving the original LP from the solution after postsolve

Model status        : Optimal
Simplex   iterations: 117
Objective value     :  5.7109808455e+03
P-D objective error :  0.0000000000e+00
HiGHS run time      :          0.09
  0.105855 seconds (10.74 k allocations: 4.740 MiB)

После 30 лет, доход с начальных 1000 ₽ составит 5710.98 ₽

Qualitative analysis of the results

To assess the effectiveness of an optimal portfolio, it is advisable to compare its profitability with the theoretical maximum — the investment of all capital in a bond with a maximum rate of 6% for the entire 30-year period. An additional metric of the analysis can be the calculation of the equivalent annual rate corresponding to the achieved level of final well-being.

In [ ]:
# Maximum possible amount
максимальная_сумма = Сумма_0_большая * (1 + 6/100)^Период_большой

# Ratio (as a percentage)
отношение = значение_целевой_функции_большое_с_ограничениями / максимальная_сумма * 100

# Equivalent interest rate
эквивалентная_ставка = ((значение_целевой_функции_большое_с_ограничениями / Сумма_0_большая)^(1/Период_большой) - 1) * 100

println("\nПолученная сумма составит $(round(отношение, digits=2))% от максимальной суммы $(round(максимальная_сумма, digits=2)) ₽,")
println("which could have been obtained by investing in one bond.")
println("\nВаш доход соответствует $(round(эквивалентная_ставка, digits=2))% годовой ставке за $(Период_большой)-летний период.")

# Visualize the results
p4 = plotInvest(КоличествоОблигаций_большое, ГодыПокупки_большие, СрокПогашения_большой, Проценты_большие, решение_большое_с_ограничениями, false)
display(p4)
Полученная сумма составит 99.43% от максимальной суммы 5743.49 ₽,
которая могла быть получена при инвестировании в одну облигацию.

Ваш доход соответствует 5.98% годовой ставке за 30-летний период.
No description has been provided for this image

The graph shows the time structure of the investment portfolio with restrictions:

  • Horizontal green lines - selected bonds

  • The beginning of the line is the year of purchase, the end is the year of repayment

  • Numbers in curly brackets {i} are bond identifiers

  • Top-down location — investment priority

The graph shows a diversified portfolio, where investments are distributed over different years and periods in accordance with a limit of no more than 60% per asset.

Conclusion

The conducted research demonstrates the effectiveness of linear programming methods for optimizing long-term investment portfolios. The developed model makes it possible to determine a capital allocation strategy that maximizes final well-being over a given time horizon.

Experimental calculations confirm the existence of a compromise between diversification and profitability: the introduction of a 60% limit on the share of one asset reduces the final yield by 4.3% compared with unlimited optimization. The scalability of the approach is verified on a task with a 30-year horizon and a portfolio of 400 bonds.

The directions for future research are to take into account the uncertainty of profitability and create algorithms for dynamic portfolio optimization in changing market conditions.