For about 2 months I've been a member of stockfetcher and have been trying to code filters that give you a statistical edge. I've had some success but not to the extent which I'd like, but other people post thier filters in the forums there and here is a promising example from kevin in GA:
Well, I haven’t posted any new filters in a while, and it got me thinking about exactly how a filter (in essence, a trading system) should be developed. My personal feeling is that simply designing an entry filter falls far short of the goal. The entry assumes all of the risk – the exit gets all of the reward. Without thinking holistically, all of the effort that you put into a filter is wasted.
So here is something that I did over the Memorial Day weekend. I’ll explain my thinking as we go forward.
First, I believe that a good trading system is inherently understandable - that is, you can look at the rationale for stock selection and understand why it is supposed to work. How many people here use exotic indicators that they really don’t understand? Even the pedestrian ones like the MACD are not really clear as to why they might work – who really understands why the MACD is a good signal? (if it actually is, which I might debate endlessly with some folks here ...)
Ones that pass the test for me are usually based on simple things that have strong statistical underpinnings. I have always looked at Bollinger Bands as a good example of statistics used wisely. Stocks that close more than 2 standard deviations away from their historical mean usually revert back to that mean. I also think that most stocks tend to move in sync with the sector or index of which they are part, and when that usual correlation is disturbed, the stock will also revert back to its normal relationship with the index.
Working from that basic premise, one should be able to develop trading systems based on perturbations from the equilibrium between a stock and its index (e.g., AAPL and the Nasdaq, or WMT and the ^SPX). These short term deviations can be effectively traded long or short using the ratio of a stock and its index as the price that one follows.
I started by simply writing some code that took the ratio of each of the component stocks of the S&P 500, and then determined the historical relationship for the pair and how far from that mean the current close was.
/*FIRST DETERMINE HISTORICAL RATIO OF STOCK TO THE ^SPX*/
SET{PRICERATIO, CLOSE / IND(^SPX,CLOSE)}
SET{RATIOMA, CMA(PRICERATIO,100)}
SET{RATIOSTD100, CSTDDEV(PRICERATIO,100)}
SET{DIFF100, PRICERATIO - RATIOMA}
SET{ZSCORE100, DIFF100 / RATIOSTD100}
SET{THRESHOLD, RATIOSTD100 * 2}
I started by simply picking 100 days as the length, but I knew that other time frames would likely be more profitable. This piece of SF code tells me the number of standard deviations from the 100 day mean for the pair (stock / ^SPX). This is a simple way to get a Bollinger Band for the ratio of two stocks.
Statistics says that only about 5% of the time should any stock be more than 2 SD from its mean, and that this is usually not sustained for long (think of these bands as elastics that want to pull the stock back to its mean – the farther out they are the harder they try to pull back).
So far so good, but I need to determine when an entry is called for, and when to exit. So I need to look at different combinations of moving averages, entry point by Z-score, and exit criteria (should also be by Z-score).
Using StrataSearch, I programmed this in and looked at several hundred combinations for the period 12/31/1999 through 12/31/2009. All S&P500 stocks were included. The result was a profitable set of times around 10-30 days, centered between 15 and 20. The typical number of days in a trade was 5-10, so I added the requirement that all trades are ended after 20 days to weed out losers.
The best times for entry typically were when the ratio crossed beyond 2 SD, then reverted back to close inside of 1 SD. I had thought that it would be a larger move (2 back to the mean, for example) but those larger moves were fewer and the percent win rates were less.
So now the code looked like this:
/*FIRST DETERMINE HISTORICAL RATIO OF SECTOR ETF TO THE SPY*/
SET{PRICERATIO, CLOSE / IND(SPY,CLOSE)}
SET{RATIOMA, CMA(PRICERATIO,20)}
SET{RATIOSTD20, CSTDDEV(PRICERATIO,20)}
SET{DIFF20, PRICERATIO - RATIOMA}
SET{ZSCORE20, DIFF20 / RATIOSTD20}
ENTRY: Z-SCORE BELOW -2
EXIT: Z-SCORE ABOVE -1 OR DAYS HELD >20
This still returned too many stocks, and during market corrections the system took some big hits. So I simply added a criteria that included “close above MA(XX)” for each stock, where XX was 50, 100, 150 or 200 days. This hopefully keeps you out of taking long positions on stocks that are tanking (keeps you “buying the dips”).
This definitely helped, and the system dramatically beat out the ^SPX, but the equity curves were still choppy and the system called for a lot of trades.
I also figured that the ratio could be out of whack but the individual stock should also look like it is oversold as well. So I looked at different settings of the Williams %R (between -70 and -100) and also looked for stocks that closed below their individual Bollinger Bands. I could have used the RSI, or MACD, or any other indicator. I chose the Williams %R because I have seen a good correlation between it and a stock being oversold in the past (and it was easy to code).
So in a single massive optimization, I evaluated all possible combinations of timeframe, Z-score entry and exit, William’s %R and close relative to the Stock’s Bollinger bands. Days were all set to the same (for any daily input, varying from 10 to 30). In the end, the best system by backtesting was as follows:
Fetcher[
S&P500
/*FIRST DETERMINE HISTORICAL RATIO OF S&P STOCK TO THE SPY OVER THE LAST 16 DAYS*/
SET{PRICERATIO, CLOSE / IND(^SPX,CLOSE)}
SET{RATIOMA16, CMA(PRICERATIO,16)}
SET{RATIOSTD16, CSTDDEV(PRICERATIO,16)}
SET{DIFF16, PRICERATIO - RATIOMA16}
SET{ZSCORE16, DIFF16 / RATIOSTD16}
SET{THRESHOLD16, RATIOSTD16 * 2}
/*NEXT, SET CRITERIA NECESSARY TO TRIGGER A PAIR TRADE*/
SET{UPPERBAND16, RATIOMA16 + THRESHOLD16}
SET{LOWERBAND16, RATIOMA16 - THRESHOLD16}
ZSCORE16 BELOW -2
WILLIAMS %R(16) BELOW -94
CLOSE BELOW LOWER BOLLINGER BAND(16,2)
CLOSE ABOVE MA(200)
DRAW LOWERBAND16 ON PLOT PRICERATIO
DRAW UPPERBAND16 ON PLOT PRICERATIO
DRAW BOLLINGER BANDS(16,2)
ADD COLUMN ZSCORE16 {Z-score}
ADD COLUMN WILLIAMS %R(16)
DRAW ZSCORE16 LINE AT -1
DRAW ZSCORE16 LINE AT -2
DRAW ZSCORE16 LINE AT 0
SORT ON COLUMN 5 ASCENDING
CHART-TIME IS 6 MONTHS
]
EXIT: Z-SCORE ABOVE -1 OR DAYS HELD >20
I’ll let the equity curve and stats speak for themselves:
Yes, that little white line at the bottom is the performance of the ^SPX over this 10 year period!
A 900% return on an all long strategy during one of the most turbulent decades in recent history, including not one but two massive recessions that wiped out many people’s lifetime savings.
About 4 trades a week on average, with a CAGR of almost 26% (compared to the ^SPX at -2.74%). You just can't argue with those kind of numbers.
What I like most about this strategy is the Sharpe ratio - StrataSearch uses the monthly ratio, so one must multiply by the square root of 12 (3.46) to annualize it. The ratio is just over 1.7, which is exceptionally high for most trading systems, especially during that period.
Also note that the portfolio size is only 10 trades at any time, and historically your money was only in the market ~46% of the time. The rest sat risk-free in cash.
Why post this? because I think that people can not only take the filter and make money with it, but hopefully learn a little about the types of analysis and thinking that goes into design and validation of a good trading system.
Enjoy!
Kevin