construct the data

asset.names = c("MSFT", "NORD", "SBUX")
er = c(0.0427, 0.0015, 0.0285)
names(er) = asset.names
covmat = matrix(c(0.0100, 0.0018, 0.0011,
                  0.0018, 0.0109, 0.0026,
                  0.0011, 0.0026, 0.0199),
                nrow=3, ncol=3)
r.free = 0.005
dimnames(covmat) = list(asset.names, asset.names)

define function globalMin.portfolio

globalMin.portfolio <-
  function(er, cov.mat, shorts=TRUE)
  {
    call <- match.call()
    
    #
    # check for valid inputs
    #
    asset.names <- names(er)
    er <- as.vector(er) # assign names if none exist
    cov.mat <- as.matrix(cov.mat)
    N <- length(er)
    if(N != nrow(cov.mat))
      stop("invalid inputs")
    if(any(diag(chol(cov.mat)) <= 0))
      stop("Covariance matrix not positive definite")
    # remark: could use generalized inverse if cov.mat is positive semi-definite
    
    #
    # compute global minimum portfolio
    #
    if(shorts==TRUE){
      cov.mat.inv <- solve(cov.mat)
      one.vec <- rep(1,N)
      w.gmin <- rowSums(cov.mat.inv) / sum(cov.mat.inv)
      w.gmin <- as.vector(w.gmin)
    } else if(shorts==FALSE){
      Dmat <- 2*cov.mat
      dvec <- rep.int(0, N)
      Amat <- cbind(rep(1,N), diag(1,N))
      bvec <- c(1, rep(0,N))
      result <- quadprog::solve.QP(Dmat=Dmat,dvec=dvec,Amat=Amat,bvec=bvec,meq=1)
      w.gmin <- round(result$solution, 6)
    } else {
      stop("shorts needs to be logical. For no-shorts, shorts=FALSE.")
    }
    
    names(w.gmin) <- asset.names
    er.gmin <- crossprod(w.gmin,er)
    sd.gmin <- sqrt(t(w.gmin) %*% cov.mat %*% w.gmin)
    gmin.port <- list("call" = call,
                      "er" = as.vector(er.gmin),
                      "sd" = as.vector(sd.gmin),
                      "weights" = w.gmin)
    class(gmin.port) <- "portfolio"
    gmin.port
  }

compute global minimum variance portfolio

gmin.port = globalMin.portfolio(er, covmat)
attributes(gmin.port)
## $names
## [1] "call"    "er"      "sd"      "weights"
## 
## $class
## [1] "portfolio"
print(gmin.port)
## $call
## globalMin.portfolio(er = er, cov.mat = covmat)
## 
## $er
## [1] 0.02489184
## 
## $sd
## [1] 0.07267607
## 
## $weights
##      MSFT      NORD      SBUX 
## 0.4411093 0.3656263 0.1932644 
## 
## attr(,"class")
## [1] "portfolio"
summary(gmin.port, risk.free=r.free)
##         Length Class  Mode   
## call    3      -none- call   
## er      1      -none- numeric
## sd      1      -none- numeric
## weights 3      -none- numeric
plot(gmin.port$weights, col="blue")

compute global minimum variance portfolio with no short sales

gmin.port.ns = globalMin.portfolio(er, covmat, shorts=FALSE)
attributes(gmin.port.ns)
## $names
## [1] "call"    "er"      "sd"      "weights"
## 
## $class
## [1] "portfolio"
print(gmin.port.ns)
## $call
## globalMin.portfolio(er = er, cov.mat = covmat, shorts = FALSE)
## 
## $er
## [1] 0.02489182
## 
## $sd
## [1] 0.072676
## 
## $weights
##     MSFT     NORD     SBUX 
## 0.441109 0.365626 0.193264 
## 
## attr(,"class")
## [1] "portfolio"
summary(gmin.port.ns, risk.free=r.free)
##         Length Class  Mode   
## call    4      -none- call   
## er      1      -none- numeric
## sd      1      -none- numeric
## weights 3      -none- numeric
plot(gmin.port.ns$weights, col="blue")