We have changed our back testing program to be more
efficient and easier to follow. In the
process we caught some semantic errors that have been corrected; these
corrections changed our data and we are working on an update to reflect the changes. Our data collector 'poorBoysData' has also been changed to collect data in reverse chronological order, they were previously sorted chronologically.
We suspect there will be one or two more updates in the
future; the functionality of this program is essentially completed and all that needs to be done are cosmetic changes.
theBackTest: http://snk.to/f-ct9e788p
The zipped file contains:
The previous downloads have also been changed.
- theBackTest.py
- poorBoysData.py
- An empty data folder called 'Tickers'
- A text file called 'SP500.txt'
- A text file 'tBT Descriptions.txt' which contains descriptions of all our variables.
The previous downloads have also been changed.
New features:
1. An easier method for calculating buy and sell signals.
2. A portfolio function where we can specify an initial investment, trading fees, and
correction for slippage.
3. A plotting option where we can display the returns of our simulated universe, the
middle x% of the universe, and the returns of the S&P 500.
Buy and Sell Changes:
Buy signals are calculated through the function
‘buySignal’ whose input is a list of dictionaries that contain open, high, low,
close, and volume data for each day up to and including the current
day. For example the first few entries for GE’s
buySignal on 2013-04-26 are:
[[ {'Date': '2013-04-26', 'Open': 21.98, 'High': 22.40,
'Low': 21.97, 'Close' 22.21, 'Volume': 47012500} ],
[ {'Date': '2013-04-25', 'Open': 22.21, 'High': 22.23,
'Low': 21.91, 'Close' 21.95, 'Volume': 41462900} ],
[ {'Date': '2013-04-24', 'Open': 21.69, 'High': 22.03,
'Low': 21.65, 'Close' 21.96, 'Volume': 51496600} ]]
From here we can calculate our buy signals through
the use of built in functions ‘sMA,’ ‘histLow’, and
‘histHigh.’ If our buy signal is triggered the function must return the buy price otherwise return False.
Let's use
‘theBullsSupplier.py’ for an example:
def buySignal(histData):
# Target price > 200 day MA
lowoverLongMA = histData[0][‘Low’] > sMA(histData[1:], longMA)
# Target price > 50 day MA
lowoverShortMA = histData[0][‘Low’] > sMA(histData[1:], shortMA)
# Target price sets new low
newLow = histData[0][‘Low’] < histLow(histData[1:], highlowPeriods)
# Our target price is the historical 10 day low
targetPrice = histLow(histData[1:], highlowPeriods)
# If the target price is higher than the current day’s open then our signal would
# trigger below our target price and become the current day’s open
if targetPrice > histData[0][‘Open’]:
targetPrice = histData[0][‘Open’]
# if lowoverLongMA, lowoverShortMA, and newLow evaluate to True our sell
# signal is triggered and our function returns targetPrice. Otherwise our
# function returns False.
if lowoverLongMA and lowoverShortMA and newLow:
return targetPrice
else:
return False
Our signals are the Boolean lowoverLongMA, lowoverShortMA, and newLow with a target buyPrice of the historical 10 day low. If the current day’s open is below our target
buy price then buyPrice will be changed to the current day's open. If all the signals evaluate to True our
buySignal function will return buyPrice and start calculating sell signals, otherwise it will return false.
The process for creating a sell signal is the exact same - return sell price if signals evaluate to True otherwise return False.
Portfolio Function:
When we cycle through our sequence of trades we send each one to our ‘portfolio’ function. This
function will take in our bank – the portfolio value prior to the trade, subtract
commission, subtract the cost basis, add the market value at time of sell, and
subtract commission. We calculate the
return of our portfolio by dividing our bank by our initial investment.
Our buy and sell price is adjusted for slippage by
multiplying our target price by a random number between (1 + [-slippage,
slippage]). If slippage was .005 our purchase
price would fall between [99.5%, 100.5%] of our target price.
Plotting:
We can now plot! In order to do so we must set 'plot' equal to True. Setting plotPopulation to True will draw a scatter plot of all portfolio
values at the end of each trade for every trial conducted; plotMiddle will
draw a scatter plot of the middle x% of portfolio values; and plotSP500 draws a line of returns for the
S&P 500 over the duration of our analysis.
It should be noted that our program's run time increases
with the amount of data. Plotting 20,000
trials between 2007-01-01 and 2012-12-31 will take more than a fair amount of
time. We can bypass the run time issue by setting 'plot' to False. This function will undergo major changes in the future.
Hello there, and great job on this project, really interested on how it may turn out. Also, would you be able to post a detailed guide on how to run this? Thanks,
ReplyDeleteDarkKunai
Hi,
DeleteThanks for the interest in the site, I'll begin working on a detailed guide and get it posed as soon as I can. I'll shoot for Sunday.
A guide has been published.
Delete