Welcome to part two of an ongoing series, where I look at different breadth indicators and their viability in describing market health. You can read – and should read! – the introductory post here:
So last post, we (ok, “I”, since I’m doing all the work around here) established a baseline by using a moving average of SPY to tell us when to get in and out of the market. If the close had ten days above its moving average, we got in. If it had ten days below the moving average, we got out. This ten-day requirement is something for which
I optimized, and it reduces whipsawing.
Rather than sticking with some tried-and-true values such as 120 or 200 days for our moving average period, we optimized the value for our in-sample period of 2010-2012 inclusive, and then saw how it fared in our out-of-sample period of 2013-2015. And for fun, we took a look at the period of 2000-2015, but knowing that using earlier data for an “OOS test” is sometimes frowned upon. A period of 85 days turned out to work best.
Moving on to our first breadth test:
One method for assessing market health I’ve seen around the web lately is the use of new highs and new lows. Here’s how you do it: count how many stocks in an index (I’m using the Russell 3000) are making new 3-month highs today, and also count how many are making new 3-month lows. We’ll use 60 trading days instead, since they’re about the same. You do that every day, and make sure you’re using historically accurate members of the index, so you don’t get any survivorship bias.
Then do some math:
hilo_breadth = new_high_count / (new_high_count + new_low_count) * 100
The “* 100” bit is just so I’m not dealing with a small number, and can think of it as a percentage instead.
I will continue to use the “ten days of signal” requirement, which standardizes these tests to the baseline, and also eliminates whipsawing. We need a threshold to tell us to trade or to get out. When our hilo_breadth is over the threshold for ten days, we get into the market. When it’s below for ten days, we get out. If it bounces around the threshold, we don’t change the status quo.
I first optimized for a single threshold for both entrance and exit**, checking values from 5 to 95 in increments of 5. Here’s a graph of the result, using CAR/MDD as the criteria:
The best value turned out to be 50. However I was skeptical about that value, because it’s so spiky. “Hey dufus, look to the left! What about that nice plateau at 5 and 10?” I appreciate you drawing my attention to that, but there’s a problem: at those values, with a 10-day requirement, no trading happens. We just don’t get values below 5 or 10 for that long in the in-sample period, even during the worst of the market moments. So the values are always above the threshold, and we’re just buyin’ and holdin’. Hah! Thought you had me, didn’t you?
Ok we’ll go with 50, but we’ll be casting a gimlet eye over our results. Next let’s see if there’s an exit value below 50 that improves the results more.
As you can see, having an exit threshold of 45 improves results a little from the original 50. Let’s use those values: entrance at 50, exit at 45 (with 10-day signal). How does our in-sample equity curve look?
It’s behaving like we’d hope it would: getting out during the rough periods and getting back in afterward. It seems to enter and exit less than ideally though, as there are big drawdowns at both the start and end of each trade.
We had four trades during the in-sample period. Three were winners, one a loser. The average trade lasted 140 days, and our CAR/MDD was 0.51.
Let’s see how this held up in the out-of-sample (OOS) test:
The good news is that it didn’t fall apart. We captured the run of 2013. We unfortunately captured the dip of October 2014. In fact, we got out just before, and then got back in for the final meltdown. Doh! And more painfully, we got slammed with the Falling-Down-Stairs of August 2015. And then got out of the market, solidifying our losses.
For the OOS period, we had three trades, only one of which was a winner (the very long one). Average length of trade was 235 days. And our CAR/MDD was 0.48.
The bad news is: that’s worse than using a simple moving average! Our OOS period for the moving-average system gave us a CAR/MDD of 0.69, and the total profit for 2013-2015 was higher as well.
For fun (I have a weird definition of ‘fun’), let’s look at 2000-2015 with our new high/new low system:
So it looks like using 60-day new highs and new lows as a breadth indicator works, sort of, but is inferior to other methods.
One thought occurs to me: perhaps a more useful method might be to look at the number of new highs as a percentage of the total, rather than comparing it to the new lows. Since we want to get out early, we want to know when stocks stop making new highs and are poised for a downturn. By the time they are making new lows, the worst is upon us. Or…and this is just crazy talk now…what if we use a lack of highs as an exit, and a lack of lows as an entrance? Hmm. Remind me to check this out if I forget. This could be a never-ending process…
Next post, we’ll look at yet another method.
** You may be wondering why I just don’t optimize separate entrance and exit thresholds freely. Rather than the method I used, which was to first determine a single threshold for both, then afterward optimize for a better exit below the entrance. I did at first try allowing free rein to entrance and exit values, but I realized that I was getting awkward values. For example, I might get an optimized value of 45 for entrance and but then 65 (higher than the entrance) for an exit. Which, when you think about it, is weird. Because you could enter and then already be set up for an exit. If it wasn’t for the ten day requirement, you’d get out the next day! And that was turning the trading system into a short-term system, with many trades lasting around ten days. They were triggered for an entrance, were already eligible for an exit, and then were just held until 10 days of exit signal were recorded. That’s a swing trade, which doesn’t tell us much about the overall market health.
Therefore I decided to optimize for a single threshold for entrances and exits first, and only then determine a threshold for exits, which had to be the same or lower. That way we know the threshold had to already be above the exit level when it first moved above the entrance level.