Trading Moving Averages with Less Whipsaws

[This article was first published on Quintuitive » R, 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.

Using a simple moving average to time markets has been a successful strategy over a very long period of time. Nothing to brag home about, but it cuts the drawdown of a buy and hold by about a half, sacrificing less than 1% of the CAGR in the process. In two words, simple yet effective. Here are the important numbers (using the S&P 500 index from 1994 to 2013, inclusive):

StatitisticSMA 200Buy and Hold
CAGR6.51%7.13%
Sharpe Ratio0.560.37
Sortino Ratio0.050.04
Pct in Market

70.1499.94
Winning Pct54.61%53.84%
Avg Drawdown2.13%2.48%
Avg Annual Drawdown9.12%15.83%
Max Drawdown28.28%56.78%
Gain to Pain0.710.45

The moving average system is better by far. First, we can leverage it 2:1 (thus, bringing CAGR up to approximately 13%) and still, our maximum drawdown is going to be comparable to the buy and hold. Furthermore, it’s in the market only 70% – less risk and probably the returns can be further boosted by putting the money to work in alternative assets.

One problem with these type of systems are whipsaws. The system works well when the price stays away from the moving average. However, in close proximity, one may have to enter/exit in succession losing money on the way.

One way to address this issue is to use an alternative “line” to trigger the exits (or the entries, for that matter). It could be a percentage band, but that’s hardly universal. Better to bring volatility into picture.

Let’s do something more robust as an illustration.

require(quantmod)
require(btutils) # For construct.indicator

# Download data
gspc = getSymbols("^GSPC",from="1900-01-01",auto.assign=F)

# Compute the returns
rets = ROC(Cl(gspc),type="discrete")

# Compute the adjusted returns
adj.rets = sqrt(runSum(rets*rets,10)/9)

# The moving average
sma = runMean(adj.rets,n=200)

# The standard deviation
stddev = sqrt(runSum(adj.rets*adj.rets,200)/199)

# Long trades are entered when the average turns positive
upper.band = 0.0

# Long trades are closed when the average return goes below -0.5 standard deviations
lower.band = -0.05*stddev

# For the "uncushioned" version use
# lower.band = 0

uu = ifelse(sma>upper.band,1,0)
dd = ifelse(sma

First we apply the moving average not to the prices, but to the returns (an interpretation of David Varadi’s Error Adjusted Momentum). The “cushioned” system goes long when the average becomes positive, but to exit, it leaves some cushion below the moving average. What do we gain?

StatitisticCushioned EAEASMA 200Buy and Hold
CAGR10.93%9.11%6.51%7.13%
Sharpe Ratio0.800.690.560.37
Sortino Ratio0.070.060.050.04
Pct in Market80.28%76.29%70.14%99.94%
Winning Pct55.21%55.00%54.61%53.84%
Avg Drawdown1.88%2.05%2.13%2.48%
Avg Annual Drawdown8.90%9.26%9.12%15.83%
Max Drawdown19.39%19.57%28.28%56.78%
Gain to Pain1.230.980.710.45

To compare apples to apples, we added two systems. The first one (dubbed EA) uses error adjusted returns to compute the SMA, but enters and exits when the SMA crosses the 0 line. The “cushioned” version is the system implemented by the above code.

Even after taking into account that the new strategies stays longer in the market, there seems to be slight improvement. But that’s not the point. The “cushioned” approach did 4 trades in total – it exited for the two bear markets. That’s about 4 trades. The non-cushioned approach had 80 trades. Mission accomplished in other words.

The post Trading Moving Averages with Less Whipsaws appeared first on Quintuitive.

To leave a comment for the author, please follow the link and comment on their blog: Quintuitive » R.

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)