[Returnanalytics-commits] r3508 - pkg/PerformanceAnalytics/R
noreply at r-forge.r-project.org
noreply at r-forge.r-project.org
Wed Aug 13 22:16:30 CEST 2014
Author: kylebalkissoon
Date: 2014-08-13 22:16:30 +0200 (Wed, 13 Aug 2014)
New Revision: 3508
Modified:
pkg/PerformanceAnalytics/R/Return.portfolio.R
Log:
Modified: pkg/PerformanceAnalytics/R/Return.portfolio.R
===================================================================
--- pkg/PerformanceAnalytics/R/Return.portfolio.R 2014-08-12 01:43:13 UTC (rev 3507)
+++ pkg/PerformanceAnalytics/R/Return.portfolio.R 2014-08-13 20:16:30 UTC (rev 3508)
@@ -1,418 +1,418 @@
-#' Calculate weighted returns for a portfolio of assets
-#'
-#' Using a time series of returns and any regular or irregular time series of weights
-#' for each asset, this function calculates the returns of a portfolio with the same
-#' periodicity of the returns data.
-#'
-#' By default, this function calculates the time series of portfolio returns given asset
-#' returns and weights. In verbose mode, the function returns a list of intermediary
-#' calculations that users may find helpful, including both asset contribution and
-#' asset value through time.
-#'
-#' When asset return and weights are matched by period, contribution is simply the
-#' weighted return of the asset. c_i = w_i * R_i Contributions are summable across the
-#' portfolio to calculate the total portfolio return.
-#'
-#' Contribution cannot be aggregated through time. For example, say we have an equal
-#' weighted portfolio of five assets with monthly returns. The geometric return of the
-#' portfolio over several months won't match any aggregation of the individual
-#' contributions of the assets, particularly if any rebalancing was done during the
-#' period.
-#'
-#' To aggregate contributions through time such that they are summable to the geometric
-#' returns of the portfolio, the calculation must track changes in the notional value of
-#' the assets and portfolio. For example, contribution during a quarter will be
-#' calculated as the change in value of the position through those three months, divided
-#' by the original value of the portfolio. Approaching it this way makes the
-#' calculation robust to weight changes as well. c_pi = V_(t-p)i - V_t)/V_ti
-#'
-#' If the user does not specify weights, an equal weight portfolio is assumed.
-#' Alternatively, a vector or single-row matrix of weights that matches the length
-#' of the asset columns may be specified. In either case, if no rebalancing period is
-#' specified, the weights will be applied at the beginning of the asset time series
-#' and no further rebalancing will take place. If a rebalancing period is specified,
-#' the portfolio will be rebalanced to the starting weights at the interval specified.
-#'
-#' Return.rebalancing will work only on daily or lower frequencies. If you are
-#' rebalancing intraday, you should be using a trades/prices framework like
-#' {\link{\code{blotter}}}, not a weights/returns framework.
-#'
-#' Irregular rebalancing can be done by specifying a time series of weights. The
-#' function uses the date index of the weights for xts-style subsetting of rebalancing
-#' periods.
-#'
-#' Weights specified for rebalancing should be thought of as "end-of-period" weights.
-#' Rebalancing periods can be thought of as taking effect immediately after the close
-#' of the bar. So, a March 31 rebalancing date will actually be in effect for April 1.
-#' A December 31 rebalancing date will be in effect on Jan 1, and so forth. This
-#' convention was chosen because it fits with common usage, and because it simplifies
-#' xts Date subsetting via endpoints.
-#'
-#' In verbose mode, the function returns a list of data and intermediary calculations.
-#' \itemize{
-#' \item{\code{returns}:}{ The portfolio returns.}
-#' \item{\code{contribution}:}{ The per period contribution to portfolio
-#' return of each asset. Contribution is calculated as BOP weight times the
-#' period's return divided by BOP value. Period contributions are summed
-#' across the individual assets to calculate portfolio return}
-#' \item{\code{BOP.Weight}:}{ Beginning of Period (BOP) Weight for each
-#' asset. An asset's BOP weight is calculated using the input weights
-#' (or assumed weights, see below) and rebalancing parameters given. The next
-#' period's BOP weight is either the EOP weights from the prior period or
-#' input weights given on a rebalance period.}
-#' \item{\code{EOP.Weight:}}{ End of Period (BOP) Weight for each asset.
-#' An asset's EOP weight is the sum of the asset's BOP weight and
-#' contribution for the period divided by the sum of the contributions and
-#' initial weights for the portfolio.}
-#' \item{\code{BOP.Value:}}{ BOP Value for each asset. The BOP value for each
-#' asset is the asset's EOP value from the prior period, unless there is a
-#' rebalance event. If there is a rebalance event, the BOP value of the
-#' asset is the rebalance weight times the EOP value of the portfolio. That
-#' effectively provides a zero-transaction cost change to the position values
-#' as of that date to reflect the rebalance. Note that the sum of the BOP
-#' values of the assets is the same as the prior period's EOP portfolio value.}
-#' \item{\code{EOP.Value:}}{ EOP Value for each asset. The EOP value is for
-#' each asset is calculated as (1 + asset return) times the asset's BOP value.
-#' The EOP portfolio value is the sum of EOP value across assets.}
-#' }
-#'
-#' To calculate BOP and EOP position value, we create an index for each position. The
-#' sum of that value across assets represents an indexed value of the total portfolio.
-#' Note that BOP and EOP position values are only computed when \code{geometric = TRUE}.
-#'
-#' From the value calculations, we can calculate different aggregations through time
-#' for the asset contributions. Those are calculated as the EOP asset value less the
-#' BOP asset value; that quantity is divided by the BOP portfolio value.
-#' Across assets, those will sum to equal the geometric chained returns of the
-#' portfolio for that same time period. The function does not do this directly, however.
-#'
-#' @aliases Return.portfolio Return.rebalancing
-#' @param R An xts, vector, matrix, data frame, timeSeries or zoo object of
-#' asset returns
-#' @param weights A time series or single-row matrix/vector containing asset
-#' weights, as decimal percentages, treated as beginning of period weights.
-#' See Details below.
-#' @param wealth.index TRUE/FALSE whether to return a wealth index. Default FALSE
-#' @param contribution if contribution is TRUE, add the weighted return
-#' contributed by the asset in a given period. Default FALSE
-#' @param geometric utilize geometric chaining (TRUE) or simple/arithmetic (FALSE)
-#' to aggregate returns. Default TRUE.
-#' @param rebalance_on Default "none"; alternatively "daily" "weekly" "monthly" "annual" to specify calendar-period rebalancing supported by \code{endpoints}.
-#' @param value The beginning of period total portfolio value. This is used for calculating position value.
-#' @param verbose If verbose is TRUE, return a list of intermediary calculations.
-#' See Details below.
-#' @param \dots any other passthru parameters. Not currently used.
-#' @return returns a time series of returns weighted by the \code{weights}
-#' parameter, or a list that includes intermediate calculations
-#' @author Peter Carl, Ross Bennett, Brian Peterson
-#' @seealso \code{\link{Return.calculate}} \code{\link{xts::endpoints}} \cr
-#' @references Bacon, C. \emph{Practical Portfolio Performance Measurement and
-#' Attribution}. Wiley. 2004. Chapter 2\cr
-#' @keywords ts multivariate distribution models
-#' @examples
-#'
-#' data(edhec)
-#' Return.rebalancing(edhec["1997",1:5], rebalance_on="quarterly") # returns time series
-#' Return.rebalancing(edhec["1997",1:5], rebalance_on="quarterly", verbose=TRUE) # returns list
-#' # with a weights object
-#' data(weights) # rebalance at the beginning of the year to various weights through time
-#' chart.StackedBar(weights)
-#' x <- Return.rebalancing(edhec["2000::",1:11], weights=weights,verbose=TRUE)
-#' chart.CumReturns(x$returns)
-#' chart.StackedBar(x$BOP.Weight)
-#' chart.StackedBar(x$BOP.Value)
-#'
-#' @rdname Return.portfolio
-#' @export Return.portfolio
-#' @export Return.rebalancing
-Return.portfolio <- Return.rebalancing <- function(R,
- weights=NULL,
- wealth.index=FALSE,
- contribution=FALSE,
- geometric=TRUE,
- rebalance_on=c(NA, 'years', 'quarters', 'months', 'weeks', 'days'),
- value=1,
- verbose=FALSE,
- ...){
- R = checkData(R, method="xts")
- rebalance_on = rebalance_on[1]
-
- # find the right unit to subtract from the first return date to create a start date
- freq = periodicity(R)
- switch(freq$scale,
- seconds = { stop("Use a returns series of daily frequency or higher.") },
- minute = { stop("Use a returns series of daily frequency or higher.") },
- hourly = { stop("Use a returns series of daily frequency or higher.") },
- daily = { time_unit = "day" },
- weekly = { time_unit = "week" },
- monthly = { time_unit= "month" },
- quarterly = { time_unit = "quarter" },
- yearly = { time_unit = "year"}
- )
-
- # calculates the end of the prior period
- start_date = seq(as.Date(index(R)[1]), length = 2, by = paste("-1", time_unit))[2]
-
- if(is.null(weights)){
- # generate equal weight vector for return columns
- weights = rep(1 / NCOL(R), NCOL(R))
- }
- if(is.vector(weights)) { # weights are a vector
- if(is.na(rebalance_on)) { # and endpoints are not specified
- # then use the weights only at the beginning of the returns series, without rebalancing
- weights = xts(matrix(weights, nrow=1), order.by=as.Date(start_date))
- } else { # and endpoints are specified
- # generate a time series of the given weights at the endpoints
- weight_dates = c(as.Date(start_date), index(R[endpoints(R, on=rebalance_on)]))
- weights = xts(matrix(rep(weights, length(weight_dates)), ncol=NCOL(R), byrow=TRUE), order.by=as.Date(weight_dates))
- }
- colnames(weights) = colnames(R)
- } else { # check the beginning_weights object for errors
- # check that weights are given in a form that is probably a time series
- weights = checkData(weights, method="xts")
- # make sure that frequency(weights)<frequency(R) ?
-
- # make sure the number of assets in R matches the number of assets in weights
- # Should we also check the names of R and names of weights?
- if(NCOL(R) != NCOL(weights)){
- if(NCOL(R) > NCOL(weights)){
- R = R[, 1:NCOL(weights)]
- warning("number of assets in beginning_weights is less than number of columns in returns, so subsetting returns.")
- } else {
- stop("number of assets is greater than number of columns in returns object")
- }
- }
- } # we should have good weights objects at this point
-
- if(as.Date(last(index(R))) < (as.Date(index(weights[1,]))+1)){
- stop(paste('last date in series',as.Date(last(index(R))),'occurs before beginning of first rebalancing period',as.Date(first(index(weights)))+1))
- }
-
- # Subset the R object if the first rebalance date is after the first date
- # in the return series
- if(as.Date(index(weights[1,])) > as.Date(first(index(R)))) {
- R <- R[paste0(as.Date(index(weights[1,]))+1, "/")]
- }
-
-
- if(geometric){
- out = Return.portfolio.geometric(R=R,
- weights=weights,
- wealth.index=wealth.index,
- contribution=contribution,
- rebalance_on=rebalance_on,
- value=value,
- verbose=verbose,
- ...=...)
- } else {
- out = Return.portfolio.arithmetic(R=R,
- weights=weights,
- wealth.index=wealth.index,
- contribution=contribution,
- rebalance_on=rebalance_on,
- verbose=verbose,
- ...=...)
- }
- return(out)
-}
-
-Return.portfolio.arithmetic <- function(R,
- weights=NULL,
- wealth.index=FALSE,
- contribution=FALSE,
- rebalance_on=c(NA, 'years', 'quarters', 'months', 'weeks', 'days'),
- verbose=FALSE,
- ...)
-{
- # bop = beginning of period
- # eop = end of period
- # Initialize objects
- bop_weights = matrix(0, NROW(R), NCOL(R))
- colnames(bop_weights) = colnames(R)
- eop_weights = period_contrib = bop_weights
- ret = vector("numeric", NROW(R))
-
- # initialize counter
- k = 1
- for(i in 1:NROW(weights)) {
- # identify rebalance from and to dates (weights[i,], weights[i+1]) and
- # subset the R(eturns) object
- from = as.Date(index(weights[i,]))+1
- if (i == nrow(weights)){
- to = as.Date(index(last(R))) # this is correct
- } else {
- to = as.Date(index(weights[(i+1),]))
- }
- returns = R[paste0(from, "::", to)]
-
- # Only enter the loop if we have a valid returns object
- if(nrow(returns) >= 1){
- # inner loop counter
- jj = 1
- for(j in 1:nrow(returns)){
- # For arithmetic returns, the beginning of period weights are always
- # equal to the rebalance weights
- bop_weights[k,] = weights[i,]
- period_contrib[k,] = coredata(returns[j,]) * bop_weights[k,]
- eop_weights[k,] = (period_contrib[k,] + bop_weights[k,]) / sum(c(period_contrib[k,], bop_weights[k,]))
- ret[k] = sum(period_contrib[k,])
-
- # increment the counters
- k = k + 1
- }
- }
- }
- R.idx = index(R)
- ret = xts(ret, R.idx)
- colnames(ret) = "portfolio.returns"
-
- if(wealth.index){
- result = cumsum(ret) + 1
- colnames(result) = "portfolio.wealthindex"
- } else {
- result = ret
- }
-
- if(verbose){
- out = list()
- out$returns = ret
- out$contribution = xts(period_contrib, R.idx)
- out$BOP.Weight = xts(bop_weights, R.idx)
- out$EOP.Weight = xts(eop_weights, R.idx)
- if(wealth.index){
- out$wealthindex = result
- }
- } else if(contribution){
- out = cbind(result, xts(period_contrib, R.idx))
- } else {
- out = result
- }
- return(out)
-}
-
-Return.portfolio.geometric <- function(R,
- weights=NULL,
- wealth.index=FALSE,
- contribution=FALSE,
- rebalance_on=c(NA, 'years', 'quarters', 'months', 'weeks', 'days'),
- value=1,
- verbose=FALSE,
- ...)
-{
- # bop = beginning of period
- # eop = end of period
- # Initialize objects
- bop_value = matrix(0, NROW(R), NCOL(R))
- colnames(bop_value) = colnames(R)
- eop_value = bop_value
-
- if(verbose | contribution){
- period_contrib = bop_value
- if(verbose){
- bop_weights = bop_value
- eop_weights = bop_value
- }
- }
- ret = eop_value_total = bop_value_total = vector("numeric", NROW(R))
-
- # The end_value is the end of period total value from the prior period
- end_value <- value
-
- # initialize counter
- k = 1
- for(i in 1:NROW(weights)) {
- # identify rebalance from and to dates (weights[i,], weights[i+1]) and
- # subset the R(eturns) object
- from = as.Date(index(weights[i,]))+1
- if (i == nrow(weights)){
- to = as.Date(index(last(R))) # this is correct
- } else {
- to = as.Date(index(weights[(i+1),]))
- }
- returns = R[paste0(from, "::", to)]
-
- # Only enter the loop if we have a valid returns object
- if(nrow(returns) >= 1){
- # inner loop counter
- jj = 1
- for(j in 1:nrow(returns)){
- # We need to know when we are at the start of this inner loop so we can
- # set the correct beginning of period value. We start a new inner loop
- # at each rebalance date.
- # Compute beginning of period values
- if(jj == 1){
- bop_value[k,] = end_value * weights[i,]
- } else {
- bop_value[k,] = eop_value[k-1,]
- }
- bop_value_total[k] = sum(bop_value[k,])
-
- # Compute end of period values
- eop_value[k,] = (1 + coredata(returns[j,])) * bop_value[k,]
- eop_value_total[k] = sum(eop_value[k,])
-
- if(contribution | verbose){
- # Compute period contribution
- period_contrib[k,] = returns[j,] * bop_value[k,] / sum(bop_value[k,])
- if(verbose){
- # Compute bop and eop weights
- bop_weights[k,] = bop_value[k,] / bop_value_total[k]
- eop_weights[k,] = eop_value[k,] / eop_value_total[k]
- }
- }
-
- # Compute portfolio returns
- # Could also compute this by summing contribution, but this way we
- # don't have to compute contribution if verbose=FALSE
- ret[k] = eop_value_total[k] / end_value - 1
-
- # Update end_value
- end_value = eop_value_total[k]
-
- # increment the counters
- jj = jj + 1
- k = k + 1
- }
- }
- }
- R.idx = index(R)
- ret = xts(ret, R.idx)
- colnames(ret) = "portfolio.returns"
-
- if(wealth.index){
- result = cumprod(1 + ret)
- colnames(result) = "portfolio.wealthindex"
- } else {
- result = ret
- }
-
- if(verbose){
- out = list()
- out$returns = ret
- out$contribution = xts(period_contrib, R.idx)
- out$BOP.Weight = xts(bop_weights, R.idx)
- out$EOP.Weight = xts(eop_weights, R.idx)
- out$BOP.Value = xts(bop_value, R.idx)
- out$EOP.Value = xts(eop_value, R.idx)
- if(wealth.index){
- out$wealthindex = result
- }
- } else if(contribution){
- out = cbind(result, xts(period_contrib, R.idx))
- } else {
- out = result
- }
- return(out)
-}
-
-###############################################################################
-# R (http://r-project.org/) Econometrics for Performance and Risk Analysis
-#
-# Copyright (c) 2004-2014 Peter Carl and Brian G. Peterson
-#
-# This R package is distributed under the terms of the GNU Public License (GPL)
-# for full details see the file COPYING
-#
-# $Id$
-#
-###############################################################################
+#' Calculate weighted returns for a portfolio of assets
+#'
+#' Using a time series of returns and any regular or irregular time series of weights
+#' for each asset, this function calculates the returns of a portfolio with the same
+#' periodicity of the returns data.
+#'
+#' By default, this function calculates the time series of portfolio returns given asset
+#' returns and weights. In verbose mode, the function returns a list of intermediary
+#' calculations that users may find helpful, including both asset contribution and
+#' asset value through time.
+#'
+#' When asset return and weights are matched by period, contribution is simply the
+#' weighted return of the asset. c_i = w_i * R_i Contributions are summable across the
+#' portfolio to calculate the total portfolio return.
+#'
+#' Contribution cannot be aggregated through time. For example, say we have an equal
+#' weighted portfolio of five assets with monthly returns. The geometric return of the
+#' portfolio over several months won't match any aggregation of the individual
+#' contributions of the assets, particularly if any rebalancing was done during the
+#' period.
+#'
+#' To aggregate contributions through time such that they are summable to the geometric
+#' returns of the portfolio, the calculation must track changes in the notional value of
+#' the assets and portfolio. For example, contribution during a quarter will be
+#' calculated as the change in value of the position through those three months, divided
+#' by the original value of the portfolio. Approaching it this way makes the
+#' calculation robust to weight changes as well. c_pi = V_(t-p)i - V_t)/V_ti
+#'
+#' If the user does not specify weights, an equal weight portfolio is assumed.
+#' Alternatively, a vector or single-row matrix of weights that matches the length
+#' of the asset columns may be specified. In either case, if no rebalancing period is
+#' specified, the weights will be applied at the beginning of the asset time series
+#' and no further rebalancing will take place. If a rebalancing period is specified,
+#' the portfolio will be rebalanced to the starting weights at the interval specified.
+#'
+#' Return.rebalancing will work only on daily or lower frequencies. If you are
+#' rebalancing intraday, you should be using a trades/prices framework like
+#' {\link{\code{blotter}}}, not a weights/returns framework.
+#'
+#' Irregular rebalancing can be done by specifying a time series of weights. The
+#' function uses the date index of the weights for xts-style subsetting of rebalancing
+#' periods.
+#'
+#' Weights specified for rebalancing should be thought of as "end-of-period" weights.
+#' Rebalancing periods can be thought of as taking effect immediately after the close
+#' of the bar. So, a March 31 rebalancing date will actually be in effect for April 1.
+#' A December 31 rebalancing date will be in effect on Jan 1, and so forth. This
+#' convention was chosen because it fits with common usage, and because it simplifies
+#' xts Date subsetting via endpoints.
+#'
+#' In verbose mode, the function returns a list of data and intermediary calculations.
+#' \itemize{
+#' \item{\code{returns}:}{ The portfolio returns.}
+#' \item{\code{contribution}:}{ The per period contribution to portfolio
+#' return of each asset. Contribution is calculated as BOP weight times the
+#' period's return divided by BOP value. Period contributions are summed
+#' across the individual assets to calculate portfolio return}
+#' \item{\code{BOP.Weight}:}{ Beginning of Period (BOP) Weight for each
+#' asset. An asset's BOP weight is calculated using the input weights
+#' (or assumed weights, see below) and rebalancing parameters given. The next
+#' period's BOP weight is either the EOP weights from the prior period or
+#' input weights given on a rebalance period.}
+#' \item{\code{EOP.Weight:}}{ End of Period (BOP) Weight for each asset.
+#' An asset's EOP weight is the sum of the asset's BOP weight and
+#' contribution for the period divided by the sum of the contributions and
+#' initial weights for the portfolio.}
+#' \item{\code{BOP.Value:}}{ BOP Value for each asset. The BOP value for each
+#' asset is the asset's EOP value from the prior period, unless there is a
+#' rebalance event. If there is a rebalance event, the BOP value of the
+#' asset is the rebalance weight times the EOP value of the portfolio. That
+#' effectively provides a zero-transaction cost change to the position values
+#' as of that date to reflect the rebalance. Note that the sum of the BOP
+#' values of the assets is the same as the prior period's EOP portfolio value.}
+#' \item{\code{EOP.Value:}}{ EOP Value for each asset. The EOP value is for
+#' each asset is calculated as (1 + asset return) times the asset's BOP value.
+#' The EOP portfolio value is the sum of EOP value across assets.}
+#' }
+#'
+#' To calculate BOP and EOP position value, we create an index for each position. The
+#' sum of that value across assets represents an indexed value of the total portfolio.
+#' Note that BOP and EOP position values are only computed when \code{geometric = TRUE}.
+#'
+#' From the value calculations, we can calculate different aggregations through time
+#' for the asset contributions. Those are calculated as the EOP asset value less the
+#' BOP asset value; that quantity is divided by the BOP portfolio value.
+#' Across assets, those will sum to equal the geometric chained returns of the
+#' portfolio for that same time period. The function does not do this directly, however.
+#'
+#' @aliases Return.portfolio Return.rebalancing
+#' @param R An xts, vector, matrix, data frame, timeSeries or zoo object of
+#' asset returns
+#' @param weights A time series or single-row matrix/vector containing asset
+#' weights, as decimal percentages, treated as beginning of period weights.
+#' See Details below.
+#' @param wealth.index TRUE/FALSE whether to return a wealth index. Default FALSE
+#' @param contribution if contribution is TRUE, add the weighted return
+#' contributed by the asset in a given period. Default FALSE
+#' @param geometric utilize geometric chaining (TRUE) or simple/arithmetic (FALSE)
+#' to aggregate returns. Default TRUE.
+#' @param rebalance_on Default "none"; alternatively "daily" "weekly" "monthly" "annual" to specify calendar-period rebalancing supported by \code{endpoints}.
+#' @param value The beginning of period total portfolio value. This is used for calculating position value.
+#' @param verbose If verbose is TRUE, return a list of intermediary calculations.
+#' See Details below.
+#' @param \dots any other passthru parameters. Not currently used.
+#' @return returns a time series of returns weighted by the \code{weights}
+#' parameter, or a list that includes intermediate calculations
+#' @author Peter Carl, Ross Bennett, Brian Peterson
+#' @seealso \code{\link{Return.calculate}} \code{\link{xts::endpoints}} \cr
+#' @references Bacon, C. \emph{Practical Portfolio Performance Measurement and
+#' Attribution}. Wiley. 2004. Chapter 2\cr
+#' @keywords ts multivariate distribution models
+#' @examples
+#'
+#' data(edhec)
+#' Return.rebalancing(edhec["1997",1:5], rebalance_on="quarterly") # returns time series
+#' Return.rebalancing(edhec["1997",1:5], rebalance_on="quarterly", verbose=TRUE) # returns list
+#' # with a weights object
+#' data(weights) # rebalance at the beginning of the year to various weights through time
+#' chart.StackedBar(weights)
+#' x <- Return.rebalancing(edhec["2000::",1:11], weights=weights,verbose=TRUE)
+#' chart.CumReturns(x$returns)
+#' chart.StackedBar(x$BOP.Weight)
+#' chart.StackedBar(x$BOP.Value)
+#'
+#' @rdname Return.portfolio
+#' @export Return.portfolio
+#' @export Return.rebalancing
+Return.portfolio <- Return.rebalancing <- function(R,
+ weights=NULL,
+ wealth.index=FALSE,
+ contribution=FALSE,
+ geometric=TRUE,
+ rebalance_on=c(NA, 'years', 'quarters', 'months', 'weeks', 'days'),
+ value=1,
+ verbose=FALSE,
+ ...){
+ R = checkData(R, method="xts")
+ rebalance_on = rebalance_on[1]
+
+ # find the right unit to subtract from the first return date to create a start date
+ freq = periodicity(R)
+ switch(freq$scale,
+ seconds = { stop("Use a returns series of daily frequency or higher.") },
+ minute = { stop("Use a returns series of daily frequency or higher.") },
+ hourly = { stop("Use a returns series of daily frequency or higher.") },
+ daily = { time_unit = "day" },
+ weekly = { time_unit = "week" },
+ monthly = { time_unit= "month" },
+ quarterly = { time_unit = "quarter" },
+ yearly = { time_unit = "year"}
+ )
+
+ # calculates the end of the prior period
+ # need to use the if on quarter as quarter is incompatible with seq (it does not work with by)
+ if(time_unit=='quarter'){ start_date = as.yearqtr(seq(as.Date(index(R)[1]), length = 2, by = paste("-3", 'month'))[2])}else{ start_date = seq(as.Date(index(R)[1]), length = 2, by = paste("-1", time_unit))[2]}
+ if(is.null(weights)){
+ # generate equal weight vector for return columns
+ weights = rep(1 / NCOL(R), NCOL(R))
+ }
+ if(is.vector(weights)) { # weights are a vector
+ if(is.na(rebalance_on)) { # and endpoints are not specified
+ # then use the weights only at the beginning of the returns series, without rebalancing
+ weights = xts(matrix(weights, nrow=1), order.by=as.Date(start_date))
+ } else { # and endpoints are specified
+ # generate a time series of the given weights at the endpoints
+ weight_dates = c(as.Date(start_date), index(R[endpoints(R, on=rebalance_on)]))
+ weights = xts(matrix(rep(weights, length(weight_dates)), ncol=NCOL(R), byrow=TRUE), order.by=as.Date(weight_dates))
+ }
+ colnames(weights) = colnames(R)
+ } else { # check the beginning_weights object for errors
+ # check that weights are given in a form that is probably a time series
+ weights = checkData(weights, method="xts")
+ # make sure that frequency(weights)<frequency(R) ?
+
+ # make sure the number of assets in R matches the number of assets in weights
+ # Should we also check the names of R and names of weights?
+ if(NCOL(R) != NCOL(weights)){
+ if(NCOL(R) > NCOL(weights)){
+ R = R[, 1:NCOL(weights)]
+ warning("number of assets in beginning_weights is less than number of columns in returns, so subsetting returns.")
+ } else {
+ stop("number of assets is greater than number of columns in returns object")
+ }
+ }
+ } # we should have good weights objects at this point
+
+ if(as.Date(last(index(R))) < (as.Date(index(weights[1,]))+1)){
+ stop(paste('last date in series',as.Date(last(index(R))),'occurs before beginning of first rebalancing period',as.Date(first(index(weights)))+1))
+ }
+
+ # Subset the R object if the first rebalance date is after the first date
+ # in the return series
+ if(as.Date(index(weights[1,])) > as.Date(first(index(R)))) {
+ R <- R[paste0(as.Date(index(weights[1,]))+1, "/")]
+ }
+
+
+ if(geometric){
+ out = Return.portfolio.geometric(R=R,
+ weights=weights,
+ wealth.index=wealth.index,
+ contribution=contribution,
+ rebalance_on=rebalance_on,
+ value=value,
+ verbose=verbose,
+ ...=...)
+ } else {
+ out = Return.portfolio.arithmetic(R=R,
+ weights=weights,
+ wealth.index=wealth.index,
+ contribution=contribution,
+ rebalance_on=rebalance_on,
+ verbose=verbose,
+ ...=...)
+ }
+ return(out)
+}
+
+Return.portfolio.arithmetic <- function(R,
+ weights=NULL,
+ wealth.index=FALSE,
+ contribution=FALSE,
+ rebalance_on=c(NA, 'years', 'quarters', 'months', 'weeks', 'days'),
+ verbose=FALSE,
+ ...)
+{
+ # bop = beginning of period
+ # eop = end of period
+ # Initialize objects
+ bop_weights = matrix(0, NROW(R), NCOL(R))
+ colnames(bop_weights) = colnames(R)
+ eop_weights = period_contrib = bop_weights
+ ret = vector("numeric", NROW(R))
+
+ # initialize counter
+ k = 1
+ for(i in 1:NROW(weights)) {
+ # identify rebalance from and to dates (weights[i,], weights[i+1]) and
+ # subset the R(eturns) object
+ from = as.Date(index(weights[i,]))+1
+ if (i == nrow(weights)){
+ to = as.Date(index(last(R))) # this is correct
+ } else {
+ to = as.Date(index(weights[(i+1),]))
+ }
+ returns = R[paste0(from, "::", to)]
+
+ # Only enter the loop if we have a valid returns object
+ if(nrow(returns) >= 1){
+ # inner loop counter
+ jj = 1
+ for(j in 1:nrow(returns)){
+ # For arithmetic returns, the beginning of period weights are always
+ # equal to the rebalance weights
+ bop_weights[k,] = weights[i,]
+ period_contrib[k,] = coredata(returns[j,]) * bop_weights[k,]
+ eop_weights[k,] = (period_contrib[k,] + bop_weights[k,]) / sum(c(period_contrib[k,], bop_weights[k,]))
+ ret[k] = sum(period_contrib[k,])
+
+ # increment the counters
+ k = k + 1
+ }
+ }
+ }
[TRUNCATED]
To get the complete diff run:
svnlook diff /svnroot/returnanalytics -r 3508
More information about the Returnanalytics-commits
mailing list