###############################################################################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 turnover_penalty optional penalty parameter for turnover constraint827#' @param weight_initial optional initial weights vector to compute turnover from828#' @param enabled TRUE/FALSE829#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.830#' @param \dots any other passthru parameters to specify box and/or group constraints831#' @return an object of class 'turnover_constraint'832#' @author Ross Bennett833#' @seealso \code{\link{add.constraint}}834#' @examples835#' data(edhec)836#' ret <- edhec[, 1:4]837#'838#' pspec <- portfolio.spec(assets=colnames(ret))839#'840#' pspec <- add.constraint(portfolio=pspec, type="turnover", turnover_target=0.6)841#' @export842turnover_constraint <- function(type="turnover", turnover_target, turnover_penalty=NULL, weight_initial=NULL, enabled=TRUE, message=FALSE, ...){843Constraint <- constraint_v2(type, enabled=enabled, constrclass="turnover_constraint", ...)844Constraint$turnover_target <- turnover_target845Constraint$weight_initial <- weight_initial846Constraint$turnover_penalty <- turnover_penalty847return(Constraint)848}849850#' constructor for diversification_constraint851#'852#' The diversification constraint specifies a target diversification value.853#' This function is called by add.constraint when type="diversification" is854#' specified, see \code{\link{add.constraint}}. Diversification is computed855#' as \code{1 - sum(weights^2)}.856#'857#' @param type character type of the constraint858#' @param div_target diversification target value859#' @param enabled TRUE/FALSE860#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.861#' @param \dots any other passthru parameters to specify diversification constraint862#' an object of class 'diversification_constraint'863#' @author Ross Bennett864#' @seealso \code{\link{add.constraint}}865#' @examples866#' data(edhec)867#' ret <- edhec[, 1:4]868#'869#' pspec <- portfolio.spec(assets=colnames(ret))870#'871#' pspec <- add.constraint(portfolio=pspec, type="diversification", div_target=0.7)872#' @export873diversification_constraint <- function(type="diversification", div_target=NULL, enabled=TRUE, message=FALSE, ...){874Constraint <- constraint_v2(type, enabled=enabled, constrclass="diversification_constraint", ...)875Constraint$div_target <- div_target876return(Constraint)877}878879#' constructor for return_constraint880#'881#' The return constraint specifes a target mean return value.882#' This function is called by add.constraint when type="return" is specified, \code{\link{add.constraint}}883#'884#' @param type character type of the constraint885#' @param return_target return target value886#' @param enabled TRUE/FALSE887#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.888#' @param \dots any other passthru parameters889#' @return an object of class 'return_constraint'890#' @author Ross Bennett891#' @seealso \code{\link{add.constraint}}892#' @examples893#' data(edhec)894#' ret <- edhec[, 1:4]895#'896#' pspec <- portfolio.spec(assets=colnames(ret))897#'898#' pspec <- add.constraint(portfolio=pspec, type="return", return_target=mean(colMeans(ret)))899#' @export900return_constraint <- function(type="return", return_target, enabled=TRUE, message=FALSE, ...){901Constraint <- constraint_v2(type, enabled=enabled, constrclass="return_constraint", ...)902Constraint$return_target <- return_target903return(Constraint)904}905906#' constructor for filter_constraint907#'908#' This function is called by add.constraint when type="filter" is specified, \code{\link{add.constraint}}909#'910#' Allows the user to specify a filter function which will take returns, weights,911#' and constraints as inputs, and can return a modified weights vector as output.912#'913#' Fundamentally, it could be used to filter out certain assets, or to ensure914#' that they must be long or short.915#'916#' Typically, filter functions will be called by the random portfolio simulation917#' function or via the fn_map function.918#'919#' @param type character type of the constraint920#' @param filter_name either a function to apply, or a name of a function to apply921#' @param enabled TRUE/FALSE922#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.923#' @param \dots any other passthru parameters to specify position limit constraints924#' @return an object of class 'position_limit_constraint'925#' @author Ross Bennett926#' @seealso \code{\link{add.constraint}}927#' @examples928#' data(edhec)929#' ret <- edhec[, 1:4]930#'931#' pspec <- portfolio.spec(assets=colnames(ret))932#'933#' pspec <- add.constraint(portfolio=pspec, type="position_limit", max_pos=3)934#' pspec <- add.constraint(portfolio=pspec, type="position_limit", max_pos_long=3, max_pos_short=1)935#' @export936position_limit_constraint <- function(type="position_limit", filter_name=NULL, enabled=TRUE, message=FALSE, ...){937938# check that filter_name either is a function or describes a function939#940Constraint <- constraint_v2(type, enabled=enabled, constrclass="position_limit_constraint", ...)941Constraint$filter_name <- filter_name942return(Constraint)943}944945#' Constructor for factor exposure constraint946#'947#' The factor exposure constraint sets upper and lower bounds on exposures to risk factors.948#' This function is called by add.constraint when type="factor_exposure" is specified, see \code{\link{add.constraint}}949#'950#' \code{B} can be either a vector or matrix of risk factor exposures (i.e. betas).951#' If \code{B} is a vector, the length of \code{B} must be equal to the number of952#' assets and lower and upper must be scalars. If \code{B} is passed in as a vector,953#' it will be converted to a matrix with one column.954#'955#' If \code{B} is a matrix, the number of rows must be equal to the number956#' of assets and the number of columns represent the number of factors. The length957#' of lower and upper must be equal to the number of factors. The \code{B} matrix should958#' have column names specifying the factors and row names specifying the assets.959#' Default column names and row names will be assigned if the user passes in a960#' \code{B} matrix without column names or row names.961#'962#' @param type character type of the constraint963#' @param assets named vector of assets specifying initial weights964#' @param B vector or matrix of risk factor exposures965#' @param lower vector of lower bounds of constraints for risk factor exposures966#' @param upper vector of upper bounds of constraints for risk factor exposures967#' @param enabled TRUE/FALSE968#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.969#' @param \dots any other passthru parameters to specify risk factor exposure constraints970#' @return an object of class 'factor_exposure_constraint'971#' @author Ross Bennett972#' @seealso \code{\link{add.constraint}}973#' @export974factor_exposure_constraint <- function(type="factor_exposure", assets, B, lower, upper, enabled=TRUE, message=FALSE, ...){975# Number of assets976nassets <- length(assets)977978# Assume the user has passed in a vector of betas979if(is.vector(B)){980# The number of betas must be equal to the number of assets981if(length(B) != nassets) stop("length of B must be equal to number of assets")982# The user passed in a vector of betas, lower and upper must be scalars983if(length(lower) != 1) stop("lower must be a scalar")984if(length(upper) != 1) stop("upper must be a scalar")985bnames <- names(B)986B <- matrix(B, ncol=1, dimnames=list(bnames))987}988# The user has passed in a matrix for B989if(is.matrix(B)){990# The number of rows in B must be equal to the number of assets991if(nrow(B) != nassets) stop("number of rows of B must be equal to number of assets")992# The user passed in a matrix for B --> lower and upper must be equal to the number of columns in the beta matrix993if(length(lower) != ncol(B)) stop("length of lower must be equal to the number of columns in the B matrix")994if(length(upper) != ncol(B)) stop("length of upper must be equal to the number of columns in the B matrix")995if(is.null(colnames(B))){996# The user has passed in a B matrix without column names specifying factors997colnames(B) <- paste("factor", 1:ncol(B), sep="")998}999if(is.null(rownames(B))){1000# The user has passed in a B matrix without row names specifying assets1001rownames(B) <- names(assets)1002}1003}10041005Constraint <- constraint_v2(type=type, enabled=enabled, constrclass="factor_exposure_constraint", ...)1006Constraint$B <- B1007Constraint$lower <- lower1008Constraint$upper <- upper1009return(Constraint)1010}10111012#' constructor for transaction_cost_constraint1013#'1014#' The transaction cost constraint specifies a proportional cost value.1015#' This function is called by add.constraint when type="transaction_cost" is specified, see \code{\link{add.constraint}}.1016#'1017#' Note that with the ROI solvers, proportional transaction cost constraint is1018#' currently only supported for the global minimum variance and quadratic1019#' utility problems with ROI quadprog plugin.1020#'1021#' @param type character type of the constraint1022#' @param assets number of assets, or optionally a named vector of assets specifying initial weights1023#' @param ptc proportional transaction cost value1024#' @param enabled TRUE/FALSE1025#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.1026#' @param \dots any other passthru parameters to specify box and/or group constraints1027#' @return an object of class 'transaction_cost_constraint'1028#' @author Ross Bennett1029#' @seealso \code{\link{add.constraint}}1030#' @examples1031#' data(edhec)1032#' ret <- edhec[, 1:4]1033#'1034#' pspec <- portfolio.spec(assets=colnames(ret))1035#'1036#' pspec <- add.constraint(portfolio=pspec, type="transaction_cost", ptc=0.01)1037#' @export1038transaction_cost_constraint <- function(type="transaction_cost", assets, ptc, enabled=TRUE, message=FALSE, ...){1039nassets <- length(assets)1040if(length(ptc) == 1) ptc <- rep(ptc, nassets)1041if(length(ptc) != nassets) stop("length of ptc must be equal to number of assets")1042Constraint <- constraint_v2(type, enabled=enabled, constrclass="transaction_cost_constraint", ...)1043Constraint$ptc <- ptc1044return(Constraint)1045}10461047#' constructor for leverage_exposure_constraint1048#'1049#' The leverage_exposure constraint specifies a maximum leverage where1050#' leverage is defined as the sum of the absolute value of the weights.1051#' Leverage exposure is computed as the sum of the absolute value of the1052#' weights, \code{sum(abs(weights))}.1053#'1054#'1055#' This should be used for constructing, for example, 130/30 portfolios or1056#' dollar neutral portfolios with 2:1 leverage. For the ROI solvers, this is1057#' implemented as a MILP problem and is not supported for problems formulated1058#' as a quadratic programming problem. This may change in the future if a MIQP1059#' solver is added.1060#'1061#' This function is called by add.constraint when type="leverage_exposure"1062#' is specified, see \code{\link{add.constraint}}.1063#'1064#' @param type character type of the constraint1065#' @param leverage maximum leverage value1066#' @param enabled TRUE/FALSE1067#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.1068#' @param \dots any other passthru parameters to specify diversification constraint1069#' an object of class 'diversification_constraint'1070#' @author Ross Bennett1071#' @seealso \code{\link{add.constraint}}1072#' @examples1073#' data(edhec)1074#' ret <- edhec[, 1:4]1075#'1076#' pspec <- portfolio.spec(assets=colnames(ret))1077#'1078#' pspec <- add.constraint(portfolio=pspec, type="leverage_exposure", leverage=1.6)1079#' @export1080leverage_exposure_constraint <- function(type="leverage_exposure", leverage=NULL, enabled=TRUE, message=FALSE, ...){1081Constraint <- constraint_v2(type, enabled=enabled, constrclass="leverage_exposure_constraint", ...)1082Constraint$leverage <- leverage1083return(Constraint)1084}10851086#' function for updating constrints, not well tested, may be broken1087#'1088#' can we use the generic update.default function?1089#' @param object object of type \code{\link{constraint}} to update1090#' @param ... any other passthru parameters, used to call \code{\link{constraint}}1091#' @author bpeterson1092#' @method update constraint10931094#' @export1095update.constraint <- function(object, ...){1096constraints <- object1097if (is.null(constraints) | !is.constraint(constraints)){1098stop("you must pass in an object of class constraints to modify")1099}1100call <- object$call1101if (is.null(call))1102stop("need an object with call component")1103extras <- match.call(expand.dots = FALSE)$...1104# if (!missing(formula.))1105# call$formula <- update.formula(formula(object), formula.)1106if (length(extras)) {1107existing <- !is.na(match(names(extras), names(call)))1108for (a in names(extras)[existing]) call[[a]] <- extras[[a]]1109if (any(!existing)) {1110call <- c(as.list(call), extras[!existing])1111call <- as.call(call)1112}1113}1114# if (hasArg(nassets)){1115# warning("changing number of assets may modify other constraints")1116# constraints$nassets<-nassets1117# }1118# if(hasArg(min)) {1119# if (is.vector(min) & length(min)!=nassets){1120# warning(paste("length of min !=",nassets))1121# if (length(min)<nassets) {stop("length of min must be equal to lor longer than nassets")}1122# constraints$min<-min[1:nassets]1123# }1124# }1125# if(hasArg(max)) {1126# if (is.vector(max) & length(max)!=nassets){1127# warning(paste("length of max !=",nassets))1128# if (length(max)<nassets) {stop("length of max must be equal to lor longer than nassets")}1129# constraints$max<-max[1:nassets]1130# }1131# }1132# if(hasArg(min_mult)){constrains$min_mult=min_mult}1133# if(hasArg(max_mult)){constrains$max_mult=max_mult}1134return(constraints)1135}11361137#' Insert a list of constraints into the constraints slot of a portfolio object1138#'1139#' This is a helper function primarily for backwards compatibility to insert1140#' constraints from a 'v1_constraint' object into the v2 'portfolio' object.1141#'1142#' @param portfolio object of class 'portfolio'1143#' @param constraints list of constraint objects1144#' @author Ross Bennett1145insert_constraints <- function(portfolio, constraints){1146# Check portfolio object1147if (is.null(portfolio) | !is.portfolio(portfolio)){1148stop("you must pass in an object of class portfolio")1149}11501151# Check that constraints is a list1152if(!is.list(constraints)) stop("constraints must be passed in as a list")11531154# Check that all objects in the list are of class constraint1155for(i in 1:length(constraints)){1156if(!is.constraint(constraints[[i]]))1157stop("constraints must be passed in as a list and all objects in constraints must be of class 'constraint'")1158}11591160portfolio$constraints <- constraints1161return(portfolio)1162}11631164#' Helper function to update v1_constraint objects to v2 specification in the portfolio object1165#'1166#' The function takes the constraints and objectives specified in the v1_constraint1167#' object and updates the portfolio object with those constraints and objectives. This1168#' function is used inside optimize.portfolio to maintain backwards compatibility1169#' if the user passes in a v1_constraint object for the constraint arg in1170#' optimize.portfolio.1171#'1172#' @param portfolio portfolio object passed into optimize.portfolio1173#' @param v1_constraint object of type v1_constraint passed into optimize.portfolio1174#' @return portfolio object containing constraints and objectives from v1_constraint1175#' @author Ross Bennett1176#' @seealso \code{\link{portfolio.spec}}, \code{\link{add.constraint}}1177#' @export1178update_constraint_v1tov2 <- function(portfolio, v1_constraint){1179if(!is.portfolio(portfolio)) stop("portfolio object must be of class 'portfolio'")1180if(!inherits(v1_constraint, "v1_constraint")) stop("v1_constraint object must be of class 'v1_constraint'")1181# Put the assets and weight_seq into slots in portfolio object1182portfolio$assets <- v1_constraint$assets1183portfolio$weight_seq <- v1_constraint$weight_seq11841185# The v1_constraint object supported 3 constraint types (weight_sum, box, and group)1186# Add weight_sum/leverage constraints from v1_constraint to portfolio1187if(!is.null(v1_constraint$min_sum) & !is.null(v1_constraint$max_sum)){1188portfolio <- add.constraint(portfolio=portfolio, type='weight_sum', min_sum=v1_constraint$min_sum, max_sum=v1_constraint$max_sum)1189}1190# Add box constraints from v1_constraint to portfolio1191if(!is.null(v1_constraint$min) & !is.null(v1_constraint$max)){1192portfolio <- add.constraint(portfolio=portfolio, type='box', min=v1_constraint$min, max=v1_constraint$max)1193}1194# Add group constraints from v1_constraint to portfolio1195if(!is.null(v1_constraint$groups) & !is.null(v1_constraint$cLO) & !is.null(v1_constraint$cUP)){1196portfolio <- add.constraint(portfolio=portfolio, type='group', groups=v1_constraint$groups, group_min=v1_constraint$cLO, group_max=v1_constraint$cUP)1197}11981199# Put the objectives from v1_constraint into the objectives slot in the portfolio1200# object. This overwrites what might already be in portfolio$objectives assuming1201# the user is using the v1_constraint object to specify the objectives1202portfolio$objectives <- v1_constraint$objectives1203return(portfolio)1204}12051206#' check if a set of weights satisfies the constraints1207#'1208#' This function checks if a set of weights satisfies all constraints. This is1209#' used as a helper function for random portfolios created with \code{rp_simplex}1210#' and \code{rp_grid} to eliminate portfolios that do not satisfy the constraints.1211#'1212#' @param weights vector of weights1213#' @param portfolio object of class 'portfolio'1214#' @return TRUE if all constraints are satisfied, FALSE if any constraint is violated1215#' @author Ross Bennett1216check_constraints <- function(weights, portfolio){12171218# get the constraints to check1219# We will check leverage, box, group, and position limit constraints1220constraints <- get_constraints(portfolio)1221min_sum <- constraints$min_sum1222max_sum <- constraints$max_sum1223min <- constraints$min1224max <- constraints$max1225groups <- constraints$groups1226cLO <- constraints$cLO1227cUP <- constraints$cUP1228group_pos <- constraints$group_pos1229div_target <- constraints$div_target1230turnover_target <- constraints$turnover_target1231turnover_penalty <- constraints$turnover_penalty1232weight_initial <- constraints$weight_initial1233max_pos <- constraints$max_pos1234max_pos_long <- constraints$max_pos_long1235max_pos_short <- constraints$max_pos_short1236leverage_exposure <- constraints$leverage1237tolerance <- .Machine$double.eps^0.512381239log_vec <- c()1240# check leverage constraints1241if(!is.null(min_sum) & !is.null(max_sum)){1242# TRUE if constraints are satisfied1243log_vec <- c(log_vec, ((sum(weights) >= min_sum) & (sum(weights) <= max_sum)))1244}12451246# check box constraints1247if(!is.null(min) & !is.null(max)){1248# TRUE if constraints are satisfied1249log_vec <- c(log_vec, (all(weights >= min) & all(weights <= max)))1250}12511252# check group constraints1253if(!is.null(groups) & !is.null(cLO) & !is.null(cUP)){1254log_vec <- c(log_vec, all(!group_fail(weights, groups, cLO, cUP, group_pos)))1255}12561257# check position limit constraints1258if(!is.null(max_pos) | !is.null(max_pos_long) | !is.null(max_pos_short)){1259log_vec <- c(log_vec, !pos_limit_fail(weights, max_pos, max_pos_long, max_pos_short))1260}12611262# check leverage exposure constraints1263if(!is.null(leverage_exposure)){1264log_vec <- c(log_vec, sum(abs(weights)) <= leverage_exposure)1265}1266# return TRUE if all constraints are satisfied, FALSE if any constraint is violated1267return(all(log_vec))1268}12691270# #' constructor for class constraint_ROI1271# #'1272# #' @param assets number of assets, or optionally a named vector of assets specifying seed weights1273# #' @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.1274# #' @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}.1275# #' @param weight_seq seed sequence of weights, see \code{\link{generatesequence}}1276# #' @author Hezky Varon1277# #' @export1278# constraint_ROI <- function(assets, op.problem, solver=c("glpk", "quadprog"), weight_seq=NULL)1279# {1280# if(op.problem == NULL || inherits(op.problem, "OP")) {1281# stop("Need to pass in optimiztion problem of ROI:::OP type.")1282# if() stop("Need to be ROI:::OP")1283# return(structure(1284# list(1285# assets = assets,1286# constrainted_objective = op.problem,1287# solver = solver[1],1288# weight_seq = weight_seq,1289# objectives = list(),1290# call = match.call()1291# ),1292# class=c("constraint_ROI","constraint")1293# ))1294# }1295129612971298