category

tutorials

Backtesting a simple trading strategy in R with quantstrat

Posted on: February 6th, 2017 3 Comments

I came across this Bloomberg video that mentioned two moving averages forming a “death cross” (scary) - have a look:

I thought: how about a simple strategy involving those exact SMA windows and testing it on that exact instrument? So I’ll create a simple strategy, which will naively use the 200 day SMA and the 50 day SMA crossovers as entry/exit triggers. From the get go, this seems like a weak strategy, but once it’s in written, I could incrementally add some improvements to it.

I’ll refer to the SMA 50 as the fast SMA and the SMA 200 as the slow one (that’s the convention).

First, it’s best to visually inspect the indicators on a chart, to determine whether this simple concept has any potential. The technical charts at Investing.com are my favourite (free) tool. Here’s what it looks like:

Not bad. It seems like the crossovers have a chance of signalling some good trades. Now let’s get to work.

Here are the rules:

  • when the fast (50 day) moving average line crosses above the slow average line, then: exit any short positions and go long

  • when the opposite happens - fast average line crosses below the slow average line, then: exit any longs and go short

It would be great if this trivial system would work, right? Then we can all be millionaires.

Anyway - the workhorse for this algorithm is the quantstrat module. It lets you create a virtual portfolio, register your indicators, signals and rules, and it will then calculate the effects of your automated actions on the portfolio. It can even account for transaction fees and other details. The aim for this article is to keep it simple.

Order sizing is a whole nother important discussion. It can be set according to current portfolio value, or unallocated cash, and/or instrument recent volatility. However, for simplicity, I’ll be using a fixed position size of 20 shares across the entire interval.

You can get the R script in this repo on github and start improving it. Don’t forget to install the required libraries. I’ll go only over the most important lines in the main script (backtest.R) here.

Starting off with library imports, and setting the config variables. The instrument and SMA length will be easy to tweak:

# Settings
sym <- "EEM"
crcy <- "USD"
from <- "2010-01-01"
nSlowSMA <- 200
nFastSMA <- 50

initEq <- 1500
order.qty <- 20

The variable names are self-explanatory. EEM is our instrument under test and `from` holds the start date for the back testing. After I fetch the data from Yahoo finance with the `getSymbols`, and some quantstrat boilerplate, I'm adding the technical indicators. Quantstrat comes with a bunch of default ones (RSI, MACD etc), and it's easy to add custom ones.

add.indicator(strat.name, name = "SMA", arguments = list(x=quote(Cl(mktdata)), n = nSlowSMA), label = slowSMA.label)
add.indicator(strat.name, name = "SMA", arguments = list(x=quote(Cl(mktdata)), n = nFastSMA), label = fastSMA.label)

The next step is to add the signals, which become TRUE when an event happens in an indicator. Again, it's easy to create custom ones, and R comes with sigComparison() (compares two indicators or two other signals), sigCrossover() (turns true when two signals or indicators cross in a certain way), sigThreshold() etc.

# Fast SMA vs slow SMA crossing
add.signal(strat.name, name = "sigCrossover",
           arguments = list(columns = c(fastSMA.label, slowSMA.label), relationship = "gt"),
           label = "fast.crossed.above.slow")
add.signal(strat.name, name = "sigCrossover",
           arguments = list(columns = c(fastSMA.label, slowSMA.label), relationship = "lt"),
           label = "fast.crossed.below.slow")

And finally, the rules to complete the strategy. There are four of them - two for long entry/exit and two for the short side:

# Long rules
############

# Enter when the fast SMA crosses above the slow SMA
add.rule(strat.name, name = "ruleSignal",
         arguments = list(sigcol = "fast.crossed.above.slow", sigval = TRUE, ordertype = "market", orderside = "long",
                          replace = FALSE, prefer = "Open", orderqty = order.qty, atrMod = "X"),
         type = "enter", path.dep = TRUE, label = "enterLong.safe")

# Exit long
add.rule(strat.name, name = "ruleSignal",
         arguments = list(sigcol = "fast.crossed.below.slow", sigval = TRUE, ordertype = "market", orderside = "long",
                          replace = FALSE, prefer = "Open", orderqty = "all", atrMod = "X"),
         type = "exit", path.dep = TRUE, label = "exitLong.on.close.crossing.fast")


# Short rules
#############

# Enter when the fast SMA crosses below the slow SMA
add.rule(strat.name, name = "ruleSignal",
         arguments = list(sigcol = "fast.crossed.below.slow", sigval = TRUE, ordertype = "market", orderside = "short",
                          replace = FALSE, prefer = "Open", orderqty = -order.qty, atrMod = "X"),
         type = "enter", path.dep = TRUE, label = "enterShort.safe")

# Exit short
add.rule(strat.name, name = "ruleSignal",
         arguments = list(sigcol = "fast.crossed.above.slow", sigval = TRUE, ordertype = "market", orderside = "short",
                          replace = FALSE, prefer = "Open", orderqty = "all", atrMod = "X"),
         type = "exit", path.dep = TRUE, label = "exitShort.on.close.crossing.fast")

 


 

A good place to start with R for quantitative finance is Quantitative Trading with R: Understanding Mathematical and Computational Tools from a Quant's Perspective, by H. Georgakopoulos. It's even got a chapter dedicated to quantstrat.


 

It's now ready to run over the given period:

applyStrategy(strategy = strat.name, portfolios = portofolio.name)
updatePortf(portofolio.name)
updateAcct(account.name)
updateEndEq(account.name)

And finally, let's display the results in a nice graph and a table that holds the stats:

chart.Posn(portofolio.name, Symbol = sym,
           TA = "add_SMA(n = 50, col = 'red'); add_SMA(n = 200, col = 'blue')")

tstats <- tradeStats(portofolio.name)
kable(t(tstats))



The results are pretty bad - average trade P&L is $-20.06, and we would have lost $257.60 over 2010.01.01 to 2017.02.06. It seems like the entries are late to the trend, and the zig-zag periods will tax this. It would do better when there's a sustained trend over a long period. Feel free to download the script and play around by changing the instrument name, testing time period and SMA lengths.

 

3 Responses

  1. Gabe Harris

    November 25, 2018

    Error in get(symbol, envir = envir) : object ‘EEM’ not found
    3.
    get(symbol, envir = envir)
    2.
    applyStrategy(strategy = strat.name, portfolios = portofolio.name) at myscriptname#393
    1.
    bt1()

    Reply
  2. Gabe Harris

    November 25, 2018

    I copied your files and got the error above…I figured I needed to try and figure it out…so added this:

    getSymbols(Symbols = “EEM”, from = “1998-01-01”, to = “2018-11-20”)

    didn’t help too much, still got this:
    Error in get(symbol, envir = envir) : object ‘EEM’ not found

    Reply
  3. Gabe Harris

    November 25, 2018

    probably obvious I am a newb to quantstrat…hopefully I find a solution before you even read this…but if not…please advise…I’m sure what you posted on github worked when you posted it…but not working for me 🙁

    Reply

Leave a Reply