TEST: Add isolated tests for .latest.

This commit is contained in:
Scott Sanderson
2016-01-19 12:58:51 -05:00
parent f440cb73b2
commit 1bf33f9ee0
4 changed files with 227 additions and 1 deletions
+63
View File
@@ -0,0 +1,63 @@
"""
Tests BoundColumn attributes and methods.
"""
from contextlib2 import ExitStack
from unittest import TestCase
from pandas import date_range, DataFrame
from pandas.util.testing import assert_frame_equal
from zipline.pipeline import Pipeline
from zipline.pipeline.data.testing import TestingDataSet as TDS
from zipline.utils.test_utils import chrange, temp_pipeline_engine
class LatestTestCase(TestCase):
@classmethod
def setUpClass(cls):
cls._stack = stack = ExitStack()
cls.calendar = cal = date_range('2014', '2015', freq='D', tz='UTC')
cls.sids = list(range(5))
cls.engine = stack.enter_context(
temp_pipeline_engine(
cal,
cls.sids,
random_seed=100,
symbols=chrange('A', 'E'),
),
)
cls.assets = cls.engine._finder.retrieve_all(cls.sids)
@classmethod
def tearDownClass(cls):
cls._stack.close()
def expected_latest(self, column, slice_):
loader = self.engine.get_loader(column)
return DataFrame(
loader.values(column.dtype, self.calendar, self.sids)[slice_],
index=self.calendar[slice_],
columns=self.sids,
)
def test_latest(self):
pipe = Pipeline(
columns={
name: getattr(TDS, name + '_col').latest
# Intentionally not including int and bool because they're not
# yet supported.
for name in ('float', 'datetime')
}
)
cal_slice = slice(20, 40)
dates_to_test = self.calendar[cal_slice]
result = self.engine.run_pipeline(
pipe,
dates_to_test[0],
dates_to_test[-1],
)
float_result = result.float.unstack()
expected_float_result = self.expected_latest(TDS.float_col, cal_slice)
assert_frame_equal(float_result, expected_float_result)
+84
View File
@@ -12,6 +12,7 @@ from numpy import (
iinfo,
uint32,
)
from numpy.random import RandomState
from pandas import DataFrame, Timestamp
from six import iteritems
from sqlite3 import connect as sqlite3_connect
@@ -24,6 +25,12 @@ from zipline.data.us_equity_pricing import (
SQLiteAdjustmentWriter,
US_EQUITY_PRICING_BCOLZ_COLUMNS,
)
from zipline.utils.numpy_utils import (
bool_dtype,
datetime64ns_dtype,
float64_dtype,
int64_dtype,
)
UINT_32_MAX = iinfo(uint32).max
@@ -110,6 +117,83 @@ class EyeLoader(PrecomputedLoader):
)
class SeededRandomLoader(PrecomputedLoader):
"""
A PrecomputedLoader that emits arrays randomly-generated with a given seed.
Parameters
----------
seed : int
Seed for numpy.random.RandomState.
columns : list[BoundColumn]
Columns that this loader should know about.
dates : iterable[datetime-like]
Same as PrecomputedLoader.
sids : iterable[int-like]
Same as PrecomputedLoader
"""
def __init__(self, seed, columns, dates, sids):
self._seed = seed
super(SeededRandomLoader, self).__init__(
{c: self.values(c.dtype, dates, sids) for c in columns},
dates,
sids,
)
def values(self, dtype, dates, sids):
"""
Make a random array of shape (len(dates), len(sids)) with ``dtype``.
"""
shape = (len(dates), len(sids))
return {
datetime64ns_dtype: self._datetime_values,
float64_dtype: self._float_values,
int64_dtype: self._int_values,
bool_dtype: self._bool_values,
}[dtype](shape)
@property
def state(self):
"""
Make a new RandomState from our seed.
This ensures that every call to _*_values produces the same output
every time for a given SeededRandomLoader instance.
"""
return RandomState(self._seed)
def _float_values(self, shape):
"""
Return uniformly-distributed floats between -0.0 and 100.0.
"""
return self.state.uniform(low=0.0, high=100.0, size=shape)
def _int_values(self, shape):
"""
Return uniformly-distributed integers between 0 and 100.
"""
return self.state.random_integers(low=0, high=100, size=shape)
def _datetime_values(self, shape):
"""
Return uniformly-distributed dates in 2014.
"""
start = Timestamp('2014', tz='UTC').asm8
offsets = self.state.random_integers(
low=0,
high=364,
size=shape,
).astype('timedelta64[D]')
return start + offsets
def _bool_values(self, shape):
"""
Return uniformly-distributed True/False values.
"""
return self.state.randn(*shape) < 0
class SyntheticDailyBarWriter(BcolzDailyBarWriter):
"""
Bcolz writer that creates synthetic data based on asset lifetime metadata.
+21
View File
@@ -0,0 +1,21 @@
"""
Loaders for zipline.pipeline.data.testing datasets.
"""
from .synthetic import EyeLoader, SeededRandomLoader
from ..data.testing import TestingDataSet
def make_eye_loader(dates, sids):
"""
Make a PipelineLoader that emits np.eye arrays for the columns in
``TestingDataSet``.
"""
return EyeLoader(TestingDataSet.columns, dates, sids)
def make_seeded_random_loader(seed, dates, sids):
"""
Make a PipelineLoader that emits random arrays seeded with `seed` for the
columns in ``TestingDataSet``.
"""
return SeededRandomLoader(seed, TestingDataSet.columns, dates, sids)
+59 -1
View File
@@ -25,6 +25,8 @@ from zipline.assets import AssetFinder
from zipline.assets.asset_writer import AssetDBWriterFromDataFrame
from zipline.assets.futures import CME_CODE_TO_MONTH
from zipline.finance.order import ORDER_STATUS
from zipline.pipeline.engine import SimplePipelineEngine
from zipline.pipeline.loaders.testing import make_seeded_random_loader
from zipline.utils import security_list
from zipline.utils.tradingcalendar import trading_days
@@ -238,6 +240,31 @@ def all_subindices(index):
)
def chrange(start, stop):
"""
Construct an iterable of length-1 strings beginning with `start` and ending
with `stop`.
Parameters
----------
start : str
The first character.
stop : str
The last character.
Returns
-------
chars: iterable[str]
Iterable of strings beginning with start and ending with stop.
Example
-------
>>> list(chrange('A', 'C'))
['A', 'B', 'C']
"""
return map(chr, range(ord(start), ord(stop) + 1))
def make_rotating_equity_info(num_assets,
first_start,
frequency,
@@ -296,7 +323,7 @@ def make_simple_equity_info(sids, start_date, end_date, symbols=None):
sids : array-like of int
start_date : pd.Timestamp
end_date : pd.Timestamp
symbols : list, optional
symbols : list, optionaln
Symbols to use for the assets.
If not provided, symbols are generated from the sequence 'A', 'B', ...
@@ -664,3 +691,34 @@ def gen_calendars(start, stop, critical_dates):
# Also test with the trading calendar.
yield (trading_days[trading_days.slice_indexer(start, stop)],)
@contextmanager
def temp_pipeline_engine(calendar, sids, random_seed, symbols=None):
"""
A contextManager that yields a SimplePipelineEngine holding a reference to
an AssetFinder generated via tmp_asset_finder.
Parameters
----------
calendar : pd.DatetimeIndex
Calendar to pass to the constructed PipelineEngine.
sids : iterable[int]
Sids to use for the temp asset finder.
random_seed : int
Integer used to seed instances of SeededRandomLoader.
symbols : iterable[str], optional
Symbols for constructed assets. Forwarded to make_simple_equity_info.
"""
equity_info = make_simple_equity_info(
sids=sids,
start_date=calendar[0],
end_date=calendar[-1],
symbols=symbols,
)
loader = make_seeded_random_loader(random_seed, calendar, sids)
get_loader = lambda column: loader
with tmp_asset_finder(equities=equity_info) as finder:
yield SimplePipelineEngine(get_loader, calendar, finder)