RSI(2) Evaluation

[This article was first published on FOSS Trading, 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.

Despite my best efforts, it’s been a month since the last post of this series. The first post replicated this simple RSI(2) strategy from the MarketSci Blog using R. The second post showed how to replicate the strategy that scales in/out of RSI(2).

This post will use the PerformanceAnalytics package to evaluate the rules that scale in/out of positions. I’ve also provided a simple function that provides some summary statistics. There is a lot of code, so I put it at the end of the post.

Table 1 contains output from my simple trade summary function (the wins and losses are in percentages, i.e. 0.69 is 69 basis points). The short side of the rule traded more often and had a lower win rate. The short side overcame its lower win rate via much higher mean and median win/loss ratios.

Table 1: RSI(2) Trade Statistics – RSI steps = 5, Size steps = 0.25
Signal # Trades % Win Mean Win Mean Loss Median Win Median Loss Mean W/L Median W/L
-1.00133580.69-0.440.53-0.251.552.12
-0.75173490.62-0.390.37-0.251.601.48
-0.50143540.43-0.360.28-0.191.191.51
-0.25158560.21-0.190.14-0.131.151.08
0.0012620NaNNaNNANANaNNA
0.25117530.26-0.310.18-0.210.830.86
0.50137580.51-0.580.31-0.350.870.89
0.75143620.88-0.890.50-0.710.990.70
1.00119631.34-1.410.80-1.110.950.71

Table 2 shows the output from the PerformanceAnalytics table.Drawdowns() function. The largest percentage drawdown occurred in late 2008, but only lasted a few weeks.

The table also shows the system is prone to drawdowns that trough quickly and take months to recover from. A week of bad trades can take months to recover from.

Table 2: RSI(2) Drawdowns – RSI steps = 5, Size steps = 0.25
From Trough To Depth Length To Trough Recovery
2008-10-062008-10-102008-10-28-0.15717512
2001-08-302001-09-212002-01-23-0.091961284
2002-07-192002-07-232002-08-20-0.08823320
2000-03-222000-04-142000-07-05-0.076731855
2009-02-172009-02-232009-04-27-0.07049544
2003-03-142003-03-212003-05-09-0.05540634
2000-10-092000-10-122000-12-06-0.05242438
2002-08-292002-09-242002-10-10-0.051301812
2008-01-022008-01-222008-03-11-0.045481434
2001-04-182001-06-182001-08-10-0.045814338

Table 3 shows the output from the PerformanceAnalytics table.DownsideRisk() function. The ratio of gain/loss deviation is encouraging. I have to defer to the PerformanceAnalytics documentation and vingettes to describe the rest of the table.

Table 3: RSI(2) Downside Risk – RSI steps = 5, Size steps = 0.25
StatisticReturn
Semi Deviation0.0050
Gain Deviation0.0094
Loss Deviation0.0076
Downside Deviation (MAR=10%)0.0099
Downside Deviation (rf=0%)0.0092
Downside Deviation (0%)0.0092
Maximum Drawdown-0.1572
VaR (99%)0.0160
Beyond VaR0.0160
Modified VaR (99%)0.0705

The chart below shows the output from the PerformanceAnalytics charts.PerformanceSummary() function. It shows the equity curves and drawdown from peak for the long and short sides of the strategy. The middle graph shows the *daily* returns for the combined strategy.


The code below has everything that created the results above. It also contains the same results for a modified RSI(2) strategy. The modified strategy uses RSI steps of 10 and sizing steps of 0.3 (i.e. RSI<10 -> size=1, 10<20 -> size=0.7, etc.).

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

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

# Calculate the RSI indicator
rsi <- RSI(Cl(GSPC), 2)

# Calculate Close-to-Close returns
ret <- ROC(Cl(GSPC))
ret[1] <- 0

# This function gives us some standard summary
# statistics for our trades.
tradeStats <- function(signals, returns) {
# Inputs:
# signals : trading signals
# returns : returns corresponding to signals

# Combine data and convert to data.frame
sysRet <- signals * returns * 100
posRet <- sysRet > 0 # Positive rule returns
negRet <- sysRet < 0 # Negative rule returns
dat <- cbind(signals,posRet*100,sysRet[posRet],sysRet[negRet],1)
dat <- as.data.frame(dat)

# Aggreate data for summary statistics
means <- aggregate(dat[,2:4], by=list(dat[,1]), mean, na.rm=TRUE)
medians <- aggregate(dat[,3:4], by=list(dat[,1]), median, na.rm=TRUE)
sums <- aggregate(dat[,5], by=list(dat[,1]), sum)

colnames(means) <- c("Signal","% Win","Mean Win","Mean Loss")
colnames(medians) <- c("Signal","Median Win","Median Loss")
colnames(sums) <- c("Signal","# Trades")

all <- merge(sums,means)
all <- merge(all,medians)

wl <- cbind( abs(all[,"Mean Win"]/all[,"Mean Loss"]),
abs(all[,”Median Win”]/all[,”Median Loss”]) )
colnames(wl) <- c("Mean W/L","Median W/L")

all <- cbind(all,wl)
return(all)
}

# This function determines position size and
# enables us to test several ideas with much
# greater speed and flexibility.
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 with the ‘rsi2pos()’ function,
# using 5 as the RSI step: 5, 10, 15, 20, 80, 85, 90, 95
# and 0.25 as the size step: 0.25, 0.50, 0.75, 1.00
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 rule returns
ret_up <- ret * sigup
colnames(ret_up) <- 'Long System Return'
ret_dn <- ret * sigdn
colnames(ret_dn) <- 'Short System Return'
ret_all <- ret * sig
colnames(ret_all) <- 'Total System Return'

# Create performance graphs
png(filename=”20090606_rsi2_performance.png”, 720, 720)
charts.PerformanceSummary(cbind(ret_up,ret_dn),methods=’none’,
main=’RSI(2) Performance – RSI steps = 5, Size steps = 0.25′)
dev.off()

# Print trade statistics table
cat(‘nRSI(2) Trade Statistics – RSI steps = 5, Size steps = 0.25n’)
print(tradeStats(sig,ret))

# Print drawdown table
cat(‘nRSI(2) Drawdowns – RSI steps = 5, Size steps = 0.25n’)
print(table.Drawdowns(ret_all, top=10))

# Print downside risk table
cat(‘nRSI(2) Downside Risk – RSI steps = 5, Size steps = 0.25n’)
print(table.DownsideRisk(ret_all))

# Calculate signals with the ‘rsi2pos()’ function
# using new RSI and size step 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 rule returns
ret_up <- ret * sigup
colnames(ret_up) <- 'Long System Return'
ret_dn <- ret * sigdn
colnames(ret_dn) <- 'Short System Return'
ret_all <- ret * sig
colnames(ret_all) <- 'Total System Return'

# Calculate performance statistics
png(filename=”20090606_rsi2_performance_updated.png”, 720, 720)
charts.PerformanceSummary(cbind(ret_up,ret_dn),methods=’none’,
main=’RSI(2) Performance – RSI steps = 10, Size steps = 0.30′)
dev.off()

# Print trade statistics table
cat(‘nRSI(2) Trade Statistics – RSI steps = 10, Size steps = 0.30n’)
print(tradeStats(sig,ret))

# Print drawdown table
cat(‘nRSI(2) Drawdowns – RSI steps = 10, Size steps = 0.30n’)
print(table.Drawdowns(ret_all, top=10))

# Print downside risk table
cat(‘nRSI(2) Downside Risk – RSI steps = 10, Size steps = 0.30n’)
print(table.DownsideRisk(ret_all))


To leave a comment for the author, please follow the link and comment on their blog: FOSS Trading.

R-bloggers.com 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)