###############################################################################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#' apply a function over a configurable trailing period13#'14#' this function is primarily designed for use with portfolio functions passing15#' 'x' or 'R' and weights, but may be usable for other things as well, see Example for a vector example.16#'17#' called with e.g.18#'19#' trailingFUN(seq(1:100), weights=NULL, n=12, FUN='mean',FUNargs=list())20#'21#' @param R an xts, vector, matrix, data frame, timeSeries or zoo object of asset returns22#' @param weights a vector of weights to test23#' @param \dots any other passthru parameters24#' @param n numeric number of trailing periods25#' @param FUN string describing the function to be called26#' @param FUNargs list describing any additional arguments27#' @export28trailingFUN <- function(R, weights, n=0, FUN, FUNargs=NULL, ...) {2930if (is.null(FUN)) {stop("you must supply a function to apply to")}3132funname<-FUN33FUN<-match.fun(FUN)3435nargs <-list(...)36if(length(nargs)==0) nargs=NULL37if (length('...')==0 | is.null('...')) {38# rm('...')39nargs=NULL40}4142if(!is.null(nrow(R))) R<-R[((nrow(R)-n)):nrow(R),]43else R<-R[(length(R)-n):length(R)]444546if(is.function(FUN)){47.formals <- formals(FUN)48onames <- names(.formals)49if(is.list(FUNargs)){50#TODO FIXME only do this if R and weights are in the argument list of the fn51if(is.null(nargs$R) | !length(nargs$R)==length(R)) nargs$R <- R52if(!is.null(nargs$x) | !length(nargs$x)==length(R)) nargs$x <- R5354if(is.null(nargs$weights)) nargs$weights <- weights5556pm <- pmatch(names(FUNargs), onames, nomatch = 0L)57if (any(pm == 0L))58warning(paste("some FUNargs stored for",funname,"do not match"))59# this line overwrites the names of things stored in $FUNargs with names from formals.60# I'm not sure it's a good idea, so commenting for now, until we prove we need it61#names(FUNargs[pm > 0L]) <- onames[pm]62.formals[pm] <- FUNargs[pm > 0L]63#now add dots64if (length(nargs)) {65dargs<-nargs66pm <- pmatch(names(dargs), onames, nomatch = 0L)67names(dargs[pm > 0L]) <- onames[pm]68.formals[pm] <- dargs[pm > 0L]69}70.formals$... <- NULL71} else {72warning('no FUNargs passed for function')73}74} else {75stop('FUN must specify an R function')76}7778tmp_measure = try((do.call(FUN,.formals)) ,silent=TRUE)7980if(inherits(tmp_measure,"try-error") | is.na(tmp_measure)) {81message(paste("trailing function generated an error or warning:",tmp_measure))82} else {83return(tmp_measure)84}85}8687