Let’s wrap this up! We established a baseline using a moving-average system on the price of SPY to determine when we enter and exit the market. Then we tested a variety of breadth indicators, using the diffusion calculation and requiring entries and exits to have ten days above or below the threshold before acting.
Our grand prize winner used a breadth indicator that counted all the stocks that were up at least 30% in the last 60 trading days, versus all the ones that were down at least 30% in the same period (based on the Russell 3000 index members). All results below are for the out-of-sample period of 2013-2015, except the last line (2000-2015, which is mostly OOS).
* the 4% Daily system doesn’t trade when ten days of signal are required. Removing that requirement gave us frequent trading, but the results were worth reporting.
I’ve highlighted the winner of each row above in green.
I tried some other breadth indicators that weren’t worth devoting a whole blog post to, because they were epic failures. For completeness’ sake, I’ll mention them here:
• Bollinger bands. This one surprised me, because I thought for sure it would be useful. The breadth indicator determines the lower and upper Bollinger Bands for each member of the Russell 3000 (with 15 periods and 2 standard deviations). I optimized for the thresholds and got 30 as a good number for each. However in the out-of-sample period, the system never exited a trade. I therefore pronounced it useless. You might find something different though.
• “20-monthly”. This one is similar to our winning system, so I’m surprised it didn’t show better performance. You record all the stocks that are 20% or higher in the last 20 trading days (approximately one month), and the ones that are down 20% or more, and do the diffusion calculation like all the rest. I got a optimized threshold of 20 for entrance and exit, but in the out-of-sample data, it never traded.
• RSI(2). This didn’t trade, much like the “4% Daily” system when using the 10-day signal requirement. It’s such a short-term indicator, it rarely stays below or above a threshold for 10 straight days.
Also, I initially started out this quest by calculating breadth diffusion values and then using a moving average of those values to determine buy and sell signals. The results were ok, but I realized one important fact: a moving average eventually catches up with your indicator if it’s range-bound. If the indicator goes from 0 to 100, and pegs 100 for ten straight days, then your 10-day moving average will also have a value of 100. If your indicator then dips to 99—below your indicator—does that mean bad news and it’s time to sell? Probably not! During the big run up of 2013, these indicators that were compared to their moving averages all stopped trading at the worst possible times (i.e. when the markets took a tiny dip in an otherwise booming run). The simpler static thresholds proved to have better results.
This process gave me some ideas for future examination, and I’ll outline them here:
• There may be added benefit in examining the under- and over-achieving stocks independently. For example, can an increasing number of stocks making new 60-period lows be a better signal for exit, regardless of what the new-high stocks are doing? This is simply a matter of calculating the number of new-low stocks and dividing by the number of stocks in the index. Or conversely, perhaps an exit signal could be derived from the number of new-high stocks falling? This could be checked for all these types of breadth indicators.
• Mean reversion, baby! Some of these systems showed interesting behavior when reversing the signals and viewing them as short-term mean reversion strategies. I discovered them while working on this project. I have not yet compared them to a strict price-based baseline though (short-period MA or RSI(2) on the price of SPY perhaps). If they look like they’ll beat the best baseline I can find, I’ll post about them.
How do you go about calculating how many stocks are up or down 30% in the Russel 3000, can that be coded in ThinkorSwim?
If you don’t need to do any backtesting, you could calculate this by hand on a daily basis. I’ve never used thinkorswim so I can’t say if it’s possible there or not.
Set up a scan for all stocks that are up over 30% over the last 60 bars, and are in the Russell 3000. Write down that number. Do another scan for all stocks that are down more than 30% over the last 60 bars, and are members of the Russell 3000 index, and record that number as well. add the two numbers, and then divide the ‘up’ number by the total of the two. You’ll get a number between 0 and 1. If it’s over .75 and remains so for ten days, you have your buy signal. If it’s less than .75 and remains so for ten days, you have a sell signal. If it wobbles above and below that number, take no action to either buy or sell.
Hope that helps!
Now test back to 1924 …
Sounds tedious! 🙂 The Russell 3000 only goes back to 1987 though, so you’d need to pick a different index. Perhaps implicit in my testing is that the wider R3k index provides leading information about the S&P 500. Might be worth taking a look at breadth on the S&P as well. If you do that, let me know! And then there’s the question of whether or not data from that long ago has relevance for today’s high-speed always-on market.
Nice series, Matt. Your wife should know you really deserve a guys’ night out for the hard work.
Thanks Adrian!
Why did you choose to keep the 10-day requirement for entry throughout your testing? It appears it was chosen as a result of in-sample optimization when using moving averages. Also, did you test higher numbers of confirmation days? I only ask because it appears in the original post that the optimization curve is still upward-sloping at 10 days, and from experience extending the window farther oftentimes reveals even more profitable values farther out.
Hi Brian. Those are good questions. I did optimize the 10-day requirement when coming up with my MA baseline. I decided to keep it uniform across all tests for consistency. Creating a separate optimization for each breadth indicator seemed like it would increase complexity without adding anything beneficial. That doesn’t mean there aren’t different optimum signals for entry and exit, but I didn’t want that to confuse my research. It seems intuitive that a multi-day delay before acting would make sense on a slow-moving indicator, whereas a short (or no) delay would make sense on a fast-moving indicator such as the “4% daily” version.
You’re right about the optimization slope, it did rise to the 10-day point and might have gone further. I did not test beyond that point, but there may be further gains to be had. I don’t pretend to have come up with the ultimate market-breadth indicator system. 🙂 Only that this one seems to work better than the traditional way of gauging market health. If you investigate the delay period further, I’d love to know your results! Thanks for commenting.
Great series, I coded the algo on quantopian, would you mind if I make it public ?
Yes by all means, go ahead!