Skip to content

quantstrat help - simple combine error using windows and walk.forward

1 message · Derek Wong

#
I have continued to work on this problem and have made some progress
although I am still stuck. As well as cleaning up the reproducible
code because it is quite long.  I am able to run this sequentially and
see some output however still with errors to the combine function.
Although I think its deeper then that.

 I receive this error after every iteration.

<simpleError in if (inherits(sret$indicators, "xts") & nrow(mktdata)
== nrow(sret$indicators)) {    mktdata <- sret$indicators
sret$indicators <- NULL}: argument is of length zero>

this error after completion

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): attempt to select
less than one element>
numValues: 15, numResults: 15, stopped: TRUE
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) :
  no non-missing arguments to max; returning -Inf

I looked into the walk.forward function and found nothing that
required this , but using the debugger it seems to be coming from
apply.paramset.  Since the apply.paramset fails we do not return
anything to the obj.func which then throws another error. However when
i run apply.paramset sequentially on my strategy, I do not get any
errors and see correct results. I am really confused as to the origin
of this error or how to rectify it especially since i am doing
everything sequentially. Which should prevent the previous problem of
calcRatio not getting pushed to nodes.

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"))

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]))

# Ratio indicator.
.blotter[[paste('portfolio',portfolio1.st,sep='.')]]$HedgeRatio <- Ratio
getHedgeRatio <- function(portfolio, timestamp) {
  portf <- getPortfolio(portfolio)
  timestamp <- format(timestamp,"%Y-%m-%d %H:%M:%S")

  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

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
}

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())

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

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

# apply.paramset(stratBBands, "BBOPT", portfolio.st,account.st)

# Objective Function
my.obj.func <- function(x)
{

  return(max(x$tradeStats$Net.Trading.PL) == x$tradeStats$Net.Trading.PL)

}

#WFA - sequential

require(foreach)
require(iterators)
registerDoSEQ()

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)






thanks again for your help as always,

Derek
On Tue, Sep 30, 2014 at 9:55 PM, Derek Wong <treydog999 at gmail.com> wrote: