{ "cells": [ { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Some Typical Financial Calculations\n", "\n", "This notebook (a) estimates CAPM equations and autocorrelations; (b) implements a simple trading strategy; (c) calculates Value at Risk using a simple model for time-varying volatility; (d) calculates the Black-Scholes option price and implied volatility; (e) calculates and draws the mean-variance frontier (w/w.o short selling restrictions)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Load Packages and Extra Functions\n", "\n", "The [Roots](https://github.com/JuliaMath/Roots.jl) package solves non-linear equations and the [StatsBase](https://github.com/JuliaStats/StatsBase.jl) package has methods for estimating autocorrelations etc." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "printyellow (generic function with 1 method)" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using Printf, Dates, DelimitedFiles, LinearAlgebra, Roots, Distributions, StatsBase\n", "\n", "include(\"jlFiles/printmat.jl\")" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "using Plots\n", "\n", "#pyplot(size=(600,400)) #use pyplot or gr\n", "gr(size=(480,320))\n", "default(fmt = :svg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Load Data" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1979-01-01 10.190 4.180\n", "1979-02-01 -2.820 -3.410\n", "1979-03-01 10.900 5.750\n", "1979-04-01 2.470 0.050\n", "\n" ] } ], "source": [ "x = readdlm(\"Data/MyData.csv\",',',skipstart=1) #monthly return data\n", "ym = round.(Int,x[:,1]) #yearmonth, like 200712\n", "Rme = x[:,2] #market excess return\n", "Rf = x[:,3] #interest rate\n", "R = x[:,4] #return small growth stocks\n", "Re = R - Rf #excess returns\n", "T = size(Rme,1)\n", "\n", "dN = Date.(string.(ym),\"yyyymm\") #convert to string and then Julia Date\n", "printmat([dN[1:4] Re[1:4] Rme[1:4]])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# CAPM\n", "\n", "The CAPM regression is\n", "\n", "$R_{it}^{e} =\\alpha_{i}+\\beta_{i}R_{mt}^{e}+\\varepsilon_{it}$,\n", "\n", "where $R_{it}^{e}$ is the excess return of asset $i$ and $R_{mt}^{e}$ is the market excess return. Theory says that $\\alpha=0$, which is easily tested." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " coeff std t-stat\n", "α -0.504 0.304 -1.656\n", "β 1.341 0.066 20.427\n", "\n", " R2: 0.519\n", "no. of observations: 388 \n" ] } ], "source": [ "x = [ones(T) Rme] #regressors\n", "y = copy(Re) #to get standard OLS notation\n", "b = x\\y #OLS\n", "u = y - x*b #residuals\n", "covb = inv(x'x)*var(u) #cov(b), see any textbook\n", "stdb = sqrt.(diag(covb)) #std(b)\n", "R2 = 1 - var(u)/var(y)\n", "\n", "printmat([b stdb b./stdb],colNames=[\"coeff\",\"std\",\"t-stat\"],rowNames=[\"α\",\"β\"])\n", "printlnPs(\"R2: \",R2)\n", "printlnPs(\"no. of observations: \",T)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Return Autocorrelation\n", "\n", "That is, the correlation of $R_{t}^{e}$ and $R_{t-s}^{e}$. \n", "\n", "It can be shown that the t-stat of an autocorrelation is $\\sqrt{T}$ times the autocorrelation." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Autocorrelations (different lags) of the excess returns in Re\n", "lag autocorr t-stat\n", "1 0.216 4.253\n", "2 0.002 0.046\n", "3 -0.018 -0.359\n", "4 -0.065 -1.289\n", "5 -0.027 -0.536\n", "\n" ] } ], "source": [ "plags = 1:5\n", "xCorr = autocor(Re,plags) #using the StatsBase package\n", "\n", "println(\"Autocorrelations (different lags) of the excess returns in Re\")\n", "printmat([xCorr sqrt(T)*xCorr],colNames=[\"autocorr\",\"t-stat\"],rowNames=string.(plags),cell00=\"lag\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# A Trading Strategy\n", "\n", "The next cell implements a very simple momentum trading strategy. \n", "\n", "1. If $R_{t-1}^{e}\\ge0$, then we hold the market index and shorten the riskfree from $t-1$ to $t$. This means that we will earn $R_{t}^{e}$.\n", "\n", "2. Instead, if $R_{t-1}^{e}<0$, then we do the opposite. This means that we will earn $-R_{t}^{e}$. \n", "\n", "This simple strategy could be coded without using a loop, but \"vectorization\" does not speed up much. " ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The annualized mean excess return of the strategy and a passive portfolio are: 26.733 3.329\n", "The annualized Sharpe ratios are: 0.932 0.112\n" ] } ], "source": [ "(w,Rp) = (fill(NaN,T),fill(NaN,T))\n", "for t = 2:T\n", " w[t] = (Re[t-1] < 0)*(-1) + (Re[t-1] >= 0)*1 #w is -1 or 1\n", " Rp[t] = w[t]*Re[t] \n", "end\n", "\n", "μ = [mean(Rp[2:end]) mean(Re[2:end])]\n", "σ = [std(Rp[2:end]) std(Re[2:end])]\n", "\n", "printlnPs(\"The annualized mean excess return of the strategy and a passive portfolio are: \",μ*12)\n", "printlnPs(\"The annualized Sharpe ratios are: \",sqrt(12)*μ./σ)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Value at Risk\n", "\n", "The next cell constructs an simple estimate of $\\sigma_t^2$ as a backward looking moving average (the RiskMetrics approach):\n", "\n", "$\\sigma_t^2 = \\lambda \\sigma_{t-1}^2 + (1-\\lambda) (R_{t-1} -\\mu)^2$,\n", "where $\\mu$ is the average return (for all data).\n", "\n", "Then, we calculate the 95% VaR by assuming a $N(\\mu,\\sigma_t^2)$ distribution:\n", "\n", "$\\textrm{VaR}_{t} = - (\\mu-1.64\\sigma_t)$.\n", "\n", "If the model is correct, then $-R_t > \\text{VaR}_{t}$ should only happen 5% of the times." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "μ = mean(Rme)\n", "\n", "λ = 0.95 #weight on old volatility\n", "σ² = fill(var(Rme),T) #RiskMetrics approach to estimate variance\n", "for t = 2:T\n", " σ²[t] = λ*σ²[t-1] + (1-λ)*(Rme[t-1]-μ)^2\n", "end\n", "\n", "VaR95 = -(μ .- 1.64*sqrt.(σ²)); #VaR at 95% level" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "xTicksLoc = [Date(1980);Date(1990);Date(2000);Date(2010)]\n", "xTicksLab = Dates.format.(xTicksLoc,\"Y\")\n", "\n", "p1 = plot( dN,VaR95,\n", " color = :blue,\n", " legend = false,\n", " xticks = (xTicksLoc,xTicksLab),\n", " ylim = (0,11),\n", " title = \"1-month Value at Risk (95%)\",\n", " ylabel = \"%\",\n", " annotation = (Date(1982),1,text(\"(for US equity market)\",8,:left)) )\n", "display(p1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Options" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Black-Scholes Option Price\n", "\n", "Let $S$ be the the current spot price of an asset and $y$ be the interest rate.\n", "\n", "The Black-Scholes formula for a European call option with strike price $K$ and time to expiration $m$ is\n", "\n", "$C =S\\Phi(d_{1}) -e^{-ym}K\\Phi(d_{2})$, where\n", "\n", "$d_{1} =\\frac{\\ln(S/K)+(y+\\sigma^{2}/2)m}{\\sigma\\sqrt{m}} \\ \\text{ and } \\ d_{2}=d_{1}-\\sigma\\sqrt{m}$ \n", "\n", "and where $\\Phi(d)$ denotes the probability of $x\\leq d$ when $x$ has an $N(0,1)$ distribution. All variables except the volatility ($\\sigma$) are directly observable. " ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "OptionBlackSPs" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\"\"\n", "Pr(z<=x) for N(0,1)\n", "\"\"\"\n", "Φ(x) = cdf(Normal(0,1),x) #a one-line function\n", "\n", "\"\"\"\n", "Calculate Black-Scholes european call option price\n", "\"\"\"\n", "function OptionBlackSPs(S,K,m,y,σ)\n", "\n", " d1 = ( log(S/K) + (y+1/2*σ^2)*m ) / (σ*sqrt(m))\n", " d2 = d1 - σ*sqrt(m)\n", " c = S*Φ(d1) - exp(-y*m)*K*Φ(d2)\n", " return c\n", "end" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " \n", "call price according to Black-Scholes: 1.358\n" ] } ], "source": [ "σ = 0.4\n", "c1 = OptionBlackSPs(10,10,0.5,0.1,σ)\n", "printlnPs(\"\\n\",\"call price according to Black-Scholes: \",c1)\n", "\n", "K = range(7,stop=13,length=51)\n", "c = OptionBlackSPs.(10,K,0.5,0.1,σ);" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p1 = plot( K,c,\n", " color = :red,\n", " legend = false,\n", " title = \"Black-Scholes call option price\",\n", " xlabel = \"strike price\",\n", " ylabel = \"option price\" )\n", "display(p1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Implied Volatility\n", "\n", "is the $\\sigma$ value that makes the Black-Scholes equation give the same option price as observed on the market. It is often interpreted as the \"market uncertainty.\"\n", "\n", "The next cell uses the call option price calculated above as the market price. The implied volatility should then equal the volatility used above (this is a way to check your coding).\n", "\n", "The next few cells instead use some data on options on German government bonds. " ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Implied volatility: 0.400, compare with: 0.4\n" ] } ], "source": [ " #solve for implied vol\n", "iv = find_zero(σ->OptionBlackSPs(10,10,0.5,0.1,σ)-c1,(0.00001,5))\n", "\n", "printlnPs(\"Implied volatility: \",iv,\", compare with: $σ\")" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "21" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# LIFFE Bunds option data, trade date April 6, 1994\n", "K = [ #strike prices; Mx1 vector\n", " 92.00; 94.00; 94.50; 95.00; 95.50; 96.00; 96.50; 97.00;\n", " 97.50; 98.00; 98.50; 99.00; 99.50; 100.0; 100.5; 101.0;\n", " 101.5; 102.0; 102.5; 103.0; 103.5 ];\n", "C = [ #call prices; Mx1 vector\n", " 5.13; 3.25; 2.83; 2.40; 2.00; 1.64; 1.31; 1.02;\n", " 0.770; 0.570; 0.400; 0.280; 0.190; 0.130; 0.0800; 0.0500;\n", " 0.0400; 0.0300; 0.0200; 0.0100; 0.0100 ];\n", "S = 97.05 #spot price\n", "m = 48/365 #time to expiry in years\n", "y = 0.0 #Interest rate: LIFFE=>no discounting\n", "N = length(K)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Strike and iv for data: \n", " 92.000 0.094\n", " 94.000 0.081\n", " 94.500 0.081\n", " 95.000 0.078\n", " 95.500 0.075\n", " 96.000 0.074\n", " 96.500 0.072\n", " 97.000 0.071\n", " 97.500 0.070\n", " 98.000 0.069\n", " 98.500 0.068\n", " 99.000 0.067\n", " 99.500 0.067\n", " 100.000 0.068\n", " 100.500 0.067\n", " 101.000 0.067\n", " 101.500 0.070\n", " 102.000 0.073\n", " 102.500 0.074\n", " 103.000 0.072\n", " 103.500 0.077\n", "\n" ] } ], "source": [ "iv = fill(NaN,N) #looping over strikes\n", "for i = 1:N\n", " iv[i] = find_zero(sigma->OptionBlackSPs(S,K[i],m,y,sigma)-C[i],(0.00001,5))\n", "end\n", "\n", "println(\"Strike and iv for data: \")\n", "printmat([K iv])" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p1 = plot( K,iv,\n", " color = :red,\n", " legend =false,\n", " title = \"Implied volatility\",\n", " xlabel = \"strike price\",\n", " annotation = (98,0.09,text(\"Bunds options April 6, 1994\",8,:left)) )\n", "display(p1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Mean-Variance Frontier\n", "\n", "Given a vector of average returns ($\\mu$) and a variance-covariance matrix ($\\Sigma$), the mean-variance frontier shows the lowest possible portfolio uncertainty for a given expected portfolio return (denoted $\\mu\\text{star}$ below).\n", "\n", "It is thus the solution to a quadratic minimization problem. The cells below will use the explicit (matrix) formulas for this solution, but we often have to resort to numerical methods when there are portfolio restrictions.\n", "\n", "It is typically plotted with the portfolio standard deviation on the horizontal axis and the portfolio expected return on the vertical axis.\n", "\n", "We calculate and plot two different mean-variance frontiers: (1) when we only consider risky assets; (2) when we also consider a risk-free asset." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "μ: \n", " 0.115\n", " 0.095\n", " 0.060\n", "\n", "Σ: \n", " 0.017 0.003 0.006\n", " 0.003 0.006 0.000\n", " 0.006 0.000 0.010\n", "\n", "Rf: \n", " 0.030\n", "\n" ] } ], "source": [ "μ = [11.5, 9.5, 6]/100 #expected returns\n", "Σ = [166 34 58; #covariance matrix\n", " 34 64 4;\n", " 58 4 100]/100^2\n", "Rf = 0.03 #riskfree return (an interest rate)\n", "\n", "println(\"μ: \")\n", "printmat(μ)\n", "println(\"Σ: \")\n", "printmat(Σ)\n", "println(\"Rf: \")\n", "printmat(Rf)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "MVCalcRf" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\"\"\n", " MVCalc(μstar,μ,Σ)\n", "\n", "Calculate the std and weights of a portfolio (with mean return μstar) on MVF of risky assets.\n", "\n", "# Input\n", "- `μstar::Vector`: K vector, mean returns to calculate results for\n", "- `μ::Vector`: n vector, mean returns\n", "- `Σ::Matrix`: nxn, covariance matrix of returns, can contain riskfree assets\n", "\n", "# Output\n", "- `StdRp::Vector`: K vector, standard deviation of mean-variance portfolio (risky only) with mean μstar\n", "- `w_p::Matrix`: Kxn, portfolio weights of \"\"\n", "\n", "\"\"\"\n", "function MVCalc(μstar,μ,Σ)\n", "\n", " (K,n) = (length(μstar),length(μ))\n", "\n", " Σ_1 = inv(Σ)\n", " a = μ'Σ_1*μ\n", " b = μ'Σ_1*ones(n)\n", " c = ones(n)'Σ_1*ones(n)\n", "\n", " (w_p,StdRp) = (fill(NaN,K,n),fill(NaN,K))\n", " for i = 1:K\n", " λ = (c*μstar[i] - b)/(a*c-b^2)\n", " δ = (a-b*μstar[i])/(a*c-b^2)\n", " w = Σ_1 *(μ*λ.+δ)\n", " StdRp[i] = sqrt(w'Σ*w)\n", " w_p[i,:] = w\n", " end\n", "\n", " return StdRp,w_p\n", "\n", "end\n", "\n", "\n", "\"\"\"\n", "Calculate the std of a portfolio (with mean μstar) on MVF of (Risky,Riskfree)\n", "\"\"\"\n", "function MVCalcRf(μstar,μ,Σ,Rf)\n", "\n", " (K,n) = (length(μstar),length(μ))\n", "\n", " μᵉ = μ .- Rf #expected excess returns\n", " Σ_1 = inv(Σ)\n", "\n", " (w_p,StdRp) = (fill(NaN,K,n),fill(NaN,K))\n", " for i = 1:K\n", " w = (μstar[i]-Rf)/(μᵉ'Σ_1*μᵉ) * Σ_1*μᵉ\n", " StdRp[i] = sqrt(w'Σ*w)\n", " w_p[i,:] = w\n", " end\n", "\n", " return StdRp,w_p\n", "\n", "end" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "μstar = range(Rf,stop=0.15,length=201)\n", "L = length(μstar)\n", "\n", "StdRp = MVCalc(μstar,μ,Σ)[1] #risky assets only, [1] to get the first output\n", "println()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p1 = plot( StdRp*100,μstar*100,\n", " linecolor = :red,\n", " xlim = (0,15),\n", " ylim = (0,15),\n", " label = \"MVF\",\n", " legend = :topleft,\n", " title = \"MVF, only risky assets\",\n", " xlabel = \"Std(Rp), %\",\n", " ylabel = \"ERp, %\" )\n", "scatter!(sqrt.(diag(Σ))*100,μ*100,color=:red,label=\"assets\")\n", "display(p1)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "StdRpRf = MVCalcRf(μstar,μ,Σ,Rf)[1] #with riskfree too\n", "println()" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p1 = plot( [StdRp StdRpRf]*100,[μstar μstar]*100,\n", " legend = nothing,\n", " linestyle = [:solid :dash],\n", " linecolor = [:red :blue],\n", " xlim = (0,15),\n", " ylim = (0,15),\n", " title = \"MVF, risky and riskfree assets\",\n", " xlabel = \"Std(Rp), %\",\n", " ylabel = \"ERp, %\" )\n", "scatter!(sqrt.(diag(Σ))*100,μ*100,color=:red)\n", "display(p1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Mean-Variance Frontier without Short Selling (extra)\n", "\n", "The code below solves (numerically) the following minimization problem \n", "\n", "$\\min \\text{Var}(R_p) \\: \\text{ s.t. } \\: \\text{E}R_p = \\mu^*$,\n", " \n", "and where we also require $w_i\\ge 0$ and $\\sum_{i=1}^{n}w_{i}=1$.\n", "\n", "To solve this, we use the packages [Convex.jl](https://github.com/jump-dev/Convex.jl) (for the interface) and [SCS.jl](https://github.com/jump-dev/SCS.jl) (for the optimization algorithm). To check for convergence, we also need a function from the [MathOptInterface.jl](https://github.com/jump-dev/MathOptInterface.jl) package." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "MathOptInterface" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using Convex, SCS\n", "import MathOptInterface\n", "const MOI = MathOptInterface" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "MeanVarNoSSPs" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\"\"\n", " MeanVarNoSSPs(μ,Σ,μstar)\n", "\n", "Calculate mean variance frontier when short sales are not allowed, using Convex and SCS\n", "\n", "\n", "# Input\n", "- `μ::Vector`: n vector, mean returns\n", "- `Σ::Matrix`: nxn, covariance matrix of returns, can contain riskfree assets\n", "- `μstar::Vector`: K vector, mean returns to calculate results for\n", "\n", "# Output\n", "- `StdRp::Vector`: K vector, standard deviation of mean-variance portfolio (risky only) with mean μstar\n", "- `w_p::Matrix`: Kxn, portfolio weights of \"\"\n", "\n", "# Requires\n", "- Convex and SCS\n", "\n", "\"\"\"\n", "function MeanVarNoSSPs(μ,Σ,μstar) #MV with no short-sales, numerical minimization\n", "\n", " (K,n) = (length(μstar),length(μ))\n", "\n", " n = length(μ)\n", " vv = findall( minimum(μ) .<= μstar .<= maximum(μ) ) #solve only if feasible\n", "\n", " w = Variable(n)\n", " Varp = quadform(w,Σ)\n", " c1 = sum(w) == 1\n", " c3 = 0 <= w #replace 0 and 1 to get other restrictions\n", " c4 = w <= 1\n", "\n", " (w_p,StdRp) = (fill(NaN,K,n),fill(NaN,K))\n", " for i in vv #loop over (feasible) μstar elements\n", " c2 = dot(w,μ) == μstar[i]\n", " problem = minimize(Varp,c1,c2,c3,c4)\n", " Convex.solve!(problem,()->SCS.Optimizer(verbose=false))\n", " if problem.status == MOI.OPTIMAL #check if solution has been found\n", " w_p[i,:] = evaluate(w)\n", " StdRp[i] = sqrt(evaluate(Varp))\n", " end\n", " end\n", "\n", " return StdRp, w_p\n", "\n", "end" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "Std_no_ss = MeanVarNoSSPs(μ,Σ,μstar)[1]\n", "println()" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p1 = plot( [StdRp Std_no_ss]*100,[μstar μstar]*100,\n", " linecolor = [:red :green],\n", " linestyle = [:solid :dash],\n", " linewidth = 2, \n", " label = [\"no constraints\" \"no short sales\"],\n", " xlim = (0,15),\n", " ylim = (0,15),\n", " legend = :topleft,\n", " title = \"MVF (with/without constraints)\",\n", " xlabel = \"Std(Rp), %\",\n", " ylabel = \"ERp, %\" )\n", "scatter!(sqrt.(diag(Σ))*100,μ*100,color=:red,label=\"assets\")\n", "display(p1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "@webio": { "lastCommId": null, "lastKernelId": null }, "anaconda-cloud": {}, "kernelspec": { "display_name": "Julia 1.6.1", "language": "julia", "name": "julia-1.6" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.6.1" } }, "nbformat": 4, "nbformat_minor": 1 }