With October over I thought it would be a good idea to review our Market Seasonality Study.
Back in May of this year, we closed a seasonality trade in anticipation for a traditionally weak period of the stock index markets. That is the months of May, June, July, August, September, and October. So what did the market do during those months?
Below is a graph of the market with a "buy" at the start of the weak period and a "sell" at the end of the weak period.
As you can see, the traditionally weak period took off! Only in the last few weeks did it start giving back some of those gains. The weak period returned a $3,375 (3.4%) gain.
We are now entering a traditionally strong season for the stock index market. This is a well known seasonality period. In this article I would like to take a closer look at the seasonality bias which so many investors and traders talk about.
Seasonality Bias
First, I would like to test the popular trading idea of buying the S&P in November and selling in May. I will test this on the cash market going back to 1983. The number of shares to buy will be adjusted based upon a fixed amount of risk. In this case, $5,000 is risked per trade based upon the volatility of the market. As this is a simple market study no slippage or commissions were taken into account. The EasyLanguage code looks like this:
CurrentMonth = Month( Date ); If ( CurrentMonth = BuyMonth ) And ( MP = 0 ) Then Buy("Buy Month") iShares contracts next bar at market; If ( CurrentMonth = SellMonth ) Then Sell("Sell Month") iShares contracts next bar at market;Testing Environment
I decided to test the strategy on the S&P cash index going back to 1960. The following assumptions were made:
* Starting account size of $100,000.
* Dates tested are from 1960 through May 2017.
* The number of shares traded will be based on volatility estimation and risking no more than $5,000 per trade.
* Volatility is estimated with a two times 20-week ATR calculation. This is done to normalize the amount of risk per trade.
* The P&L is not accumulated to the starting equity.
* There are no deductions for commissions and slippage.
* No stops were used.
From here we can plug into the input values the buy month (November) and sell month (May). Doing this we generate the following equity graph:
It sure looks like these months have a long bias as those are some nice results. What would the equity curve look like if we reverse the BuyMonth and SellMonth? That is, let's buy in May and sell in November. Below is the equity curve for this inverted system.
The early years of 1960 to about 1970 the equity curve went lower and lower. Then it started climbing reaching an equity peak of 1987. During this period, the strategy was producing positive results. That equity peak occurred during 1987 should be familiar. That was the year we had the massive one day market crash on October 19th known as Black Monday. That's when Dow Jones Industrial Average dropped 22% in one day!
Since that event the behavior of market participants has been altered. This is not unlike the radical market changes which occurred after the 2000 market peek where much of the trending characteristics of the major markets were replaced by mean reversion tendencies. So far the basic seasonality study looks interesting. However, keep in mind that we do not have any stops in place.
Nor do we have any entry filter that would prevent us from opening a trade during a bear market. If the market is falling strongly when our BuyMonth rolls around we may not wish to purchase right away.
Likewise, we have no exit filter to prevent us from exiting when the market may be on a strong rise during the month of May. It's conceivable that the market may be experiencing a strong bull run when our usual SellMonth of May rolls around.
Simple Moving Average Filter
In order to avoid buying and selling at the wrong times I'm going to introduce a 30-period simple moving average (SMA) to act as a short-term trend filter. This filter will be used to prevent us from immediately buying into a falling market or selling into a rising market. For example, if our SellMonth of May rolls around and the market happens to be rising (trading above the 30-period SMA), we do not sell just yet. We wait until unit price closes below the short-term SMA. The same idea is applied to the buying side, but reversed. We will not go long until price closed above the short-term SMA. Within EasyLanguage we can create a buy/sell confirmation flag called BuyFlag and SellFlag which will indicate when the proper go-long or sell conditions appear based upon our short-term trend filter.
if ( MinorTrendLen > 0 ) Then BuyFlag = Close > Average( Close, MinorTrendLen )
Else BuyFlag = true;
If ( MinorTrendLen > 0 ) Then SellFlag = Close < Average( Close, MinorTrendLen )
Else SellFlag = true;
The MinorTrendLen variable is actually an input value which holds the look-back period to be used in the SMA calculation. You will notice there is an additional check to see if the look-back period is zero. This is done so we can enable or disable this SMA filter. If you enter zero for the look-back period, the code will always set our BuyFlag and SellFlag to true. This effectively disables our short-term market filter. This is a handy way to enable and disable filters from the system inputs. Below is the performance of both our Baseline system and the system with our SMA filter:
Seasonality Trade
Baseline | SMA Filter | |
---|---|---|
Net Profit | $383,604 | $419,965 |
Profit Factor | 5.73 | 6.22 |
Total Trades | 57 | 57 |
%Winners | 75% | 74% |
Avg.Trade Net Profit | $6,730 | $7,368 |
Annual Rate of Return | 2.71% | 2.83% |
Sharpe Ratio | 0.15 | 0.16 |
Max Drawdown(Intraday) | $36,151 | $29,979 |
RINA Index | 145 | 158 |
We increased the net profit, profit factor, average profit per trade, annual rate of return, and the expectancy score. The max intraday drawdown fell as well. Overall it looks like the SMA filter adds value.
MACD Filter
A standard MACD filter is a well known indicator that may help with timing. I'm going to add a MACD calculation, using the default settings, and only open a new trade when the MACD line is above zero. Likewise, I'm only going to sell when the MACD line is below zero. Within EasyLanguage we can create a MACD filter by creating two Boolean flags called MACDBull and MACDBear which will indicate when the proper major market trend is in our favor.
If ( MACD_Filter ) Then
Begin
MyMACD = MACD( Close, FastLength, SlowLength );
MACDAvg = XAverage( MyMACD, MACDLength );
MACDDiff = MyMACD - MACDAvg;
If ( MyMACD crosses over 0 ) Then
Begin
MACDBull = True;
MACDBear = False;
End
Else If ( MyMACD crosses under 0 ) Then
Begin
MACDBull = False;
MACDBear = True;
End;
End
Else Begin
MACDBull = True;
MACDBear = True;
End;
Below are the results with the MACD filter:
Seasonlity Trade
Baseline | SMA Filter | MACD Filter | |
---|---|---|---|
Net Profit | $383,604 | $419,965 | $367,642 |
Profit Factor | 5.73 | 6.22 | 4.74 |
Total Trades | 57 | 57 | 56 |
%Winners | 75% | 74% | 71% |
Avg.Trade Net Profit | $6,730 | $7,368 | $6,565 |
Annual Rate of Return | 2.71% | 2.83% | 2.65% |
Sharpe Ratio | 0.15 | 0.16 | 0.12 |
Max Drawdown(Intraday) | $36,151 | $29,979 | $36,151 |
RINA Index | 145 | 158 | 111 |
Utilizing the MACD filter and comparing it to our baseline system, we reduced every so slightly many of the performance metrics. Overall, it does not appear to be better than our SMA filter.
RSI Filter
For our final filter I will try the RSI indicator with its default loopback period of 14. Again, like the MACD calculation, I want price moving in our direction so I want the RSI calculation to be above zero when opening a position and below zero when closing a position.
If ( RSI_Filter ) Then
Begin
RSIBull = RSI(Close, 14 ) > 50;
RSIBear = RSI(Close, 14 ) < 50;
End
Else Begin
RSIBull = true;
RSIBear = true;
End;
Seasonality Trade
Baseline | SMA Filter | MACD Filter | RSI Filter | |
---|---|---|---|---|
Net Profit | $383,604 | $419,965 | $367,642 | $381,850 |
Profit Factor | 5.73 | 6.22 | 4.74 | 5.39 |
Total Trades | 57 | 57 | 56 | 57 |
%Winners | 75% | 74% | 71% | 75% |
Avg.Trade Net Profit | $6,730 | $7,368 | $6,565 | $6,699 |
Annual Rate of Return | 2.71% | 2.83% | 2.65% | 2.70% |
Sharpe Ratio | 0.15 | 0.16 | 0.12 | 0.14 |
Max Drawdown(Intraday) | $36,151 | $29,979 | $36,151 | $35,052 |
RINA Index | 145 | 158 | 111 | 137 |
The RSI filter performed better than the MACD filter. Comparing it to the Baseline, we see it's very similar. With the RSI filter the net profit, profit factor and RINA Index are all slightly lower.
In the end it does appear applying a SMA filter or an RSI filter can improve the baseline results. Both filters are rather simple to implement and were tested for this article with their default values. Of course, you could take this simple study much further.
Apply SMA Filter on Weak Seasonality Period?
After performing the different filter tests and choosing the simple moving average filter, I started wondering what it would look like if we applied to the weak seasonality period. Below are the before-and-after equity curves.
It appears the simple moving average filter helps a lot. While we can still see the weak seasonality period equity curve is not nearly as attractive as the strong seasonality period, it did improve to somewhat decent.
Conclusion
It certainly appears there is a significant seasonal edge to the S&P market. The very trading rules we used above for the S&P cash market could be applied to the SPY and DIA ETF markets. I've tested those ETFs and they produce very similar results. The S&P futures market also produces similar results. It even appears to work well on some stocks.
Keep in mind this market study did not utilize any market stops. How can this study be used? With a little work an automated trading system could be built from this study. Another use would be to apply this study as a filter for trading other systems.
This seasonality filter could be applied to both automated trading systems or even discretionary trading. It may not be much help for intraday trading, but it may be worth testing. Anyway, just being aware of these major market cycles can be helpful in understanding what's going on with today's markets and where they may be going in the near future.
Hope you found this study helpful.
Hi Jeff,
Thanks for an interesting article. At a glance, this would appear to give something between a four and five percent annual return on investment in its basic form. You state that the basic form is not a complete trading system, but I beg to differ . . .
For a buy and hold investor (who has no stops anyway, and will undergo whatever draw-down the market drags him through), then this offers a significant improvement. There is a timing element, which is systematic, and there is also a reduced amount of time spent in the market, which represents a systematic reduction of risk. So yes, it’s all pretty basic, but to someone operating on the very most basic level (the typical buy-and-hold investor), this approach may well be a gift.
Good point Bluehoseshoe in regards to the buy and hold investor. I really did not think of it that way. Thanks for posting!
The RSI filter is something completely new! Really strong stats!
Maybe shorting the lookback period would improve results?
https://nightlypatterns.wordpress.com
That would be worth testing. I did no optimization on any of the inputs.
Why is the Sharpe Ratio so low?
[…] Market Seasonality Study- system trader … – Jeff is the founder of System Trader Success – an inBox magazine dedicated to sharing great ideas and concepts from the world of automated trading systems. […]
[…] a recent article, Seasonality Study, I took a look at the classic seasonality effect as seen in the U.S. markets. Briefly recapping […]