Nothing
#'@title Portfolio Optimization by Benders decomposition
#'
#'@description BDportfolio_optim is a linear program for financial portfolio optimization.
#' Portfolio risk is measured by one of the risk measures from the list c("CVAR", "DCVAR", "LSAD", "MAD").
#' Benders decomposition method is explored to enable optimization for very large returns samples (\eqn{\sim 10^6}).
#'
#' The optimization problem is:\cr
#' \eqn{\min F({\theta^{T}} r)} \cr
#' over \cr
#' \eqn{\theta^{T} E(r)} \eqn{\ge} \eqn{portfolio\_return}, \cr
#' \eqn{LB} \eqn{\le \theta \le} \eqn{UB}, \cr
#' \eqn{Aconstr} \eqn{\theta \le} \eqn{bconstr}, \cr
#' where \cr
#' \eqn{F} is a measure of risk; \cr
#' \eqn{r} is a time series of returns of assets; \cr
#' \eqn{\theta} is a vector of portfolio weights. \cr
#'
#'@usage BDportfolio_optim(dat, portfolio_return,
#'risk=c("CVAR", "DCVAR","LSAD","MAD"), alpha=0.95,
#'Aconstr=NULL, bconstr=NULL, LB=NULL, UB=NULL, maxiter=500,tol=1e-8)
#'
#'@param dat Time series of returns data; dat = cbind(rr, pk), where \eqn{rr} is an array (time series) of asset returns,
#' for \eqn{n} returns and \eqn{k} assets it is an array with \eqn{\dim(rr) = (n, k)},
#' \eqn{pk} is a vector of length \eqn{n} containing probabilities of returns.
#'@param portfolio_return Target portfolio return.
#'@param risk Risk measure chosen for optimization; one of "CVAR", "DCVAR", "LSAD", "MAD", where
#' "CVAR" -- denotes Conditional Value-at-Risk (CVaR),
#' "DCVAR" -- denotes deviation CVaR,
#' "LSAD" -- denotes Lower Semi Absolute Deviation,
#' "MAD" -- denotes Mean Absolute Deviation.
#'@param alpha Value of alpha quantile used to compute portfolio VaR and CVaR; used also as quantile value for risk measures CVAR and DCVAR.
#'@param Aconstr Matrix defining additional constraints, \eqn{\dim (Aconstr) = (m,k)}, where
#' \eqn{k} -- number of assets, \eqn{m} -- number of constraints.
#'@param bconstr Vector defining additional constraints, length (\eqn{bconstr}) \eqn{ = m}.
#'@param LB Vector of length k, lower bounds of portfolio weights \eqn{\theta}; warning: condition LB = NULL is equivalent to LB = rep(0, k) (lower bound zero).
#'@param UB Vector of length k, upper bounds for portfolio weights \eqn{\theta}.
#'@param maxiter Maximal number of iterations.
#'@param tol Accuracy of computations, stopping rule.
#'
#'
#'@return BDportfolio_optim returns a list with items:
#' \tabular{llll}{
#'\code{return_mean} \tab vector of asset returns mean values. \cr
#'
#'\code{mu} \tab realized portfolio return.\cr
#'
#'\code{theta} \tab portfolio weights.\cr
#'
#'\code{CVaR} \tab portfolio CVaR.\cr
#'
#'\code{VaR} \tab portfolio VaR.\cr
#'
#'\code{MAD} \tab portfolio MAD.\cr
#'
#'\code{risk} \tab portfolio risk measured by the risk measure chosen for optimization.\cr
#'
#'\code{new_portfolio_return} \tab modified target portfolio return; when the original target portfolio return \cr
#'
#'\code{ } \tab is to high for the problem, the optimization problem is solved for \cr
#'
#'\code{ } \tab new_portfolio_return as the target return. \cr
#'}
#'
#'@examples
#'
#'library (Rsymphony)
#'library(Rglpk)
#'library(mvtnorm)
#'k = 3
#'num =100
#'dat <- cbind(rmvnorm (n=num, mean = rep(0,k), sigma=diag(k)), matrix(1/num,num,1))
#'# a data sample with num rows and (k+1) columns for k assets;
#'port_ret = 0.05 # target portfolio return
#'alpha_optim = 0.95
#'
#'# minimal constraints set: \eqn{\sum \theta_{i} = 1}
#'# has to be in two inequalities: \eqn{1 - \epsilon <= \sum \theta_{i} <= 1 + \epsilon}
#'a0 <- rep(1,k)
#'Aconstr <- rbind(a0,-a0)
#'bconstr <- c(1+1e-8, -1+1e-8)
#'
#'LB <- rep(0,k)
#'UB <- rep(1,k)
#'
#'res <- BDportfolio_optim(dat, port_ret, "CVAR", alpha_optim,
#' Aconstr, bconstr, LB, UB, maxiter=200, tol=1e-8)
#'
#'cat ( c("Benders decomposition portfolio:\n\n"))
#'cat(c("weights \n"))
#'print(res$theta)
#'
#'cat(c("\n mean = ", res$mu, " risk = ", res$risk,
#'"\n CVaR = ", res$CVaR, " VaR = ", res$VaR, "\n MAD = ", res$MAD, "\n\n"))
#'
#'
#'@references Benders, J.F., Partitioning procedures for solving mixed-variables programming problems. Number. Math., 4 (1962), 238--252, reprinted in
#' Computational Management Science 2 (2005), 3--19. DOI: 10.1007/s10287-004-0020-y.
#'
#'Konno, H., Piecewise linear risk function and portfolio optimization, Journal of the Operations Research Society of Japan, 33 (1990), 139--156.
#'
#'Konno, H., Yamazaki, H., Mean-absolute deviation portfolio optimization model and its application to Tokyo stock market. Management Science, 37 (1991), 519--531.
#'
#'Konno, H., Waki, H., Yuuki, A., Portfolio optimization under lower partial risk measures, Asia-Pacific Financial Markets, 9 (2002), 127--140. DOI: 10.1023/A:1022238119491.
#'
#'Kunzi-Bay, A., Mayer, J., Computational aspects of minimizing conditional value at risk. Computational Management Science, 3 (2006), 3--27. DOI: 10.1007/s10287-005-0042-0.
#'
#'Rockafellar, R.T., Uryasev, S., Optimization of conditional value-at-risk. Journal of Risk, 2 (2000), 21--41. DOI: 10.21314/JOR.2000.038.
#'
#'Rockafellar, R. T., Uryasev, S., Zabarankin, M., Generalized deviations in risk analysis. Finance and Stochastics, 10 (2006), 51--74. DOI: 10.1007/s00780-005-0165-8.
#'
#'
#'@export
BDportfolio_optim <- function (dat, portfolio_return, risk = c("CVAR", "DCVAR", "LSAD", "MAD"), alpha = 0.95,
Aconstr =NULL, bconstr= NULL, LB = NULL, UB = NULL, maxiter = 500, tol = 1e-8 )
{
ep = tol # tolerance for optimal solution
rdigits <- -round( log10( tol ) )
k = ncol(dat)-1
n = nrow(dat)
if (!is.null(Aconstr)){
nVar <- k # number of variables
nCon <- length(bconstr) # number of constraints
if( !all.equal( dim( Aconstr ), c( nCon, nVar ) ) == TRUE ) {
stop( paste( "Matrix A must have as many rows as constraints (=elements of vector b)",
" and as many columns as variables (=assets).\n" ) )
}
}
if (!is.null(LB)){
if (length(LB) != k){
stop( paste( "Length of vector LB (=lower limits for portfolio weights) must be the same",
"as a number of assets.\n" ) )
}
}
if (!is.null(UB)){
if (length(UB) != k){
stop( paste( "Length of vector UB (=upper limits for portfolio weights) must be the same",
"as a number of assets.\n" ) )
}
}
pk = dat[,k+1]
ra = as.matrix(dat[,1:k])
## Labels
if( is.null(colnames(ra))) {
ralab <- as.character(1:k)
} else {
ralab <- colnames(ra)
}
mu = matrix(0,1,k)
for (i in 1:n ){
mu = mu + ra[i, ] * pk[i]
}
colnames (mu) = ralab
rownames (mu) = as.character("assets excess returns")
lowb = LB[mu<0] * mu[mu<0]
upb = UB[mu>0] * mu[mu>0]
if (!is.null(UB) && ((sum(upb) + sum(lowb)) <= portfolio_return)) {
cat ( c("Expected portfolio return is to high for the problem\n"))
cat ( c("portfolio_return is set to highest accepted value \n"))
portfolio_return = (sum(upb) + sum(lowb)) - portfolio_return/20
cat (c ("new portfolio_return = ", portfolio_return, "\n"))
}
rr = ra - matrix(1,n,1)%*%mu
dimnames (rr) = NULL
risk = toupper(risk)
risk <- match.arg(risk)
cvarind = switch(risk,
CVAR = TRUE,
DCVAR = TRUE,
LSAD = FALSE,
MAD = FALSE)
clin = c(rep(0,k), 1)
if (cvarind) clin = c(clin, 1/(1-alpha))
if (is.null(Aconstr) ){
Acon1 = NULL
}
else {
rcon = nrow(Aconstr)
if (cvarind) {
a0 = matrix(0,rcon,2)}
else {
a0 = matrix(0,rcon,1)
}
Acon1 = cbind(Aconstr, a0)
}
if (cvarind) {
a1 = c(mu,0,0)}
else {
a1 = c(mu,0)
}
a4 = .make_diag(rep(1,k))
if (cvarind) {
a5 = matrix(0, k, 2)}
else {
a5 = matrix(0, k, 1)
}
amat2 = cbind(a4, a5)
if (is.null(UB) || is.null(LB)) amat2 = NULL
Amat = rbind(Acon1, -a1, amat2)
if (is.null(bconstr)) {
bcon1 = NULL
}
else {
if (is.null(LB)){
bcon1 = bconstr
}
else {
bcon1 = bconstr - LB %*% t(Aconstr)
}
}
if (is.null(LB)){
prcor = 0
bcor1 = 0
bcor2 = NULL
LB = 0
}
else {
prcor = LB %*% t(mu)
if ( is.null(UB)){
bcor2 = NULL
}
else{
bcor2 = UB - LB
}
}
bmat = c(bcon1,-portfolio_return + prcor, bcor2)
K_set <- rep(1,n)
loop = 0
repeat
{
loop = loop+1
eta = t(pk * K_set) %*% rr
p_i = sum( pk * K_set )
if (cvarind) {
a3 = c(eta,p_i,1)}
else {
a3 = c(eta,1)
}
Amat = rbind(Amat, -a3)
dimnames (Amat) = NULL
if (length(LB) > 1 ) bcor1 = LB %*% t(eta)
bmat = c(bmat, bcor1)
bmat = as.vector(bmat, mode="numeric")
res = Rsymphony::Rsymphony_solve_LP(clin, Amat, dir = rep( "<=", length(bmat)), bmat, max = FALSE, node_limit = 3000)
sol = res$solution
if (cvarind) {
K_tem = rr %*% (sol[1:k]+ LB) + sol[k+1]*matrix(1,n,1)}
else {
K_tem = rr %*% (sol[1:k]+ LB)
}
K_set <- K_tem < 0
w_plus = -t(pk * K_set) %*% K_tem
if (cvarind) {
w_star = -(eta %*% (sol[1:k]+ LB) + p_i *sol[k+1])}
else {
w_star = -(eta %*% (sol[1:k]+ LB))
}
if (cvarind) {
F_bar = sol[k+1] + w_plus/ (1- alpha)
F_down = sol[k+1] + w_star/ (1- alpha)
}
else {
F_bar = w_plus
F_down = w_star
}
if (loop == 1) F_min = F_bar
F_min = min(F_bar, F_min)
if (F_min - F_down <= ep) break
if (loop > maxiter){
cat(c("Maximal number of iteration has been exceeded. \n"))
break
}
}
port_weights = (sol[1:k]+LB )
weight = round(c(port_weights), digits = rdigits)
weight = matrix(weight, k,1)
rownames (weight) = ralab
colnames (weight) = as.character("portfolio weights")
ret_mu = mu %*% port_weights
RM = F_down
RM = switch(risk,
CVAR = RM - ret_mu,
DCVAR = RM,
LSAD = RM,
MAD = 2* RM)
if (ret_mu > (portfolio_return +ep)) {
cat ( c("Target portfolio return is to small for the problem.\n"))
cat ( c("Problem has been solved for target portfolio return = ", round(ret_mu, digits = rdigits), "\n"))
}
loss = -ra %*% port_weights
risk_st = .RISK_post (loss, pk, alpha)
return (list (return_mean = mu, mu = round(ret_mu, digits = rdigits), theta = weight, CVaR = round(risk_st$cvar, digits = rdigits),
VaR = round(risk_st$var, digits = rdigits), MAD = round(risk_st$mad, digits = rdigits), risk = round(RM, digits = rdigits),
new_portfolio_return = portfolio_return ))
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.