Search code examples
rparallel-processingxtsquantmodquantstrat

Optimizing Signal Parameters with Quantstrat results in error: attempt to select less than one element


I have a simple long-only bollinger strategy implemented in quantstrat (reproducable example below). The code runs properly, but now I want to optimize the sigThreshold values (i.e. 0.3 and 0.7). The apply.paramset function runs through the defined parameter range but in the end shows the error

error calling combine function:
<simpleError in fun(result.1, result.2, 

...

result.137, result.138): attempt to select less than one element>

which I don't know how to Interpret. I am happy for any advice. Please find the code below:

require(quantstrat)
require(foreach)
require(doParallel)

startDate <- '2010-01-01'  
endDate <-  '2015-01-01'   
symbols = c('XLF')
Sys.setenv(TZ='UTC')       


getSymbols(symbols, 
           src='yahoo', 
           index.class=c('POSIXt','POSIXct'),
           from=startDate, to=endDate, 
           adjust=TRUE)


initDate <- startDate
initEq <- 1000000
currency('USD')
stock(symbols, currency='USD',multiplier=1)


rm.strat('bbands') 
initPortf(name='bbands', symbols, initDate=initDate)
initAcct(name='bbands', portfolios='bbands',
         initDate=initDate, initEq=initEq)
initOrders(portfolio='bbands', initDate=initDate)


strategy.st <- 'bbands'
account.st <- 'my.account'
portfolio.st <- 'bollinger'
strategy(strategy.st, store=TRUE)


# add indicators
add.indicator(strategy.st, name = 'BBands',
              arguments = list(HLC = quote(Cl(mktdata)), 
                               maType='SMA', 
                               n=40, 
                               sd=2), 
              label='BBands')


#add signals
add.signal(strategy.st, name='sigThreshold',
           arguments=list(column='pctB', 
                          threshold = 1.0, 
                          relationship='gt', 
                          cross=TRUE),
           label='Cl.gt.UpperBand')

add.signal(strategy.st, name='sigThreshold',
           arguments=list(column ='pctB', 
                          threshold = 0, 
                          relationship='lt', 
                          cross=TRUE),
           label='Cl.lt.LowerBand')

add.signal(strategy.st, name='sigThreshold',
           arguments=list(column ='pctB', 
                          threshold = 0.7, 
                          relationship='gte', 
                          cross = TRUE),
           label='Cl.gte.0.7')

add.signal(strategy.st, name='sigThreshold',
           arguments=list(column ='pctB', 
                          threshold = 0.3, 
                          relationship='lte', 
                          cross = TRUE),
           label='Cl.lt.0.3')



#add rules
add.rule(strategy.st, name='ruleSignal',
         arguments=list(sigcol='Cl.gt.UpperBand',
                        TxnFees = -5,
                        sigval=TRUE, 
                        orderqty= 'all',
                        ordertype='market', 
                        orderside=NULL),

         type='exit')

add.rule(strategy.st, name='ruleSignal',
         arguments=list(sigcol='Cl.lt.LowerBand',
                        TxnFees = -5,
                        sigval=TRUE, 
                        orderqty= 500, 
                        ordertype='market', 
                        orderside=NULL, 
                        osFUN=osMaxPos),
         type='enter')

add.rule(strategy.st, name='ruleSignal',
         arguments=list(sigcol='Cl.gte.0.7',
                        TxnFees = -5,
                        sigval=TRUE, 
                        orderqty= 500,
                        ordertype='market', 
                        orderside=NULL, 
                        osFUN=osMaxPos),
         type='enter')

add.rule(strategy.st, name='ruleSignal',
         arguments=list(sigcol='Cl.lt.0.3',
                        TxnFees = -5,
                        sigval=TRUE, 
                        orderqty='all',
                        ordertype='market', 
                        orderside=NULL),
         type='exit')

# add positon limit
addPosLimit('bbands', 'XLF', timestamp=initDate, maxpos=500, minpos=0)


SD = 2
N = 40

out <- applyStrategy('bbands',
  portfolios='bbands',parameters=list(sd=SD,n=N))


updatePortf('bbands')
updateAcct('bbands')
updateEndEq('bbands')

Now, here is what the whole question is about

# start optimization here
.upper.range.distribution = seq(0.4, 0.9, by = 0.05)
.lower.range.distribution = seq(0, 0.7, by = 0.05)

add.distribution(strategy.st,
                 paramset.label = 'sigThreshold',
                 component.type = 'signal',
                 component.label = 'Cl.lt.0.3',
                 variable = list(threshold = .lower.range.distribution),
                 label = 'LOWER'
)


add.distribution(strategy.st,
                 paramset.label = 'sigThreshold',
                 component.type = 'signal',
                 component.label = 'Cl.gte.0.7',
                 variable = list(threshold = .upper.range.distribution),
                 label = 'UPPER'
)


add.distribution.constraint(strategy.st,
                            paramset.label = 'sigThreshold',
                            distribution.label.1 = 'LOWER',
                            distribution.label.2 = 'UPPER',
                            operator = '<',
                            label = 'RANGE'
)


rm.strat(portfolio.st)
rm.strat(account.st)

initPortf(portfolio.st, symbols='XLF', initDate=initDate, currency='USD')
initAcct(account.st, portfolios=portfolio.st,
         initDate=initDate, currency='USD')
initOrders(portfolio.st, initDate=initDate)



apply.paramset(strategy.st, paramset.label='sigThreshold',
               portfolio.st=portfolio.st, account.st=account.st, nsamples=0)

Solution

  • You need to add the position limit to your portfolio before you call apply.paramset. And you probably want to assign the output of apply.paramset to something. This worked for me using the latest revisions of blotter and quantstrat.

    addPosLimit(portfolio.st, 'XLF', timestamp=initDate, maxpos=500, minpos=0)
    ap <- apply.paramset(strategy.st, paramset.label='sigThreshold',
        portfolio.st=portfolio.st, account.st=account.st, nsamples=0)