Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 95 additions & 1 deletion R/optimize.portfolio.R
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ optimize.portfolio_v2 <- function(
portfolio=NULL,
constraints=NULL,
objectives=NULL,
optimize_method=c("DEoptim","random","ROI","pso","GenSA"),
optimize_method=c("DEoptim","random","ROI","pso","GenSA","mco"),
search_size=20000,
trace=FALSE, ...,
rp=NULL,
Expand Down Expand Up @@ -815,6 +815,100 @@ optimize.portfolio_v2 <- function(

} ## end case for DEoptim

###Multiple Criteria Optimization
if(optimize_method=="mco"){
stopifnot(requireNamespace("mco", quietly = TRUE))

###Generates multi objective function for the NSGA2 optimizer
GenerateMultiObjectiveFunction <- function(PortfolioObject, ...) {
objectives <- PortfolioObject$objectives
if (is.null(objectives))
stop("Cannot create multi-objective function; portfolio has no objective function(s)")

constraints <- get_constraints(PortfolioObject,...)

# list of calls to objective functions
ObjectiveFunctionCalls <- lapply(objectives, function(obj) {
objFun <- as.name(obj$name)
objCall <- as.call(c(list(objFun, weights=quote(weights), R=quote(R)), obj$arguments))
call("*", objCall, obj$multiplier)
})

# objective function without body
MultiObjectiveFunction <- function(weights, R, ...) { }

# set objective function body
if (!is.null(constraints$min_sum) || !is.null(constraints$max_sum)) {
callVector <- as.call(c(as.name("c"), ObjectiveFunctionCalls))
body(MultiObjectiveFunction) <- substitute({
if (!is.null(max_sum) && is.finite(max_sum)) {
if (sum(weights) > max_sum) {
weights <- (max_sum/sum(weights))*weights
}
}
if (!is.null(min_sum) && is.finite(min_sum)) {
if (sum(weights) < min_sum) {
weights <- (min_sum/sum(weights))*weights
}
}
callObjectiveFunctions
}, list(min_sum = constraints$min_sum, max_sum = constraints$max_sum,
callObjectiveFunctions = callVector))
} else {
body(MultiObjectiveFunction) <- call("{", ObjectiveFunctionCalls[[1]])
}
return(MultiObjectiveFunction)
}
MultiObjectiveFunction <- GenerateMultiObjectiveFunction(portfolio)

###Compile the function
if (requireNamespace("compiler")) {
MultiObjectiveFunction <- compiler::cmpfun(MultiObjectiveFunction)
}

###for debugging
#print(MultiObjectiveFunction)

###@TODO Generate a multi contraint function. Currently box, min_sum,max_sum,long_only are supported without it

###Bounds from constraints
if(is.null(constraints$min)){
lower.bounds=rep(-Inf,ncol(R))
}else{
lower.bounds=constraints$min}
if(is.null(constraints$max)){
upper.bounds=rep(Inf,ncol(R))
}else{ upper.bounds=constraints$max}

minw = nsga2(MultiObjectiveFunction,idim=ncol(R),odim=length(portfolio$objectives),upper.bounds = upper.bounds,lower.bounds = lower.bounds,vectorized = FALSE, R, ...)

#For Testing
#print(minw)

if(inherits(minw,"try-error")) { minw=NULL }
if(is.null(minw)){
message(paste("Optimizer was unable to find a solution for target"))
return (paste("Optimizer was unable to find a solution for target"))
}

###Double check this
###It's pareto optimal so it should not actually matter

value=minw$value[which.min(minw$value[minw$pareto.optimal,1]),]
weights=minw$par[which.min(minw$value[minw$pareto.optimal,1]),]

out <- list(weights=weights, objective_measures=last(minw$value),out=value)
}










# case for random portfolios optimization method
if(optimize_method=="random"){
# issue message if min_sum and max_sum are too restrictive
Expand Down