[R-SIG-Finance] quantstrat help - simple combine error using windows and walk.forward

Derek Wong treydog999 at gmail.com
Thu Sep 25 04:36:12 CEST 2014


Hello,

I am having a lot of trouble with using walk.forward.in windows. i get the
following error. This may be related to bug #5814. If someone could please
help me i would really appreciate it. The reproducible code below is based
on the pair_trade example. Thanks.

error calling combine function:
<simpleError in fun(result.1, result.2, result.3, result.4, result.5,
result.6,     result.7, result.8, result.9, result.10, result.11,
result.12,     result.13, result.14, result.15, result.16, result.17,
result.18,     result.19, result.20, result.21, result.22, result.23,
result.24,     result.25, result.26, result.27, result.28, result.29,
result.30,     result.31, result.32, result.33, result.34, result.35,
result.36,     result.37, result.38, result.39, result.40, result.41,
result.42,     result.43, result.44, result.45, result.46, result.47,
result.48,     result.49, result.50, result.51, result.52, result.53,
result.54,     result.55, result.56, result.57, result.58, result.59,
result.60,     result.61, result.62, result.63, result.64, result.65,
result.66,     result.67, result.68, result.69, result.70, result.71,
result.72,     result.73, result.74, result.75, result.76, result.77,
result.78,     result.79, result.80, result.81, result.82, result.83,
result.84,     result.85, result.86, result.87, result.88, result.89,
result.90,     result.91, result.92, result.93, result.94, result.95,
result.96,     result.97, result.98, result.99, result.100, result.101,
result.102, result.103, result.104, result.105, result.106,     result.107,
result.108, result.109, result.110, result.111,     result.112, result.113,
result.114, result.115, result.116,     result.117, result.118, result.119,
result.120, result.121,     result.122, result.123, result.124, result.125,
result.126,     result.127, result.128, result.129, result.130, result.131,
    result.132, result.133, result.134, result.135, result.136,
result.137, result.138, result.139, result.140, result.141,     result.142,
result.143, result.144, result.145, result.146,     result.147, result.148,
result.149, result.150, result.151,     result.152, result.153, result.154,
result.155, result.156,     result.157, result.158, result.159, result.160,
result.161,     result.162, result.163, result.164, result.165, result.166,
    result.167, result.168, result.169, result.170, result.171,
result.172, result.173, result.174, result.175, result.176,     result.177,
result.178, result.179, result.180): attempt to select less than one
element>
Error in walk.forward(pairStrat, paramset.label = "BBOPT", portfolio.st =
portfolio1.st,  :
  obj.func() returned empty result
In addition: Warning message:
In max(x$tradeStats$Net.Trading.PL <http://net.trading.pl/>) :
  no non-missing arguments to max; returning -Inf


Reproducable code below:
------------------------------------------------------------


#Kindly contributed to quantstrat by Garrett See
#code borrowed heavily from existing quantstrat demos

# This is a simple pairs trading example intended to illustrate how you can
# extend existing quantstrat functionality.  It uses addPosLimits to specify
# levels and position limits, and shows how to pass a custom order sizing
# function to osFUN

# Note that it would be easier to build a spread first and treat it as a single
# instrument instead of dealing with a portfolio of stocks.

## given 2 stocks, calculate the ratio of their notional values.  If the ratio
# falls below it's 2 stdev band, then when it crosses back above it,
buy stock 1
# and sell stock 2.  If the ratio rises above it's 2 stdev band, then when it
# crosses back below it, sell stock 1 and buy stock 2.  If the ratio crosses
# its moving average, then flatten any open positions.

# The Qty of Stock A that it buys (sells) = MaxPos / lvls
# The Qty of Stock B that is sells (buys) = MaxPos * Ratio / lvls

require(quantstrat)

suppressWarnings(rm("order_book.pair1",pos=.strategy))
suppressWarnings(rm("account.pairs", "portfolio.pair1", pos=.blotter))
suppressWarnings(rm("initDate", "endDate", "startDate", "initEq", "SD", "N",
                    "symb1", "symb2", "portfolio1.st", "account.st",
                    "pairStrat", "out1"))

##### PLACE DEMO AND TEST DATES HERE #################
#
#if(isTRUE(options('in_test')$in_test))
#  # use test dates
#  {initDate="2011-01-01"
#  endDate="2012-12-31"
#  } else
#  # use demo defaults
#  {initDate="1999-12-31"
#  endDate=Sys.Date()}

initDate <- '2009-01-01'
endDate <- '2011-05-01'
startDate <- '2009-01-02'
initEq <- 100000
SD <- 2
N <- 20

MaxPos <- 1500  #max position in stockA;
# max position in stock B will be max * ratio, i.e. no hard position limit in
# Stock B
lvls <- 3  #how many times to fade; Each order's qty will = MaxPos/lvls

symb1 <- 'SPY' #change these to try other pairs
symb2 <- 'DIA' #if you change them, make sure position limits still make sense
portfolio1.st <- 'pair1'account.st <- 'pairs'

getSymbols(c(symb1, symb2), from=startDate, to=endDate, adjust=TRUE)

# The following function is used to make sure the timestamps of all symbols are
# the same deletes rows where one of the stocks is missing data
alignSymbols <- function(symbols, env=.GlobalEnv) {
  # This is a simplified version of qmao::alignSymbols()
  if (length(symbols) < 2)
    stop("Must provide at least 2 symbols")
  if (any(!is.character(symbols)))
    stop("Symbols must be vector of character strings.")
  ff <- get(symbols[1],env=env)
  for (sym in symbols[-1]) {
    tmp.sym <- get(sym,env=env)
    ff <- merge(ff, tmp.sym, all=FALSE)
  }
  for (sym in symbols) {
    assign(sym,ff[,grep(sym, colnames(ff))], env=env)
  }
  symbols
}
alignSymbols(c(symb1, symb2))

# Define Instruments
currency("USD")
stock(symb1, currency="USD", multiplier=1)
stock(symb2, currency="USD", multiplier=1)

# Initialize Portfolio, Account, and Orders
initPortf(name=portfolio1.st, c(symb1,symb2), initDate=initDate)
initAcct(account.st, portfolios=portfolio1.st, initDate=initDate, initEq=initEq)
initOrders(portfolio=portfolio1.st, initDate=initDate)

# osFUN will need to know which symbol is leg 1 and which is leg 2 as well as
# what the values are for MaxPos and lvls.  So, create a slot in portfolio to
# hold this info.
pair <- c(1, 2, MaxPos, lvls)
names(pair) <- c(symb1, symb2, "MaxPos", "lvls")
.blotter[[paste('portfolio', portfolio1.st, sep='.')]]$pair <- pair

# Create initial position limits and levels by symbol
# allow 3 entries for long and short if lvls=3.
addPosLimit(portfolio=portfolio1.st, timestamp=initDate, symbol=symb1,
            maxpos=MaxPos, longlevels=lvls, minpos=-MaxPos, shortlevels=lvls)
addPosLimit(portfolio=portfolio1.st, timestamp=initDate, symbol=symb2,
            maxpos=MaxPos, longlevels=lvls, minpos=-MaxPos, shortlevels=lvls)

# Create a strategy object
pairStrat <- strategy('pairStrat')

# Indicator function
calcRatio <- function(x) {
  #returns the ratio of notional close prices for 2 symbols
  x1 <- get(x[1])
  x2 <- get(x[2])
  mult1 <- getInstrument(x[1])$multiplier
  mult2 <- getInstrument(x[2])$multiplier
  rat <- (mult1 * Cl(x1)) / (mult2 * Cl(x2))
  colnames(rat) <- 'Ratio'
  rat
}
# Indicator used for determining entry/exits
Ratio <- calcRatio(c(symb1[1], symb2[1]))

# Store hedge ratio in portfolio so that it's available for order sizing
# function. In this example, the hedge ratio happens to be the same as the
# Ratio indicator.
.blotter[[paste('portfolio',portfolio1.st,sep='.')]]$HedgeRatio <- Ratio
#and make a function to get the most recent HedgeRatio
getHedgeRatio <- function(portfolio, timestamp) {
  portf <- getPortfolio(portfolio)
  timestamp <- format(timestamp,"%Y-%m-%d %H:%M:%S")
  # above line ensures you don't get last value of next day if using intraday
  # data and timestamp=midnight
  toDate <- paste("::", timestamp, sep="")
  Ratio <- last(portf$HedgeRatio[toDate])
  as.numeric(Ratio)
}

# Create an indicator - BBands on the Ratio
pairStrat <- add.indicator(strategy=pairStrat, name = "calcRatio",
                           arguments=list(x=c(symb1,symb2)))
pairStrat <- add.indicator(strategy=pairStrat, name = "BBands",
                           arguments=list(HLC=quote(Ratio), sd=SD, n=N,
                                          maType='SMA'),
                           label = "BBands")

#applyIndicators(strategy=pairStrat,mktdata=get(symb1[1])) #for debugging

# Create signals - buy when crossing lower band from below, sell when crossing
# upper band from above, flatten when crossing mavg from above or from below
pairStrat <- add.signal(strategy=pairStrat, name="sigCrossover",
                        arguments=list(columns=c("Ratio","up"),
                                       relationship="lt"),
                        label="cross.up")
pairStrat <- add.signal(strategy=pairStrat, name="sigCrossover",
                        arguments=list(columns=c("Ratio","dn"),
                                       relationship="gt"),
                        label="cross.dn")
pairStrat <- add.signal(strategy=pairStrat, name="sigCrossover",
                        arguments=list(columns=c("Ratio","mavg"),
                                       relationship="lt"),
                        label="cross.mid.fa")
pairStrat <- add.signal(strategy=pairStrat, name="sigCrossover",
                        arguments=list(columns=c("Ratio","mavg"),
                                       relationship="gt"),
                        label="cross.mid.fb")

# make an order sizing function
#######################_ORDER SIZING FUNCTION_##################################
# check to see which stock it is. If it's the second stock, reverse
orderqty and
# orderside
osSpreadMaxPos <- function (data, timestamp, orderqty, ordertype, orderside,
                            portfolio, symbol, ruletype, ..., orderprice) {
  portf <- getPortfolio(portfolio)
  #check to make sure pair slot has the things needed for this function
  if (!any(portf$pair == 1) && !(any(portf$pair == 2)))
    stop('pair must contain both values 1 and 2')
  if (!any(names(portf$pair) == "MaxPos") || !any(names(portf$pair) == "lvls"))
    stop('pair must contain MaxPos and lvls')

  if (portf$pair[symbol] == 1) legside <- "long"
  if (portf$pair[symbol] == 2) legside <- "short"
  MaxPos <- portf$pair["MaxPos"]
  lvls <- portf$pair["lvls"]
  ratio <- getHedgeRatio(portfolio, timestamp)
  pos <- getPosQty(portfolio, symbol, timestamp)
  PosLimit <- getPosLimit(portfolio, symbol, timestamp)
  qty <- orderqty
  if (legside == "short") {#symbol is 2nd leg
    ## Comment out next line to use equal ordersizes for each stock.
    addPosLimit(portfolio=portfolio, timestamp=timestamp, symbol=symbol,
                maxpos=round(MaxPos*ratio,0), longlevels=lvls,
                minpos=round(-MaxPos*ratio,0), shortlevels=lvls)
    ##
    qty <- -orderqty #switch orderqty for Stock B
  }

  if (qty > 0) orderside = 'long'
  if (qty < 0) orderside = 'short'

  orderqty <- osMaxPos(data=data,timestamp=timestamp, orderqty=qty,
                       ordertype=ordertype, orderside=orderside,
                       portfolio=portfolio, symbol=symbol, ruletype=ruletype,
                       ...)

  #Add the order here instead of in the ruleSignal function
  if (!is.null(orderqty) & !orderqty == 0 & !is.null(orderprice)) {
    addOrder(portfolio=portfolio, symbol=symbol,
             timestamp=timestamp, qty=orderqty, price=as.numeric(orderprice),
             ordertype=ordertype, side=orderside, replace=FALSE,
             status="open", ...=...)
  }
  return(0) #so that ruleSignal function doesn't also try to place an order
}
################################################################################

# Create entry and exit rules for longs  and for shorts. Both symbols will get
# the same buy/sell signals, but osMaxPos will reverse those for the second
# symbol.
# orderqty's are bigger than PosLimits allow. osMaxPos will adjust the orderqty
# down to 1/3 the max allowed. (1/3 is because we are using 3 levels in
# PosLimit)
pairStrat <- add.rule(strategy=pairStrat, name='ruleSignal',
                      arguments=list(sigcol="cross.dn", sigval=TRUE,
                                     orderqty=1e6, ordertype='market',
                                     orderside=NULL, osFUN='osSpreadMaxPos'),
                      type='enter')
pairStrat <- add.rule(strategy=pairStrat, name='ruleSignal',
                      arguments=list(sigcol="cross.up", sigval=TRUE,
                                     orderqty=-1e6, ordertype='market',
                                     orderside=NULL, osFUN='osSpreadMaxPos'),
                      type='enter')
pairStrat <- add.rule(strategy=pairStrat, name='ruleSignal',
                      arguments=list(sigcol="cross.mid.fb", sigval=TRUE,
                                     orderqty='all', ordertype='market',
                                     orderside=NULL),
                      type='exit')
pairStrat <- add.rule(strategy=pairStrat, name='ruleSignal',
                      arguments=list(sigcol="cross.mid.fa", sigval=TRUE,
                                     orderqty='all', ordertype='market',
                                     orderside=NULL),
                      type='exit')


## for debugging
# applySignals(strategy=pairStrat,
#              mktdata=applyIndicators(strategy=pairStrat, mktdata=get(symb1)))
##

out1<-applyStrategy(strategy=pairStrat, portfolios=portfolio1.st)

updatePortf(Portfolio=portfolio1.st,
            Dates=paste("::", as.Date(Sys.time()), sep=''))
updateAcct(account.st, Dates=paste(startDate, endDate, sep="::"))
updateEndEq(account.st, Dates=paste(startDate, endDate, sep="::"))
getEndEq(account.st, Sys.time())

dev.new()
chart.Posn(Portfolio=portfolio1.st, Symbol=symb1)
dev.new()
chart.Posn(Portfolio=portfolio1.st, Symbol=symb2)
dev.new()
chartSeries(Cl(get(symb1))/Cl(get(symb2)), TA="addBBands(n=N,sd=SD)")

ret1 <- PortfReturns(account.st)
ret1$total <- rowSums(ret1)
#ret1

if("package:PerformanceAnalytics" %in% search() ||
     require("PerformanceAnalytics",quietly=TRUE)) {
  #  getSymbols("SPY", from='1999-01-01')
  #  SPY.ret <- Return.calculate(SPY$SPY.Close)
  #  tmp <- merge(SPY.ret,ret1$total,all=FALSE)
  dev.new()
  charts.PerformanceSummary(ret1$total, geometric=FALSE, wealth.index=TRUE)
}


###############################################################################
# R (http://r-project.org/) Quantitative Strategy Model Framework
#
# Package Copyright (c) 2009-2012
# Peter Carl, Dirk Eddelbuettel, Brian G. Peterson, Jeffrey Ryan, and
Joshua Ulrich
#
# This library is distributed under the terms of the GNU Public License (GPL)
# for full details see the file COPYING
#
# $Id$
#
###############################################################################

##### PLACE THIS BLOCK AT END OF DEMO SCRIPT ###################
# book  = getOrderBook(port)
# stats = tradeStats(port)
# rets  = PortfReturns(acct)
################################################################

#Distribution Initialization
pairStrat <- add.distribution(strategy = pairStrat,
                 paramset.label ="BBOPT",
                 component.type = "indicator",
                 component.label = "BBands",
                 variable = list(n = seq(10,200, by = 10)),
                 label = "n")

pairStrat <- add.distribution(strategy = pairStrat,
                 paramset.label ="BBOPT",
                 component.type = "indicator",
                 component.label = "BBands",
                 variable = list(sd = seq(1,5,by = 0.5)),
                 label = "sd")


# Objective Function
my.obj.func <- function(x)
{
  # pick one of the following objective functions (uncomment)

  #return(max(x$tradeStats$Max.Drawdown) == x$tradeStats$Max.Drawdown)

  return(max(x$tradeStats$Net.Trading.PL <http://net.trading.pl/>) ==
x$tradeStats$Net.Trading.PL <http://net.trading.pl/>)

}

#WFA
require(doParallel)
cl <- makePSOCKcluster(2)
registerDoParallel(cl)

result <- walk.forward(pairStrat,
                       paramset.label = "BBOPT",
                       portfolio.st = portfolio1.st,
                       account.st = account.st,
                       period = "months",
                       k.training = 6,
                       k.testing = 3,
                       obj.func = my.obj.func,
                       obj.args = list(x=quote(result$apply.paramset)),
                       audit.prefix = "wfa",
                       anchored = FALSE,
                       verbose = TRUE)

	[[alternative HTML version deleted]]



More information about the R-SIG-Finance mailing list