Strategy simulation often includes going through same steps : determining entry and exit moments, calculating number of shares capital and pnl. To limit the number of lines of code needed to perform a backtest, the twp library includes a simple backtesting module.
The backtest module is a very simple version of a vectorized backtester. It can be used as a stand-alone module without the rest of the tradingWithPython library.
All of the functionality is accessible through the Backtest class, which will be demonstrated here.
The backtester needs an instrument price and entry/exit signals to do its job. Let's start by creating simple data series to test it.
In [2]:
import pandas as pd
import tradingWithPython as twp
assert twp.__version__ >= '0.0.12' , 'Please update your twp module '
# create example signals for demonstrating backterster functionality
price = pd.Series(arange(10)) # price, just a linear incrementing series
signal = pd.Series(index=price.index) # strategy signal, in $
signal[2] = 100 # go long 100$
signal[3] = 0 # exit
signal[6] = -100 # go short 100$
signal[8] = 0 # exit
pd.DataFrame({'price':price,'signal':signal}) # show price and signal as a table.
Out[2]:
The Backtest
class is created with these arguments:
The signal can be either capital to trade or number of shares to buy/sell. 'capital' is the default mode, to use number of shares, use signalType='shares'
All calculations are perfromed at class creation.
Results of the calculations can be acessed as follows:
In [5]:
# use price and signal to perform a backtest
bt = twp.Backtest(price,signal) # create class, simulate
bt.plotTrades() # plot price and trades (short=red markers, long=green markers)
figure()
bt.pnl.plot(style='x-') # plot pnl
title('pnl')
print 'Sharpe: ', bt.sharpe
# normally, daily change in shares (delta) is not included in the Backtest.data .
# Sometimes it is handy to have it, for adding transaction cost for example.
# You can easily add it :
bt.data['delta'] = bt.data['shares'].diff().fillna(0)
bt.data # output strategy data
Out[5]:
In [249]:
# start with getting some data to test on
import tradingWithPython.lib.yahooFinance as yahoo # yahoo finance module
stockname = 'TSLA'
n = 30*12
price = yahoo.getHistoricData(stockname)#['adj_close'][-n:]
#price.plot()
normally a buy/sell signal would be generated by a strategy. For simplicity I'll just simulate holding a position between two dates
In [3]:
import pandas as pd
import tradingWithPython as twp
import tradingWithPython.lib.yahooFinance as yahoo # yahoo finance module
stockname = 'SCTY' #'SCTY' # 'TSLA' #'SCTY' #'TSLA''GS''NFLX' 'AMZN'
n = (5*4)*12
# adj_close close high low open volume
price = yahoo.getHistoricData(stockname)['open'][-n:]
In [4]:
figsize(15,9)
initialCash=25000
min_stock = 30
# sumulate two trades
strategy = pd.Series(index=price.index) #init signal series
# while setting values this way, make sure that the dates are actually in the index
#strategy[pd.datetime(2008,1,3)] = -10000 # go short
#strategy[pd.datetime(2009,1,5)] = 10000 # go long 10k$ on this day
#strategy[pd.datetime(2013,1,8)] = 0 # close position
# manual Strategy
if False:
strategy[price.index[50]] = -10000 # go short
strategy[price.index[100]] = 10000 # go long 10k$ on this day
strategy[price.index[150]] = 0 # close position
# automatic strategy
else:
# find when to buy and sell
import trendy
reload(trendy)
import tradingWithPython as twp
#order=trendy.iterlines(price, window = 30, charts = True)
a,b,c,d, order = trendy.segtrends(price, segments = n/5, charts = True, momentum=True);
#print order
# force sell at the end
order[-1] = 0
title('automatic strategy')
order=[el*min_stock for el in order]
# create a stratgy from order
for i, idx in enumerate(price.index):
if order[i]!=0:
strategy[idx] = order[i]
# do the backtest
bt = twp.Backtest(price, strategy, initialCash=initialCash, signalType='shares')
bt.plotTrades()
figure()
bt.pnl.plot()
title('pnl')
figure()
bt.data.plot()
title('all strategy data')
#bt.data
Out[4]:
In [267]:
print order
In [222]:
bt.data
Out[222]:
In [94]:
reload(trendy)
price = price[:]
# Generate general support/resistance trendlines and show the chart
# winow < 1 is considered a fraction of the length of the data set
#trendy.gentrends(price, window = 1.0/3, charts = True);
# Generate a series of support/resistance lines by segmenting the price history
a,b,c,d, order = trendy.segtrends(price, segments = n/10, charts = True); # equivalent to gentrends with window of 1/2
#print order
#trendy.segtrends(price, segments = 5, charts = True); # plots several S/R lines
# Generate smaller support/resistance trendlines to frame price over smaller periods
#trendy.minitrends(price, window = 10, charts = True)
# Iteratively generate trading signals based on maxima/minima in given window
#trendy.iterlines(price, window = 60, charts = True); # buy at green dots, sell at red dots
In [9]:
print pd.__version__
print np.__version__
print matplotlib.__version__
In [7]:
Out[7]:
In [ ]: