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

In the Follow-Up Part 1, I explored some of the functions in the quantstrat package that allowed us to drill down trade by trade to explain the difference in performance of the two strategies. By doing this, I found that my choice of a volatility measure may not have been the best choice. Although the volatility filter kept me out of trades during periods of higher volatility, it also had a negative impact on position sizing and overall return.

The volatility measure presented in the original post was the 52 period standard deviation of the 1 period change of close prices. I made a custom indicator to incorporate the volatility filter into the buy rule. Here is the original RB function:

#Custom indicator function
RB <- function(x,n){
x <- x
roc <- ROC(x, n=1, type="discrete")
sd <- runSD(roc,n, sample= FALSE)
med <- runMedian(sd,n)
mavg <- SMA(x,n)
signal <- ifelse(sd < med & x > mavg,1,0)
colnames(signal) <- "RB"
reclass(signal,x)
}

The new volatility filter will be the 52 period standard deviation of close prices. Now, the buy rule can be interpreted as follows:

• Buy Rule: Go long if close is greater than the 52 period SMA and the 52 period standard deviation of close prices is less than its median over the last N periods.
• Exit Rule: Exit if long and close is less than the N period SMA

A slight change to the RB function will do the trick, I will call it RBrev1 (that is my creative side coming out )

#Custom indicator function
RBrev1 <- function(x,n){
x <- x
sd <- runSD(x, n, sample= FALSE)
med <- runMedian(sd,n)
mavg <- SMA(x,n)
signal <- ifelse(sd < med & x > mavg,1,0)
colnames(signal) <- "RB"
#ret <- cbind(x,roc,sd,med,mavg,signal) #Only use for further analysis of indicator
#colnames(ret) <- c("close","roc","sd","med","mavg","RB") #Only use for further analysis of indicator
reclass(signal,x)
}

I will test the strategy on the adjusted close of the S&P500 using weekly prices from 1/1/1990 to 1/1/2000 just as in the previous post.

And the winner is… both! There is no difference in performance on this single instrument in this specific window of time I used for the test.

Always do your own testing to decide whether or not a filter of any kind will add value to your system. This single instrument test in the series of posts showed that choosing the “wrong” volatility filter can hinder performance and another choice of volatility filter doesn’t have much impact, if any, at all.

How do you think the volatility filter will affect a multiple instrument test?

require(PerformanceAnalytics)
require(quantstrat)

suppressWarnings(rm("order_book.RBtest",pos=.strategy))
suppressWarnings(rm("account.RBtest","portfolio.RBtest",pos=.blotter))
suppressWarnings(rm("account.st","portfolio.st","symbols","stratBBands","initDate","initEq",'start_t','end_t'))

sym.st = "GSPC"
currency("USD")
stock(sym.st, currency="USD",multiplier=1)
getSymbols("^GSPC", src='yahoo', index.class=c("POSIXt","POSIXct"), from='1990-01-01', to='2012-04-17')
GSPC <- to.weekly(GSPC,indexAt='lastof',drop.time=TRUE)

#Custom Order Sizing Function to trade percent of equity based on a stopsize
osPCTEQ <- function(timestamp, orderqty, portfolio, symbol, ruletype, ...){
tempPortfolio <- getPortfolio(portfolio.st)
dummy <- updatePortf(Portfolio=portfolio.st, Dates=paste('::',as.Date(timestamp),sep=''))
trading.pl <- sum(getPortfolio(portfolio.st)$summary$Realized.PL) #change to ..$summary$Net.Trading.PL for Total Equity Position Sizing
assign(paste("portfolio.",portfolio.st,sep=""),tempPortfolio,pos=.blotter)
ClosePrice <- as.numeric(Cl(mktdata[timestamp,]))
mavg <- as.numeric(mktdata$SMA52[timestamp,]) sign1 <- ifelse(ClosePrice > mavg, 1, -1) sign1[is.na(sign1)] <- 1 Posn = getPosQty(Portfolio = portfolio.st, Symbol = sym.st, Date = timestamp) StopSize <- as.numeric(mktdata$SDEV[timestamp,]*StopMult) #Stop = SDAVG * StopMult !Must have SDAVG or other indictor to determine stop size
orderqty <- ifelse(Posn == 0, sign1*round(DollarRisk/StopSize), 0) # number contracts traded is equal to DollarRisk/StopSize
return(orderqty)
}

#Function that calculates the n period standard deviation of close prices.
#This is used in place of ATR so that I can use only close prices.
SDEV <- function(x, n){
sdev <- runSD(x, n, sample = FALSE)
colnames(sdev) <- "SDEV"
reclass(sdev,x)
}

#Custom indicator function
RBrev1 <- function(x,n){
x <- x
sd <- runSD(x, n, sample= FALSE)
med <- runMedian(sd,n)
mavg <- SMA(x,n)
signal <- ifelse(sd < med & x > mavg,1,0)
colnames(signal) <- "RB"
#ret <- cbind(x,roc,sd,med,mavg,signal) #Only use for further analysis of indicator
#colnames(ret) <- c("close","roc","sd","med","mavg","RB") #Only use for further analysis of indicator
reclass(signal,x)
}

initDate='1900-01-01'
initEq <- 100000

trade.percent <- .05 #percent risk used in sizing function
StopMult = 1 #stop size used in sizing function

#Name the portfolio and account
portfolio.st='RBtest'
account.st='RBtest'

#Initialization
initPortf(portfolio.st, symbols=sym.st, initPosQty=0, initDate=initDate, currency="USD")
initAcct(account.st,portfolios=portfolio.st, initDate=initDate, initEq=initEq)
initOrders(portfolio=portfolio.st,initDate=initDate)

#Name the strategy
stratRB <- strategy('RBtest')

#The first indicator is the 52 period SMA
#The second indicator is the RB indicator. The RB indicator returns a value of 1 when close > SMA & volatility < runMedian(volatility, n = 52)
stratRB <- add.indicator(strategy = stratRB, name = "SMA", arguments = list(x = quote(Cl(mktdata)), n=52), label="SMA52")
stratRB <- add.indicator(strategy = stratRB, name = "RBrev1", arguments = list(x = quote(Cl(mktdata)), n=52), label="RB")
stratRB <- add.indicator(strategy = stratRB, name = "SDEV", arguments = list(x = quote(Cl(mktdata)), n=52), label="SDEV")

#The buy signal is when the RB indicator crosses from 0 to 1
#The exit signal is when the close crosses below the SMA
stratRB <- add.signal(strategy = stratRB, name="sigThreshold", arguments = list(threshold=1, column="RB",relationship="gte", cross=TRUE),label="RB.gte.1")
stratRB <- add.signal(strategy = stratRB, name="sigCrossover", arguments = list(columns=c("Close","SMA52"),relationship="lt"),label="Cl.lt.SMA")

stratRB <- add.rule(strategy = stratRB, name='ruleSignal', arguments = list(sigcol="RB.gte.1", sigval=TRUE, orderqty=1000, ordertype='market', orderside='long', osFUN = 'osPCTEQ', pricemethod='market', replace=FALSE), type='enter', path.dep=TRUE)
stratRB <- add.rule(strategy = stratRB, name='ruleSignal', arguments = list(sigcol="Cl.lt.SMA", sigval=TRUE, orderqty='all', ordertype='market', orderside='long', pricemethod='market',TxnFees=0), type='exit', path.dep=TRUE)

# Process the indicators and generate trades
start_t<-Sys.time()
out<-try(applyStrategy(strategy=stratRB , portfolios=portfolio.st))
end_t<-Sys.time()
print("Strategy Loop:")
print(end_t-start_t)

start_t<-Sys.time()
updatePortf(Portfolio=portfolio.st,Dates=paste('::',as.Date(Sys.time()),sep=''))
end_t<-Sys.time()
print("updatePortf execution time:")
print(end_t-start_t)

chart.Posn(Portfolio=portfolio.st,Symbol=sym.st)

#Update Account
updateAcct(account.st)

#Update Ending Equity
updateEndEq(account.st)

#ending equity
getEndEq(account.st, Sys.Date()) + initEq

#View order book to confirm trades
getOrderBook(portfolio.st)

#Trade Statistics for CAGR, Max DD, and MAR
#calculate total equity curve performance Statistics
ec <- tail(cumsum(getPortfolio(portfolio.st)$summary$Net.Trading.PL),-1)
ec$initEq <- initEq ec$totalEq <- ec$Net.Trading.PL + ec$initEq
ec$maxDD <- ec$totalEq/cummax(ec$totalEq)-1 ec$logret <- ROC(ec$totalEq, n=1, type="continuous") ec$logret[is.na(ec$logret)] <- 0 RBrev1WI <- exp(cumsum(ec$logret)) #growth of $1 #write.zoo(RBrev1WI, file = "E:\\volfiltertest.csv", sep=",") period.count <- NROW(ec)-104 #Use 104 because there is a 104 week lag for the 52 week SD and 52 week median of SD year.count <- period.count/52 maxDD <- min(ec$maxDD)*100
totret <- as.numeric(last(ec$totalEq))/as.numeric(first(ec$totalEq))
CAGR <- (totret^(1/year.count)-1)*100
MAR <- CAGR/abs(maxDD)

Perf.Stats <- c(CAGR, maxDD, MAR)
names(Perf.Stats) <- c("CAGR", "maxDD", "MAR")
Perf.Stats

transactions <- getTxns(Portfolio = portfolio.st, Symbol = sym.st)
#write.zoo(transactions, file = "E:\\filtertxn.csv")

charts.PerformanceSummary(ec\$logret, wealth.index = TRUE, ylog = TRUE, colorset = "steelblue2", main = "SMA with Volatility Filter System Performance")        