By Priyanka Sah
In our previous article on introduction to Zipline package in Python, we created an algorithm for moving crossover strategy. Recall, Zipline is a Python library for trading applications and is used to create an event-driven system that can support both backtesting and live trading.
In the previous article, we also learned how to implement Moving Average Crossover strategy on Zipline. The strategy code in Zipline reads data from Yahoo directly, performs the backtest and plots the results. We recommend that you brush up a few essential concepts, covered in the previous post, before going further:
- Installation (how to install Zipline on local)
- Structure (format to write a code in Zipline)
- Import and backtest on OHLC data in CSV format
- Import and use data from Google Finance for research/analysis
- Calculate and print backtesting results such as PnL, number of trades, etc
The post serves as a guide for serious quants and DIY Algo traders who want to make use of Python or Zipline packages independently for backtesting and hypothesis testing of their trading ideas. In this post, we will assume that the data is from the US markets. It is possible to use other markets’ data sets for analysis with some edits and additions in the code. We will share the same in a later post.
The Parts of the code on Zipline – what we have learned already
The problem with the existing method?Zipline provides an inbuilt function “loads_bars_from_yahoo()” that fetches data from Yahoo in given range and uses that data for all the calculations. Though very easy to use, this function only works with Yahoo data. Using this function, we cannot backtest on different data sets such as
- Commodities data – yahoo does not provide
- Simulated data sets created and saved in csv format
Zipline accepts the data in panel form. To understand how Zipline treats and understands data, we must learn a little bit about data structures in Python.
Data Structures in PandaPandas structures data in three forms essentially: Series (1D), Data Frame (2D), Panel (3D)
The basic method to create a Series is to call:
s = pd.Series(data, index=index)
A series accepts different kinds of objects such as a Python dictionary, ndarray, a scalar value (like 5).
- Data Frame:
It is one of the most commonly used pandas objects and accepts different types of inputs such as Dict of 1D ndarrays, lists, dicts, or Series; 2-D numpy.ndarray; Structured or record ndarray; a Series.
The three axes are named as below:
- items: axis 0, each item corresponds to a DataFrame contained inside
- major_axis: axis 1, it is the index (rows) of each of the DataFrames
- minor_axis: axis 2, it is the columns of each of the DataFrame
While it is easy to import .csv data in Panda as a dataframe, it is not possible to do the same in Zipline directly. However, we have found a roundabout to this problem:
This is a powerful technique which will help you in importing data from different sources such as:
- Import OHLC data in a CSV format in zipline (we will show how)
- Read data from online sources other than Yahoo which connect with Panda (we will show how)
- Read data from Quandl in Zipline (this is left as an exercise for you!)
Import the data in python
Use DataReader to read data from GooglePandas provide a function Datareader which allows the user to specify the date range and the source. You can use Yahoo, Google or any other data source.
#code pip install pandas import pytz from pandas.io.data import DataReader from collections import OrderedDict data = OrderedDict() start_date = '9/17/2011' end_date = '6/24/2015' data['SPY'] = DataReader('SPY',data_source='google',start=start_date, end=end_date) print data['SPY'].head() type(data['SPY'])This is how a DataFrame looks like when you print the first 6 rows:
Use read_csv function to import a CSV filePandas provide another function read_csv that fetches the csv file from a specified location. Please note that the CSV should be in a proper format so that it runs in a correct fashion when called by a strategy algorithm in Zipline.
Format of CSV file:
The First column is the “Date” column, the second column is “Open”, the third column is “High”, the fourth column is “Low”, the fifth column is “Close”, and the sixth column is “Volume”. None of the columns should be blank or with missing values.
Reading CSV file:
#code import pandas as pd datas = OrderedDict() data['SPY'] = pd.read_csv('SPY.csv', index_col=0, parse_dates=['Date']) print data['SPY'].head() type(data['SPY'])
Note in the code above:
Name of the stock is “SPY”
We are already in the directory where the CSV file “SPY.csv” is saved, else you need to specify the path as well.
Convert DataFrame to Panel
#code panel = pd.Panel(data) panel.minor_axis = ['Open', 'High', 'Low', 'Close', 'Volume'] panel.major_axis = panel.major_axis.tz_localize(pytz.utc)Zipline accepts ['Open', 'High', 'Low', 'Close', 'Volume'] data as minor axis and ‘Date’ as major axis in UTC time format. Since if we did not have the Date in UTC format, we convert it by using “tz_localize(pytz.utc)”.
Now ‘panel’ is the dataset ‘data’ saved in the panel format. This is how a Panel format looks like:
Use this new data structure Panel to run your strategy
#initializing trading enviroment algo_obj = TradingAlgorithm(initialize=initialize, handle_data=handle_data, capital_base = 100000.0) #run algo perf_manual = algo_obj.run(panel)That’s it! Now you can easily run the previously explained Moving Crossover strategy on a CSV data file! Go on, give it a try!
You can fetch the Quandl(US data) data, and try generating signals on the same.
Backtesting on ZiplineIn the previous post, we backtested a simple Moving Crossover strategy and plotted cash and PnL for each trading day. Now, we will calculate PnL and the total number of trades for the entire trading period.
Recall that the results are automatically saved in ‘perf_manual’. Using the same, we can calculate any performance ratios or numbers that we need.
#code #calculation print "total pnl : " + str(float(perf_manual[["PnL"]].iloc[-1])) buy_trade = perf_manual[["status"]].loc[perf_manual["status"] == "buy"].count() sell_trade = perf_manual[["status"]].loc[perf_manual["status"] == "sell"].count() total_trade = buy_trade + sell_trade print "buy trade : " + str(int(buy_trade)) + " sell trade : " + str(int(sell_trade)) + " total trade : " + str(int(total_trade))
Looks like this strategy lost more than 50% of initial capital!
To change the initial capital and other parameters to optimize your backtesting excel results, you need to initialize the TradingAlgorithm() accordingly. ‘capital_base’ is used to define the initial cash, ‘data_frequency’ is used to define the data frequency. For example:
algo_obj = TradingAlgorithm(initialize=initialize, handle_data=handle_data, capital_base = 80000.0)
(By default the capital is 100000.0.)
Go through the official documentation of TradingAlgorithm() function to try and learn more!
Next StepRead our next post that will introduce you to zipline package in python with its benefits, how to install it, and using it code the moving crossover strategy for financial trade. Click here to read now.
We have noticed that some users are facing challenges while downloading the market data from Yahoo and Google Finance platforms. In case you are looking for an alternative source for market data, you can use Quandl for the same.
Download Data File
- mac_excel_ zipline.txt