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

В этом примере показано, как использовать основанный на проблеме подход для решения инвестиционной задачи с детерминированными возвратами в течение фиксированного количества лет 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 облигационных 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)mk.

Определить β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 от общего капитала в том году (включая возвраты по облигациям, которые в настоящее время находятся в периоде погашения). Вы получаете следующую систему неравенств:

x1Pmax×Capital0x2Pmax×(β1x1+β6x6)x3Pmax×(β2x2+β62x6+β8x8+β9x9)x4Pmax×(β3x3+β63x6+β82x8+β92x9)x5Pmax×(β4x4+β64x4+β83x8+β93x9)x6Pmax×Capital0x7Pmax×(β4x4+β64x4+β83x8+β93x9)x8Pmax×(β1x1+β6x6)x9Pmax×(β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;

Рассчитать время, когда облигации могут быть куплены или проданы. The 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-by- 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 

В этом случае алгоритму dual-simplex потребовалось меньше времени, чтобы получить одно и то же решение.

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

Чтобы почувствовать решение, сравните его с количеством 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)

Похожие темы