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 [3]:
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[3]:
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 [4]:
# 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[4]:
In [10]:
# start with getting some data to test on
import tradingWithPython.lib.yahooFinance as yahoo # yahoo finance module
stockname = 'TSLA'
price = yahoo.getHistoricData(stockname)['adj_close'][-300:]
price.plot()
Out[10]:
normally a buy/sell signal would be generated by a strategy. For simplicity I'll just simulate holding a position between two dates
In [26]:
figsize(10,6)
# 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
import tradingWithPython as twp
order=trendy.iterlines(price, window = 30, charts = True)
title('automatic strategy')
order=[el*1000 for el in order]
# create a stratgy
for i, idx in enumerate(price.index):
if order[i]!=0:
strategy[idx] = order[i]
#print idx,order[i]
# force sell at the end
order[-1] = 0
# do the backtest
bt = twp.Backtest(price, strategy, initialCash=1000)
bt.plotTrades()
figure()
bt.pnl.plot()
title('pnl')
figure()
bt.data.plot()
title('all strategy data')
Out[26]:
In [ ]: