Portfolio Optimization

Optimal Portfolio Allocation

Define a portfolio \(p\) as the weighted average over a vector consisting of \(k\) random assets. We will call this vector \(X\) \[X = (x_1,x_2,\dots,x_k) \] This vector follows a multivariate normal distribution that depends on expected individual asset returns \(\mu\) and variance \(\Sigma\). \[X\sim N_k(\mu,\Sigma) \] The linear combination of these random variables across a vector of weights \(\omega\) is our portfolio: \[p = \omega^T X \] Portfolio weights are determined by the share of total wealth allocated to each asset. I will limit the weight \(\omega_i\) of each asset in the portfolio to fall between -0.5 and 0.5, since we expect to reduce the effects of extreme distributions. A negative allocation is equivalent to short-selling that asset.

Thus the expected return from a portfolio is just the weighted average of the expected returns of each of the \(k\) assets, \[\mu_p = E[\omega^T X] = w^T \mu \] We will define risk as the variance of these returns. Since the movements of individual prices, dividends and returns within the stock market are correlated, a portfolio can either magnify or mitigate the risks of holding two assets. This can be captured in the covariance matrix \[\mathbf{\Sigma} = E[(X - E[X])^T(X - E[X])] \] The portfolio variance \(\sigma_p^2\) is a scalar that depends on the asset allocation weights and on the covariance matrix: \[\sigma_p^2 = \omega^T \Sigma \omega \] The investor holds the portfolio for a limited time. The optimization problem has two parts. First, the investor selects weights to minimize risk while exceeding a minimum level of returns \(\mu^*\). That is, \[ \begin{align} \min \quad &\sigma_p^2 \\ s.t \quad &\omega^T\mu \geq \mu^*\\ & \omega^T\mathbf{1} = 1 \end{align} \] Above, 𝟏 is a vector of 1’s with the same dimension as \(\omega\). The equation tells us that the weights must always sum to 1. At the same time, the investor wishes to maximize returns while not exceeding the maximum acceptable level of risk \(\sigma^{2*}\): \[ \begin{align} \max \quad &\mu_p \\ s.t \quad &\omega^T\Sigma\omega \leq \sigma^{2*}\\ & \omega^T\mathbf{1} = 1 \end{align} \] Let us write down the Lagrangigan function for minimum risk problem. \[\mathcal{L}(\omega;\alpha,\beta) = \omega^T\Sigma\omega + \alpha(\omega^T\mu-\mu^*) + \beta(\omega^T\mathbf{1}-1) \] In below code, we selected some log-return of 6 stocks from US financial market.

Data <- na.omit(
  read.csv('data.csv'))[,2:7]
Data.Return <- matrix(colMeans(Data, na.rm=TRUE))
Data.Sigma <- matrix(var(Data), nrow = 6)
Data.ALLReturn <- sum(Data.Return)
summary(Data)
##    ReturnAAPL           ReturnAMZN          ReturnGOOGL        
##  Min.   :-0.1235494   Min.   :-0.1099725   Min.   :-0.0837750  
##  1st Qu.:-0.0066726   1st Qu.:-0.0075797   1st Qu.:-0.0057941  
##  Median : 0.0004347   Median : 0.0009351   Median : 0.0006613  
##  Mean   : 0.0008499   Mean   : 0.0015286   Mean   : 0.0008065  
##  3rd Qu.: 0.0091601   3rd Qu.: 0.0109668   3rd Qu.: 0.0079925  
##  Max.   : 0.0887413   Max.   : 0.1574570   Max.   : 0.1625843  
##     ReturnPG            ReturnIBM            ReturnPFE         
##  Min.   :-0.0583778   Min.   :-8.279e-02   Min.   :-0.0529910  
##  1st Qu.:-0.0042545   1st Qu.:-5.660e-03   1st Qu.:-0.0050942  
##  Median : 0.0001166   Median : 1.077e-04   Median : 0.0000000  
##  Mean   : 0.0002517   Mean   : 5.195e-05   Mean   : 0.0004963  
##  3rd Qu.: 0.0048381   3rd Qu.: 6.036e-03   3rd Qu.: 0.0056283  
##  Max.   : 0.0404066   Max.   : 8.864e-02   Max.   : 0.0706667

We can use Linear algebra to easily solve above optimization problem.

$$ \left[\begin{matrix} 2\Sigma &\mu &\mathbf{1}\ \mu^T &0 &0\ \mathbf{1} &0 &0 \end{matrix}\right] \left[ \begin{matrix} \omega^*\ \alpha\ \beta \end{matrix} \right]

\left[ \begin{matrix} 0\ \mu^*\ 1 \end{matrix} \right] $$

One <- matrix(c(rep(1,6)))
mu_A <- rbind(Data.Return,0,0)
One_A <- rbind(One,0,0)
A <- rbind(cbind(2*Data.Sigma,Data.Return,One),t(mu_A),t(One_A))
b <- matrix(c(rep(0,6),Data.ALLReturn,1))
w <- solve(A) %*% b
print(w[1:6])
## [1]  0.9628248  1.8291883  0.3325440 -0.8750244 -2.1217234  0.8721908

Considering the each asset in the portfolio would fall between -0.5 and 0.5.

library(magrittr)
library(quadprog)
library(ggplot2)
## Warning: 程辑包'ggplot2'是用R版本4.1.3 来建造的
min.allocation <- -0.5
max.allocation <- 0.5
# Get the number of stocks
n <- ncol(Data.Sigma)

# Matrices to solve with quadprog, with max and min constraints
Amat <- cbind(1, diag(n), -diag(n))
bvec <- c(1, rep(min.allocation, n), rep(-max.allocation, n))
meq <- 1
# Set up the iterator
iters <- seq(from=0, to=0.5, by=0.0005) %>% setNames(nm = .)
dvec <- lapply(iters, function(i) Data.Return * i)

# Get the solutions
sols <- lapply(dvec, solve.QP, Dmat = Data.Sigma, Amat = Amat, bvec = bvec, meq = meq)

# Summarize and return the results
######################################
# Generate a list of summary functions
funs <- list("Std.Dev" = function(x) sqrt(x %*%  Data.Sigma %*% x),
             "Exp.Return" = function(x) x %*% Data.Return,
             "sharpe" = function(x) x %*% Data.Return / 
                 sqrt(x %*% Data.Sigma %*% x))

# Apply to the solutions list
summ <- lapply(funs, function(f) lapply(sols, 
                function(s) f(s$solution))) %>% 
    lapply(as.numeric) %>%
    do.call(what = "cbind")

# Combine into a results data frame
results <- lapply(sols, function(s) s$solution) %>% 
    do.call(what = "rbind") %>% 
    data.frame(summ)
print(subset(results, sharpe == max(sharpe)))
##              X1  X2        X3       X4         X5        X6    Std.Dev
## 0.139 0.3058853 0.5 0.1206132 0.105117 -0.4430847 0.4114692 0.01359214
##        Exp.Return     sharpe
## 0.139 0.001329192 0.09779127
# Plot port and efficient Frontier
eff.plot <- function(eff, eff.optimal.point) {
  p <- ggplot(eff, aes(x=Std.Dev, y=Exp.Return)) + 
      geom_line(col="Red") +
      geom_point(data=eff.optimal.point, aes(x=Std.Dev, y=Exp.Return, label=sharpe),
               size=5) +
      annotate(geom="text", x=eff.optimal.point$Std.Dev,
             y=eff.optimal.point$Exp.Return,
             label=paste("Risk: ",
                         round(eff.optimal.point$Std.Dev*100, digits=3),"\nReturn: ",
                         round(eff.optimal.point$Exp.Return*100, digits=4),"%\nSharpe: ",
                         round(eff.optimal.point$sharpe*100, digits=2), "%", sep=""),
             hjust=0, vjust=1.2) +
    ggtitle("Efficient Frontier and Optimal Portfolio") +
    labs(x = "Risk (standard deviation of portfolio)", y = "Return")
    return(p)
}
eff <- results
eff.optimal.point <- subset(eff, sharpe == max(sharpe))
eff.plot(eff,eff.optimal.point)
## Warning: Ignoring unknown aesthetics: label