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} 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