Nuts and Bolts of Quantstrat, Part II

[This article was first published on QuantStrat TradeR » R, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Last week, I covered the boilerplate code in quantstrat.

This post will cover parameters and adding indicators to strategies in quantstrat.

Let’s look at a the code I’m referring to for this walkthrough:

pctATR <- .02
period <- 10
atrOrder <- TRUE

nRSI <- 2
buyThresh <- 20
sellThresh <- 80
nSMA <- 200

add.indicator(, name="lagATR", 
              arguments=list(HLC=quote(HLC(mktdata)), n=period), 

add.indicator(, name="RSI",
              arguments=list(price=quote(Cl(mktdata)), n=nRSI),

add.indicator(, name="SMA",
              arguments=list(x=quote(Cl(mktdata)), n=nSMA),

This code contains two separate chunks–parameters and indicators. The parameters chunk is simply a place to store values in one area, and then call them as arguments to the add.indicator and add.signal functions. Parameters are simply variables assigned to values that can be updated when a user wishes to run a demo (or in other ways, when running optimization processes).

Indicators are constructs computed from market data, and some parameters that dictate the settings of the function used to compute them. Most well-known indicators, such as the SMA (simple moving average), EMA, and so on, usually have one important component, such as the lookback period (aka the ubiquitous n). These are the parameters I store in the parameters chunk of code.

Adding an indicator in quantstrat has five parts to it. They are:

1) The add.indicator function call
2) The name of the strategy to add the indicator to (which I always call, standing for strategy string)
3) The name of the indicator function, in quotes (E.G. such as “SMA”, “RSI”, etc.)
4) The arguments to the above indicator function, which are the INPUTS in this statement arguments=list(INPUTS)
5) The label that signals and possibly rules will use–which is the column name in the mktdata object.

Notice that the market data (mktdata) input to the indicators has a more unique input style, as it’s wrapped in a quote() function call. This quote function call essentially tells the strategy that the strategy will obtain the object referred to in the quotes later. The mktdata object is initially the OHLCV(adjusted) price time series one originally obtains from yahoo (or elsewhere), as far as my demos will demonstrate for the foreseeable future. However, the mktdata object will later come to contain all of the indicators and signals added within the strategy. So because of this, here are some functions that one should familiarize themselves with regarding some time series data munging:

Op: returns all columns in the mktdata object containing the term “Open”
Hi: returns all columns in the mktdata object containing the term “High”
Lo: returns all columns in the mktdata object containing the term “Low”
Cl: returns all columns in the mktdata object containing the term “Close”
Vo: returns all columns in the mktdata object containing the term “Volume”
HLC: returns all columns in the mktdata object containing “High”, “Low”, or “Close”.
OHLC: same as above, but includes “Open”.

These all ignore case.

For these reasons, please avoid using these “reserved” terms when labeling (that is, column naming in step 5) your indicators/signals/rules. One particularly easy mistake to make is using the word “slow”. For instance, a naive labeling convention may be to use “maFast” and “maSlow” as labels for, say, a 50-day and 200-day SMA, respectively, and then maybe implement an indicator that uses an HLC for an argument, such as ATR. This may create errors down the line when more than one column has the name “Low”. In the old (CRAN) version of TTR–that is, the version that gets installed if one simply types in


as opposed to

install.packages("TTR",  repos="")

the SMA function will still append the term “Close” to the output. I’m sure some of you have seen some obscure error when calling applyStrategy. It might look something like this:

length of 'dimnames' [2] not equal to array extent

This arises as the result of bad labeling. The CRAN version of TTR runs into this from time to time, and if you’re stuck on that version, a kludge to work around this is instead of using


to use


instead. That [,1] specifies only the first column in which the term “Close” appears. However, I simply recommend upgrading to a newer version of TTR from R-forge. On Windows, this means using R 3.0.3 rather than 3.1.1, due to R-forge’s lack of binaries for Windows for the most recent version of TTR (only source is available), at least as of the time of this writing.

On a whole, however, I highly recommend avoiding reserved market data keywords (open, high, low, close, volume, and analogous keywords for tick data) for labels.

One other aspect to note about labeling indicators is that the indicator column name is not merely the argument to “label”, but rather, the label you provide is appended onto the output of the function. In DSTrading and IKTrading, for instance, all of the indicators (such as FRAMA) come with output column headings. So, when computing the FRAMA of a time series, you may get something like this:

> test <- FRAMA(SPY)
> head(test)
           FRAMA trigger
2000-01-06    NA      NA
2000-01-07    NA      NA
2000-01-10    NA      NA
2000-01-11    NA      NA
2000-01-12    NA      NA
2000-01-13    NA      NA

When adding indicators, the user-provided label will come after a period following the initial column name output, and the column name will be along the lines of “FunctionOutput.userLabel”.

Beyond pitfalls and explanations of labeling, the other salient aspect of indicators is the actual indicator function that’s called, and how its arguments function.

When adding indicators, I use the following format:

add.indicator(, name="INDICATOR_FUNCTION",
              arguments=list(x=quote(Cl(mktdata)), OTHERINPUTS),

This is how these two aspects work:

The INDICATOR_FUNCTION is an actual R function that should take in some variant of an OHLC object (whether one column–most likely close, HLC, or whatever else). Functions such as RSI, SMA, and lagATR (from my IKTrading library) are all examples of such functions. To note, there is nothing “official” as opposed to “custom” about the functions I use for indicators. Indicators are merely R functions (that can be written by any R user) that take in a price series as one of the arguments.

The inputs to these functions are enclosed in the arguments input to the add.indicator function. That is, the part of the syntax that looks like this:

arguments=list(x=quote(Cl(mktdata)), n=nSMA)

These arguments are the inputs to the function. For instance, if one would write:


One would see:

function (x, n = 10, ...) 

In this case, x is a time series based on the market data (that is, the mktdata object), and n is a parameter. As pointed out earlier, the syntax for the mktdata involves the use of the quote function. However, all other parameters to the SMA (or any other) function call are static, at least per individual backtest (these can vary when doing optimization/parameter exploration). Thus, for the classic 200-day simple moving average, the appropriate syntax would contain:

add.indicator(, "SMA",
              arguments=list(x=quote(Cl(mktdata)), n=200),

In my backtests, I store the argument to n above the add.indicator call in my parameters chunk of code for ease of location. The reason for this is that when adding multiple indicators, signals, and rules, it’s fairly easy to lose track of a hard-coded value among the interspersed code, so I prefer to keep my numerical values collected in one place and reference them in the actual indicator, signal, and rule syntax.

Lastly, one final piece of advice is that when constructing a strategy, one need not have all the signals and rules implemented just to check how the indicators will be added to the mktdata object. Instead, try this, after running the code through the add.indicator syntax and no further if you’re ever unsure what your mktdata object will look like. Signals (at least in my demos) will start off with a commented


bit of syntax. If you see that line, you know that there are no more indicators to add. In any case, the following is a quick way of inspecting indicator output.

test <- applyIndicators(, mktdata=OHLC(SOME_DATA_HERE))

For example, using XLB:

test <- applyIndicators(, mktdata=OHLC(XLB))
head(test, 12)

Which would give the output:

> head(test, 12)
           XLB.Open XLB.High  XLB.Low XLB.Close atr.atrX  EMA.rsi SMA.sma
2003-01-02 15.83335 16.09407 15.68323  16.08617       NA       NA      NA
2003-01-03 16.03877 16.05457 15.91235  15.99926       NA       NA      NA
2003-01-06 16.10988 16.41011 16.10988  16.30740       NA 78.00000      NA
2003-01-07 16.38641 16.38641 16.18098  16.25209       NA 60.93750      NA
2003-01-08 16.19679 16.19679 15.80964  15.83335       NA 14.13043      NA
2003-01-09 15.95186 16.12568 15.92026  16.07827       NA 54.77099      NA
2003-01-10 15.94396 16.27579 15.94396  16.25209       NA 72.94521      NA
2003-01-13 16.35480 16.37061 16.12568  16.18098       NA 54.89691      NA
2003-01-14 16.12568 16.25999 16.12568  16.25999       NA 70.89800      NA
2003-01-15 16.17308 16.17308 15.90445  15.99136       NA 20.77648      NA
2003-01-16 15.95976 16.18098 15.95976  16.07827       NA 45.64200      NA
2003-01-17 15.97556 16.13358 15.92816  15.95186 0.281271 23.85808      NA

This allows a user to see how the indicators will be appended to the mktdata object in the backtest. If the call to applyIndicators fails, it means that there most likely is an issue with labeling (column naming).

Next week, I’ll discuss signals, which are a bit more defined in scope.

Thanks for reading.

To leave a comment for the author, please follow the link and comment on their blog: QuantStrat TradeR » R. offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)