본문 바로가기

Data Mining & R

Backtesting with TWP backtesting module

http://nbviewer.jupyter.org/urls/dl.dropboxusercontent.com/u/11352905/notebooks/twp_302b_backtesting.ipynb

Backtesting with TWP backtesting module

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 tradingWithPythonlibrary.

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 [1]:
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[1]:
pricesignal
00NaN
11NaN
22100
330
44NaN
55NaN
66-100
77NaN
880
99NaN

The Backtest class is created with these arguments:

  • price Series with instrument price.
  • signal Series with capital to invest (long+,short-) or number of shares.
  • sitnalType capital to bet or number of shares 'capital' mode is default.
  • initialCash starting cash. 0 is default

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:

  • Backtest.data full data table, excel-like. pnl column is cumulative
  • Backtest.pnl easy access to pnl column of the data table
  • Backtest.sharpe Sharpe of the pnl
In [2]:
# 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
Sharpe:  2.17999027863
Out[2]:
pricesharesvaluecashpnldelta
0000000
1100000
2250100-100050
33005050-50
440050500
550050500
66-17-10215250-17
77-17-119152330
8800161617
990016160

Test on real price data

Now let's use the backtester on some real price

In [3]:
# start with getting some data to test on

import tradingWithPython.lib.yahooFinance as yahoo # yahoo finance module

price = yahoo.getHistoricData('SPY')['adj_close'][-2000:]

price.plot()
Got 5404 days of data
Out[3]:
<matplotlib.axes.AxesSubplot at 0x7f26950>

normally a buy/sell signal would be generated by a strategy. For simplicity I'll just simulate holding a position between two dates

In [5]:
figsize(10,6)

# sumulate two trades
signal = pd.Series(index=price.index) #init signal series

# while setting values this way, make sure that the dates are actually in the index
signal[pd.datetime(2008,1,3)] = -10000 # go short 
signal[pd.datetime(2009,1,5)] = 10000 # go long 10k$ on this day
signal[pd.datetime(2013,1,8)] = 0 # close position


bt = twp.Backtest(price,signal,initialCash=0)
bt.plotTrades()

figure()
bt.pnl.plot()
title('pnl')

figure()
bt.data.plot()
title('all strategy data')
Out[5]:
<matplotlib.text.Text at 0x85247b0>
<matplotlib.figure.Figure at 0x8369930>