Problem with calculating tangency portfolio

Hi.
I have been struggling with the implantation of tangency portfolio in R for a while now, and was hoping some of you might know the answer :slight_smile:

This is the way my professor solved the problem, which works fine:

source("performance.r")
source("markowitz.r")
library("quadprog")

#*********************************************
# Importing the data and selecting the time window
#*********************************************

# read data
data = read.table("marketdata.txt", header=TRUE)

# construct the time series object
data <- ts(data[,2:ncol(data)], start=c(1926,1), frequency = 12)
''
# find the time index of portfolio start 
year.start <- 2000
ind.start <- which(time(data) == year.start)

# you can index the columns in a ts-object by names!
r.stocks <- as.numeric(data[,"STOCKS"])
r.bonds <- as.numeric(data[,"BONDS"])
r.tbill <- as.numeric(data[,"TBILLS"])

nobs <- length(r.stocks)

#*********************************************
# Portfolio management part of the program
#*********************************************

lookback.period = 3*12    # length of lookback period in a number of months
n <- nobs - ind.start + 1 # number of monthly portfolio returns 
r.tanportfolio = rep(0,n)
assets <- cbind(r.stocks, r.bonds)

for (i in 1:n) {
  # find the indices of the lookback period
  period.end <- ind.start + i - 2
  period.start <- period.end - lookback.period + 1
  if (period.start < 1) period.start <- 1 # index cannot be less than 1
  
  # estimate the covariance matrix and mean returns
  covmat <- cov(assets[period.start:period.end,])
  er <- apply(assets[period.start:period.end,], 2, mean)
  # get the risk-free rate of return
  rf <- r.tbill[period.end+1]
  Rf = rf

    w.tanportfolio = tanportfolio(er, covmat, Rf , shorts=FALSE)
    r.tanportfolio[i] = sum(w.tanportfolio*assets[period.end+1])

}

The problems occur when I am trying to replicate the method with another dataset:

source("performance.r")
source("markowitz.r")
source("functions.r")
source("ols.r")
library("quadprog")



data = read.csv("Portfolios_Formed_on_BE-ME.csv", header = TRUE)
data = ts((data[,11:20]/100), start=c(1926,7), frequency = 12)

asset.names = colnames(data) 

factors = read.table(file="F-F_Research_Data_Factors.txt", header=TRUE)
factors = ts((factors[,2:ncol(factors)]/100), start=c(1926,7), frequency = 12)

year.start = 1964
ind.start = which(time(data) == year.start)

assets = as.matrix(data) #transform ts to a matrix


assets1 = as.numeric(assets[,1])
assets2 = as.numeric(assets[,2])
assets3 = as.numeric(assets[,3])
assets4 = as.numeric(assets[,4])
assets5 = as.numeric(assets[,5])
assets6 = as.numeric(assets[,6])
assets7 = as.numeric(assets[,7])
assets8 = as.numeric(assets[,8])
assets9 = as.numeric(assets[,9])
assets10 = as.numeric(assets[,10])

risk.free <- as.numeric(factors[,"RF"])

assets = cbind(assets1, assets2, assets3, assets4, assets5, assets6, assets7,
               assets8, assets9, assets10)

nobs = length(assets1)


nAssets = ncol(assets)

lookback.period = 5*12    # length of lookback period in a number of months
n = nobs - ind.start + 1 # number of monthly portfolio returns 
r.tanportfolio = rep(0,n)

for (i in 1:n) {
  # find the indices of the lookback period
  period.end <- ind.start + i - 2
  period.start <- period.end - lookback.period + 1
  if (period.start < 1) period.start <- 1 # index cannot be less than 1
  
  # estimate the covariance matrix and mean returns
  covmat <- cov(assets[period.start:period.end,])
  er <- apply(assets[period.start:period.end,], 2, mean)
  # get the risk-free rate of return
  
  Rf <- risk.free[period.end+1]
  
  w.tanportfolio = tanportfolio(er, covmat, Rf , shorts=FALSE)
  r.tanportfolio[i] = sum(w.tanportfolio*assets[period.end+1])
}

The error message is as follows: Error in tanportfolio(er, covmat, Rf, shorts = FALSE) :
Risk-free rate greater than mean return on global minimum variance portfolio.
However, when i calculate the values this is not the case..

Here is the code for the tanportfolio:

tanportfolio <- function(er, covmat, Rf, shorts=TRUE) {
  # computes the tangency portfolio
  #
  # inputs:
  # er    			    N x 1 vector of expected returns
  # covmat  			  N x N covariance matrix of returns
  # r          	    scalar, risk-free rate return
  #
  # output is a vector of portfolio weights
  
  #
  # check for valid inputs
  #
  er <- as.vector(er)
  covmat <- as.matrix(covmat)
  if (!is.numeric(Rf) || length(Rf) !=1)
    stop("Risk-free rate is not a scalar")
  if(length(er) != nrow(covmat))
    stop("Mismatch in number of rows")
  if(any(diag(chol(covmat)) <= 0))
    stop("Covariance matrix is not positive definite")
  
  #
  # compute global minimum variance portfolio
  #
  w <- gmvportfolio(covmat, shorts=shorts)
  if(w %*% er < Rf)
    stop("Risk-free rate greater than mean return on global minimum variance portfolio")
  #
  # compute the weights of the tangency portfolio
  #
  if(shorts==TRUE){
    # closed-form solutions when short sales are allowed
    ones <- rep(1, nrow(covmat))  # vector of ones
    covmat.inv <- solve(covmat)   # inverse of covariance matrix
    w <- (covmat.inv %*% (er-Rf))/as.numeric(ones %*% covmat.inv %*% (er-Rf))
    w <- as.vector(w)  
  } else if(shorts==FALSE){
    # numerical solution with no short sales
    n = nrow(covmat)
    Dmat <- covmat
    dvec <- rep.int(0, n)
    Amat <- cbind(er-Rf, diag(1,n))
    bvec <- c(1, rep(0,n))
    result <- solve.QP(Dmat=Dmat,dvec=dvec,Amat=Amat,bvec=bvec,meq=1)
    w <- round(result$solution/sum(result$solution), 6)
  } else {
    stop("shorts needs to be logical. For no-shorts, shorts=FALSE.")
  }  
  return(w)
}  

Big thanks to anyone able to help! :slight_smile:

Hi, and welcome!

Preliminarily, while the code blocks are helpful, they aren't quite a reproducible example, called a reprex, which is always helpful in attracting more and better answers.

In this case, however, it seems pretty clear that differences with the FALSE condition in tanportfolio() vs. w.tanportfolio <- tanportfolio(er, covmat, Rf , shorts=FALSE) is due to some difference in the datasets passed to the er argument, but I have no way of testing that.

2 Likes

Thank you for answering! :slight_smile:
Here is the link to download all the datasets applied https://gofile.io/?c=i2c3Bc

Is this enough to reprex?

Which one is er? If it's constructed from two or more of the files, can you post a reprex with the code, assuming the data linked to is in the working directory?

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.