In [1]:
using Dates, LinearAlgebra
include("jlFiles/printmat.jl")
include("jlFiles/printTable.jl")
Out[1]:
In [2]:
using Plots
#pyplot(size=(600,400)) #pyplot() or gr()
gr(size=(480,320))
default(fmt = :svg)
MV analysis starts with providing the vector of expected returns $\mu$ and the covariance matrix $\Sigma$ of the investable assets.
Then, it plots the "mean variance" frontier: it is a scatter plot showing the lowest possible portfolio standard deviation, Std$(R_p)$, on the horizontal axis at a required average return, $\text{E}R_p=\mu^*$, on the vertical axis. Clearly, we consider many different $\mu^*$ values to create the scatter.
Remember: to calculate the expected return and the variance of a portfolio with portfolio weights in the vector $w$, use
$\text{E}R_p = w'\mu$ and
$\text{Var}(R_p) = w'\Sigma w$.
Also, the sum of the portfolio weights should be one.
In [3]:
μ = [11.5; 6]/100 #means
Σ = [166 58; #covariance matrix
58 100]/100^2
printblue("expected returns, %:")
printmat(μ*100)
printblue("covariance matrix, bp:")
printmat(Σ*100^2)
In [4]:
w₁ = 1.5:-0.05:-0.5 #different possible weights on asset 1
n = length(w₁)
(ERp,StdRp) = (fill(NaN,n),fill(NaN,n))
for i = 1:n
#local w #only needed in REPL/script
w = [w₁[i];1-w₁[i]]
ERp[i] = w'μ
StdRp[i] = sqrt(w'Σ*w)
end
In [5]:
plot( StdRp*100,ERp*100,
legend = nothing,
linecolor = :red,
xlim = (0,15),
ylim = (0,15),
title = "Mean vs standard deviation",
xlabel = "Std(Rp), %",
ylabel = "ERp, %" )
scatter!(sqrt.(diag(Σ))*100,μ*100,markercolor=:red)
Out[5]:
With 3 or more assets we have to solve the optimization problem
$\min \text{Var}(R_p) \: \text{ s.t. } \: \text{E}R_p = \mu^*$,
and clearly also that the sum of portfolio weights is one. We do not (at this point) impose any further restrictions.
This can be done with a numerical minimization routine or by linear algebra (at least when we do not put any further restrictions on the portfolio weights). The next cells use the linear algebra approach.
In [6]:
μ = [11.5; 9.5; 6]/100 #expected returns
Σ = [166 34 58; #covariance matrix
34 64 4;
58 4 100]/100^2
Rf = 0.03
printblue("μ and Rf in %:")
printmat(μ*100)
printmat(Rf*100)
printblue("Σ in bp:")
printmat(Σ*100^2)
In [7]:
w = [0 0.22 0.02 0.25; #different portfolios (one in each column)
1 0.30 0.63 0.68;
0 0.48 0.35 0.07]
μσ = fill(NaN,2,size(w,2))
for i = 1:size(w,2)
μσ[:,i] = [w[:,i]'μ,sqrt(w[:,i]'Σ*w[:,i])]*100
end
printlnPs("mean and std (in %) of portfolio ")
printTable(μσ,["A","1","2","3"],["mean","std"])
All portfolios on the MV frontier of risky assets only have (a vector of) portfolio weights as in
$w = \Sigma^{-1}(\mu \lambda + \mathbf{1} \delta)$,
where the scalars $\lambda$ and $\delta$ depend on $(\mu,\Sigma,\mu^*)$ (see below for the details) and where $\mathbf{1}$ is a vector of ones.
Some of the intermediate calculations are as follows. (Later on we put it all in a function)
In [8]:
μstar = 0.1 #required average return
n = length(μ)
Σ_1 = inv(Σ)
printblue("The steps of calculating a MV portfolio at μstar=$μstar:")
println("inv(Σ):")
printmat(Σ_1)
A = μ'Σ_1*μ
B = μ'Σ_1*ones(n)
C = ones(n)'Σ_1*ones(n)
printlnPs("A, B, C: ",[A B C ])
λ = (C*μstar - B)/(A*C-B^2)
δ = (A-B*μstar)/(A*C-B^2)
printlnPs("λ and δ: ",[λ δ])
w = Σ_1 *(μ*λ + ones(n)*δ) #in Julia, we could do Σ_1 *(μ*λ .+ δ)
println("w: ")
printmat(w)
In [9]:
"""
Calculate the std and weights of a portfolio (with mean return μstar) on MVF of risky assets
"""
function MVCalc(μstar,μ,Σ)
n = length(μ)
Σ_1 = inv(Σ)
A = μ'Σ_1*μ
B = μ'Σ_1*ones(n)
C = ones(n)'Σ_1*ones(n)
λ = (C*μstar - B)/(A*C-B^2)
δ = (A-B*μstar)/(A*C-B^2)
w = Σ_1 *(μ*λ.+δ)
StdRp = sqrt(w'Σ*w)
return StdRp,w
end
Out[9]:
In [10]:
(StdAt10,wAt10) = MVCalc(0.1,μ,Σ)
printlnPs("Testing: std and w of the portfolio with a mean return of 10%: ",StdAt10)
printmat(wAt10)
In [11]:
μstar = range(Rf,stop=0.15,length=101)
L = length(μstar)
StdRp = fill(NaN,L) #loop over different required average returns, (μstar)
for i = 1:L
StdRp[i] = MVCalc(μstar[i],μ,Σ)[1] #[1] picks out only the first function output
end
plot( StdRp*100,μstar*100,
legend = nothing,
linecolor = :red,
xlim = (0,15),
ylim = (0,15),
title = "Mean vs standard deviation",
xlabel = "Std(Rp), %",
ylabel = "ERp, %" )
scatter!(sqrt.(diag(Σ))*100,μ*100,markercolor=:red)
Out[11]:
All portfolios on the MV frontier of both risky and riskfree have (a vector of) portfolio weights on the risky assets as in
$w=\frac{\mu^{\ast}-R_{f}}{(\mu^{e})^{\prime}\Sigma^{-1}\mu^{e}}\Sigma^{-1} \mu^{e}$,
where $\mu^*$ is the required average return.
The weight of the riskfree asset is $1-\mathbf{1}'w$
In [12]:
"""
Calculate the std of a portfolio (with mean μstar) on MVF of (Risky,Riskfree)
"""
function MVCalcRf(μstar,μ,Σ,Rf)
μᵉ = μ .- Rf #expected excess returns
Σ_1 = inv(Σ)
w = (μstar-Rf)/(μᵉ'Σ_1*μᵉ) * Σ_1*μᵉ
StdRp = sqrt(w'Σ*w)
return StdRp,w
end
Out[12]:
In [13]:
(StdAt10Rf,wAt10Rf) = MVCalcRf(0.1,μ,Σ,Rf)
printlnPs("Testing: std and w of the portfolio with a mean return of 10%: ",StdAt10Rf)
printmat(wAt10Rf)
In [14]:
StdRpRf = fill(NaN,L) #loop over required average returns (μstar)
for i = 1:L
StdRpRf[i] = MVCalcRf(μstar[i],μ,Σ,Rf)[1]
end
plot( [StdRp StdRpRf]*100,μstar*100,
legend = nothing,
linecolor = [:red :blue],
xlim = (0,15),
ylim = (0,15),
title = "Mean vs standard deviation",
xlabel = "Std(Rp), %",
ylabel = "ERp, %" )
scatter!(sqrt.(diag(Σ))*100,μ*100,markercolor=:red)
Out[14]:
The tangency portfolio is a particular portfolio on the MV frontier of risky and riskfree, where the weights on the risky assets sum to one. It is therefore also on the MV frontier of risky assets only. The vector of portfolio weights is
$w_{T}=\frac{\Sigma^{-1}\mu^{e}}{\mathbf{1}^{\prime}\Sigma^{-1}\mu^{e}}$
In [15]:
function MVTangencyP(μ,Σ,Rf) #calculates the tangency portfolio
n = length(μ)
μᵉ = μ .- Rf #expected excess returns
Σ_1 = inv(Σ)
w = Σ_1 *μᵉ/(ones(n)'Σ_1*μᵉ)
muT = w'μ + (1-sum(w))*Rf
StdT = sqrt(w'Σ*w)
return w,muT,StdT
end
Out[15]:
In [16]:
(wT,μT,σT) = MVTangencyP(μ,Σ,Rf)
println("Tangency portfolio: ")
printmat(wT)
printlnPs("mean and std of tangency portfolio, %: ",[μT σT]*100)
By mixing the tangency portfolio and the riskfree, we can create any point on the MV frontier of risky and riskfree (also called the Capital Market Line, CML).
The code below shows the expected return and standard deviation of several portfolio (different $v$ values) of the form
$R_p = v R_T + (1-v)R_f$ where $R_T=w_T'R$
In [17]:
v = [0;0.44;1;1.41] #try different mixes of wT and Rf
(ERLev,StdLev) = (similar(v),similar(v)) #to store results in
for i = 1:length(v) #loop over different v values (mix of wT and Rf)
ERLev[i] = v[i]'μT + (1-v[i])*Rf #portfolio with v[i] in wT and 1-v[i] in Rf
StdLev[i] = abs(v[i])*σT
end
plot( [StdRp StdRpRf]*100,μstar*100,
legend= nothing,
linecolor = [:red :blue],
xlim = (0,15),
ylim = (0,15),
title = "Mean vs standard deviation",
xlabel = "Std(Rp), %",
ylabel = "ERp, %" )
scatter!(StdLev*100,ERLev*100)
Out[17]:
In [18]:
μb = [9; 6]/100 #means
Σb = [ 256 0;
0 144]/100^2
Rfb = 1/100
wT, = MVTangencyP(μb,Σb,Rfb)
printmat(wT)
wT, = MVTangencyP([13; 6]/100,Σb,Rfb)
printmat(wT)
Σb = [ 1 -0.8;
-0.8 1]
wT, = MVTangencyP(μb,Σb,Rfb)
printmat(wT)
Σb = [ 1 0.8;
0.8 1]
wT, = MVTangencyP(μb,Σb,Rfb)
printmat(wT)
In [19]:
"""
Calculate the global minimum variance portfolio
"""
function MVMinimumVarP(μ,Σ,Rf)
n = length(μ)
μᵉ = μ .- Rf
Σ_1 = inv(Σ)
w = Σ_1*ones(n)/(ones(n)'Σ_1*ones(n))
mu = w'μ + (1-sum(w))*Rf
Std = sqrt(w'Σ*w)
return w,mu,Std
end
Out[19]:
In [20]:
(wT,μT,σT) = MVTangencyP(μ,Σ,Rf)
(wMvp,muMvp,StdMvp) = MVMinimumVarP(μ,Σ,Rf)
println("Tangency and minimum variance portfolios: ")
printmat([wT wMvp])
In [21]:
v = range(-2.5,stop=2.5,length=101) #try different mixes of wT and wMvp
(ERLev,StdLev) = (similar(v),similar(v))
for i = 1:length(v)
#local w #only needed in REPL/script
w = v[i]*wT + (1-v[i])*wMvp #portfolio with v[i] in wT and 1-v[i] in wMvp
ERLev[i] = w'μ
StdLev[i] = sqrt(w'Σ*w)
end
plot( [StdRp StdRpRf]*100,μstar*100,
legend = nothing,
linecolor = [:red :blue],
xlim = (0,15),
ylim = (0,15),
title = "Mean vs standard deviation",
xlabel = "Std(Rp), %",
ylabel = "ERp, %",
annotation = (0.5,14,text("(scatter points are new)",8,:left)) )
scatter!(StdLev*100,ERLev*100,markercolor=:magenta)
Out[21]:
In [22]:
function MVCalcX(μstar,μ,Σ) #the std of a portfolio on MVF of risky assets
n = length(μ)
A = [Σ μ ones(n);
μ' 0 0;
ones(n)' 0 0];
wλδ = A\[zeros(n);μstar;1]
w = wλδ[1:n]
StdRp = sqrt(w'Σ*w)
return StdRp,w
end
function MVCalcRfX(μstar,μ,Σ,Rf) #calculates the std of a portfolio
n = length(μ) #on MVF of (Risky,Riskfree)
μᵉ = μ .- Rf
A = [Σ μᵉ;
μᵉ' 0]
wλ = A\[zeros(n);(μstar-Rf)]
w = wλ[1:n]
StdRp = sqrt(w'Σ*w)
return StdRp,w
end
Out[22]:
In [23]:
wAt10 = MVCalc(0.1,μ,Σ)[2]
wAt10X = MVCalcX(0.1,μ,Σ)[2]
printlnPs("w of the portfolio (risky only) with a mean return of 10%: ")
printmat([wAt10 wAt10X])
wAt10Rf = MVCalcRf(0.1,μ,Σ,Rf)[2]
wAt10RfX = MVCalcRfX(0.1,μ,Σ,Rf)[2]
printlnPs("w of the portfolio with a mean return of 10%: ")
printmat([wAt10Rf wAt10RfX])
In [ ]: