The DVI indicator is a well-known indicator, created by David Varadi from CSS Analytics. It was introduced in 2009 as a good predictor for the S&P 500 over the past 30 years. Its performance on the S&P 500 has been studied in the blogosphere comprehensively. None of these studies, however, contained everything I was looking for, and since I have a few indicators on my todo list, I decided to use the DVI to create an approach for analyzing indicators.



The DVI indicator oscillates between 0 and 1. The basic strategy to trade it, is to enter short if the close is above 0.5, and a long otherwise. Despite its simplicity, this strategy is quite successful.

To get an idea of the performance, I started with the following chart which shows the expected returns (mean) over different time frames (from 1 to 8 days). The y-axis is the expected return (normalized to a daily return), while the x-axis is the DVI value (0 to 1 divided in 10 bins, using 0.1 as a step).

The utility code to produce this chart is available here and is interesting on its own – I love R! The chart was created using the following code:

require(quantmod) source("dvi.analysis.r") getSymbols("SPY", from="1900-01-01") aa = dvi.analysis(Cl(SPY["/2009"]), lags=seq(1,8), normalize=T, file.path="dvi.png")

My plan is to use 2010 onwards as out-of-sample set, that’s why these analysis are performed ending 2009.

Now, what clues this chart provides for trading? As expected (by the performance of the basic strategy), 0.5 seems like a good entry point. The returns are consistently positive below and negative above. The long positions (below 0.5) observe quite nice, although not perfect, improvement the lower the value of the indicator. Thus, one may consider strategies which increase the position size as the indicator moves lower.

Things are even more interesting for short positions. The most striking observation is that the returns are positive (losing trades) for DVI measurements higher than 0.9! That seems to suggest that once the value passes 0.9, exiting a short, or even entering a long, may improve the returns. Even the prior bar (at DVI of 0.8) is not that big on the 1-day portion – it may make sense to exit shorts even earlier. If you wonder how does this make sense – well, when the market trends up, it simply keeps pushing. I have seen a few of these short trades this year – quite painful.

If we switch from mean to median, the chart is completely different.

require(quantmod) source("dvi.analysis.r") getSymbols("SPY", from="1900-01-01") aa = dvi.analysis(Cl(SPY["/2009"]), lags=seq(1,8), normalize=T, file.path="dvi.median.png", func=median)

Positive returns for almost all values. What this means, is that when we go short, money are made not by increasing the winning number of short trades, but their relative win. This can be confirmed from the raw data returned by the function:

require(quantmod) source("dvi.analysis.r") getSymbols("SPY", from="1900-01-01") aa = dvi.analysis(Cl(SPY["/2009"]), lags=seq(1,8), normalize=T, file.path="dvi.median.png", func=mean) bb = na.exclude(aa$raw.res$'1'$'0.6') length(bb[bb<0]) # result 209 length(bb[bb>=0]) # result 207

Are the results statistically significant? Not sure how to measure that, but … From the observation that the number of short and long trades is approximately the same even for winning shorts on average, we can conclude that the system is not much different than a coin toss in terms of successful trades. However:

require(quantmod) source("dvi.analysis.r") getSymbols("SPY", from="1900-01-01") aa = dvi.analysis(Cl(SPY["/2009"]), lags=seq(1,8), normalize=T, file.path="dvi.median.png", func=mean) bb = na.exclude(aa$raw.res$'1'$'0.6') cc = as.numeric(na.exclude(aa$rets$"1")) t.test(bb, mu=mean(cc), conf.level=0.99, alternative="less") # One Sample t-test # # data: bb # t = -2.9498, df = 415, p-value = 0.00168 # alternative hypothesis: true mean is less than 0.0002968406 # 99 percent confidence interval: # -Inf -2.223589e-05 # sample estimates: # mean of x # -0.001234948 # The above could be interpreted that the probability of obtaining a sample # with this mean is very, very low. res = 0 for(ii in 1:1000) { if(mean(sample(cc, size=NROW(bb)) < mean(bb)) { res = res + 1 } } print(res) # The result is consistently below 10, or less than 1%

Pretty interesting, right? My conclusion is that the DVI is really, really good on the SPY.