1

We use indicators external to trading data that we merge with an OLHC object. Our objective is to build a quantstrat model that addresses multiple equities, but we continue to get error messages that indicate we have not properly built such. Furthermore, the error refers to EMA even though we do not explicitly use an EMA.

I started with #FXQuantTrader code accepted as the answer here: quantstrat: how to create multiple indicators, signal rules, which I can make work with indicators external to OHLC data and a custom function called by add.signal, but cannot make the jump to multiple equities.

This Stack Overflow entry R - Quantstart: Testing Strategy on Multiple Equities purports to address the subject directly, but the example provided offers the suggestion to

“load a test strategy you'd use your own”

which begs the question of how to build a multi-equity strategy. Trying to implement the applyStrategy call from this post using my strategy yields the error

“formal argument 'n' matched by multiple actual arguments”.

Commenting out the parameters section of this call gets me back to the original error.

The Quantstrat documentation https://www.rdocumentation.org/packages/quantstrat/versions/0.16.2 refers to the following, but each example uses only one equity: (a) MaCross (body of the documentation), (b) DataCamp course https://www.datacamp.com/community/blog/financial-trading-in-r-with-ilya-kipnis (I took it in its entirety), (c) the presentation of qauntstrat http://past.rinfinance.com/agenda/2018/BrianPeterson.html#1 at the R/Finance 2018 conference uses one EFT: EEM.

Other resources include Guy Yollin’s notes http://www.r-programming.org/papers, but slides 18, 21 and 41 of his first (setup) deck presents the same standard deviation and lookback “n” as does the SO post referred to above without an explanation. I tried various combinations, but still had the same errors.

Ilna Kipnis’s “Nuts & Bolts…” blog posts https://quantstrattrader.wordpress.com/2014/09/09/nuts-and-bolts-of-quantstrat-part-i/ demonstrate the use of multiple equities, but he does not list parameters in the applyStrategy call, so perhaps this is not where my problem is. This seems to be confirmed by to Tim Trice’s online quantstrat book, (another resource to which the quantstrat documentation refers) where he says – in the context of a multiple equity application – that

“There is no need to get into additional parameters at the moment.” (Section 5.5).

I also experimented with the “apply” function but without any success.

.blotter <- new.env()
.strategy <- new.env()

fastMA = 12 
slowMA = 26 

currency('USD')
startDate='2017-03-24'
endDate = "2017-08-05" 

initEq=1000000
portfolio.st='macd'
account.st='macd'

symbols <- c("NOV", # National-Oilwell Varco, Inc.
         "AERI", # Aerie Pharmaceuticals Inc
         "AGN" # Allergan plc
)

Cx.AERI <- c(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0)
Cx.AGN <- c(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
Cx.NOV <- c(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0)
getSymbols(symbols,from=startDate, to=endDate) # gets xts object

Tplus <- merge.xts(AERI, AGN, NOV, Cx.AERI,Cx.AGN,Cx.NOV) 
stock.st='Tplus'
initPortf(portfolio.st,symbols=stock.st)
initAcct(account.st,portfolios=portfolio.st)
initOrders(portfolio=portfolio.st)
stock(symbols,currency="USD", multiplier =1) 
strat.st<-portfolio.st
strategy(strat.st, store=TRUE)

add.indicator(strat.st, name = "MACD", 
          arguments = list(x=quote(Cl(mktdata)),
                           nFast=fastMA, 
                           nSlow=slowMA),
          label='_' 
)

macdSMAsig2 <- function(data) {
  sig <- data[, "Cx._"] >0 & data[, "macd._"] > 0
  colnames(sig) <- "upSig"
  sig
}

add.signal(strat.st,name="macdSMAsig2",
       arguments = list(data = quote(mktdata)),
       label="enterSig"
)

add.signal(strat.st,name="sigThreshold",
       arguments = list(column="signal._",
                        relationship="lt",
                        threshold=0,
                        cross=TRUE),
       label="signal.lt.zero"
)

add.rule(strat.st,name='ruleSignal', 
     # be careful to get the label of the signal column correct:
     arguments = list(sigcol="upSig.enterSig",
                      sigval=TRUE, 
                      orderqty=100, 
                      ordertype='market', 
                      orderside='long', 
                      threshold=NULL),
     type='enter',
     label='enter',
     storefun=FALSE
)

add.rule(strat.st,name='ruleSignal', 
     arguments = list(sigcol="signal.lt.zero",
                      sigval=TRUE, 
                      orderqty='all', 
                      ordertype='market', 
                      orderside='long', 
                      threshold=NULL,
                      orderset='exit2'),
     type='exit',
     label='exit'
)

out<-applyStrategy(strat.st , portfolios=portfolio.st,verbose=TRUE)

I expected some trades, but instead got this error message:

 >Error in EMA(c(45.849998, 45.549999, 45.450001, 45.25, 45.450001, 
 45.349998,  :   ncol(x) > 1. EMA only supports univariate 'x'
W Barker
  • 324
  • 2
  • 8
  • 1
    I think your issue must be because MACD must call EMA and there is a clash in the dimensions of the data (not being fed one at a time as you expect). One thing you could possibly do is run each backtest individually, store the results/transactions, and then use that in a loop to combine the results create a sort of "portfolio" return. Meaning this would give you details of if/when you would have a position in another symbol, update your P/L accordingly, etc. Quantstrat can't do this on it's own. – Jared Marks Jul 31 '19 at 15:52

2 Answers2

1

I think your issue must be because MACD must call EMA and there is a clash in the dimensions of the data (not being fed one at a time as you expect). As far as I know quantstrat can only run an INDIVIDUAL backtest for more than one equity at a time but not be aware of what happened in the other backtests. One thing you could possibly do is run each backtest individually, store the results/transactions, and then use that in a loop to combine the results create a sort of "portfolio" return. Meaning this would give you details of if/when you would have a position in another symbol, update your P/L accordingly, etc.

Jared Marks
  • 948
  • 8
  • 15
  • Yes, #Jared, I understand quantstrat is not meant to address situations where the taking any given position is dependent on the taking another position; that is not what I intend. As you suggest, I have since learned that while quantstrat does process multiple equities simultaneously (and independently), it expects external indicators to be appended to each security obj separately. So I need to merge.xts my Cx.NOV signals with the NOV object created by getSymbols, etc. I've done that, but now I get "Error in `[.xts`(data, , "Cx._") : subscript out of bounds." Is my label "Cx._" is wrong? – W Barker Aug 01 '19 at 00:08
  • @WBarker It may help to try calling `applyIndicators` and `applySignals` to help debug errors like this, and look at say `tail(mktdata)` to see that things are what you expect at each step, rather than call the full `applyStrategy`. Your error sounds like you have an issue in a signal function (when you call `applySignals`. – FXQuantTrader Aug 01 '19 at 01:49
0

Extending on the existing answer, your error should make more sense to you if you do this:

Cl(Tplus)

Notice how 3 columns are returned, instead of one close column? (Cl is searching for column names with "close/Close" in them, and returns them all (similarly for Hi,Lo, etc)

The MACD function expects one univariate (close) price series, otherwise you get the error (MACD calls the EMA functions which expect univariate price series)

The most simple fix is to do this:

add.indicator(strat.st, name = "MACD", 
              arguments = list(x=quote(Cl(mktdata)[,1]),
                               nFast=fastMA, 
                               nSlow=slowMA),
              label='_' 
)

out<-applyStrategy(strat.st , portfolios=portfolio.st,verbose=TRUE)

head(mktdata)

Now you don't get your error (though you want to do more than compute the MACD on just one time series I think).. You can now see that 2 columns are added. These are the output of calling MACD on the first close price series.

If you want to compute the MACD of all 3 price series in the one market data object, which appears to be Tplus for you, you should write a custom indicator function that computes the MACD of each individual series, merge the results, and return from the customer indicator function. (I'm guessing you want to do things like, say, buy AERI if MACD(cl(AERI)) > MACD(cl(AERI)) and MACD(cl(AERI) > MACD(cl(NOV)), that's why you store multiple price series in one mktdata object). Hope this makes sense.

By the way, while it's true applyStrategy looks by symbol independently, you can allow for some interactivity between trades on different price series using applyStrategy.rebalancing instead, if you really needed to do so. applyStrategy.rebalancing effectively runs a double loop: loop over a batch of bars/rows, then across each symbol within that batch of bars, repeat until traversing the entire time range of the datasets. This double loop permits some trade decisions (e.g. rulePctEquity) based on trades on different market data at specific points in time in the backtest.

To drastically speed up simulations on large data sets, the idea of quantstrat is to precompute (vectorise ideally) as much as possibel in advance before looping through the rows of the market data, looking for the rows where a trade action might occur.

It's easy to modify applyStrategy.rebalancing to double loop on each bar if you really wanted to, but it comes at an incredibly high computational cost if you're scanning over months/years of bar (or more realistically tick) data.

FXQuantTrader
  • 6,821
  • 3
  • 36
  • 67