Skip to content

Commit

Permalink
TST/BUG: Cover all reindex session public methods.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Eddie Hebert committed Sep 1, 2016
1 parent 959baf7 commit d463a98
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 20 deletions.
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
omit =
*/python?.?/*
*/site-packages/nose/*
exclude_lines =
raise NotImplementedError
98 changes: 97 additions & 1 deletion tests/data/test_resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -651,6 +658,18 @@ def test_load_raw_arrays(self):
"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'))
Expand All @@ -661,6 +680,65 @@ def test_load_raw_arrays(self):
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)

Expand All @@ -669,3 +747,21 @@ def test_get_last_traded_dt(self):
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.")
14 changes: 11 additions & 3 deletions zipline/data/resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
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

Expand Down Expand Up @@ -584,15 +586,21 @@ def first_trading_day(self):
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):
Expand Down
15 changes: 15 additions & 0 deletions zipline/data/session_bars.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@
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.
Expand Down
22 changes: 6 additions & 16 deletions zipline/data/us_equity_pricing.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@
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
Expand Down Expand Up @@ -99,21 +104,6 @@
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(
Expand Down

0 comments on commit d463a98

Please sign in to comment.