TST/BUG: Cover all reindex session public methods.

Increase coverage on `ReindexSessionBarReader` so that all methods which
are considered part of the interface are covered by `test_resample`.

Fix bug in `get_value`, exposed by increased coverage, where the
`NoDataOnDate` exception was bubbling from the bcolz reader all the way
up when a session which was a holidy on the underlying reader was passed
to the reindex reader. (The reindex reader should return nan/0 in that
case.)

Also, move location of data index exceptions so that they are agnostic
to bcolz/us_equity_pricing; since the exception is now used by the
resample module to fix aforementioned bug.
This commit is contained in:
Eddie Hebert 2016-09-01 11:14:42 -04:00
parent 959baf7fe0
commit d463a9855b
5 changed files with 131 additions and 20 deletions

View file

@ -2,3 +2,5 @@
omit =
*/python?.?/*
*/site-packages/nose/*
exclude_lines =
raise NotImplementedError

View file

@ -616,8 +616,15 @@ class TestReindexSessionBars(WithBcolzEquityDailyBarReader,
# Dates are chosen to span Thanksgiving, which is not a Holiday on
# us_futures.
START_DATE = pd.Timestamp('2015-11-01', tz='UTC')
START_DATE = pd.Timestamp('2015-11-02', tz='UTC')
END_DATE = pd.Timestamp('2015-11-30', tz='UTC')
# November 2015
# Su Mo Tu We Th Fr Sa
# 1 2 3 4 5 6 7
# 8 9 10 11 12 13 14
# 15 16 17 18 19 20 21
# 22 23 24 25 26 27 28
# 29 30
def init_instance_fixtures(self):
super(TestReindexSessionBars, self).init_instance_fixtures()
@ -651,6 +658,18 @@ class TestReindexSessionBars(WithBcolzEquityDailyBarReader,
"The reindexed result after dropping nans should have 20 days, "
"because Thanksgiving is a NYSE holiday.")
tday = pd.Timestamp('2015-11-26', tz='UTC')
# Thanksgiving, 2015-11-26.
# Is a holiday in NYSE, but not in us_futures.
tday_loc = outer_sessions.get_loc(tday)
assert_almost_equal(
nan,
opens[1][tday_loc],
err_msg="2015-11-26 should be `nan`, since Thanksgiving is a "
"holiday in the reader's calendar.")
# Thanksgiving, 2015-11-26.
# Is a holiday in NYSE, but not in us_futures.
tday_loc = outer_sessions.get_loc(pd.Timestamp('2015-11-26', tz='UTC'))
@ -661,6 +680,65 @@ class TestReindexSessionBars(WithBcolzEquityDailyBarReader,
err_msg="2015-11-26 should be `nan`, since Thanksgiving is a "
"holiday in the reader's calendar.")
def test_load_raw_arrays_holiday_start(self):
tday = pd.Timestamp('2015-11-26', tz='UTC')
outer_sessions = self.trading_calendar.sessions_in_range(
tday, self.END_DATE)
result = self.reader.load_raw_arrays(
OHLCV, tday, self.END_DATE, [1, 2])
opens = DataFrame(data=result[0], index=outer_sessions,
columns=[1, 2])
opens_with_price = opens.dropna()
self.assertEqual(
3,
len(opens),
"The reindexed result should have 3 days, which is the number of "
"business days in from Thanksgiving to end of 2015-11.")
self.assertEqual(
2,
len(opens_with_price),
"The reindexed result after dropping nans should have 2 days, "
"because Thanksgiving is a NYSE holiday.")
def test_load_raw_arrays_holiday_end(self):
tday = pd.Timestamp('2015-11-26', tz='UTC')
outer_sessions = self.trading_calendar.sessions_in_range(
self.START_DATE, tday)
result = self.reader.load_raw_arrays(
OHLCV, self.START_DATE, tday, [1, 2])
opens = DataFrame(data=result[0], index=outer_sessions,
columns=[1, 2])
opens_with_price = opens.dropna()
self.assertEqual(
19,
len(opens),
"The reindexed result should have 19 days, which is the number of "
"business days in from start of 2015-11 up to Thanksgiving.")
self.assertEqual(
18,
len(opens_with_price),
"The reindexed result after dropping nans should have 18 days, "
"because Thanksgiving is a NYSE holiday.")
def test_get_value(self):
assert_almost_equal(self.reader.get_value(1, self.START_DATE, 'open'),
10.0,
err_msg="The open of the fixture data on the "
"first session should be 10.")
tday = pd.Timestamp('2015-11-26', tz='UTC')
assert_almost_equal(self.reader.get_value(1, tday, 'close'), nan,
err_msg="Thanksgiving is a NYSE holiday, but "
"futures trading is open. Result should be nan.")
assert_almost_equal(self.reader.get_value(1, tday, 'volume'), 0,
err_msg="Thanksgiving is a NYSE holiday, but "
"futures trading is open. Result should be 0.")
def test_last_availabe_dt(self):
self.assertEqual(self.reader.last_available_dt, self.END_DATE)
@ -669,3 +747,21 @@ class TestReindexSessionBars(WithBcolzEquityDailyBarReader,
self.assertEqual(self.reader.get_last_traded_dt(asset,
self.END_DATE),
self.END_DATE)
def test_sessions(self):
sessions = self.reader.sessions
self.assertEqual(21, len(sessions),
"There should be 21 sessions in 2015-11.")
self.assertEqual(pd.Timestamp('2015-11-02', tz='UTC'),
sessions[0])
self.assertEqual(pd.Timestamp('2015-11-30', tz='UTC'),
sessions[-1])
def test_first_trading_day(self):
self.assertEqual(self.reader.first_trading_day, self.START_DATE)
def test_trading_calendar(self):
self.assertEqual('us_futures',
self.reader.trading_calendar.name,
"The calendar for the reindex reader should be the "
"specified futures calendar.")

View file

@ -15,11 +15,13 @@ from collections import OrderedDict
from abc import ABCMeta, abstractmethod
import numpy as np
from numpy import nan
import pandas as pd
from pandas import DataFrame
from six import with_metaclass
from zipline.data.minute_bars import MinuteBarReader
from zipline.data.us_equity_pricing import NoDataOnDate
from zipline.data.session_bars import SessionBarReader
from zipline.utils.memoize import lazyval
@ -584,15 +586,21 @@ class ReindexBarReader(with_metaclass(ABCMeta)):
return self._reader.first_trading_day
def get_value(self, sid, dt, field):
return self._reader.get_value(sid, dt, field)
try:
return self._reader.get_value(sid, dt, field)
except NoDataOnDate:
if field == 'volume':
return 0
else:
return nan
@abstractmethod
def _outer_dts(self, start_dt, end_dt):
pass
raise NotImplementedError
@abstractmethod
def _inner_dts(self, start_dt, end_dt):
pass
raise NotImplementedError
@property
def trading_calendar(self):

View file

@ -15,6 +15,21 @@ from abc import ABCMeta, abstractmethod, abstractproperty
from six import with_metaclass
class NoDataOnDate(Exception):
"""
Raised when a spot price can be found for the sid and date.
"""
pass
class NoDataBeforeDate(Exception):
pass
class NoDataAfterDate(Exception):
pass
class SessionBarReader(with_metaclass(ABCMeta)):
"""
Reader for OHCLV pricing data at a session frequency.

View file

@ -49,7 +49,12 @@ from six import (
string_types,
)
from zipline.data.session_bars import SessionBarReader
from zipline.data.session_bars import (
SessionBarReader,
NoDataAfterDate,
NoDataBeforeDate,
NoDataOnDate,
)
from zipline.utils.calendars import get_calendar
from zipline.utils.functional import apply
from zipline.utils.preprocess import call
@ -99,21 +104,6 @@ SQLITE_STOCK_DIVIDEND_PAYOUT_COLUMN_DTYPES = {
UINT32_MAX = iinfo(uint32).max
class NoDataOnDate(Exception):
"""
Raised when a spot price can be found for the sid and date.
"""
pass
class NoDataBeforeDate(Exception):
pass
class NoDataAfterDate(Exception):
pass
def check_uint32_safe(value, colname):
if value >= UINT32_MAX:
raise ValueError(