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).Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
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.
Signal | # Trades | % Win | Mean Win | Mean Loss | Median Win | Median Loss | Mean W/L | Median W/L |
---|---|---|---|---|---|---|---|---|
-1.00 | 133 | 58 | 0.69 | -0.44 | 0.53 | -0.25 | 1.55 | 2.12 |
-0.75 | 173 | 49 | 0.62 | -0.39 | 0.37 | -0.25 | 1.60 | 1.48 |
-0.50 | 143 | 54 | 0.43 | -0.36 | 0.28 | -0.19 | 1.19 | 1.51 |
-0.25 | 158 | 56 | 0.21 | -0.19 | 0.14 | -0.13 | 1.15 | 1.08 |
0.00 | 1262 | 0 | NaN | NaN | NA | NA | NaN | NA |
0.25 | 117 | 53 | 0.26 | -0.31 | 0.18 | -0.21 | 0.83 | 0.86 |
0.50 | 137 | 58 | 0.51 | -0.58 | 0.31 | -0.35 | 0.87 | 0.89 |
0.75 | 143 | 62 | 0.88 | -0.89 | 0.50 | -0.71 | 0.99 | 0.70 |
1.00 | 119 | 63 | 1.34 | -1.41 | 0.80 | -1.11 | 0.95 | 0.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.
From | Trough | To | Depth | Length | To Trough | Recovery |
---|---|---|---|---|---|---|
2008-10-06 | 2008-10-10 | 2008-10-28 | -0.157 | 17 | 5 | 12 |
2001-08-30 | 2001-09-21 | 2002-01-23 | -0.091 | 96 | 12 | 84 |
2002-07-19 | 2002-07-23 | 2002-08-20 | -0.088 | 23 | 3 | 20 |
2000-03-22 | 2000-04-14 | 2000-07-05 | -0.076 | 73 | 18 | 55 |
2009-02-17 | 2009-02-23 | 2009-04-27 | -0.070 | 49 | 5 | 44 |
2003-03-14 | 2003-03-21 | 2003-05-09 | -0.055 | 40 | 6 | 34 |
2000-10-09 | 2000-10-12 | 2000-12-06 | -0.052 | 42 | 4 | 38 |
2002-08-29 | 2002-09-24 | 2002-10-10 | -0.051 | 30 | 18 | 12 |
2008-01-02 | 2008-01-22 | 2008-03-11 | -0.045 | 48 | 14 | 34 |
2001-04-18 | 2001-06-18 | 2001-08-10 | -0.045 | 81 | 43 | 38 |
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.
Statistic | Return |
---|---|
Semi Deviation | 0.0050 |
Gain Deviation | 0.0094 |
Loss Deviation | 0.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 VaR | 0.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
# 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.