ContextBase Logo

Synopsis

This document utilizes the “QuantMod”, and “PerformanceAnalytics”, R packages for Backtesting of Automated Trading Stategies.

Working Directory, and Required Packages

# Set Working Directory
# setwd(" ")

# Required Packages
# if (!require("quantmod")) { install.packages("quantmod"); require("quantmod") }
# if (!require("PerformanceAnalytics")) { install.packages("PerformanceAnalytics"); require("PerformanceAnalytics") }
# if (!require("rgl")) { install.packages("rgl"); require("rgl") }
# if (!require("rugarch")) { install.packages("rugarch"); require("rugarch") }

library(RCurl)
library(xts)
library(zoo)
library(quantmod) # data, plotting, quant modelling
library(PerformanceAnalytics) # performance and risk management
library(rugarch) #GARCH high volatility forecasting
library(knitr)

Downloading Stock Ticker Data from Yahoo Finances

# Import Original data
ticker_lookup <- getSymbols("TWTR", auto.assign=F,
                            from="2014-01-01", to="2016-01-01")

ticker_lookup2 <- as.data.frame(cbind(ticker_lookup[,1], ticker_lookup[,2], ticker_lookup[,3], ticker_lookup[,4], ticker_lookup[,5], ticker_lookup[,6]))

ticker_data <-  as.data.frame(cbind(date = rownames(ticker_lookup2), ticker_lookup2))

colnames(ticker_data) <- c("date","open","high ","low ","close ","volume ","adjusted")

rownames(ticker_data) <- NULL

Trading Strategy Backtest

daysSinceHigh <- function(x, n){
   apply(embed(x, n), 1, which.max)-1
}

myStrat <- function(x, nHold=100, nHigh=200) {
    position <- ifelse(daysSinceHigh(x, nHigh)<=nHold,1,0)
    c(rep(0,nHigh-1),position)
}

getSymbols('TWTR', from='2011-01-01')
## [1] "TWTR"
myStock <- Cl(TWTR)
myPosition <- myStrat(myStock,40,45)
bmkReturns <- dailyReturn(myStock, type = "arithmetic")
myReturns <- bmkReturns*Lag(myPosition,1)
myReturns[1] <- 0

names(bmkReturns) <- 'TWTR'
names(myReturns) <- 'Me'

Trading Strategy Backtest Charts

charts.PerformanceSummary(cbind(bmkReturns,myReturns))

Performance <- function(x) {
    cumRetx = Return.cumulative(x)
    annRetx = Return.annualized(x, scale=252)
    sharpex = SharpeRatio.annualized(x, scale=252)
    winpctx = length(x[x > 0])/length(x[x != 0])
    annSDx = sd.annualized(x, scale=252)
    
    DDs <- findDrawdowns(x)
    maxDDx = min(DDs$return)
    maxLx = max(DDs$length)

    Perf = c(cumRetx, annRetx, sharpex, winpctx, annSDx, maxDDx, maxLx)
    names(Perf) = c("Cumulative Return", "Annual Return","Annualized Sharpe Ratio",
        "Win %", "Annualized Volatility", "Maximum Drawdown", "Max Length Drawdown")
    return(Perf)
}

cbind(Me=Performance(myReturns),TWTR=Performance(bmkReturns))
##                                  Me        TWTR
## Cumulative Return        -0.4560617  -0.5574610
## Annual Return            -0.1936313  -0.2503363
## Annualized Sharpe Ratio  -0.4312546  -0.4436876
## Win %                     0.4968553   0.4914530
## Annualized Volatility     0.4489952   0.5642175
## Maximum Drawdown         -0.7053375  -0.8088937
## Max Length Drawdown     483.0000000 680.0000000

Test Strategy

testStrategy <- function(myStock, nHold=100, nHigh=200) {
    myPosition <- myStrat(myStock,nHold,nHigh)
    bmkReturns <- dailyReturn(myStock, type = "arithmetic")
    myReturns <- bmkReturns*Lag(myPosition,1)
    myReturns[1] <- 0
    names(bmkReturns) <- 'TWTR'
    names(myReturns) <- 'Me'
    
    charts.PerformanceSummary(cbind(bmkReturns,myReturns))
    cbind(Me=Performance(myReturns),Index=Performance(bmkReturns))
}

getSymbols('TWTR', from='2014-01-01')
## [1] "TWTR"
testStrategy(na.omit(TWTR),40,45)

##                                  Me       Index
## Cumulative Return        -0.5258818  -0.6943077
## Annual Return            -0.2428592  -0.3571290
## Annualized Sharpe Ratio  -0.8411437  -0.6497178
## Win %                     0.4077670   0.4849850
## Annualized Volatility     0.2887249   0.5496679
## Maximum Drawdown         -0.6016240  -0.7969565
## Max Length Drawdown     599.0000000 675.0000000

SMA (Simple Moving Average) Strategy Backtest

tickerData.xts <- xts(as.numeric(Cl(ticker_data)),
                      order.by=as.Date(ticker_data$date))
tickerData.z = zoo(x=Cl(ticker_data), order.by=as.Date(ticker_data$date))

# Specify the prices and store our models
prices <- tickerData.xts[,1]

# Calculate the indicators we need for our strategy
CCI20 <- CCI(prices, 20)
RSI3 <- RSI(prices, 3)
DEMA10 <- DEMA(prices, n = 10, v = 1, wilder = FALSE)
DEMA10c <- prices - DEMA10  
DEMA10c <- DEMA10c/.0001

buy.signal <- ifelse(RSI3 < 30 & CCI20 > -290 & CCI20 < -100 & DEMA10c > -40 & DEMA10c < 750, 1, NA)

sell.signal <- ifelse(DEMA10c > 5 & DEMA10c < 200 & CCI20 > 100 & CCI20 < 500 & RSI3 > 10, -1 ,NA)

# Create indicator
sma <- SMA(tickerData.xts, n=1)

# Construct trading rule
sig <- Lag(ifelse(sma$SMA < buy.signal, 1, -1))

# The trading rules/equity curve
retSMA <- ROC(tickerData.xts) * sig

SMA Backtest Results

SMA Backtest Charts
plot(sma)

chartSeries(ticker_lookup, theme = chartTheme('white'),
            TA=c(addVo(),addBBands(), addMACD()))

# Evaluate strategy performance
retdrawDowns <- table.Drawdowns(retSMA, top=10)
kable(retdrawDowns)
From Trough To Depth Length To Trough Recovery
2014-05-12 2014-05-12 NA -0.0573 2 1 NA
retReturns <- table.CalendarReturns(retSMA); retReturns
##      Jan Feb Mar Apr  May Jun Jul Aug Sep Oct Nov Dec Lag.1
## 2014  NA  NA  NA 3.7 -5.7  NA  NA  NA  NA  NA  NA  NA  -2.2
## 2015  NA  NA  NA  NA   NA  NA  NA  NA  NA  NA  NA  NA    NA
retDownsideRisk <- table.DownsideRisk(retSMA)
kable(retDownsideRisk)
Lag.1
Semi Deviation 0.0335
Gain Deviation NA
Loss Deviation NA
Downside Deviation (MAR=210%) 0.0464
Downside Deviation (Rf=0%) 0.0405
Downside Deviation (0%) 0.0405
Maximum Drawdown 0.0573
Historical VaR (95%) -0.0526
Historical ES (95%) -0.0573
Modified VaR (95%) -0.0898
Modified ES (95%) -0.0907
charts.PerformanceSummary(retSMA)

DVI Strategy Backtest

getSymbols('MSFT', from='2011-01-01')
## [1] "MSFT"
# Calculate DVI indicator
dvi <- DVI(Cl(MSFT))

# Construct trading rule
# Since this trading rule is simple--we're long 100% if the DVI is below 0.5 and short 100% otherwise--it can be written in a single line.

# Create signal: (long (short) if DVI is below (above) 0.5).
# Lag so yesterday's signal is applied to today's returns
sig <- Lag(ifelse(dvi$e1 < 0.5, 1, -1))

# The trading rules/equity curve
# The code below takes today's percentage return and multiplies it by yesterday's signal / position size (always +/- 100% in this example).

# Calculate signal-based returns
Trading_Strategy_Returns <- ROC(dvi$e1)*sig

# Evaluate strategy performance

# The trading rules/equity curve
Trading_Strategy_Returns <- ROC(Cl(MSFT))*sig

plot(Trading_Strategy_Returns)

# Evaluate strategy performance
retdrawDowns <- table.Drawdowns(Trading_Strategy_Returns, top=10)
kable(retdrawDowns)
From Trough To Depth Length To Trough Recovery
2015-10-02 2016-01-29 2016-04-20 -0.2303 138 82 56
2015-04-20 2015-04-28 2015-08-21 -0.1586 88 7 81
2014-09-22 2014-11-13 2015-01-27 -0.1435 88 39 49
2013-10-25 2013-11-06 2014-02-03 -0.1287 68 9 59
2016-05-26 2016-08-11 NA -0.1141 73 54 NA
2012-09-07 2012-10-25 2012-11-12 -0.0985 45 35 10
2015-02-12 2015-03-12 2015-04-10 -0.0982 40 20 20
2013-04-24 2013-05-06 2013-07-19 -0.0946 61 9 52
2013-03-12 2013-04-10 2013-04-22 -0.0806 29 21 8
2012-06-11 2012-06-20 2012-07-06 -0.0656 19 8 11
retReturns <- table.CalendarReturns(Trading_Strategy_Returns); retReturns
##       Jan Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec MSFT.Close
## 2011   NA  NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA         NA
## 2012   NA  NA   NA   NA   NA  2.2 -0.2  1.6 -0.9  3.4  1.2  0.6        8.2
## 2013 -1.7 0.5  0.0  1.2  0.4 -0.5 -0.5  0.4  0.9 -0.3  1.4 -0.3        1.3
## 2014 -2.6 1.2  1.0  1.0 -1.5  0.4 -0.7  1.2 -1.0 -1.9  1.7  1.2       -0.1
## 2015 -3.9 0.5  0.1  0.0  0.8  0.7 -0.4 -4.0  0.8  1.4 -1.6  1.5       -4.3
## 2016  0.7 3.3 -0.6 -0.1  0.3  0.0  0.2 -0.2  0.1   NA   NA   NA        3.6
retDownsideRisk <- table.DownsideRisk(Trading_Strategy_Returns)
kable(retDownsideRisk)
MSFT.Close
Semi Deviation 0.0104
Gain Deviation 0.0114
Loss Deviation 0.0105
Downside Deviation (MAR=210%) 0.0145
Downside Deviation (Rf=0%) 0.0098
Downside Deviation (0%) 0.0098
Maximum Drawdown 0.2303
Historical VaR (95%) -0.0206
Historical ES (95%) -0.0321
Modified VaR (95%) -0.0192
Modified ES (95%) -0.0192
charts.PerformanceSummary(Trading_Strategy_Returns)