In Short Term Trading Strategies That Work Larry Connors and Cesar Alvarez present a trading strategy built on buying pullbacks and selling breakouts. Without getting into too much detail the goal is to be the bull's supplier. Here's our slightly modified set-up:
Signals:
- Security is priced above the 50 day MA, and
- Security is priced above 100 day MA, and
- Security hits a new 10 day low
- A new 10 day high is met, or
- Price drops below the 50 day MA, or
- Price drops below our stop loss, or
- We've been holding the security for 10 days
Results:
- Number of trials: 1,000
- Purchase price: 100% of low
- Stop loss: None
- Short MA: 50 days
- Long MA: 100 days
Here are the results of a simple back test for each year using the default parameters:
We can also see how buying below the 10-day low performs by changing the purchase price. The purchase price 98% of low means we will purchase the security if the price falls 2% below it's 10-day low. If a year is not listed in a table that year had no valid trades.
100% of low
99% of low
98% of low
97% of low
96% of low
95% of low
94% of low
93% of low
92% of low
91% of low
90% of low
Scenarios for stop losses have not been posted since they failed to provide any premium when being implemented. They were unsuccessful in early testing by prematurely selling trades and for that we did not conduct any further experiments.
Code:
Third party packages: None
With a basic understanding of programming our code is pretty easy to implement. All we have to do is set the appropriate parameters, run the program, and wait for it to terminate. Our code is making a fair amount of calculations so longer time frames and more trials lead to longer run times. Average run times were just under 1 minute when testing the S&P 500 for one year with 1,00 trials; placed in the proper hands our program can more than certainly be made quicker.
FileSnack link: http://snk.to/f-chmfo5ic
The zipped file contains:
- Main program: theBullsSupplier.py
- S&P 500 companies: SP500.txt
- Empty folder: Tickers
- Data collector: poorBoysData.py
To ensure the program runs properly save all the contents to the same location.
How the code works:
Our program begins by back testing a predefined set of tickers using the buy and sell signals listed above. In order to reduce biased results we ‘walk’ through our time period, that is we feed our program a single day at a time. Each day we calculate buy signals and if the conditions are met we’ll purchase the security, hold it, and continue to cycle through each proceeding day by calculating sell signals. Once the security is bought and sold we'll write down the trade and move onto the next day. Each trade has the following information:
[Purchase date, Purchase price, Sell date, Sell price, Return, Ticker]
The end result is a list of every single trade that could have potentially occurred in the predefined date range with the predefined list of tickers.
We then sort these entries by purchase date in ascending order (oldest first) and every trade that falls on the same day is put into a list and archived in a dictionary with key-word purchaseDate. The result is a data structure we can query to pull a list of all trades that could have occurred on a specified date.
purchaseDict['2012-01-03'] is associated with the entry:
['2012-01-03', 36.73, '2012-01-13', 35.71, 0.972, 'DPS']
['2012-01-03', 26.78, '2012-01-18', 27.13, 1.013, 'MO']
['2012-01-03', 78.73, '2012-01-13', 81.89, 1.04, 'ORLY']
Which are all the trades that could have occurred on January 03, 2012.
We now have all the information we need to begin an actual back test. We begin with the first possible purchase date, search our dictionary to obtain a list of all possible trades that could have occurred on that day, choose one at random, write down the trade's information, and move onto the next available purchase date.
The first possible purchase date in 2012 was January 3. If we picked one trade at random - say DPS - we would have sold on January 13th and the next possible trade date would have been January 17th:
['2012-01-17', 27.26, '2012-01-31', 25.45, 0.934, 'AVY']
Choose a random trade from the list and repeat. In this case there is only one trade to choose from.
Once all dates have been exhausted we can calculate an annual return by finding the product of all the year's trades. We will do this for a specified number of times and output the average and standard deviation of annual returns. Since our data doesn't tell us which trade occurred when it’s impossible for us to define the exact sequence of trades. The idea is if we get enough random trade sequences we'll create a universe of possible scenarios with the most likely being the most prevalent -- it's a way we can calculate a return and cover all our bases at the same time.
Further Functionality and Modification
Our code has two primary functions:
- Back test a batch of stocks and return the average and standard deviation
- Find and return the stocks that performed the best in a given period of time
To specify what we would like to do we change the True/False value of the user defined variables 'batchTest' and 'performedBest.' batchTest will print the average and standard deviation of returns; performedBest will print the 'topPerformers' number of best performers. We change the variable ‘numTrials’ to define how many scenarios we want our back test to complete.
Each analysis will test tickers from the S&P 500 or a user defined list of tickers. To use the S&P 500 change the user defined variable 'isSP500' to True; to use a more specific set of tickers change 'isBatch' to True and populate the 'batchList' with the tickers you want to back test. Remember, these are pulling from a file so if the file doesn't contain the ticker's data the program won't run. To download a stock’s data in the proper format use the Poor Boy's Data collector given in an earlier post.
We can also modify our purchase and sell parameters. If we wanted to buy the stock below the 10-day low by a certain percentage we would change the 'percentOfMin' variable to (1-percent under low). For example, if we only wanted to purchase a stock if the price fell 2% below the 10-day low we would change 'percentOfMin' to .98.
On the other hand, if we wanted to use a stop loss we would set 'stopLoss' to (1-maximum loss). So, if we wanted to lose no more than 2% we would change 'stopLoss' to .98.
The code also lets us specify the short and long moving averages. 'daysIMA' is considered the short moving average and 'daysInLongMA' is the long moving average. I apologize for the poor naming conventions.