###############################################################################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#' constructor for class portfolio13#'14#' The portfolio object is created with \code{portfolio.spec}. The portfolio15#' object is an S3 object of class 'portfolio' used to hold the initial asset weights,16#' constraints, objectives, and other information about the portfolio. The only17#' required argument to \code{portfolio.spec} is \code{assets}.18#'19#' The portfolio object contains the following elements:20#' \describe{21#' \item{\code{assets}}{ named vector of the seed weights}22#' \item{\code{category_labels}}{ character vector to categorize the assets by sector, geography, etc.}23#' \item{\code{weight_seq}}{ sequence of weights used by \code{\link{random_portfolios}}. See \code{\link{generatesequence}}}24#' \item{\code{constraints}}{ a list of constraints added to the portfolio object with \code{\link{add.constraint}}}25#' \item{\code{objectives}}{ a list of objectives added to the portfolio object with \code{\link{add.objective}}}26#' \item{\code{call}}{ the call to \code{portfolio.spec} with all of the specified arguments}27#' }28#'29#' @param assets number of assets, or optionally a named vector of assets specifying seed weights. If seed weights are not specified, an equal weight portfolio will be assumed.30#' @param name give the portfolio a name, the default name will be 'portfolio'31#' @param category_labels character vector to categorize assets by sector, industry, geography, market-cap, currency, etc. Default NULL32#' @param weight_seq seed sequence of weights, see \code{\link{generatesequence}} Default NULL33#' @param message TRUE/FALSE. The default is message=FALSE. Display messages if TRUE.34#' @return an object of class \code{portfolio}35#' @author Ross Bennett, Brian G. Peterson36#' @aliases portfolio37#' @seealso38#' \code{\link{add.constraint}},39#' \code{\link{add.objective}},40#' \code{\link{optimize.portfolio}}41#' @examples42#' data(edhec)43#' pspec <- portfolio.spec(assets=colnames(edhec))44#' pspec <- portfolio.spec(assets=10, weight_seq=generatesequence())45#' @export46portfolio.spec <- function(assets=NULL, name = 'portfolio', category_labels=NULL, weight_seq=NULL, message=FALSE) {47# portfolio.spec is based on the v1_constraint object, but removes48# constraint specification49if (is.null(assets)) {50stop("You must specify the assets")51}5253if(!is.null(assets)){54# TODO FIXME this doesn't work quite right on matrix of assets55if(is.numeric(assets)){56if (length(assets) == 1) {57nassets = assets58# we passed in a number of assets, so we need to create the vector59if(message) message("assuming equal weighted seed portfolio")60assets <- rep(1 / nassets, nassets)61} else {62nassets = length(assets)63}64# and now we may need to name them65if (is.null(names(assets))) {66for(i in 1:length(assets)){67names(assets)[i] <- paste("Asset",i,sep=".")68}69}70}71if(is.character(assets)){72nassets = length(assets)73assetnames = assets74if(message) message("assuming equal weighted seed portfolio")75assets <- rep(1 / nassets, nassets)76names(assets) <- assetnames # set names, so that other code can access it,77# and doesn't have to know about the character vector78# print(assets)79}80# if assets is a named vector, we'll assume it is current weights81}8283# If category_labels is not null then the user has passed in category_labels84if(!is.null(category_labels)){85if(!is.character(category_labels)){86stop("category_labels must be a character vector")87}88if(length(category_labels) != length(assets)) {89stop("length(category_labels) must be equal to length(assets)")90}91# Turn category_labels into a list that can be used with group constraints92unique_labels <- unique(category_labels)93tmp <- list()94for(i in 1:length(unique_labels)){95tmp[[unique_labels[i]]] <- which(category_labels == unique_labels[i])96}97category_labels <- tmp98}99100## now structure and return101return(structure(102list(103name = name,104assets = assets,105category_labels = category_labels,106weight_seq = weight_seq,107constraints = list(),108objectives = list(),109call = match.call()110),111class=c("portfolio.spec","portfolio")112))113}114115#' check function for portfolio116#'117#' @param x object to test for type \code{portfolio}118#' @author Ross Bennett119#' @export120is.portfolio <- function( x ) {121inherits( x, "portfolio" )122}123124#' Regime Portfolios125#'126#' Construct a \code{regime.portfolios} object that contains a time series of127#' regimes and portfolios corresponding to the regimes.128#'129#' Create a \code{regime.portfolios} object to support regime switching130#' optimization. This object is then passed in as the \code{portfolio}131#' argument in \code{optimize.portfolio}. The regime is detected and the132#' corresponding portfolio is selected. For example, if the current133#' regime is 1, then portfolio 1 will be selected and used in the134#' optimization.135#'136#' @param regime xts or zoo object specifying the regime137#' @param portfolios list of portfolios created by138#' \code{combine.portfolios} with corresponding regimes139#' @return a \code{regime.portfolios} object with the following elements140#' \describe{141#' \item{regime: }{An xts object of the regime}142#' \item{portfolio: }{List of portfolios corresponding to the regime}143#' }144#' @author Ross Bennett145#' @export146regime.portfolios <- function(regime, portfolios){147if(!inherits(regime, c("xts", "zoo"))) stop("regime object must be an xts or zoo object")148if(!inherits(portfolios, "portfolio.list")) stop("portfolios object must be a portfolio.list object")149150n.regimes <- length(unique(regime))151n.portfolios <- length(portfolios)152if(n.regimes != n.portfolios) stop("Number of portfolios must match the number of regimes")153154# Check to ensure the assets in each portfolio are equal155for(i in 2:length(portfolios)){156if(!identical(portfolios[[1]]$assets, portfolios[[i]]$assets)){157stop("The assets in each portfolio must be identical")158}159}160# get the unique asset names of each portfolio161# asset names matter in hierarchical optimization162asset.names <- unique(unlist(lapply(portfolios, function(x) names(x$assets))))163assets <- rep(1 / length(asset.names), length(asset.names))164names(assets) <- asset.names165# structure and return166return(structure(list(regime=regime, portfolio.list=portfolios, assets=assets),167class=c("regime.portfolios", "portfolio")))168}169170171