This strategy is going to use the volume weighted average price (VWAP) as an indicator to trade mean version back to VWAP. **Annualized Sharpe Ratio (Rf=0%) is**** 0.9016936**.

This post is a response to http://gekkoquant.com/2012/07/29/trading-strategy-sp-vwap-trend-follow/ where there was a bug in the code indicating that VWAP wasn’t reverting (this didn’t sit well with me, or some of the people who commented). As always don’t take my word for anything, backtest the strategy yourself. One of the dangers of using R or Matlab is that it’s easy for forward bias to slip into your code. There are libraries such as Quantstrat for R which protect against this, but I’ve found them terribly slow to run.

Trade logic:

- All conditions are checked at the close, and the trade held for one day from the close
- If price/vwap > uLim go short
- If price/vwap < lLim go long

Onto the code:

^{?}View Code RSPLUS

library("quantmod") library("PerformanceAnalytics") #Trade logic - Look for mean reversion #If price/vwap > uLim go SHORT #If price/vwap < lLim go LONG #Script parameters symbol <- "^GSPC" #Symbol nlookback <- 3 #Number of days to lookback and calculate vwap uLim <- 1.001 #If price/vwap > uLim enter a short trade lLim <- 0.999 #If price/vwap < lLim enter a long trade #Specify dates for downloading data startDate = as.Date("2006-01-01") #Specify what date to get the prices from symbolData <- new.env() #Make a new environment for quantmod to store data in getSymbols(symbol, env = symbolData, src = "yahoo", from = startDate) mktdata <- eval(parse(text=paste("symbolData$",sub("^","",symbol,fixed=TRUE)))) mktdata <- head(mktdata,-1) #Hack to fix some stupid duplicate date problem with yahoo #Calculate volume weighted average price vwap <- VWAP(Cl(mktdata), Vo(mktdata), n=nlookback) #Can calculate vwap like this, but it is slower #vwap <- runSum(Cl(mktdata)*Vo(mktdata),nlookback)/runSum(Vo(mktdata),nlookback) #Calulate the daily returns dailyRet <- Delt(Cl(mktdata),k=1,type="arithmetic") #Daily Returns #signal = price/vwap signal <- Cl(mktdata) / vwap signal[is.na(signal)] <- 1 #Setting to one means that no trade will occur for NA's #Stripping NA's caused all manner of problems in a previous post trade <- apply(signal,1, function(x) {if(x<lLim) { return (1) } else { if(x>uLim) { return(-1) } else { return (0) }}}) #Calculate the P&L #The daily ret is DailyRet(T)=(Close(T)-Close(T-1))/Close(T-1) #We enter the trade on day T so need the DailyRet(T+1) as our potential profit #Hence the lag in the line below strategyReturns <- trade * lag(dailyRet,-1) strategyReturns <- na.omit(strategyReturns) #### Performance Analysis ### #Calculate returns for the index indexRet <- dailyRet #Daily returns colnames(indexRet) <- "IndexRet" zooTradeVec <- cbind(as.zoo(strategyReturns),as.zoo(indexRet)) #Convert to zoo object colnames(zooTradeVec) <- c(paste(symbol," VWAP Trade"),symbol) zooTradeVec <- na.omit(zooTradeVec) #Lets see how all the strategies faired against the index dev.new() charts.PerformanceSummary(zooTradeVec,main=paste("Performance of ", symbol, " VWAP Strategy"),geometric=FALSE) #Lets calculate a table of montly returns by year and strategy cat("Calander Returns - Note 13.5 means a return of 13.5%\n") print(table.CalendarReturns(zooTradeVec)) #Calculate the sharpe ratio cat("Sharpe Ratio") print(SharpeRatio.annualized(zooTradeVec)) |

Thanks for the quick correction. Keep up the good work!

sorry for the dumb question but what does uLim ILim means?

It’s just a threshold used to determine when to enter the trade

So if price/vwap is greater than the upper limit (uLim) do a trade

So if uLim was 1.02 a trade would only occur when the price is greater than 1.02*vwap. The larger uLim (or smaller lower limit lLim) then the strategy waits for a more extreme move away from vwap before trading. This means that there will be less trades per year and will probably reduce the sharpe ratio (although doesn’t mean it is a bad strategy).

If you want to look for more extreme moves then it might be best to run the strategy on many different index/stocks so that you have a greater chance of being in the market.

Thanks. Interesting idea. From the graph it looks like equity curve became sideways since mid 2009. Any thoughts on what could be the reason? Low ,market volatility?

Normally any trend trading strategies works good if the VIX is at higher levels. However when the VIX dropped then you trading strategy might give you less losses or profits. 2010- mid of 2013 market volatility is very less across the globe. Those are the periods one should stop trading their stratgegy.

Thanks for sharing this code! I reproduced it and it works well. My question is, when you wrote the variable ‘trade’, you only wrote when to long and short nothing about when to close a position. so, you must assume the trader will close the open position at the daily closing price by the end of each trading day, right?

Hello! Thanks for the article!

Why not to replace this:

trade <- apply(signal,1, function(x) {if(xuLim) { return(-1) } else { return (0) }}})

with this:

trade <- ifelse(signal uLim, -1, 0))

I did not test it, but it should work. And should work faster.

Whoa, little mistake:

trade <- ifelse(signal uLim, -1, 0))