Custom Markets Trading Calendar with Zipline (Bitcoin/cryptocurrency example) - Python Programming for Finance p.28




Hello and welcome to part 4 of the zipline local tutorial series. Up to this point, we've covered installing Zipline, using it locally, and even incorporating your own data to some degree, but, in this tutorial, we're going to dive a bit deeper with customizing the trading calendar.

With the Zipline defaults, trading occurs Monday through Friday (except for various holidays), and between 9:30am and 4pm EST. What if you wanted to trade a different market? Maybe different times, different timezones, or maybe you have different holidays? If you wanted to trade the Japanese markets, or India's markets, the NYSE calendar is obviously not acceptable! What about with something like Cryptocurrencies, where the markets are never closed?

In this tutorial, I will be showing you how to do just that!

To do this, let us bop on over to our zipline installation directory and go into utils/calendars. For me, that's: C:\Python35\Lib\site-packages\zipline\utils\calendars. Not sure where Zipline is installed? No big deal. Run python in your command line, import zipline, then do zipline.__file__, the output of this will be where it's located.

For example:

C:\Users\H\Desktop\asfa>C:/Python35/python
Python 3.5.0 (v3.5.0:374f501f4567, Sep 13 2015, 02:27:37) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import zipline
>>> zipline.__file__
'C:\\Python35\\lib\\site-packages\\zipline\\__init__.py'

So, I am not going to mess around with creating a custom calendar, I am just going to modify the NYSE one. First, it would be wise to make a backup/copy though, so do that. I just made a copy in the calendars directory.

Now, create a new file called C:\Python35\Lib\site-packages\zipline\utils\calendars\exchange_calendar_twentyfourhr.py becomes:

from datetime import time
from zipline.utils.memoize import lazyval
from pandas.tseries.offsets import CustomBusinessDay
from pytz import timezone
from .trading_calendar import TradingCalendar


class TwentyFourHR(TradingCalendar):
    """
    Exchange calendar for 24/7 trading.

    Open Time: 12am, UTC
    Close Time: 11:59pm, UTC

    """
    @property
    def name(self):
        return "twentyfourhr"

    @property
    def tz(self):
        return timezone("UTC")

    @property
    def open_time(self):
        return time(0, 0)

    @property
    def close_time(self):
        return time(23, 59)

    @lazyval
    def day(self):
        return CustomBusinessDay(
            weekmask='Mon Tue Wed Thu Fri Sat Sun',
        )

Very simple calendar to read. Opens at midnight, closes 1 minute before midnight, and repeat! Timezone is shifted to UTC as well, to match our data. This is the default calendar anyway, so that's all we need to do to change that. Next, we need some data. I have hosted BTC-USD.csv, which is minute-data for the USD value of Bitcoin from the GDAX exchange. Not interested in bitcoin? That's fine, feel free to replace with whatever data you want!

Once you have that file, assuming you're in your notebook:

%load_ext zipline

Now, for our data, I am going to do the following:

data = OrderedDict()
data['BTC'] = pd.read_csv("BTC-USD.csv")

data['BTC']['date'] = pd.to_datetime(data['BTC']['time'], unit='s', utc=True)
data['BTC'].set_index('date', inplace=True)
data['BTC'].drop('time', axis=1, inplace=True)
data['BTC'] = data['BTC'].resample("1min").mean()
data['BTC'].fillna(method="ffill", inplace=True)
data['BTC'] = data['BTC'][["low","high","open","close","volume"]]
print(data['BTC'].head())

panel = pd.Panel(data)
panel.minor_axis = ["low","high","open","close","volume"]
panel.major_axis = panel.major_axis.tz_localize(pytz.utc)
print(panel)

Notice that we convert from unix time to datetime, but also we do a 1 minute resample, then a forward fill. What's up with that? Well, this data is coming from the GDAX API, coming in real time. There might be minutes where the exchange goes down, the API stops responding, or maybe my server collecting the data goes down. There will be gaps. We need to be 100% certain there are no gaps, so I resample by minute to be 100% certain we have data by the minute. Now, if there were long enough gaps, this means there could be NaN data. To handle for this, we'll just do a forward fill.

Next, let's cover our strategy:

def initialize(context):
    set_benchmark(symbol("BTC"))


def handle_data(context, data):

    slowma = data.history(symbol("BTC"), fields='price', bar_count=50, frequency='1m').mean()
    fastma = data.history(symbol("BTC"), fields='price', bar_count=10, frequency='1m').mean()


    if fastma < slowma:
        if symbol("BTC") not in get_open_orders():
            order_target_percent(symbol("BTC"), 0.04)

    if fastma > slowma:
        if symbol("BTC") not in get_open_orders():
            order_target_percent(symbol("BTC"), 0.96)

    record(BTC=data.current(symbol('BTC'), fields='price'))

Pretty simple strategy. We have a fast and slow ma. We buy when the fast MA is above the slow, and sell when it's below.

Now, we want to import our new calendar:

from zipline.utils.calendars.exchange_calendar_twentyfourhr import TwentyFourHR

Now in the zipline.run_algorithm, we can use the trading_calendar parameter with: trading_calendar=TwentyFourHR(),, so this block would be:

perf = zipline.run_algorithm(start=datetime(2018, 3, 25, 0, 0, 0, 0, pytz.utc),
                      end=datetime(2018, 3, 26, 0, 0, 0, 0, pytz.utc),
                      initialize=initialize,
                      trading_calendar=TwentyFourHR(),
                      capital_base=10000,
                      handle_data=handle_data,
                      data_frequency ='minute',
                      data=panel)

Full script up to this point:

import pandas as pd
from collections import OrderedDict
import pytz
from zipline.api import order, record, symbol, set_benchmark, order_target_percent, get_open_orders
from zipline.utils.calendars.exchange_calendar_twentyfourhr import TwentyFourHR
import zipline
import matplotlib.pyplot as plt
from datetime import datetime


def initialize(context):
    set_benchmark(symbol("BTC"))


def handle_data(context, data):

    slowma = data.history(symbol("BTC"), fields='price', bar_count=50, frequency='1m').mean()
    fastma = data.history(symbol("BTC"), fields='price', bar_count=10, frequency='1m').mean()

    if fastma < slowma:
        if symbol("BTC") not in get_open_orders():
            order_target_percent(symbol("BTC"), 0.04)

    if fastma > slowma:
        if symbol("BTC") not in get_open_orders():
            order_target_percent(symbol("BTC"), 0.96)

    record(BTC=data.current(symbol('BTC'), fields='price'))


data = OrderedDict()
data['BTC'] = pd.read_csv("BTC-USD.csv")

data['BTC']['date'] = pd.to_datetime(data['BTC']['time'], unit='s', utc=True)
data['BTC'].set_index('date', inplace=True)
data['BTC'].drop('time', axis=1, inplace=True)
data['BTC'] = data['BTC'].resample("1min").mean()
data['BTC'].fillna(method="ffill", inplace=True)
data['BTC'] = data['BTC'][["low","high","open","close","volume"]]
print(data['BTC'].head())

panel = pd.Panel(data)
panel.minor_axis = ["low","high","open","close","volume"]
panel.major_axis = panel.major_axis.tz_localize(pytz.utc)
print(panel)


perf = zipline.run_algorithm(start=datetime(2018, 2, 7, 0, 0, 0, 0, pytz.utc),
                      end=datetime(2018, 3, 26, 0, 0, 0, 0, pytz.utc),
                      initialize=initialize,
                      trading_calendar=TwentyFourHR(),
                      capital_base=10000,
                      handle_data=handle_data,
                      data_frequency ='minute',
                      data=panel)

Now we can check out the results when this is done with:

perf.head()

Graphing with:

import matplotlib.pyplot as plt
from matplotlib import style

style.use("ggplot")

perf.portfolio_value.pct_change().fillna(0).add(1).cumprod().sub(1).plot(label='portfolio')
perf.BTC.pct_change().fillna(0).add(1).cumprod().sub(1).plot(label='benchmark')
plt.legend(loc=2)

plt.show()
python tutorials

There you have it! At this point, you're good to go! If you haven't check out the Quantopian API docs, which also have a Zipline-specific section. In most cases, if you can do something in the algorithms section of Quantopian, you can do it in Zipline too. You just need to import the functions you plan to use, rather than just using them (often on Quantopian, if you want to use some API function, you don't need to import anything).

The next tutorial:





  • Intro and Getting Stock Price Data - Python Programming for Finance p.1
  • Handling Data and Graphing - Python Programming for Finance p.2
  • Basic stock data Manipulation - Python Programming for Finance p.3
  • More stock manipulations - Python Programming for Finance p.4
  • Automating getting the S&P 500 list - Python Programming for Finance p.5
  • Getting all company pricing data in the S&P 500 - Python Programming for Finance p.6
  • Combining all S&P 500 company prices into one DataFrame - Python Programming for Finance p.7
  • Creating massive S&P 500 company correlation table for Relationships - Python Programming for Finance p.8
  • Preprocessing data to prepare for Machine Learning with stock data - Python Programming for Finance p.9
  • Creating targets for machine learning labels - Python Programming for Finance p.10 and 11
  • Machine learning against S&P 500 company prices - Python Programming for Finance p.12
  • Testing trading strategies with Quantopian Introduction - Python Programming for Finance p.13
  • Placing a trade order with Quantopian - Python Programming for Finance p.14
  • Scheduling a function on Quantopian - Python Programming for Finance p.15
  • Quantopian Research Introduction - Python Programming for Finance p.16
  • Quantopian Pipeline - Python Programming for Finance p.17
  • Alphalens on Quantopian - Python Programming for Finance p.18
  • Back testing our Alpha Factor on Quantopian - Python Programming for Finance p.19
  • Analyzing Quantopian strategy back test results with Pyfolio - Python Programming for Finance p.20
  • Strategizing - Python Programming for Finance p.21
  • Finding more Alpha Factors - Python Programming for Finance p.22
  • Combining Alpha Factors - Python Programming for Finance p.23
  • Portfolio Optimization - Python Programming for Finance p.24
  • Zipline Local Installation for backtesting - Python Programming for Finance p.25
  • Zipline backtest visualization - Python Programming for Finance p.26
  • Custom Data with Zipline Local - Python Programming for Finance p.27
  • Custom Markets Trading Calendar with Zipline (Bitcoin/cryptocurrency example) - Python Programming for Finance p.28