Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Though it’s more than two weeks later, here’s the second post in the series that will demonstrate how to build, test, and implement a trading strategy with R. You can find the first post here.

The first post replicated this simple RSI(2) strategy from the MarketSci Blog. This second post will demonstrate how to replicate this strategy that scales in/out of RSI(2).

A couple notes before moving to the code:

1. The rsi2pos() function isn’t necessary, but it provides an example of how to define a function. Plus, it enables us to test several ideas with much greater speed and flexibility.
2. The ifelse() function works on entire vectors at once, avoiding costly loops (loops are costly in R because it’s an interpreted language). Since we can potentially modify the entire ‘size’ vector, we must be mindful of the order of the tests.

On to the code!

# Attach the quantmod and TTR packages.
# You can install packages via:
# install.packages(c(“quantmod”,”TTR”))
library(quantmod)
library(TTR)

# Pull S&P500 index data from Yahoo! Finance
getSymbols(“^GSPC”, from=”2000-01-01″, to=”2008-12-07″)

# Calculate the RSI indicator
rsi <- RSI(Cl(GSPC),2) # Calculate Close-to-Close returns
# (this assumes we open/close our positions
# at each day’s close)
ret <- ROC(Cl(GSPC))
ret <- 0 # Define our position-sizing function
rsi2pos <- function(ind, indIncr=5, posIncr=0.25) {
# Inputs:
# ind : indicator vector
# indIncr : indicator value increments/breakpoints
# posIncr : position value increments/breakpoints

# Initialize result vector
size <- rep(0,NROW(ind)) # Long
size <- ifelse(ind < 4*indIncr, (1-posIncr*3), size)
size <- ifelse(ind < 3*indIncr, (1-posIncr*2), size)
size <- ifelse(ind < 2*indIncr, (1-posIncr*1), size)
size <- ifelse(ind < 1*indIncr, (1-posIncr*0), size) # Short
size <- ifelse(ind > 100-4*indIncr, 3*posIncr-1, size)
size <- ifelse(ind > 100-3*indIncr, 2*posIncr-1, size)
size <- ifelse(ind > 100-2*indIncr, 1*posIncr-1, size)
size <- ifelse(ind > 100-1*indIncr, 0*posIncr-1, size)

# Today’s position (‘size’) is based on today’s
# indicator, but we need to apply today’s position
# to the Close-to-Close return at tomorrow’s close.
size <- lag(size) # Replace missing signals with no position
# (generally just at beginning of series)
size[is.na(size)] <- 0 # Return results
return(size)
}

# Calculate signals using the ‘rsi2pos()’ function
sig <- rsi2pos(rsi, 5, 0.25) # Break out the long (up) and short (dn) signals
sigup <- ifelse(sig > 0, sig, 0)
sigdn <- ifelse(sig < 0, sig, 0) # Calculate equity curves
eq_up <- cumprod(1+ret*sigup)
eq_dn <- cumprod(1+ret*sigdn)
eq_all <- cumprod(1+ret*sig) # Replicate Michael’s nice chart (again)
png(filename=”20090430_rsi2_replication.png”)
plot.zoo( cbind(eq_up, eq_dn), plot.type=”single”,
ylab=c(“Long”,”Short”), col=c(“green”,”red”),
main=”RSI(2) Strategy, with Position Scaling:n 2000-01-03 through 2008-12-07″ )
dev.off() # Calculate signals using the ‘rsi2pos()’ function
# with new values
sig <- rsi2pos(rsi, 10, 0.3) # Break out the long (up) and short (dn) signals
sigup <- ifelse(sig > 0, sig, 0)
sigdn <- ifelse(sig < 0, sig, 0) # Calculate equity curves
eq_up <- cumprod(1+ret*sigup)
eq_dn <- cumprod(1+ret*sigdn)
eq_all <- cumprod(1+ret*sig) # Re-plot equity curves using updated values
png(filename=”20090501_rsi2_updated.png”)
plot.zoo( cbind(eq_up, eq_dn), plot.type=”single”,
ylab=c(“Long”,”Short”), col=c(“green”,”red”),
main=”Updated RSI(2) Strategy, with Position Scaling:n 2000-01-03 through 2008-12-07″ )
dev.off() Visual inspection of the charts seems to indicate the updated RSI(2) strategy has slightly higher returns, but more volatility and larger drawdowns. The next post will use the PerformanceAnalytics package to evaluate the volatility, drawdowns, and related metrics associated with these strategies. I will do my best to post it less than two weeks from now! 