Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
braverock
GitHub Repository: braverock/portfolioanalytics
Path: blob/master/R/mult.layer.portfolio.R
1433 views
1
2
3
# I am going to start with two levels. Once that is working, I will generalize
4
# to any arbitrary number of levels.
5
6
#' Multple Layer Portfolio Specification
7
#'
8
#' Create and specify a multiple layer portfolio
9
#'
10
#' The \code{sub.portfolios} slot is a list where each element contains the
11
#' portfolio object and rebalancing parameters for the optimization of the
12
#' sub portfolio.
13
#' This allows, for example, each sub portfolio to have different rebalancing
14
#' frequencies (i.e. monthly or quarterly), optimization methods, etc.
15
#'
16
#' Each sub portfolio is optimized with \code{optimize.portfolio.rebalancing}
17
#' to create a time series of proxy returns.
18
#'
19
#' The "top level" portfolio is used to specify the constraints and objectives
20
#' to control the optimization given the proxy returns of each sub portfolio.
21
#'
22
#' @param portfolio the "top level" portfolio
23
#' @param levels number of levels of sub-portfolios
24
#' @param \dots any additional parameters
25
#' @return a \code{mult.portfolio.spec} object with the top level portfolio
26
#' and sub portfolios with optimization parameters for each sub portfolio
27
#' @author Ross Bennett
28
#' @export
29
mult.portfolio.spec <- function(portfolio, levels=2, ...){
30
structure(c(list(top.portfolio = portfolio,
31
sub.portfolios = list()),
32
list(...)),
33
class="mult.portfolio.spec")
34
}
35
36
# constructor for sub.portfolio object
37
sub.portfolio <- function(portfolio,
38
optimize_method = c("DEoptim","random","ROI","pso","GenSA"),
39
search_size = 20000,
40
rp = NULL,
41
rebalance_on = NULL,
42
training_period = NULL,
43
trailing_periods = NULL,
44
...){
45
# Check to make sure that the portfolio passed in is a portfolio object
46
if (!is.portfolio(portfolio)) stop("portfolio passed in is not of class 'portfolio'")
47
48
# structure and return
49
return(structure( c(list(portfolio = portfolio,
50
optimize_method = optimize_method[1],
51
search_size = search_size,
52
rp = rp,
53
rebalance_on = rebalance_on,
54
training_period = training_period,
55
trailing_periods = trailing_periods),
56
list(...)),
57
class="sub.portfolio"
58
) # end structure
59
)
60
}
61
62
#' Add sub-portfolio
63
#'
64
#' Add a sub-portfolio to a multiple layer portfolio specification object
65
#'
66
#' @param mult.portfolio a \code{mult.portfolio.spec} object
67
#' @param portfolio a \code{portfolio} object to add as a sub portfolio.
68
#' @param optimize_method optimization method for the sub portfolio
69
#' @param search_size integer, how many portfolios to test, default 20,000
70
#' @param rp matrix of random portfolio weights, default NULL, mostly for automated use by rebalancing optimization or repeated tests on same portfolios
71
#' @param rebalance_on haracter string of period to rebalance on. See
72
#' \code{\link[xts]{endpoints}} for valid names.
73
#' @param training_period an integer of the number of periods to use as
74
#' a training data in the front of the returns data
75
#' @param trailing_periods an integer with the number of periods to roll over
76
#' (i.e. width of the moving or rolling window), the default is NULL will
77
#' run using the returns data from inception
78
#' @param \dots additonal passthrough parameters to \code{\link{optimize.portfolio.rebalancing}}
79
#' @param indexnum the index number of the sub portfolio. If \code{indexnum=NULL}
80
#' (the default), then the sub portfolio object is appended to the list of
81
#' sub portfolios in the \code{mult.portfolio} object. If \code{indexnum} is
82
#' specified, the portfolio in that index number is overwritten.
83
#' @seealso \code{\link{mult.portfolio.spec}} \code{\link{portfolio.spec}} \code{\link{optimize.portfolio}} \code{\link{optimize.portfolio.rebalancing}}
84
#' @author Ross Bennett
85
#' @export
86
add.sub.portfolio <- function(mult.portfolio,
87
portfolio,
88
optimize_method = c("DEoptim","random","ROI","pso","GenSA"),
89
search_size = 20000,
90
rp = NULL,
91
rebalance_on = NULL,
92
training_period = NULL,
93
trailing_periods = NULL,
94
...,
95
indexnum = NULL){
96
# Check to make sure that the portfolio passed in is a portfolio mult.portfolio
97
if(!inherits(mult.portfolio, "mult.portfolio.spec")) stop("mult.portfolio must be of class 'mult.portfolio.spec'")
98
99
# construct a sub portfolio object
100
tmp_portfolio <- sub.portfolio(portfolio=portfolio,
101
optimize_method=optimize_method[1],
102
search_size=search_size,
103
rp=rp,
104
rebalance_on=rebalance_on,
105
training_period=training_period,
106
trailing_periods=trailing_periods,
107
...=...)
108
109
if(inherits(tmp_portfolio, "sub.portfolio")){
110
if(!hasArg(indexnum) | (hasArg(indexnum) & is.null(indexnum))){
111
indexnum <- length(mult.portfolio$sub.portfolios)+1
112
}
113
mult.portfolio$sub.portfolios[[indexnum]] <- tmp_portfolio
114
}
115
return(mult.portfolio)
116
}
117
118
# This function calls optimize.portfolio.rebalancing on each sub portfolio
119
# according to the given optimization parameters and returns an xts object
120
# representing the proxy returns of each sub portfolio
121
proxy.mult.portfolio <- function(R, mult.portfolio, ...){
122
# Check to make sure that the mult.portfolio passed in is a
123
# mult.portfolio.spec object
124
if(!inherits(mult.portfolio, "mult.portfolio.spec")){
125
stop("mult.portfolio must be of class 'mult.portfolio.spec'")
126
}
127
128
n.sub.portfolios <- length(mult.portfolio$sub.portfolios)
129
if(n.sub.portfolios <= 1) stop("Must have more than 1 sub portfolio")
130
131
# Initialize list to store the returns for each sub portfolio
132
ret <- vector("list", n.sub.portfolios)
133
134
# Loop through the sub portfolios and call optimize.portfolio.rebalancing
135
# on each sub portfolio and its optimization parameters
136
for(i in 1:n.sub.portfolios){
137
#print(paste("sub portfolio", i))
138
tmp <- mult.portfolio$sub.portfolios[[i]]
139
140
# We need to subset the R object based on the names of portfolio$assets in
141
# the sub portfolio
142
# This requires that asset names match colnames(R)
143
R.tmp <- R[,names(tmp$portfolio$assets)]
144
if(ncol(R.tmp) != length(tmp$portfolio$assets)){
145
stop("R object of returns not subset correctly. Make sure the names of
146
the assets in the sub portfolio match the column names of the R object")
147
}
148
# This needs to support anything in ... that could be passed to optimize.portfolio
149
.formals <- formals(optimize.portfolio.rebalancing)
150
.formals <- modify.args(formals=.formals, arglist=NULL, R=R, dots=TRUE)
151
.formals <- modify.args(formals=.formals, arglist=tmp, dots=TRUE)
152
.formals$... <- NULL
153
#print(.formals)
154
opt <- try(do.call(optimize.portfolio.rebalancing, .formals), silent=TRUE)
155
if(!inherits(opt, "try-error")) {
156
w <- extractWeights(opt)
157
# geometric chaining TRUE/FALSE, should be FALSE if any weights are negative
158
g <- ifelse(any(w < 0), FALSE, TRUE)
159
ret.tmp <- Return.portfolio(R.tmp, weights = w, geometric = g)
160
colnames(ret.tmp) <- paste("proxy", i, sep=".")
161
ret[[i]] <- ret.tmp
162
#print(ret[[i]])
163
} else {
164
stop(paste("optimize.portfolio.rebalancing for sub portfolio", i, "generated an error or warning:", opt))
165
}
166
}
167
proxy.ret <- na.omit(do.call(cbind, ret))
168
return(proxy.ret)
169
}
170
171
172
173