Originally Contributed by: Arpit Bhatia

Optimization models play an increasingly important role in financial decisions. Many computational finance problems can be solved efficiently using modern optimization techniques.

This tutorial solves the famous Markowitz Portfolio Optimization problem with data from lecture notes from a course taught at Georgia Tech by Shabir Ahmed.

This tutorial uses the following packages

using JuMP
import Ipopt
import Statistics

Suppose we are considering investing 1000 dollars in three non-dividend paying stocks, IBM (IBM), Walmart (WMT), and Southern Electric (SEHI), for a one-month period.

We will use the initial money to buy shares of the three stocks at the current market prices, hold these for one month, and sell the shares off at the prevailing market prices at the end of the month.

As a rational investor, we hope to make some profit out of this endeavor, i.e., the return on our investment should be positive.

Suppose we bought a stock at $p$ dollars per share in the beginning of the month, and sold it off at $s$ dollars per share at the end of the month. Then the one-month return on a share of the stock is $\frac{s-p}{p}$.

Since the stock prices are quite uncertain, so is the end-of-month return on our investment. Our goal is to invest in such a way that the expected end-of-month return is at least $50 or 5%. Furthermore, we want to make sure that the “risk” of not achieving our desired return is minimum. Note that we are solving the problem under the following assumptions: 1. We can trade any continuum of shares. 2. No short-selling is allowed. 3. There are no transaction costs. We model this problem by taking decision variables$x_{i}, i=1,2,3,$denoting the dollars invested in each of the 3 stocks. Let us denote by$\tilde{r}_{i}$the random variable corresponding to the monthly return (increase in the stock price) per dollar for stock$i$. Then, the return (or profit) on$x_{i}$dollars invested in stock$i$is$\tilde{r}_{i} x_{i},$and the total (random) return on our investment is$\sum_{i=1}^{3} \tilde{r}_{i} x_{i}.$The expected return on our investment is then$\mathbb{E}\left[\sum_{i=1}^{3} \tilde{r}_{i} x_{i}\right]=\sum_{i=1}^{3} \overline{r}_{i} x_{i},$where$\overline{r}_{i}$is the expected value of the$\tilde{r}_{i}.$Now we need to quantify the notion of “risk” in our investment. Markowitz, in his Nobel prize winning work, showed that a rational investor’s notion of minimizing risk can be closely approximated by minimizing the variance of the return of the investment portfolio. This variance is given by: $$$\operatorname{Var}\left[\sum_{i=1}^{3} \tilde{r}_{i} x_{i}\right] = \sum_{i=1}^{3} \sum_{j=1}^{3} x_{i} x_{j} \sigma_{i j}$$$ where$\sigma_{i j}$is the covariance of the return of stock$i$with stock$j$. Note that the right hand side of the equation is the most reduced form of the expression and we have not shown the intermediate steps involved in getting to this form. We can also write this equation as: $$$\operatorname{Var}\left[\sum_{i=1}^{3} \tilde{r}_{i} x_{i}\right] =x^{T} Q x$$$ Where$Q$is the covariance matrix for the random vector$\tilde{r}\$.

Finally, we can write the model as:

\begin{aligned} \min x^{T} Q x \\ \text { s.t. } \sum_{i=1}^{3} x_{i} \leq 1000.00 \\ \overline{r}^{T} x \geq 50.00 \\ x \geq 0 \end{aligned}

After that long discussion, let's now use JuMP to solve the portfolio optimization problem for the data given below.

MonthIBMWMTSEHI
November-0093.04351.8261.063
December-0084.58552.8230.938
January-01111.45356.4771.000
February-0199.52549.8050.938
March-0195.81950.2871.438
April-01114.70851.5211.700
May-01111.51551.5312.540
June-01113.21148.6642.390
July-01104.94255.7443.120
August-0199.82747.9162.980
September-0191.60749.4381.900
October-01107.93751.3361.750
November-01115.59055.0811.800
stock_data = [
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
]
13×3 Matrix{Float64}:
93.043  51.826  1.063
84.585  52.823  0.938
111.453  56.477  1.0
99.525  49.805  0.938
95.819  50.287  1.438
114.708  51.521  1.7
111.515  51.531  2.54
113.211  48.664  2.39
104.942  55.744  3.12
99.827  47.916  2.98
91.607  49.438  1.9
107.937  51.336  1.75
115.59   55.081  1.8

Calculating stock returns

stock_returns = Array{Float64}(undef, 12, 3)
for i in 1:12
stock_returns[i, :] =
(stock_data[i+1, :] .- stock_data[i, :]) ./ stock_data[i, :]
end
stock_returns
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

Calculating the expected value of monthly return:

r = Statistics.mean(stock_returns; dims = 1)
1×3 Matrix{Float64}:
0.0260022  0.00810132  0.0737159

Calculating the covariance matrix Q

Q = Statistics.cov(stock_returns)
3×3 Matrix{Float64}:
0.018641    0.00359853  0.00130976
0.00359853  0.00643694  0.00488727
0.00130976  0.00488727  0.0686828

JuMP Model

portfolio = Model(Ipopt.Optimizer)
set_silent(portfolio)
@variable(portfolio, x[1:3] >= 0)
@objective(portfolio, Min, x' * Q * x)
@constraint(portfolio, sum(x) <= 1000)
@constraint(portfolio, sum(r[i] * x[i] for i in 1:3) >= 50)
optimize!(portfolio)

objective_value(portfolio)
22634.417849884143
value.(x)
3-element Vector{Float64}:
497.0455298498641
-9.670578501817138e-9
502.95448015948085

Tip

This tutorial was generated using Literate.jl. View the source .jl file on GitHub.