Tuesday, April 30, 2013

Sell to the Bulls: Buy the 10 Day Low

[Note: Data is out of date -- we are working on an update.]

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:


Buy
  • Security is priced above the 50 day MA, and
  • Security is priced above 100 day MA, and
  • Security hits a new 10 day low
Sell
  • 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:


To get a feel for how our strategy performs we have to back test different scenarios and find which combination of parameters is optimal.  These are our default parameters:

  • 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:

Year
Begin
End
Return
Stdev.
2008
1,000
1,457
45.74%
.239
2009
1,000
1,828
82.90%
.378
2010
1,000
1,711
71.11%
.251
2011
1,000
1,323
32.32%
.198
2012
1,000
1,426
42.66%
.204

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
Year
Begin
End
Return
Stdev.
2008
1,000
1,451
45.18%
0.237
2009
1,000
1,836
83.62%
0.369
2010
1,000
1,724
72.44%
0.257
2011
1,000
1,318
31.89%
0.2
2012
1,000
1,427
42.75%
0.207

       99% of low
Year
Begin
End
Return
Stdev.
2008
1,000
1,254
25.42%
0.165
2009
1,000
2,554
155.43%
0.568
2010
1,000
1,659
66.0%
0.208
2011
1,000
1,316
31.69%
0.183
2012
1,000
1,751
75.15%
0.265

       98% of low
Year
Begin
End
Return
Stdev.
2008
1,000
1,486
48.67%
0.19
2009
1,000
4,521
352.18%
0.637
2010
1,000
1,993
99.35%
0.218
2011
1,000
1,328
32.83%
0.182
2012
1,000
1,325
32.51%
0.147

       97% of low
Year
Begin
End
Return
Stdev.
2008
1,000
1,464
46.47%
0.165
2009
1,000
2,852
185.28%
0.26
2010
1,000
1,489
48.96%
0.152
2011
1,000
1,045
4.51%
0.083
2012
1,000
1,167
16.8%
0.055

       96% of low
Year
Begin
End
Return
Stdev.
2008
1,000
1,389
38.97%
0.051
2009
1,000
2,912
191.28%
0.367
2010
1,000
1,111
11.16%
0.055
2011
1,000
1,155
15.53%
0.031
2012
1,000
1,777
77.75%
0.069

       95% of low
Year
Begin
End
Return
Stdev.
2008
1,000
1,345
34.59%
0.041
2009
1,000
2,164
116.49%
0.266
2010
1,000
1,122
12.24%
0.058
2011
1,000
1,073
7.35%
0.005
2012
1,000
1,263
26.36%
0.006

       94% of low
Year
Begin
End
Return
Stdev.
2008
1,000
1,194
19.42%
0.036
2009
1,000
1,904
90.42%
0.151
2010
1,000
1,013
1.36%
0.013
2011
1,000
984
-1.54%
0.0
2012
1,000
1,202
20.28%
0.0

       93% of low
Year
Begin
End
Return
Stdev.
2008
1,000
1,293
29.37%
0.0
2009
1,000
1,724
72.48%
0.127
2010
1,000
1,025
2.59%
0.0
2011
1,000
970
-2.92%
0.0
2012
1,000
972
-2.8%
0.0

       92% of low
Year
Begin
End
Return
Stdev.
2008
1,000
1,010
1.05%
0.0
2009
1,000
1,413
41.35%
0.0
2012
1,000
972
-2.8%
0.0

       91% of low
Year
Begin
End
Return
Stdev.
2009
1,000
1,055
5.52%
0.0
2012
1,000
972
-2.8%
0.0

       90% of low
Year
Begin
End
Return
Stdev.
2009
1,000
1,066
6.69%
0.0

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:


Language: Python 2.7
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:
  1. Main program: theBullsSupplier.py
  2. S&P 500 companies: SP500.txt
  3. Empty folder: Tickers
  4. Data collector: poorBoysData.py
To ensure the program runs properly save all the contents to the same location.


How the code works:


I'll try to make this as concise as possible but I make no promises.  It's important we understand the program’s underbelly so we can fully modify it and more accurately interpret the results.  Let's start at the top.

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.

Monday, April 29, 2013

Poor Boy's Data

Before we start collecting data we have to remember that a model is only as good as the data it uses but quality data comes with an expensive price tag and we don't have the cash to spare. For our purposes Yahoo! Finance will have to suffice.


Code


Language: Python 2.7
3rd Party Packages: None

This program is pretty simple to use, all we have to do is run the program and wait for it to terminate. Our data comes from Yahoo! so depending on the internet connection and the amount of data being downloaded this may take a few minutes to complete.

FileSnack link:  http://snk.to/f-c7clb99t

The zipped file contains:
  1. Main program: poorBoysData.py 
  2. S&P 500 companies: SP500.txt
  3. Empty folder: Tickers
To ensure the program runs properly keep the contents in the same location.  If we're saving to the desktop poorBoysData.py, SP500.txt, and Tickers all need to be on the desktop; if we're saving to the documents folder then poorBoysData.py, SP500.txt, and Tickers all need to be saved to the documents folder.


How the Code Works


Our Yahoo! Data Collector is pretty straight forward.  Our program navigates to Yahoo, downloads the historical data, and saves it to the file [stock's ticker].txt in the folder 'Tickers.'

In our back test we always ran into problems with 4:1 stock splits and special one-time dividends so to correct these data errors we replaced Yahoo!'s open, high, low, and close with the following:
Open = (Open / Close) * Adjusted Close
High= (High/ Close) * Adjusted Close
Low= (Low/ Close) * Adjusted Close
Close = Adjusted Close
The price adjustment can be turned off by setting adjustPrices to False.


Further Functionality and Modification


There are four primary variables that if changed will make the program perform a different function and collect data in a different way. We can choose to collect data from the S&P500 by changing isSP500 to True or we can collect a specific set of data by setting isUsersTickers to True and populating the usersTickerList list with ticker symbols.  The fourth is the adjustPrices variable mentioned above. 

Wednesday, April 24, 2013

Purpose of theBrokeQuant


If you’re anything like me you’re looking to trade while maintaining a day job, a life, and a budget.  This puts us at a severe disadvantage as we’re not always around a computer to check our positions and most unfortunately we can’t afford those fancy programs, super high-speed connections, or thoroughly scrubbed, extremely accurate data that helps us do so.  We must improvise with what we have: the Internet, a computer, and programming knowledge. 

Throughout this blog I will publish tools, code, and analyses that will help us better understand and track the market without drastically altering our wallets or our already busy schedules.