###############################################################################1# R (https://r-project.org/) Numeric Methods for Optimization of Portfolios2#3# Copyright (c) 2004-2021 Brian G. Peterson, Peter Carl, Ross Bennett, Kris Boudt4#5# This library is distributed under the terms of the GNU Public License (GPL)6# for full details see the file COPYING7#8# $Id$9#10###############################################################################1112#' @rdname constraint13#' @export14constraint_v1 <- function(assets=NULL, ... ,min,max,min_mult,max_mult,min_sum=.99,max_sum=1.01,weight_seq=NULL)15{ # based on GPL R-Forge pkg roi by Stefan Thuessel,Kurt Hornik,David Meyer16if (hasArg(min) & hasArg(max)) {17if (is.null(assets) & (!length(min)>1) & (!length(max)>1)) {18stop("You must either specify the assets or pass a vector for both min and max")19}20}2122if(!is.null(assets)){23# TODO FIXME this doesn't work quite right on matrix of assets24if(is.numeric(assets)){25if (length(assets) == 1) {26nassets=assets27#we passed in a number of assets, so we need to create the vector28message("assuming equal weighted initial portfolio")29assets<-rep(1/nassets,nassets)30} else {31nassets = length(assets)32}33# and now we may need to name them34if (is.null(names(assets))) {35for(i in 1:length(assets)){36names(assets)[i]<-paste("Asset",i,sep=".")37}38}39}40if(is.character(assets)){41nassets=length(assets)42assetnames=assets43message("assuming equal weighted initial portfolio")44assets<-rep(1/nassets,nassets)45names(assets)<-assetnames # set names, so that other code can access it,46# and doesn't have to know about the character vector47# print(assets)48}49# if assets is a named vector, we'll assume it is current weights50}5152if(hasArg(min) | hasArg(max)) {53if (length(min)>1 & length(max)>1){54if (length(min)!=length(max)) { stop("length of min and max must be the same") }55}5657if (length(min)==1) {58message("min not passed in as vector, replicating min to length of length(assets)")59min <- rep(min,nassets)60}61if (length(min)!=nassets) stop(paste("length of min must be equal to 1 or the number of assets",nassets))6263if (length(max)==1) {64message("max not passed in as vector, replicating max to length of length(assets)")65max <- rep(max,nassets)66}67if (length(max)!=nassets) stop(paste("length of max must be equal to 1 or the number of assets",nassets))6869} else {70message("no min or max passed in, assuming 0 and 1")71min <- rep(0,nassets)72max <- rep(1,nassets)73}7475names(min)<-names(assets)76names(max)<-names(assets)7778if(hasArg(min_mult) | hasArg(max_mult)) {79if (length(min_mult)>1 & length(max_mult)>1){80if (length(min_mult)!=length(max_mult) ) { stop("length of min_mult and max_mult must be the same") }81} else {82message("min_mult and max_mult not passed in as vectors, replicating min_mult and max_mult to length of assets vector")83min_mult = rep(min_mult,nassets)84max_mult = rep(max_mult,nassets)85}86}8788if(!hasArg(min_sum) | !hasArg(max_sum)) {89min_sum = NULL90max_sum = NULL91}9293if (!is.null(names(assets))) {94assetnames<-names(assets)95if(hasArg(min)){96names(min)<-assetnames97names(max)<-assetnames98} else {99min = NULL100max = NULL101}102if(hasArg(min_mult)){103names(min_mult)<-assetnames104names(max_mult)<-assetnames105} else {106min_mult = NULL107max_mult = NULL108}109}110##now adjust min and max to account for min_mult and max_mult from initial111if(!is.null(min_mult) & !is.null(min)) {112tmp_min <- assets*min_mult113#TODO FIXME this creates a list, and it should create a named vector or matrix114min[which(tmp_min>min)]<-tmp_min[which(tmp_min>min)]115}116if(!is.null(max_mult) & !is.null(max)) {117tmp_max <- assets*max_mult118#TODO FIXME this creates a list, and it should create a named vector or matrix119max[which(tmp_max<max)]<-tmp_max[which(tmp_max<max)]120}121122## now structure and return123return(structure(124list(125assets = assets,126min = min,127max = max,128min_mult = min_mult,129max_mult = max_mult,130min_sum = min_sum,131max_sum = max_sum,132weight_seq = weight_seq,133objectives = list(),134call = match.call()135),136class=c("v1_constraint","constraint")137))138}139140141#' constructors for class constraint142#'143#' See main documentation entry in \code{\link{add.constraint}}.144#'145#' This includes the deprecated constructor for the \code{v1_constraint} object for backwards compatibility.146#'147#' @param assets number of assets, or optionally a named vector of assets specifying initial weights148#' @param min numeric or named vector specifying minimum weight box constraints149#' @param max numeric or named vector specifying minimum weight box constraints150#' @param min_mult numeric or named vector specifying minimum multiplier box constraint from initial weight in \code{assets}151#' @param max_mult numeric or named vector specifying maximum multiplier box constraint from initial weight in \code{assets}152#' @param min_sum minimum sum of all asset weights, default .99153#' @param max_sum maximum sum of all asset weights, default 1.01154#' @param weight_seq seed sequence of weights, see \code{\link{generatesequence}}155#' @param type character type of the constraint to add or update156#' @param enabled TRUE/FALSE to enabled the constraint157#' @param \dots any other passthru parameters158#' @param constrclass name of class for the constraint159#' @author Peter Carl, Brian G. Peterson, Ross Bennett160#' @seealso \code{\link{add.constraint}}161#' @aliases constraint constraint_v2162#' @rdname constraint163#' @export constraint164#' @export constraint_v2165constraint <- constraint_v2 <- function(type, enabled=TRUE, ..., constrclass="v2_constraint"){166if(!hasArg(type)) stop("you must specify a constraint type")167if (hasArg(type)) if(is.null(type)) stop("you must specify a constraint type")168169## now structure and return170return(structure( c(list(type = type,171enabled=enabled),172list(...)),173class=c(constrclass, "constraint")174) # end structure175)176}177178#' General interface for adding and/or updating optimization constraints.179#'180#' This is the main function for adding and/or updating constraints to the \code{\link{portfolio.spec}} object.181#'182#' The following constraint types may be specified:183#' \describe{184#' \item{\code{weight_sum}, \code{weight}, \code{leverage}}{ Specify constraint on the sum of the weights, see \code{\link{weight_sum_constraint}} }185#' \item{\code{full_investment}}{ Special case to set \code{min_sum=1} and \code{max_sum=1} of weight sum constraints }186#' \item{\code{dollar_neutral}, \code{active}}{ Special case to set \code{min_sum=0} and \code{max_sum=0} of weight sum constraints }187#' \item{\code{box}}{ box constraints for the individual asset weights, see \code{\link{box_constraint}} }188#' \item{\code{long_only}}{ Special case to set \code{min=0} and \code{max=1} of box constraints }189#' \item{\code{group}}{ specify the sum of weights within groups and the number of assets with non-zero weights in groups, see \code{\link{group_constraint}} }190#' \item{\code{turnover}}{ Specify a constraint for target turnover. Turnover is calculated from a set of initial weights, see \code{\link{turnover_constraint}} }191#' \item{\code{diversification}}{ target diversification of a set of weights, see \code{\link{diversification_constraint}} }192#' \item{\code{position_limit}}{ Specify the number of non-zero, long, and/or short positions, see \code{\link{position_limit_constraint}} }193#' \item{\code{return}}{ Specify the target mean return, see \code{\link{return_constraint}}}194#' \item{\code{factor_exposure}}{ Specify risk factor exposures, see \code{\link{factor_exposure_constraint}}}195#' \item{\code{leverage_exposure}}{ Specify a maximum leverage exposure, see \code{\link{leverage_exposure_constraint}}}196#' }197#'198#' @param portfolio an object of class 'portfolio' to add the constraint to, specifying the constraints for the optimization, see \code{\link{portfolio.spec}}199#' @param type character type of the constraint to add or update, currently 'weight_sum' (also 'leverage' or 'weight'), 'box', 'group', 'turnover', 'diversification', 'position_limit', 'return', 'factor_exposure', or 'leverage_exposure'200#' @param enabled TRUE/FALSE. The default is enabled=TRUE.201#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.202#' @param \dots any other passthru parameters to specify constraints203#' @param indexnum if you are updating a specific constraint, the index number in the $constraints list to update204#' @author Ross Bennett205#' @seealso206#' \code{\link{portfolio.spec}}207#' \code{\link{weight_sum_constraint}},208#' \code{\link{box_constraint}},209#' \code{\link{group_constraint}},210#' \code{\link{turnover_constraint}},211#' \code{\link{diversification_constraint}},212#' \code{\link{position_limit_constraint}},213#' \code{\link{return_constraint}},214#' \code{\link{factor_exposure_constraint}},215#' \code{\link{leverage_exposure_constraint}}216#' @examples217#' data(edhec)218#' returns <- edhec[, 1:4]219#' fund.names <- colnames(returns)220#' pspec <- portfolio.spec(assets=fund.names)221#'222#' # Add the full investment constraint that specifies the weights must sum to 1.223#' pspec <- add.constraint(portfolio=pspec, type="weight_sum", min_sum=1, max_sum=1)224#'225#' # The full investment constraint can also be specified with type="full_investment"226#' pspec <- add.constraint(portfolio=pspec, type="full_investment")227#'228#' # Another common constraint is that portfolio weights sum to 0.229#' pspec <- add.constraint(portfolio=pspec, type="weight_sum", min_sum=0, max_sum=0)230#' pspec <- add.constraint(portfolio=pspec, type="dollar_neutral")231#' pspec <- add.constraint(portfolio=pspec, type="active")232#'233#' # Add box constraints234#' pspec <- add.constraint(portfolio=pspec, type="box", min=0.05, max=0.4)235#'236#' # min and max can also be specified per asset237#' pspec <- add.constraint(portfolio=pspec,238#' type="box",239#' min=c(0.05, 0, 0.08, 0.1),240#' max=c(0.4, 0.3, 0.7, 0.55))241#'242#' # A special case of box constraints is long only where min=0 and max=1243#' # The default action is long only if min and max are not specified244#' pspec <- add.constraint(portfolio=pspec, type="box")245#' pspec <- add.constraint(portfolio=pspec, type="long_only")246#'247#' # Add group constraints248#' pspec <- add.constraint(portfolio=pspec,249#' type="group",250#' groups=list(c(1, 2, 1), 4),251#' group_min=c(0.1, 0.15),252#' group_max=c(0.85, 0.55),253#' group_labels=c("GroupA", "GroupB"),254#' group_pos=c(2, 1))255#'256#' # Add position limit constraint such that we have a maximum number257#' # of three assets with non-zero weights.258#' pspec <- add.constraint(portfolio=pspec, type="position_limit", max_pos=3)259#'260#' # Add diversification constraint261#' pspec <- add.constraint(portfolio=pspec, type="diversification", div_target=0.7)262#'263#' # Add turnover constraint264#' pspec <- add.constraint(portfolio=pspec, type="turnover", turnover_target=0.2)265#'266#' # Add target mean return constraint267#' pspec <- add.constraint(portfolio=pspec, type="return", return_target=0.007)268#'269#' # Example using the indexnum argument270#' portf <- portfolio.spec(assets=fund.names)271#' portf <- add.constraint(portf, type="full_investment")272#' portf <- add.constraint(portf, type="long_only")273#'274#' # indexnum corresponds to the index number of the constraint275#' # The full_investment constraint was the first constraint added and has276#' # indexnum=1277#' portf$constraints[[1]]278#'279#' # View the constraint with indexnum=2280#' portf$constraints[[2]]281#'282#' # Update the constraint to relax the sum of weights constraint283#' portf <- add.constraint(portf, type="weight_sum",284#' min_sum=0.99, max_sum=1.01,285#' indexnum=1)286#'287#' # Update the constraint to modify the box constraint288#' portf <- add.constraint(portf, type="box",289#' min=0.1, max=0.8,290#' indexnum=2)291#' @export292add.constraint <- function(portfolio, type, enabled=TRUE, message=FALSE, ..., indexnum=NULL){293# Check to make sure that the portfolio passed in is a portfolio object294if (!is.portfolio(portfolio)) {stop("portfolio passed in is not of class portfolio")}295296# Check to make sure a type is passed in as an argument297if (!hasArg(type)) stop("you must supply a type of constraints to create")298299assets <- portfolio$assets300tmp_constraint = NULL301302# Currently supports box and group constraints. Will add more later.303switch(type,304# Box constraints305box = {tmp_constraint <- box_constraint(assets=assets,306type=type,307enabled=enabled,308message=message,309...=...)310},311# special case of box constraints for long_only312long_only = {tmp_constraint <- box_constraint(assets=assets,313type=type,314enabled=enabled,315message=message,316min=0,317max=1,318...=...)319},320# Group constraints321group = {tmp_constraint <- group_constraint(assets=assets,322type=type,323enabled=enabled,324message=message,325...=...)326},327# Sum of weights constraints328weight=, leverage=, weight_sum = {tmp_constraint <- weight_sum_constraint(type=type,329enabled=enabled,330message=message,331...=...)332},333# Special case of weight_sum constraint for full investment334full_investment = {tmp_constraint <- weight_sum_constraint(type=type,335min_sum=1,336max_sum=1,337enabled=enabled,338message=message,339...=...)340},341# Special case of weight_sum constraint for dollar neutral or active342dollar_neutral=, active= {tmp_constraint <- weight_sum_constraint(type=type,343min_sum=0,344max_sum=0,345enabled=enabled,346message=message,347...=...)348},349# Turnover constraint350turnover = {tmp_constraint <- turnover_constraint(type=type,351enabled=enabled,352message=message,353...=...)354},355# Diversification constraint356diversification = {tmp_constraint <- diversification_constraint(type=type,357enabled=enabled,358message=message,359...=...)360},361# Position limit constraint362position_limit = {tmp_constraint <- position_limit_constraint(assets=assets,363type=type,364enabled=enabled,365message=message,366...=...)367},368# Return constraint369return = {tmp_constraint <- return_constraint(type=type,370enabled=enabled,371message=message,372...=...)373},374# factor exposure constraint375factor_exposure=, factor_exposures = {tmp_constraint <- factor_exposure_constraint(assets=assets,376type=type,377enabled=enabled,378message=message,379...=...)380},381# transaction cost constraint382transaction=, transaction_cost = {tmp_constraint <- transaction_cost_constraint(assets=assets,383type=type,384enabled=enabled,385message=message,386...=...)387},388# leverage exposure constraint389leverage_exposure = {tmp_constraint <- leverage_exposure_constraint( type=type,390enabled=enabled,391message=message,392...=...)393},394filter = {tmp_constraint <- filter_constraint(assets=assets,395type=type,396enabled=enabled,397message=message,398...=...)399},400# Do nothing and return the portfolio object if type is NULL401null = {return(portfolio)}402)403if(is.constraint(tmp_constraint)) {404if(!hasArg(indexnum) | (hasArg(indexnum) & is.null(indexnum))) indexnum <- length(portfolio$constraints)+1405tmp_constraint$call <- match.call()406portfolio$constraints[[indexnum]] <- tmp_constraint407}408return(portfolio)409}410411#' constructor for box_constraint.412#'413#' Box constraints specify the upper and lower bounds on the weights of the assets.414#' This function is called by add.constraint when type="box" is specified. See \code{\link{add.constraint}}.415#'416#' @param type character type of the constraint417#' @param assets number of assets, or optionally a named vector of assets specifying initial weights418#' @param min numeric or named vector specifying minimum weight box constraints419#' @param max numeric or named vector specifying minimum weight box constraints420#' @param min_mult numeric or named vector specifying minimum multiplier box constraint from initial weight in \code{assets}421#' @param max_mult numeric or named vector specifying maximum multiplier box constraint from initial weight in \code{assets}422#' @param enabled TRUE/FALSE423#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.424#' @param \dots any other passthru parameters to specify box constraints425#' @return an object of class 'box_constraint'426#' @author Ross Bennett427#' @seealso \code{\link{add.constraint}}428#' @examples429#' data(edhec)430#' ret <- edhec[, 1:4]431#'432#' pspec <- portfolio.spec(assets=colnames(ret))433#'434#' # defaults to min=0 and max=1435#' pspec <- add.constraint(pspec, type="box")436#'437#' # specify box constraints as a scalar438#' pspec <- add.constraint(pspec, type="box", min=0.05, max=0.45)439#'440#' # specify box constraints per asset441#' pspec <- add.constraint(pspec,442#' type="box",443#' min=c(0.05, 0.10, 0.08, 0.06),444#' max=c(0.45, 0.55, 0.35, 0.65))445#'446#' @export447box_constraint <- function(type="box", assets, min, max, min_mult, max_mult, enabled=TRUE, message=FALSE, ...){448# Based on the constraint function for object of class constraint_v1 that449# included specifying box constraints.450451# Get the length of the assets vector452nassets <- length(assets)453454if(type=="long_only"){455min <- rep(0, nassets)456max <- rep(1, nassets)457}458459# Check that the length of min and max are the same460if(hasArg(min) | hasArg(max)) {461if (length(min) > 1 & length(max) > 1){462if (length(min) != length(max)) { stop("length of min and max must be the same") }463}464465# If the user passes in a scalar for min, then create a min vector466if (length(min) == 1) {467if(message) message("min not passed in as vector, replicating min to length of length(assets)")468min <- rep(min, nassets)469}470if (length(min) != nassets) stop(paste("length of min must be equal to 1 or the number of assets:", nassets))471472# If the user passes in a scalar for max, then create a max vector473if (length(max) == 1) {474if(message) message("max not passed in as vector, replicating max to length of length(assets)")475max <- rep(max, nassets)476}477if (length(max) != nassets) stop(paste("length of max must be equal to 1 or the number of assets:", nassets))478479} else {480# Default to min=0 and max=1 if min or max are not passed in481if(message) message("no min or max passed in, assuming 0 and 1")482min <- rep(0, nassets)483max <- rep(1, nassets)484}485486# Set the names of the min and max vector to the names of the assets vector487names(min) <- names(assets)488names(max) <- names(assets)489490# Checks for min_mult and max_mult491if(hasArg(min_mult) | hasArg(max_mult)) {492if (length(min_mult) > 1 & length(max_mult) > 1){493if (length(min_mult) != length(max_mult) ) { stop("length of min_mult and max_mult must be the same") }494} else {495if(message) message("min_mult and max_mult not passed in as vectors, replicating min_mult and max_mult to length of assets vector")496min_mult = rep(min_mult, nassets)497max_mult = rep(max_mult, nassets)498}499}500501if (!is.null(names(assets))) {502assetnames <- names(assets)503if(hasArg(min)){504names(min) <- assetnames505names(max) <- assetnames506} else {507min = NULL508max = NULL509}510if(hasArg(min_mult)){511names(min_mult) <- assetnames512names(max_mult) <- assetnames513} else {514min_mult = NULL515max_mult = NULL516}517}518519# now adjust min and max to account for min_mult and max_mult from initial520if(!is.null(min_mult) & !is.null(min)) {521tmp_min <- assets * min_mult522#TODO FIXME this creates a list, and it should create a named vector or matrix523min[which(tmp_min > min)] <- tmp_min[which(tmp_min > min)]524}525if(!is.null(max_mult) & !is.null(max)) {526tmp_max <- assets * max_mult527#TODO FIXME this creates a list, and it should create a named vector or matrix528max[which(tmp_max < max)] <- tmp_max[which(tmp_max < max)]529}530531Constraint <- constraint_v2(type=type, enabled=enabled, constrclass="box_constraint", ...)532Constraint$min <- min533Constraint$max <- max534return(Constraint)535}536537#' constructor for group_constraint538#'539#' Group constraints specify the grouping of the assets, weights of the groups, and number of postions (i.e. non-zero weights) iof the groups.540#' This function is called by add.constraint when type="group" is specified. see \code{\link{add.constraint}}541#'542#' @param type character type of the constraint543#' @param assets number of assets, or optionally a named vector of assets specifying initial weights544#' @param groups list of vectors specifying the groups of the assets545#' @param group_labels character vector to label the groups (e.g. size, asset class, style, etc.)546#' @param group_min numeric or vector specifying minimum weight group constraints547#' @param group_max numeric or vector specifying minimum weight group constraints548#' @param group_pos vector specifying the number of non-zero weights per group549#' @param enabled TRUE/FALSE550#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.551#' @param \dots any other passthru parameters to specify group constraints552#' @return an object of class 'group_constraint'553#' @author Ross Bennett554#' @seealso \code{\link{add.constraint}}555#' @examples556#' data(edhec)557#' ret <- edhec[, 1:4]558#'559#' pspec <- portfolio.spec(assets=colnames(ret))560#'561#' # Assets 1 and 3 are groupA562#' # Assets 2 and 4 are groupB563#' pspec <- add.constraint(portfolio=pspec,564#' type="group",565#' groups=list(groupA=c(1, 3),566#' groupB=c(2, 4)),567#' group_min=c(0.15, 0.25),568#' group_max=c(0.65, 0.55))569#'570#' # 2 levels of grouping (e.g. by sector and geography)571#' pspec <- portfolio.spec(assets=5)572#' # Assets 1, 3, and 5 are Tech573#' # Assets 2 and 4 are Oil574#' # Assets 2, 4, and 5 are UK575#' # Assets 1 and are are US576#' group_list <- list(group1=c(1, 3, 5),577#' group2=c(2, 4),578#' groupA=c(2, 4, 5),579#' groupB=c(1, 3))580#'581#' pspec <- add.constraint(portfolio=pspec,582#' type="group",583#' groups=group_list,584#' group_min=c(0.15, 0.25, 0.2, 0.1),585#' group_max=c(0.65, 0.55, 0.5, 0.4))586#'587#' @export588group_constraint <- function(type="group", assets, groups, group_labels=NULL, group_min, group_max, group_pos=NULL, enabled=TRUE, message=FALSE, ...) {589if(!is.list(groups)) stop("groups must be passed in as a list")590nassets <- length(assets)591ngroups <- length(groups)592groupnames <- names(groups)593594# comment out so the user can pass in multiple levels of groups595# may want a warning message596# count <- sum(sapply(groups, length))597# if(count != nassets) {598# message("count of assets in groups must be equal to the number of assets")599# }600601# Checks for group_min602if (length(group_min) == 1) {603if(message) message("group_min not passed in as vector, replicating group_min to length of groups")604group_min <- rep(group_min, ngroups)605}606if (length(group_min) != ngroups) stop(paste("length of group_min must be equal to 1 or the length of groups:", ngroups))607608# Checks for group_max609if (length(group_max) == 1) {610if(message) message("group_max not passed in as vector, replicating group_max to length of groups")611group_max <- rep(group_max, ngroups)612}613if (length(group_max) != ngroups) stop(paste("length of group_max must be equal to 1 or the length of groups:", ngroups))614615# construct the group_label vector if groups is a named list616if(!is.null(groupnames)){617group_labels <- groupnames618}619620# Construct the group_label vector if it is not passed in621if(is.null(group_labels) & is.null(groupnames)){622group_labels <- paste(rep("group", ngroups), 1:ngroups, sep="")623}624625if(length(group_labels) != length(groups)) stop("length of group_labels must be equal to the length of groups")626627# Construct group_pos vector628if(!is.null(group_pos)){629# Check the length of the group_pos vector630if(length(group_pos) != length(groups)) stop("length of group_pos must be equal to the length of groups")631# Check for negative values in group_pos632if(any(group_pos < 0)) stop("all elements of group_pos must be positive")633# Elements of group_pos cannot be greater than count of assets in groups634if(any(group_pos > sapply(groups, length))){635group_pos <- pmin(group_pos, sapply(groups, length))636}637}638639Constraint <- constraint_v2(type, enabled=enabled, constrclass="group_constraint", ...)640Constraint$groups <- groups641Constraint$group_labels <- group_labels642Constraint$cLO <- group_min643Constraint$cUP <- group_max644Constraint$group_pos <- group_pos645return(Constraint)646}647648#' constructor for weight_sum_constraint649#'650#' The constraint specifies the upper and lower bound on the sum of the weights.651#' This function is called by add.constraint when "weight_sum", "leverage", "full_investment", "dollar_neutral", or "active" is specified as the type. see \code{\link{add.constraint}}652#'653#' Special cases for the weight_sum constraint are "full_investment" and "dollar_nuetral" or "active"654#'655#' If \code{type="full_investment"}, \code{min_sum=1} and \code{max_sum=1}656#'657#' If \code{type="dollar_neutral"} or \code{type="active"}, \code{min_sum=0}, and \code{max_sum=0}658#'659#' @param type character type of the constraint660#' @param min_sum minimum sum of all asset weights, default 0.99661#' @param max_sum maximum sum of all asset weights, default 1.01662#' @param enabled TRUE/FALSE663#' @param \dots any other passthru parameters to specify weight_sum constraints664#' @return an object of class 'weight_sum_constraint'665#' @author Ross Bennett666#' @seealso \code{\link{add.constraint}}667#' @examples668#' data(edhec)669#' ret <- edhec[, 1:4]670#'671#' pspec <- portfolio.spec(assets=colnames(ret))672#'673#' # min_sum and max_sum can be specified with type="weight_sum" or type="leverage"674#' pspec <- add.constraint(pspec, type="weight_sum", min_sum=1, max_sum=1)675#'676#' # Specify type="full_investment" to set min_sum=1 and max_sum=1677#' pspec <- add.constraint(pspec, type="full_investment")678#'679#' # Specify type="dollar_neutral" or type="active" to set min_sum=0 and max_sum=0680#' pspec <- add.constraint(pspec, type="dollar_neutral")681#' pspec <- add.constraint(pspec, type="active")682#' @export683weight_sum_constraint <- function(type="weight_sum", min_sum=0.99, max_sum=1.01, enabled=TRUE, ...){684switch(type,685full_investment = {686max_sum <- 1687min_sum <- 1688},689dollar_neutral = {690max_sum <- 0691min_sum <- 0692},693active = {694max_sum <- 0695min_sum <- 0696}697)698Constraint <- constraint_v2(type, enabled=enabled, constrclass="weight_sum_constraint", ...)699Constraint$min_sum <- min_sum700Constraint$max_sum <- max_sum701return(Constraint)702}703704#' check function for constraints705#'706#' @param x object to test for type \code{constraint}707#' @author Brian G. Peterson708#' @export709is.constraint <- function( x ) {710inherits( x, "constraint" )711}712713#' Helper function to get the enabled constraints out of the portfolio object714#'715#' When the v1_constraint object is instantiated via constraint, the arguments716#' min_sum, max_sum, min, and max are either specified by the user or default717#' values are assigned. These are required by other functions such as718#' \code{optimize.portfolio} and \code{constrained_objective} . This function719#' will check that these variables are in the portfolio object in the720#' constraints list. We will default to \code{min_sum=1} and \code{max_sum=1}721#' if leverage constraints are not specified. We will default to \code{min=-Inf}722#' and \code{max=Inf} if box constraints are not specified.723#' This function is used at the beginning of optimize.portfolio and other724#' functions to extract the constraints from the portfolio object. We Use the725#' same naming as the v1_constraint object.726#'727#' @param portfolio an object of class 'portfolio'728#' @return an object of class 'constraint' which is a flattened list of enabled constraints729#' @author Ross Bennett730#' @seealso \code{\link{portfolio.spec}}731get_constraints <- function(portfolio){732if(!is.portfolio(portfolio)) stop("portfolio passed in is not of class portfolio")733734if(length(portfolio$constraints) == 0) stop("No constraints passed in")735736out <- list()737out$min_sum <- NA738out$max_sum <- NA739out$min <- NA740out$max <- NA741742for(constraint in portfolio$constraints) {743if(constraint$enabled){744if(inherits(constraint, "weight_sum_constraint")){745out$min_sum <- constraint$min_sum746out$max_sum <- constraint$max_sum747}748if(inherits(constraint, "box_constraint")){749out$min <- constraint$min750out$max <- constraint$max751}752if(inherits(constraint, "group_constraint")){753out$groups <- constraint$groups754out$group_labels <- constraint$group_labels755out$cLO <- constraint$cLO756out$cUP <- constraint$cUP757out$group_pos <- constraint$group_pos758}759if(inherits(constraint, "turnover_constraint")){760out$turnover_target <- constraint$turnover_target761out$turnover_penalty <- constraint$turnover_penalty762out$weight_initial <- constraint$weight_initial763}764if(inherits(constraint, "diversification_constraint")){765out$div_target <- constraint$div_target766out$conc_aversion <- constraint$conc_aversion767}768if(inherits(constraint, "position_limit_constraint")){769out$max_pos <- constraint$max_pos770out$max_pos_long <- constraint$max_pos_long771out$max_pos_short <- constraint$max_pos_short772}773if(inherits(constraint, "return_constraint")){774out$return_target <- constraint$return_target775}776if(inherits(constraint, "factor_exposure_constraint")){777out$B <- constraint$B778out$lower <- constraint$lower779out$upper <- constraint$upper780}781if(inherits(constraint, "transaction_cost_constraint")){782out$ptc <- constraint$ptc783}784if(inherits(constraint, "leverage_exposure_constraint")){785out$leverage <- constraint$leverage786}787}788}789790# min_sum, max_sum, min, and max are required to be passed in and enabled791if(is.na(out$min_sum) | is.na(out$max_sum)) {792# return(NULL)793# stop("Leverage constraint min_sum and max_sum are not enabled or passed in")794# Default to full investment constraint795out$min_sum <- 1796out$max_sum <- 1797}798if(length(out$min) == 1 | length(out$max) == 1) {799if(is.na(out$min) | is.na(out$max)){800# return(NULL)801# stop("Box constraints min and max are not enabled or passed in")802# Default to min=-Inf and max=Inf for unconstrained weights803nassets <- length(portfolio$assets)804out$min <- rep(-Inf, nassets)805out$max <- rep(Inf, nassets)806}807}808# structure and return class of type constraint809return(structure(out, class="constraint"))810}811812#' constructor for turnover_constraint813#'814#' The turnover constraint specifies a target turnover value.815#' This function is called by add.constraint when type="turnover" is specified, see \code{\link{add.constraint}}.816#' Turnover is calculated from a set of initial weights. Turnover is817#' computed as \code{sum(abs(initial_weights - weights)) / N} where \code{N} is818#' the number of assets.819#'820#' Note that with the ROI solvers, turnover constraint is currently only821#' supported for the global minimum variance and quadratic utility problems822#' with ROI quadprog plugin.823#'824#' @param type character type of the constraint825#' @param turnover_target target turnover value826#' @param enabled TRUE/FALSE827#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.828#' @param \dots any other passthru parameters to specify box and/or group constraints829#' @return an object of class 'turnover_constraint'830#' @author Ross Bennett831#' @seealso \code{\link{add.constraint}}832#' @examples833#' data(edhec)834#' ret <- edhec[, 1:4]835#'836#' pspec <- portfolio.spec(assets=colnames(ret))837#'838#' pspec <- add.constraint(portfolio=pspec, type="turnover", turnover_target=0.6)839#' @export840turnover_constraint <- function(type="turnover", turnover_target, turnover_penalty=NULL, weight_initial=NULL, enabled=TRUE, message=FALSE, ...){841Constraint <- constraint_v2(type, enabled=enabled, constrclass="turnover_constraint", ...)842Constraint$turnover_target <- turnover_target843Constraint$weight_initial <- weight_initial844Constraint$turnover_penalty <- turnover_penalty845return(Constraint)846}847848#' constructor for diversification_constraint849#'850#' The diversification constraint specifies a target diversification value.851#' This function is called by add.constraint when type="diversification" is852#' specified, see \code{\link{add.constraint}}. Diversification is computed853#' as \code{1 - sum(weights^2)}.854#'855#' @param type character type of the constraint856#' @param div_target diversification target value857#' @param enabled TRUE/FALSE858#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.859#' @param \dots any other passthru parameters to specify diversification constraint860#' an object of class 'diversification_constraint'861#' @author Ross Bennett862#' @seealso \code{\link{add.constraint}}863#' @examples864#' data(edhec)865#' ret <- edhec[, 1:4]866#'867#' pspec <- portfolio.spec(assets=colnames(ret))868#'869#' pspec <- add.constraint(portfolio=pspec, type="diversification", div_target=0.7)870#' @export871diversification_constraint <- function(type="diversification", div_target=NULL, enabled=TRUE, message=FALSE, ...){872Constraint <- constraint_v2(type, enabled=enabled, constrclass="diversification_constraint", ...)873Constraint$div_target <- div_target874return(Constraint)875}876877#' constructor for return_constraint878#'879#' The return constraint specifes a target mean return value.880#' This function is called by add.constraint when type="return" is specified, \code{\link{add.constraint}}881#'882#' @param type character type of the constraint883#' @param return_target return target value884#' @param enabled TRUE/FALSE885#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.886#' @param \dots any other passthru parameters887#' @return an object of class 'return_constraint'888#' @author Ross Bennett889#' @seealso \code{\link{add.constraint}}890#' @examples891#' data(edhec)892#' ret <- edhec[, 1:4]893#'894#' pspec <- portfolio.spec(assets=colnames(ret))895#'896#' pspec <- add.constraint(portfolio=pspec, type="return", return_target=mean(colMeans(ret)))897#' @export898return_constraint <- function(type="return", return_target, enabled=TRUE, message=FALSE, ...){899Constraint <- constraint_v2(type, enabled=enabled, constrclass="return_constraint", ...)900Constraint$return_target <- return_target901return(Constraint)902}903904#' constructor for filter_constraint905#'906#' This function is called by add.constraint when type="filter" is specified, \code{\link{add.constraint}}907#'908#' Allows the user to specify a filter function which will take returns, weights,909#' and constraints as inputs, and can return a modified weights vector as output.910#'911#' Fundamentally, it could be used to filter out certain assets, or to ensure912#' that they must be long or short.913#'914#' Typically, filter functions will be called by the random portfolio simulation915#' function or via the fn_map function.916#'917#' @param type character type of the constraint918#' @param filter_name either a function to apply, or a name of a function to apply919#' @param enabled TRUE/FALSE920#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.921#' @param \dots any other passthru parameters to specify position limit constraints922#' @return an object of class 'position_limit_constraint'923#' @author Ross Bennett924#' @seealso \code{\link{add.constraint}}925#' @examples926#' data(edhec)927#' ret <- edhec[, 1:4]928#'929#' pspec <- portfolio.spec(assets=colnames(ret))930#'931#' pspec <- add.constraint(portfolio=pspec, type="position_limit", max_pos=3)932#' pspec <- add.constraint(portfolio=pspec, type="position_limit", max_pos_long=3, max_pos_short=1)933#' @export934position_limit_constraint <- function(type="position_limit", filter_name=NULL, enabled=TRUE, message=FALSE, ...){935936# check that filter_name either is a function or describes a function937#938Constraint <- constraint_v2(type, enabled=enabled, constrclass="position_limit_constraint", ...)939Constraint$filter_name <- filter_name940return(Constraint)941}942943#' Constructor for factor exposure constraint944#'945#' The factor exposure constraint sets upper and lower bounds on exposures to risk factors.946#' This function is called by add.constraint when type="factor_exposure" is specified, see \code{\link{add.constraint}}947#'948#' \code{B} can be either a vector or matrix of risk factor exposures (i.e. betas).949#' If \code{B} is a vector, the length of \code{B} must be equal to the number of950#' assets and lower and upper must be scalars. If \code{B} is passed in as a vector,951#' it will be converted to a matrix with one column.952#'953#' If \code{B} is a matrix, the number of rows must be equal to the number954#' of assets and the number of columns represent the number of factors. The length955#' of lower and upper must be equal to the number of factors. The \code{B} matrix should956#' have column names specifying the factors and row names specifying the assets.957#' Default column names and row names will be assigned if the user passes in a958#' \code{B} matrix without column names or row names.959#'960#' @param type character type of the constraint961#' @param assets named vector of assets specifying initial weights962#' @param B vector or matrix of risk factor exposures963#' @param lower vector of lower bounds of constraints for risk factor exposures964#' @param upper vector of upper bounds of constraints for risk factor exposures965#' @param enabled TRUE/FALSE966#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.967#' @param \dots any other passthru parameters to specify risk factor exposure constraints968#' @return an object of class 'factor_exposure_constraint'969#' @author Ross Bennett970#' @seealso \code{\link{add.constraint}}971#' @export972factor_exposure_constraint <- function(type="factor_exposure", assets, B, lower, upper, enabled=TRUE, message=FALSE, ...){973# Number of assets974nassets <- length(assets)975976# Assume the user has passed in a vector of betas977if(is.vector(B)){978# The number of betas must be equal to the number of assets979if(length(B) != nassets) stop("length of B must be equal to number of assets")980# The user passed in a vector of betas, lower and upper must be scalars981if(length(lower) != 1) stop("lower must be a scalar")982if(length(upper) != 1) stop("upper must be a scalar")983bnames <- names(B)984B <- matrix(B, ncol=1, dimnames=list(bnames))985}986# The user has passed in a matrix for B987if(is.matrix(B)){988# The number of rows in B must be equal to the number of assets989if(nrow(B) != nassets) stop("number of rows of B must be equal to number of assets")990# The user passed in a matrix for B --> lower and upper must be equal to the number of columns in the beta matrix991if(length(lower) != ncol(B)) stop("length of lower must be equal to the number of columns in the B matrix")992if(length(upper) != ncol(B)) stop("length of upper must be equal to the number of columns in the B matrix")993if(is.null(colnames(B))){994# The user has passed in a B matrix without column names specifying factors995colnames(B) <- paste("factor", 1:ncol(B), sep="")996}997if(is.null(rownames(B))){998# The user has passed in a B matrix without row names specifying assets999rownames(B) <- names(assets)1000}1001}10021003Constraint <- constraint_v2(type=type, enabled=enabled, constrclass="factor_exposure_constraint", ...)1004Constraint$B <- B1005Constraint$lower <- lower1006Constraint$upper <- upper1007return(Constraint)1008}10091010#' constructor for transaction_cost_constraint1011#'1012#' The transaction cost constraint specifies a proportional cost value.1013#' This function is called by add.constraint when type="transaction_cost" is specified, see \code{\link{add.constraint}}.1014#'1015#' Note that with the ROI solvers, proportional transaction cost constraint is1016#' currently only supported for the global minimum variance and quadratic1017#' utility problems with ROI quadprog plugin.1018#'1019#' @param type character type of the constraint1020#' @param assets number of assets, or optionally a named vector of assets specifying initial weights1021#' @param ptc proportional transaction cost value1022#' @param enabled TRUE/FALSE1023#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.1024#' @param \dots any other passthru parameters to specify box and/or group constraints1025#' @return an object of class 'transaction_cost_constraint'1026#' @author Ross Bennett1027#' @seealso \code{\link{add.constraint}}1028#' @examples1029#' data(edhec)1030#' ret <- edhec[, 1:4]1031#'1032#' pspec <- portfolio.spec(assets=colnames(ret))1033#'1034#' pspec <- add.constraint(portfolio=pspec, type="transaction_cost", ptc=0.01)1035#' @export1036transaction_cost_constraint <- function(type="transaction_cost", assets, ptc, enabled=TRUE, message=FALSE, ...){1037nassets <- length(assets)1038if(length(ptc) == 1) ptc <- rep(ptc, nassets)1039if(length(ptc) != nassets) stop("length of ptc must be equal to number of assets")1040Constraint <- constraint_v2(type, enabled=enabled, constrclass="transaction_cost_constraint", ...)1041Constraint$ptc <- ptc1042return(Constraint)1043}10441045#' constructor for leverage_exposure_constraint1046#'1047#' The leverage_exposure constraint specifies a maximum leverage where1048#' leverage is defined as the sum of the absolute value of the weights.1049#' Leverage exposure is computed as the sum of the absolute value of the1050#' weights, \code{sum(abs(weights))}.1051#'1052#'1053#' This should be used for constructing, for example, 130/30 portfolios or1054#' dollar neutral portfolios with 2:1 leverage. For the ROI solvers, this is1055#' implemented as a MILP problem and is not supported for problems formulated1056#' as a quadratic programming problem. This may change in the future if a MIQP1057#' solver is added.1058#'1059#' This function is called by add.constraint when type="leverage_exposure"1060#' is specified, see \code{\link{add.constraint}}.1061#'1062#' @param type character type of the constraint1063#' @param leverage maximum leverage value1064#' @param enabled TRUE/FALSE1065#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.1066#' @param \dots any other passthru parameters to specify diversification constraint1067#' an object of class 'diversification_constraint'1068#' @author Ross Bennett1069#' @seealso \code{\link{add.constraint}}1070#' @examples1071#' data(edhec)1072#' ret <- edhec[, 1:4]1073#'1074#' pspec <- portfolio.spec(assets=colnames(ret))1075#'1076#' pspec <- add.constraint(portfolio=pspec, type="leverage_exposure", leverage=1.6)1077#' @export1078leverage_exposure_constraint <- function(type="leverage_exposure", leverage=NULL, enabled=TRUE, message=FALSE, ...){1079Constraint <- constraint_v2(type, enabled=enabled, constrclass="leverage_exposure_constraint", ...)1080Constraint$leverage <- leverage1081return(Constraint)1082}10831084#' function for updating constrints, not well tested, may be broken1085#'1086#' can we use the generic update.default function?1087#' @param object object of type \code{\link{constraint}} to update1088#' @param ... any other passthru parameters, used to call \code{\link{constraint}}1089#' @author bpeterson1090#' @method update constraint10911092#' @export1093update.constraint <- function(object, ...){1094constraints <- object1095if (is.null(constraints) | !is.constraint(constraints)){1096stop("you must pass in an object of class constraints to modify")1097}1098call <- object$call1099if (is.null(call))1100stop("need an object with call component")1101extras <- match.call(expand.dots = FALSE)$...1102# if (!missing(formula.))1103# call$formula <- update.formula(formula(object), formula.)1104if (length(extras)) {1105existing <- !is.na(match(names(extras), names(call)))1106for (a in names(extras)[existing]) call[[a]] <- extras[[a]]1107if (any(!existing)) {1108call <- c(as.list(call), extras[!existing])1109call <- as.call(call)1110}1111}1112# if (hasArg(nassets)){1113# warning("changing number of assets may modify other constraints")1114# constraints$nassets<-nassets1115# }1116# if(hasArg(min)) {1117# if (is.vector(min) & length(min)!=nassets){1118# warning(paste("length of min !=",nassets))1119# if (length(min)<nassets) {stop("length of min must be equal to lor longer than nassets")}1120# constraints$min<-min[1:nassets]1121# }1122# }1123# if(hasArg(max)) {1124# if (is.vector(max) & length(max)!=nassets){1125# warning(paste("length of max !=",nassets))1126# if (length(max)<nassets) {stop("length of max must be equal to lor longer than nassets")}1127# constraints$max<-max[1:nassets]1128# }1129# }1130# if(hasArg(min_mult)){constrains$min_mult=min_mult}1131# if(hasArg(max_mult)){constrains$max_mult=max_mult}1132return(constraints)1133}11341135#' Insert a list of constraints into the constraints slot of a portfolio object1136#'1137#' This is a helper function primarily for backwards compatibility to insert1138#' constraints from a 'v1_constraint' object into the v2 'portfolio' object.1139#'1140#' @param portfolio object of class 'portfolio'1141#' @param constraints list of constraint objects1142#' @author Ross Bennett1143insert_constraints <- function(portfolio, constraints){1144# Check portfolio object1145if (is.null(portfolio) | !is.portfolio(portfolio)){1146stop("you must pass in an object of class portfolio")1147}11481149# Check that constraints is a list1150if(!is.list(constraints)) stop("constraints must be passed in as a list")11511152# Check that all objects in the list are of class constraint1153for(i in 1:length(constraints)){1154if(!is.constraint(constraints[[i]]))1155stop("constraints must be passed in as a list and all objects in constraints must be of class 'constraint'")1156}11571158portfolio$constraints <- constraints1159return(portfolio)1160}11611162#' Helper function to update v1_constraint objects to v2 specification in the portfolio object1163#'1164#' The function takes the constraints and objectives specified in the v1_constraint1165#' object and updates the portfolio object with those constraints and objectives. This1166#' function is used inside optimize.portfolio to maintain backwards compatibility1167#' if the user passes in a v1_constraint object for the constraint arg in1168#' optimize.portfolio.1169#'1170#' @param portfolio portfolio object passed into optimize.portfolio1171#' @param v1_constraint object of type v1_constraint passed into optimize.portfolio1172#' @return portfolio object containing constraints and objectives from v1_constraint1173#' @author Ross Bennett1174#' @seealso \code{\link{portfolio.spec}}, \code{\link{add.constraint}}1175#' @export1176update_constraint_v1tov2 <- function(portfolio, v1_constraint){1177if(!is.portfolio(portfolio)) stop("portfolio object must be of class 'portfolio'")1178if(!inherits(v1_constraint, "v1_constraint")) stop("v1_constraint object must be of class 'v1_constraint'")1179# Put the assets and weight_seq into slots in portfolio object1180portfolio$assets <- v1_constraint$assets1181portfolio$weight_seq <- v1_constraint$weight_seq11821183# The v1_constraint object supported 3 constraint types (weight_sum, box, and group)1184# Add weight_sum/leverage constraints from v1_constraint to portfolio1185if(!is.null(v1_constraint$min_sum) & !is.null(v1_constraint$max_sum)){1186portfolio <- add.constraint(portfolio=portfolio, type='weight_sum', min_sum=v1_constraint$min_sum, max_sum=v1_constraint$max_sum)1187}1188# Add box constraints from v1_constraint to portfolio1189if(!is.null(v1_constraint$min) & !is.null(v1_constraint$max)){1190portfolio <- add.constraint(portfolio=portfolio, type='box', min=v1_constraint$min, max=v1_constraint$max)1191}1192# Add group constraints from v1_constraint to portfolio1193if(!is.null(v1_constraint$groups) & !is.null(v1_constraint$cLO) & !is.null(v1_constraint$cUP)){1194portfolio <- add.constraint(portfolio=portfolio, type='group', groups=v1_constraint$groups, group_min=v1_constraint$cLO, group_max=v1_constraint$cUP)1195}11961197# Put the objectives from v1_constraint into the objectives slot in the portfolio1198# object. This overwrites what might already be in portfolio$objectives assuming1199# the user is using the v1_constraint object to specify the objectives1200portfolio$objectives <- v1_constraint$objectives1201return(portfolio)1202}12031204#' check if a set of weights satisfies the constraints1205#'1206#' This function checks if a set of weights satisfies all constraints. This is1207#' used as a helper function for random portfolios created with \code{rp_simplex}1208#' and \code{rp_grid} to eliminate portfolios that do not satisfy the constraints.1209#'1210#' @param weights vector of weights1211#' @param portfolio object of class 'portfolio'1212#' @return TRUE if all constraints are satisfied, FALSE if any constraint is violated1213#' @author Ross Bennett1214check_constraints <- function(weights, portfolio){12151216# get the constraints to check1217# We will check leverage, box, group, and position limit constraints1218constraints <- get_constraints(portfolio)1219min_sum <- constraints$min_sum1220max_sum <- constraints$max_sum1221min <- constraints$min1222max <- constraints$max1223groups <- constraints$groups1224cLO <- constraints$cLO1225cUP <- constraints$cUP1226group_pos <- constraints$group_pos1227div_target <- constraints$div_target1228turnover_target <- constraints$turnover_target1229turnover_penalty <- constraints$turnover_penalty1230weight_initial <- constraints$weight_initial1231max_pos <- constraints$max_pos1232max_pos_long <- constraints$max_pos_long1233max_pos_short <- constraints$max_pos_short1234leverage_exposure <- constraints$leverage1235tolerance <- .Machine$double.eps^0.512361237log_vec <- c()1238# check leverage constraints1239if(!is.null(min_sum) & !is.null(max_sum)){1240# TRUE if constraints are satisfied1241log_vec <- c(log_vec, ((sum(weights) >= min_sum) & (sum(weights) <= max_sum)))1242}12431244# check box constraints1245if(!is.null(min) & !is.null(max)){1246# TRUE if constraints are satisfied1247log_vec <- c(log_vec, (all(weights >= min) & all(weights <= max)))1248}12491250# check group constraints1251if(!is.null(groups) & !is.null(cLO) & !is.null(cUP)){1252log_vec <- c(log_vec, all(!group_fail(weights, groups, cLO, cUP, group_pos)))1253}12541255# check position limit constraints1256if(!is.null(max_pos) | !is.null(max_pos_long) | !is.null(max_pos_short)){1257log_vec <- c(log_vec, !pos_limit_fail(weights, max_pos, max_pos_long, max_pos_short))1258}12591260# check leverage exposure constraints1261if(!is.null(leverage_exposure)){1262log_vec <- c(log_vec, sum(abs(weights)) <= leverage_exposure)1263}1264# return TRUE if all constraints are satisfied, FALSE if any constraint is violated1265return(all(log_vec))1266}12671268# #' constructor for class constraint_ROI1269# #'1270# #' @param assets number of assets, or optionally a named vector of assets specifying seed weights1271# #' @param op.problem an object of type "OP" (optimization problem, of \code{ROI}) specifying the complete optimization problem, see ROI help pages for proper construction of OP object.1272# #' @param solver string argument for what solver package to use, must have ROI plugin installed for that solver. Currently support is for \code{glpk} and \code{quadprog}.1273# #' @param weight_seq seed sequence of weights, see \code{\link{generatesequence}}1274# #' @author Hezky Varon1275# #' @export1276# constraint_ROI <- function(assets, op.problem, solver=c("glpk", "quadprog"), weight_seq=NULL)1277# {1278# if(op.problem == NULL || inherits(op.problem, "OP")) {1279# stop("Need to pass in optimiztion problem of ROI:::OP type.")1280# if() stop("Need to be ROI:::OP")1281# return(structure(1282# list(1283# assets = assets,1284# constrainted_objective = op.problem,1285# solver = solver[1],1286# weight_seq = weight_seq,1287# objectives = list(),1288# call = match.call()1289# ),1290# class=c("constraint_ROI","constraint")1291# ))1292# }1293129412951296