exponenta event banner

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

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

Постановка проблемы

Предположим, что у вас есть начальная сумма денег Capital_0 инвестировать в течение периода времени T года в N облигации с нулевым купоном. Каждая облигация выплачивает фиксированную процентную ставку, объединяет инвестиции каждый год и выплачивает основную сумму плюс совокупные проценты в конце срока погашения. Цель заключается в максимизации общей суммы денег после T годы.

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

В этом примере сначала показана настройка проблемы в небольшом регистре, а затем формулируется общий регистр.

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

Вводный пример

Начните с небольшого примера:

  • Стартовая сумма для инвестирования Capital_0 составляет 1000 долларов.

  • Период времени T составляет 5 лет.

  • Количество облигаций N равно 4.

  • Для моделирования неинвестированных денег, иметь один вариант B0 доступны каждый год со сроком погашения 1 год и процентной ставкой 0%.

  • Облигация 1, обозначаемая B1, может быть приобретена в 1 году, имеет срок погашения 4 года и процентную ставку 2%.

  • Облигация 2, обозначаемая B2, может быть приобретена в 5 году, имеет срок погашения 1 год и процентную ставку 4%.

  • Облигация 3, обозначаемая B3, может быть приобретена во 2-м году, имеет срок погашения 4 года и процентную ставку 6%.

  • Облигация 4, обозначаемая B4, может быть приобретена во 2-м году, имеет срок погашения 3 года и процентную ставку 6%.

Разделив первый опционный B0 на 5 облигаций со сроком погашения 1 год и процентной ставкой 0%, эта проблема может быть эквивалентно смоделирована как имеющая в общей сложности 9 доступных облигаций, таких что для k=1..9

  • Вход k вектора PurchaseYears представляет начало года, когда эта облигация k доступен для покупки.

  • Вход k вектора Maturity представляет собой срок погашения mk облигации k.

  • Вход k вектора MaturityYears представляет собой конец года, когда эта облигация k доступен для продажи.

  • Вход k вектора InterestRates представляет собой процентную процентную ставку, k.

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

% Time period in years
T = 5;
% Number of bonds
N = 4;
% Initial amount of money
Capital_0 = 1000;
% Total number of buying oportunities
nPtotal = N+T;
% Purchase times
PurchaseYears = [1;2;3;4;5;1;5;2;2];
% Bond durations
Maturity = [1;1;1;1;1;4;1;4;3];
% Bond sale times
MaturityYears = PurchaseYears + Maturity - 1;
% Interest rates in percent
InterestRates = [0;0;0;0;0;2;4;6;6];
% Return after one year of interest
rt = 1 + InterestRates/100;

plotInvestments(N,PurchaseYears,Maturity,InterestRates)

Переменные принятия решений

Представление переменных принятия решений вектором x, где x(k) - долларовая сумма инвестиций в облигации k, для k = 1,...,9. По истечении срока выплаты за инвестиции x(k) является

x (k) (1 + αk/100) мк.

Определить βk = 1 + αk/100 и определить rk как общий возврат облигацииk:

rk = (1 + αk/100) mk = βkmk.

x = optimvar('x',nPtotal,'LowerBound',0);
% Total returns
r = rt.^Maturity;

Целевая функция

Цель - выбрать инвестиции, чтобы максимально увеличить объем собранных денег в конце года T. Из сюжета вы видите, что инвестиции собираются в различные промежуточные годы и реинвестируются. В конце года T, деньги, возвращенные из инвестиций 5, 7 и 8, могут быть собраны и представляют ваше конечное богатство:

maxxx5r5+x7r7+x8r8

Создайте задачу оптимизации для максимизации и включите целевую функцию.

interestprob = optimproblem('ObjectiveSense','maximize');
interestprob.Objective = x(5)*r(5) + x(7)*r(7) + x(8)*r(8);

Линейные ограничения: Инвестировать не больше, чем у вас

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

x1+x6=Capital0

Затем в течение следующих лет вы собираете доходы от погашения облигаций и реинвестируете их в новые доступные облигации для получения системы уравнений:

x2+x8+x9=r1x1x3=r2x2x4=r3x3x5+x7=r4x4+r6x6+r9x9

investconstr = optimconstr(T,1);
investconstr(1) = x(1) + x(6) == Capital_0;
investconstr(2) = x(2) + x(8) + x(9) == r(1)*x(1);
investconstr(3) = x(3) == r(2)*x(2);
investconstr(4) = x(4) == r(3)*x(3);
investconstr(5) = x(5) + x(7) == r(4)*x(4) + r(6)*x(6) + r(9)*x(9);
interestprob.Constraints.investconstr = investconstr;

Связанные зависимости: без заимствования

Поскольку каждая вложенная сумма должна быть положительной, каждая запись в векторе решения x должна быть положительной. Включите это ограничение, задав нижнюю границу для вектора решения x. Явная верхняя граница для вектора решения отсутствует.

x.LowerBound = 0;

Решить проблему

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

options = optimoptions('linprog','Algorithm','interior-point');
[sol,fval,exitflag] = solve(interestprob,'options',options)
Solving problem using linprog.

Solution found during presolve.
sol = struct with fields:
    x: [9x1 double]

fval = 1.2625e+03
exitflag = 
    OptimalSolution

Визуализация решения

Флаг выхода указывает, что решатель нашел оптимальное решение. Стоимость fval, возвращаемый как второй выходной аргумент, соответствует конечному богатству. Посмотрите на окончательную сумму инвестиций и распределение инвестиций с течением времени.

fprintf('After %d years, the return for the initial $%g is $%g \n',...
    T,Capital_0,fval);
After 5 years, the return for the initial $1000 is $1262.48 
plotInvestments(N,PurchaseYears,Maturity,InterestRates,sol.x)

Оптимальные инвестиции с ограниченным капиталом

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

x1≤Pmax×Capital0x2≤Pmax× (β1x1 +β6x6) x3≤Pmax× (β2x2 +β62x6 +β8x8 +β9x9) x4≤Pmax× (β3x3 +β63x6 +β82x8 +β92x9) x5≤Pmax× (β4x4 +β64x4 +β83x8 +β93x9) x6≤Pmax×Capital0x7≤Pmax× (β4x4 +β64x4 +β83x8 +β93x9) x8≤Pmax× (β1x1 +β6x6) x9≤Pmax× (β1x1 +β6x6)

% Maximum percentage to invest in any bond
Pmax = 0.6;

constrlimit = optimconstr(nPtotal,1);
constrlimit(1) = x(1) <= Pmax*Capital_0;
constrlimit(2) = x(2) <= Pmax*(rt(1)*x(1) + rt(6)*x(6));
constrlimit(3) = x(3) <= Pmax*(rt(2)*x(2) + rt(6)^2*x(6) + rt(8)*x(8) + rt(9)*x(9));
constrlimit(4) = x(4) <= Pmax*(rt(3)*x(3) + rt(6)^3*x(6) + rt(8)^2*x(8) + rt(9)^2*x(9));
constrlimit(5) = x(5) <= Pmax*(rt(4)*x(4) + rt(6)^4*x(6) + rt(8)^3*x(8) + rt(9)^3*x(9));
constrlimit(6) = x(6) <= Pmax*Capital_0;
constrlimit(7) = x(7) <= Pmax*(rt(4)*x(4) + rt(6)^4*x(6) + rt(8)^3*x(8) + rt(9)^3*x(9));
constrlimit(8) = x(8) <= Pmax*(rt(1)*x(1) + rt(6)*x(6));
constrlimit(9) = x(9) <= Pmax*(rt(1)*x(1) + rt(6)*x(6));

interestprob.Constraints.constrlimit = constrlimit;

Решить проблему, вложив не более 60% в какой-либо один актив. Постройте график полученных покупок. Обратите внимание, что ваше окончательное богатство меньше, чем инвестиции без этого ограничения.

[sol,fval] = solve(interestprob,'options',options);
Solving problem using linprog.

Minimum found that satisfies the constraints.

Optimization completed because the objective function is non-decreasing in
feasible directions, to within the selected value of the function tolerance,
and constraints are satisfied to within the selected value of the constraint
tolerance.
fprintf('After %d years, the return for the initial $%g is $%g \n',...
    T,Capital_0,fval);
After 5 years, the return for the initial $1000 is $1207.78 
plotInvestments(N,PurchaseYears,Maturity,InterestRates,sol.x)

Модель произвольного размера

Создайте модель для общей версии проблемы. Проиллюстрировать его с помощью T = 30 лет и 400 случайно сформированных облигаций с процентными ставками от 1 до 6%. Эта настройка приводит к проблеме линейного программирования с 430 переменными принятия решений.

% For reproducibility
rng default 
% Initial amount of money
Capital_0 = 1000;
% Time period in years
T = 30;
% Number of bonds
N = 400;
% Total number of buying oportunities
nPtotal = N + T;
% Generate random maturity durations
Maturity = randi([1 T-1],nPtotal,1);
% Bond 1 has a maturity period of 1 year
Maturity(1:T) = 1;
% Generate random yearly interest rate for each bond
InterestRates = randi(6,nPtotal,1);
% Bond 1 has an interest rate of 0 (not invested)
InterestRates(1:T) = 0;
% Return after one year of interest
rt = 1 + InterestRates/100;
% Compute the return at the end of the maturity period for each bond:
r = rt.^Maturity;

% Generate random purchase years for each option
PurchaseYears = zeros(nPtotal,1);
% Bond 1 is available for purchase every year
PurchaseYears(1:T)=1:T;
for i=1:N
    % Generate a random year for the bond to mature before the end of
    % the T year period
    PurchaseYears(i+T) = randi([1 T-Maturity(i+T)+1]);
end

% Compute the years where each bond reaches maturity at the end of the year
MaturityYears = PurchaseYears + Maturity - 1;

Вычислите время, когда облигации могут быть куплены или проданы. buyindex матрица содержит потенциальное время покупки, и sellindex матрица содержит потенциальное время продажи для каждой облигации.

buyindex = false(nPtotal,T); % allocate nPtotal-by-T matrix
for ii = 1:T
    buyindex(:,ii) = PurchaseYears == ii;
end
sellindex = false(nPtotal,T);
for ii = 1:T
    sellindex(:,ii) = MaturityYears == ii;
end

Настройте переменные оптимизации, соответствующие облигациям.

x = optimvar('x',nPtotal,1,'LowerBound',0);

Создайте задачу оптимизации и целевую функцию.

interestprob = optimproblem('ObjectiveSense','maximize');
interestprob.Objective = sum(x(sellindex(:,T)).*r(sellindex(:,T)));

Для удобства создайте временный массив xBuy, столбцы которого представляют облигации, которые мы можем купить в каждый период времени.

xBuy = repmat(x,1,T).*double(buyindex);

Аналогично, создайте временный массив xSell, столбцы которого представляют облигации, которые мы можем продать в каждый период времени.

xSell = repmat(x,1,T).*double(sellindex);

Возврат, созданный для продажи этих границ, равен

xReturnFromSell = xSell.*repmat(r,1,T);

Создайте ограничение, заключающееся в том, что сумма, вложенная в каждый период времени, является суммой, проданной в предыдущий период времени.

interestprob.Constraints.InitialInvest = sum(xBuy(:,1)) == Capital_0;
interestprob.Constraints.InvestConstraint = sum(xBuy(:,2:T),1) == sum(xReturnFromSell(:,1:T-1),1);

Решение без ограничения удержания

Решите проблему.

tic
[sol,fval,exitflag] = solve(interestprob,'options',options);
Solving problem using linprog.

Minimum found that satisfies the constraints.

Optimization completed because the objective function is non-decreasing in
feasible directions, to within the selected value of the function tolerance,
and constraints are satisfied to within the selected value of the constraint
tolerance.
toc
Elapsed time is 0.205049 seconds.

Насколько хорошо поступили инвестиции?

fprintf('After %d years, the return for the initial $%g is $%g \n',...
    T,Capital_0,fval);
After 30 years, the return for the initial $1000 is $5167.58 

Решение с ограниченным объемом активов

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

Pmax = 0.4;

Создание active матрица, соответствующая временам, когда связь может быть удержана, и cactive матрица, которая содержит совокупную продолжительность каждой активной связи. Итак, стоимость облигации j в момент времени t является x(j)*(rt^cactive).

active = double(buyindex | sellindex);
for ii = 1:T
    active(:,ii) = double((ii >= PurchaseYears) & (ii <= MaturityYears));
end
cactive = cumsum(active,2);
cactive = cactive.*active;

Создайте матрицу, запись (j, p) которой представляет значение облигации j в период времени p:

bondValue = repmat(x, 1, T).*active.*(rt.^(cactive));

Определите общую стоимость инвестиций в каждом временном интервале, чтобы можно было наложить ограничение на ограниченные запасы. mvalue - это деньги, вложенные во все облигации в конце каждого периода времени, nPtotalоколо-T matrix.moneyavailable - сумма по облигациям денег, вложенных в начале периода времени, означающая стоимость портфеля в каждый момент времени.

constrlimit = optimconstr(nPtotal,T);
constrlimit(:,1) = xBuy(:,1) <= Pmax*Capital_0;
constrlimit(:,2:T) = xBuy(:,2:T)  <= repmat(Pmax*sum(bondValue(:,1:T-1),1), nPtotal, 1).*double(buyindex(:,2:T));
interestprob.Constraints.constrlimit = constrlimit;

Решить проблему с ограниченными холдингами.

tic
[sol,fval,exitflag] = solve(interestprob,'options',options);
Solving problem using linprog.

Minimum found that satisfies the constraints.

Optimization completed because the objective function is non-decreasing in
feasible directions, to within the selected value of the function tolerance,
and constraints are satisfied to within the selected value of the constraint
tolerance.
toc
Elapsed time is 1.315237 seconds.
fprintf('After %d years, the return for the initial $%g is $%g \n',...
    T,Capital_0,fval);
After 30 years, the return for the initial $1000 is $5095.26 

Чтобы ускорить работу решателя, попробуйте использовать алгоритм двойного симплекса.

options = optimoptions('linprog','Algorithm','dual-simplex');
tic
[sol,fval,exitflag] = solve(interestprob,'options',options);
Solving problem using linprog.

Optimal solution found.
toc
Elapsed time is 0.416850 seconds.
fprintf('After %d years, the return for the initial $%g is $%g \n',...
    T,Capital_0,fval);
After 30 years, the return for the initial $1000 is $5095.26 

В этом случае алгоритму двойного симплекса потребовалось меньше времени для получения того же решения.

Качественный анализ результатов

Чтобы почувствовать решение, сравните его с суммой fmax что вы получите, если сможете вложить все свои стартовые деньги в одну облигацию с процентной ставкой 6% (максимальная процентная ставка) в течение всего 30-летнего периода. Можно также вычислить эквивалентную процентную ставку, соответствующую конечному благосостоянию.

% Maximum amount
fmax = Capital_0*(1+6/100)^T;
% Ratio (in percent)
rat = fval/fmax*100;
% Equivalent interest rate (in percent)
rsol = ((fval/Capital_0)^(1/T)-1)*100;

fprintf(['The amount collected is %g%% of the maximum amount $%g '...
    'that you would obtain from investing in one bond.\n'...
    'Your final wealth corresponds to a %g%% interest rate over the %d year '...
    'period.\n'], rat, fmax, rsol, T)
The amount collected is 88.7137% of the maximum amount $5743.49 that you would obtain from investing in one bond.
Your final wealth corresponds to a 5.57771% interest rate over the 30 year period.
plotInvestments(N,PurchaseYears,Maturity,InterestRates,sol.x,false)

Связанные темы