Beispiel #1
0
    def test_retrieve_specific_type(self, type_, lookup_name, failure_type):
        equities = make_simple_equity_info(
            range(5), start_date=pd.Timestamp("2014-01-01"), end_date=pd.Timestamp("2015-01-01")
        )
        max_equity = equities.index.max()
        futures = make_commodity_future_info(first_sid=max_equity + 1, root_symbols=["CL"], years=[2014])
        equity_sids = [0, 1]
        future_sids = [max_equity + 1, max_equity + 2, max_equity + 3]
        if type_ == Equity:
            success_sids = equity_sids
            fail_sids = future_sids
        else:
            fail_sids = equity_sids
            success_sids = future_sids

        with tmp_asset_finder(equities=equities, futures=futures) as finder:
            # Run twice to exercise caching.
            lookup = getattr(finder, lookup_name)
            for _ in range(2):
                results = lookup(success_sids)
                self.assertIsInstance(results, dict)
                self.assertEqual(set(results.keys()), set(success_sids))
                self.assertEqual(valmap(int, results), dict(zip(success_sids, success_sids)))
                self.assertEqual({type_}, {type(asset) for asset in itervalues(results)})
                with self.assertRaises(failure_type):
                    lookup(fail_sids)
                with self.assertRaises(failure_type):
                    # Should fail if **any** of the assets are bad.
                    lookup([success_sids[0], fail_sids[0]])
Beispiel #2
0
    def setUp(self):
        self.constants = {
            # Every day, assume every stock starts at 2, goes down to 1,
            # goes up to 4, and finishes at 3.
            USEquityPricing.low: 1,
            USEquityPricing.open: 2,
            USEquityPricing.close: 3,
            USEquityPricing.high: 4,
        }
        self.asset_ids = [1, 2, 3]
        self.dates = date_range('2014-01', '2014-03', freq='D', tz='UTC')
        self.loader = PrecomputedLoader(
            constants=self.constants,
            dates=self.dates,
            sids=self.asset_ids,
        )

        self.asset_info = make_simple_equity_info(
            self.asset_ids,
            start_date=self.dates[0],
            end_date=self.dates[-1],
        )
        environment = TradingEnvironment()
        environment.write_data(equities_df=self.asset_info)
        self.asset_finder = environment.asset_finder
        self.assets = self.asset_finder.retrieve_all(self.asset_ids)
Beispiel #3
0
 def test_group_by_type(self):
     equities = make_simple_equity_info(
         range(5),
         start_date=pd.Timestamp('2014-01-01'),
         end_date=pd.Timestamp('2015-01-01'),
     )
     futures = make_commodity_future_info(
         first_sid=6,
         root_symbols=['CL'],
         years=[2014],
     )
     # Intersecting sid queries, to exercise loading of partially-cached
     # results.
     queries = [
         ([0, 1, 3], [6, 7]),
         ([0, 2, 3], [7, 10]),
         (list(equities.index), list(futures.index)),
     ]
     with tmp_asset_finder(equities=equities, futures=futures) as finder:
         for equity_sids, future_sids in queries:
             results = finder.group_by_type(equity_sids + future_sids)
             self.assertEqual(
                 results,
                 {
                     'equity': set(equity_sids),
                     'future': set(future_sids)
                 },
             )
Beispiel #4
0
    def setUpClass(cls):
        cls.AAPL = 1
        cls.MSFT = 2
        cls.BRK_A = 3
        cls.assets = [cls.AAPL, cls.MSFT, cls.BRK_A]
        asset_info = make_simple_equity_info(
            cls.assets,
            Timestamp('2014'),
            Timestamp('2015'),
            ['AAPL', 'MSFT', 'BRK_A'],
        )
        cls.env = trading.TradingEnvironment()
        cls.env.write_data(equities_df=asset_info)
        cls.tempdir = tempdir = TempDirectory()
        tempdir.create()
        try:
            cls.raw_data, cls.bar_reader = cls.create_bar_reader(tempdir)
            cls.adj_reader = cls.create_adjustment_reader(tempdir)
            cls.pipeline_loader = USEquityPricingLoader(
                cls.bar_reader, cls.adj_reader
            )
        except:
            cls.tempdir.cleanup()
            raise

        cls.dates = cls.raw_data[cls.AAPL].index.tz_localize('UTC')
        cls.AAPL_split_date = Timestamp("2014-06-09", tz='UTC')
Beispiel #5
0
 def test_group_by_type(self):
     equities = make_simple_equity_info(
         range(5),
         start_date=pd.Timestamp('2014-01-01'),
         end_date=pd.Timestamp('2015-01-01'),
     )
     futures = make_commodity_future_info(
         first_sid=6,
         root_symbols=['CL'],
         years=[2014],
     )
     # Intersecting sid queries, to exercise loading of partially-cached
     # results.
     queries = [
         ([0, 1, 3], [6, 7]),
         ([0, 2, 3], [7, 10]),
         (list(equities.index), list(futures.index)),
     ]
     with tmp_asset_finder(equities=equities, futures=futures) as finder:
         for equity_sids, future_sids in queries:
             results = finder.group_by_type(equity_sids + future_sids)
             self.assertEqual(
                 results,
                 {'equity': set(equity_sids), 'future': set(future_sids)},
             )
Beispiel #6
0
 def setUpClass(cls):
     cls.__calendar = date_range("2014", "2015", freq=trading_day)
     cls.__assets = assets = Int64Index(arange(1, 20))
     cls.__tmp_finder_ctx = tmp_asset_finder(
         equities=make_simple_equity_info(assets, cls.__calendar[0], cls.__calendar[-1])
     )
     cls.__finder = cls.__tmp_finder_ctx.__enter__()
     cls.__mask = cls.__finder.lifetimes(cls.__calendar[-30:], include_start_date=False)
Beispiel #7
0
    def setUpClass(cls):
        cls.env = TradingEnvironment()
        day = cls.env.trading_day

        cls.assets = Int64Index([1, 2, 3])
        cls.dates = date_range("2015-01-01", "2015-01-31", freq=day, tz="UTC")

        asset_info = make_simple_equity_info(cls.assets, start_date=cls.dates[0], end_date=cls.dates[-1])
        cls.env.write_data(equities_df=asset_info)
        cls.asset_finder = cls.env.asset_finder
Beispiel #8
0
 def setUpClass(cls):
     cls.__calendar = date_range('2014', '2015', freq=trading_day)
     cls.__assets = assets = Int64Index(arange(1, 20))
     cls.__tmp_finder_ctx = tmp_asset_finder(
         equities=make_simple_equity_info(
             assets,
             cls.__calendar[0],
             cls.__calendar[-1],
         ))
     cls.__finder = cls.__tmp_finder_ctx.__enter__()
     cls.__mask = cls.__finder.lifetimes(
         cls.__calendar[-30:],
         include_start_date=False,
     )
Beispiel #9
0
 def setUpClass(cls):
     cls._cleanup_stack = stack = ExitStack()
     equity_info = make_simple_equity_info(
         cls.sids,
         start_date=pd.Timestamp('2013-01-01', tz='UTC'),
         end_date=pd.Timestamp('2015-01-01', tz='UTC'),
     )
     cls.cols = {}
     cls.dataset = {
         sid: df
         for sid, df in enumerate(
             case.rename(columns={DATE_FIELD_NAME: ANNOUNCEMENT_FIELD_NAME})
             for case in cls.event_dates_cases)
     }
Beispiel #10
0
    def test_retrieve_all(self):
        equities = make_simple_equity_info(
            range(5),
            start_date=pd.Timestamp('2014-01-01'),
            end_date=pd.Timestamp('2015-01-01'),
        )
        max_equity = equities.index.max()
        futures = make_commodity_future_info(
            first_sid=max_equity + 1,
            root_symbols=['CL'],
            years=[2014],
        )

        with tmp_asset_finder(equities=equities, futures=futures) as finder:
            all_sids = finder.sids
            self.assertEqual(len(all_sids), len(equities) + len(futures))
            queries = [
                # Empty Query.
                (),
                # Only Equities.
                tuple(equities.index[:2]),
                # Only Futures.
                tuple(futures.index[:3]),
                # Mixed, all cache misses.
                tuple(equities.index[2:]) + tuple(futures.index[3:]),
                # Mixed, all cache hits.
                tuple(equities.index[2:]) + tuple(futures.index[3:]),
                # Everything.
                all_sids,
                all_sids,
            ]
            for sids in queries:
                equity_sids = [i for i in sids if i <= max_equity]
                future_sids = [i for i in sids if i > max_equity]
                results = finder.retrieve_all(sids)
                self.assertEqual(sids, tuple(map(int, results)))

                self.assertEqual(
                    [Equity for _ in equity_sids] +
                    [Future for _ in future_sids],
                    list(map(type, results)),
                )
                self.assertEqual(
                    (
                        list(equities.symbol.loc[equity_sids]) +
                        list(futures.symbol.loc[future_sids])
                    ),
                    list(asset.symbol for asset in results),
                )
Beispiel #11
0
    def test_retrieve_all(self):
        equities = make_simple_equity_info(
            range(5),
            start_date=pd.Timestamp('2014-01-01'),
            end_date=pd.Timestamp('2015-01-01'),
        )
        max_equity = equities.index.max()
        futures = make_commodity_future_info(
            first_sid=max_equity + 1,
            root_symbols=['CL'],
            years=[2014],
        )

        with tmp_asset_finder(equities=equities, futures=futures) as finder:
            all_sids = finder.sids
            self.assertEqual(len(all_sids), len(equities) + len(futures))
            queries = [
                # Empty Query.
                (),
                # Only Equities.
                tuple(equities.index[:2]),
                # Only Futures.
                tuple(futures.index[:3]),
                # Mixed, all cache misses.
                tuple(equities.index[2:]) + tuple(futures.index[3:]),
                # Mixed, all cache hits.
                tuple(equities.index[2:]) + tuple(futures.index[3:]),
                # Everything.
                all_sids,
                all_sids,
            ]
            for sids in queries:
                equity_sids = [i for i in sids if i <= max_equity]
                future_sids = [i for i in sids if i > max_equity]
                results = finder.retrieve_all(sids)
                self.assertEqual(sids, tuple(map(int, results)))

                self.assertEqual(
                    [Equity for _ in equity_sids] +
                    [Future for _ in future_sids],
                    list(map(type, results)),
                )
                self.assertEqual(
                    (
                        list(equities.symbol.loc[equity_sids]) +
                        list(futures.symbol.loc[future_sids])
                    ),
                    list(asset.symbol for asset in results),
                )
Beispiel #12
0
    def setUpClass(cls):
        cls._cleanup_stack = stack = ExitStack()
        equity_info = make_simple_equity_info(
            cls.sids,
            start_date=pd.Timestamp('2013-01-01', tz='UTC'),
            end_date=pd.Timestamp('2015-01-01', tz='UTC'),
        )
        cls.cols = {}
        cls.dataset = {sid: df for sid, df in enumerate(
            case.rename(
                columns={DATE_FIELD_NAME: ANNOUNCEMENT_FIELD_NAME}
            ) for case in cls.event_dates_cases)}
        cls.finder = stack.enter_context(
            tmp_asset_finder(equities=equity_info),
        )

        cls.loader_type = EarningsCalendarLoader
Beispiel #13
0
    def setUp(self):
        self.constants = {
            # Every day, assume every stock starts at 2, goes down to 1,
            # goes up to 4, and finishes at 3.
            USEquityPricing.low: 1,
            USEquityPricing.open: 2,
            USEquityPricing.close: 3,
            USEquityPricing.high: 4,
        }
        self.assets = [1, 2, 3]
        self.dates = date_range("2014-01", "2014-03", freq="D", tz="UTC")
        self.loader = ConstantLoader(constants=self.constants, dates=self.dates, assets=self.assets)

        self.asset_info = make_simple_equity_info(self.assets, start_date=self.dates[0], end_date=self.dates[-1])
        environment = TradingEnvironment()
        environment.write_data(equities_df=self.asset_info)
        self.asset_finder = environment.asset_finder
Beispiel #14
0
    def setUp(self):
        self.__calendar = date_range('2014', '2015', freq=trading_day)
        self.__assets = assets = Int64Index(arange(1, 20))

        # Set up env for test
        env = TradingEnvironment()
        env.write_data(equities_df=make_simple_equity_info(
            assets,
            self.__calendar[0],
            self.__calendar[-1],
        ), )
        self.__finder = env.asset_finder

        # Use a 30-day period at the end of the year by default.
        self.__mask = self.__finder.lifetimes(
            self.__calendar[-30:],
            include_start_date=False,
        )
Beispiel #15
0
    def setUpClass(cls):
        cls.env = TradingEnvironment()
        day = cls.env.trading_day

        cls.sids = sids = Int64Index([1, 2, 3])
        cls.dates = dates = date_range(
            '2015-02-01',
            '2015-02-28',
            freq=day,
            tz='UTC',
        )

        asset_info = make_simple_equity_info(
            cls.sids,
            start_date=Timestamp('2015-01-31', tz='UTC'),
            end_date=Timestamp('2015-03-01', tz='UTC'),
        )
        cls.env.write_data(equities_df=asset_info)
        cls.asset_finder = cls.env.asset_finder

        cls.raw_data = DataFrame(
            data=arange(len(dates) * len(sids), dtype=float).reshape(
                len(dates),
                len(sids),
            ),
            index=dates,
            columns=cls.asset_finder.retrieve_all(sids),
        )

        close_loader = DataFrameLoader(USEquityPricing.close, cls.raw_data)
        volume_loader = DataFrameLoader(
            USEquityPricing.volume,
            cls.raw_data * 2,
        )

        cls.engine = SimplePipelineEngine(
            {
                USEquityPricing.close: close_loader,
                USEquityPricing.volume: volume_loader,
            }.__getitem__,
            cls.dates,
            cls.asset_finder,
        )
Beispiel #16
0
    def setUpClass(cls):
        cls.env = TradingEnvironment()
        day = cls.env.trading_day

        cls.assets = Int64Index([1, 2, 3])
        cls.dates = date_range(
            '2015-01-01',
            '2015-01-31',
            freq=day,
            tz='UTC',
        )

        asset_info = make_simple_equity_info(
            cls.assets,
            start_date=cls.dates[0],
            end_date=cls.dates[-1],
        )
        cls.env.write_data(equities_df=asset_info)
        cls.asset_finder = cls.env.asset_finder
Beispiel #17
0
    def test_retrieve_specific_type(self, type_, lookup_name, failure_type):
        equities = make_simple_equity_info(
            range(5),
            start_date=pd.Timestamp('2014-01-01'),
            end_date=pd.Timestamp('2015-01-01'),
        )
        max_equity = equities.index.max()
        futures = make_commodity_future_info(
            first_sid=max_equity + 1,
            root_symbols=['CL'],
            years=[2014],
        )
        equity_sids = [0, 1]
        future_sids = [max_equity + 1, max_equity + 2, max_equity + 3]
        if type_ == Equity:
            success_sids = equity_sids
            fail_sids = future_sids
        else:
            fail_sids = equity_sids
            success_sids = future_sids

        with tmp_asset_finder(equities=equities, futures=futures) as finder:
            # Run twice to exercise caching.
            lookup = getattr(finder, lookup_name)
            for _ in range(2):
                results = lookup(success_sids)
                self.assertIsInstance(results, dict)
                self.assertEqual(set(results.keys()), set(success_sids))
                self.assertEqual(
                    valmap(int, results),
                    dict(zip(success_sids, success_sids)),
                )
                self.assertEqual(
                    {type_},
                    {type(asset)
                     for asset in itervalues(results)},
                )
                with self.assertRaises(failure_type):
                    lookup(fail_sids)
                with self.assertRaises(failure_type):
                    # Should fail if **any** of the assets are bad.
                    lookup([success_sids[0], fail_sids[0]])
Beispiel #18
0
    def setUpClass(cls):
        cls.env = TradingEnvironment()
        day = cls.env.trading_day

        cls.sids = sids = Int64Index([1, 2, 3])
        cls.dates = dates = date_range(
            '2015-02-01',
            '2015-02-28',
            freq=day,
            tz='UTC',
        )

        asset_info = make_simple_equity_info(
            cls.sids,
            start_date=Timestamp('2015-01-31', tz='UTC'),
            end_date=Timestamp('2015-03-01', tz='UTC'),
        )
        cls.env.write_data(equities_df=asset_info)
        cls.asset_finder = cls.env.asset_finder

        cls.raw_data = DataFrame(
            data=arange(len(dates) * len(sids), dtype=float).reshape(
                len(dates), len(sids),
            ),
            index=dates,
            columns=cls.asset_finder.retrieve_all(sids),
        )

        close_loader = DataFrameLoader(USEquityPricing.close, cls.raw_data)
        volume_loader = DataFrameLoader(
            USEquityPricing.volume,
            cls.raw_data * 2,
        )

        cls.engine = SimplePipelineEngine(
            {
                USEquityPricing.close: close_loader,
                USEquityPricing.volume: volume_loader,
            }.__getitem__,
            cls.dates,
            cls.asset_finder,
        )
Beispiel #19
0
    def setUpClass(cls):
        cls.env = TradingEnvironment()
        day = cls.env.trading_day

        cls.asset_ids = [1, 2, 3]
        cls.dates = date_range(
            '2015-01-01',
            '2015-01-31',
            freq=day,
            tz='UTC',
        )

        asset_info = make_simple_equity_info(
            cls.asset_ids,
            start_date=cls.dates[0],
            end_date=cls.dates[-1],
        )
        cls.env.write_data(equities_df=asset_info)
        cls.asset_finder = cls.env.asset_finder
        cls.assets = cls.asset_finder.retrieve_all(cls.asset_ids)
Beispiel #20
0
    def setUp(self):
        self.__calendar = date_range('2014', '2015', freq=trading_day)
        self.__assets = assets = Int64Index(arange(1, 20))

        # Set up env for test
        env = TradingEnvironment()
        env.write_data(
            equities_df=make_simple_equity_info(
                assets,
                self.__calendar[0],
                self.__calendar[-1],
            ),
        )
        self.__finder = env.asset_finder

        # Use a 30-day period at the end of the year by default.
        self.__mask = self.__finder.lifetimes(
            self.__calendar[-30:],
            include_start_date=False,
        )
Beispiel #21
0
    def setUpClass(cls):
        cls._cleanup_stack = stack = ExitStack()
        cls.sids = A, B, C, D, E = range(5)
        equity_info = make_simple_equity_info(
            cls.sids,
            start_date=pd.Timestamp('2013-01-01', tz='UTC'),
            end_date=pd.Timestamp('2015-01-01', tz='UTC'),
        )
        cls.finder = stack.enter_context(
            tmp_asset_finder(equities=equity_info),
        )

        cls.earnings_dates = {
            # K1--K2--E1--E2.
            A: to_series(
                knowledge_dates=['2014-01-05', '2014-01-10'],
                earning_dates=['2014-01-15', '2014-01-20'],
            ),
            # K1--K2--E2--E1.
            B: to_series(
                knowledge_dates=['2014-01-05', '2014-01-10'],
                earning_dates=['2014-01-20', '2014-01-15']
            ),
            # K1--E1--K2--E2.
            C: to_series(
                knowledge_dates=['2014-01-05', '2014-01-15'],
                earning_dates=['2014-01-10', '2014-01-20']
            ),
            # K1 == K2.
            D: to_series(
                knowledge_dates=['2014-01-05'] * 2,
                earning_dates=['2014-01-10', '2014-01-15'],
            ),
            E: pd.Series(
                data=[],
                index=pd.DatetimeIndex([]),
                dtype='datetime64[ns]',
            ),
        }
Beispiel #22
0
    def setUpClass(cls):
        cls._cleanup_stack = stack = ExitStack()
        cls.sids = A, B, C, D, E = range(5)
        equity_info = make_simple_equity_info(
            cls.sids,
            start_date=pd.Timestamp('2013-01-01', tz='UTC'),
            end_date=pd.Timestamp('2015-01-01', tz='UTC'),
        )
        cls.finder = stack.enter_context(
            tmp_asset_finder(equities=equity_info), )

        cls.earnings_dates = {
            # K1--K2--E1--E2.
            A:
            to_series(
                knowledge_dates=['2014-01-05', '2014-01-10'],
                earning_dates=['2014-01-15', '2014-01-20'],
            ),
            # K1--K2--E2--E1.
            B:
            to_series(knowledge_dates=['2014-01-05', '2014-01-10'],
                      earning_dates=['2014-01-20', '2014-01-15']),
            # K1--E1--K2--E2.
            C:
            to_series(knowledge_dates=['2014-01-05', '2014-01-15'],
                      earning_dates=['2014-01-10', '2014-01-20']),
            # K1 == K2.
            D:
            to_series(
                knowledge_dates=['2014-01-05'] * 2,
                earning_dates=['2014-01-10', '2014-01-15'],
            ),
            E:
            pd.Series(
                data=[],
                index=pd.DatetimeIndex([]),
                dtype='datetime64[ns]',
            ),
        }
Beispiel #23
0
    NoDeltasWarning,
)
from zipline.pipeline.loaders.blaze.core import (
    NonNumpyField,
    NonPipelineField,
    no_deltas_rules,
)
from zipline.utils.numpy_utils import repeat_last_axis
from zipline.utils.test_utils import tmp_asset_finder, make_simple_equity_info

nameof = op.attrgetter('name')
dtypeof = op.attrgetter('dtype')
asset_infos = (
    (make_simple_equity_info(
        tuple(map(ord, 'ABC')),
        pd.Timestamp(0),
        pd.Timestamp('2015'),
    ), ),
    (make_simple_equity_info(
        tuple(map(ord, 'ABCD')),
        pd.Timestamp(0),
        pd.Timestamp('2015'),
    ), ),
)
with_extra_sid = parameterized.expand(asset_infos)


class BlazeToPipelineTestCase(TestCase):
    @classmethod
    def setUpClass(cls):
        cls.dates = dates = pd.date_range('2014-01-01', '2014-01-03')
Beispiel #24
0
from toolz import keymap, valmap, concatv
from toolz.curried import operator as op

from zipline.pipeline import Pipeline, CustomFactor
from zipline.pipeline.data import DataSet, BoundColumn
from zipline.pipeline.engine import SimplePipelineEngine
from zipline.pipeline.loaders.blaze import from_blaze, BlazeLoader, NoDeltasWarning
from zipline.pipeline.loaders.blaze.core import NonNumpyField, NonPipelineField, no_deltas_rules
from zipline.utils.numpy_utils import float64_dtype, int64_dtype, repeat_last_axis
from zipline.utils.test_utils import tmp_asset_finder, make_simple_equity_info


nameof = op.attrgetter("name")
dtypeof = op.attrgetter("dtype")
asset_infos = (
    (make_simple_equity_info(tuple(map(ord, "ABC")), pd.Timestamp(0), pd.Timestamp("2015")),),
    (make_simple_equity_info(tuple(map(ord, "ABCD")), pd.Timestamp(0), pd.Timestamp("2015")),),
)
with_extra_sid = parameterized.expand(asset_infos)


def _utc_localize_index_level_0(df):
    """``tz_localize`` the first level of a multiindexed dataframe to utc.

    Mutates df in place.
    """
    idx = df.index
    df.index = pd.MultiIndex.from_product((idx.levels[0].tz_localize("utc"), idx.levels[1]), names=idx.names)
    return df

Beispiel #25
0
)
from zipline.pipeline.loaders.blaze.core import (
    NonNumpyField,
    NonPipelineField,
    no_deltas_rules,
)
from zipline.utils.numpy_utils import repeat_last_axis
from zipline.utils.test_utils import tmp_asset_finder, make_simple_equity_info


nameof = op.attrgetter('name')
dtypeof = op.attrgetter('dtype')
asset_infos = (
    (make_simple_equity_info(
        tuple(map(ord, 'ABC')),
        pd.Timestamp(0),
        pd.Timestamp('2015'),
    ),),
    (make_simple_equity_info(
        tuple(map(ord, 'ABCD')),
        pd.Timestamp(0),
        pd.Timestamp('2015'),
    ),),
)
with_extra_sid = parameterized.expand(asset_infos)


class BlazeToPipelineTestCase(TestCase):
    @classmethod
    def setUpClass(cls):
        cls.dates = dates = pd.date_range('2014-01-01', '2014-01-03')
Beispiel #26
0
class EventLoaderCommonMixin(object):
    sids = A, B, C, D, E = range(5)
    equity_info = make_simple_equity_info(
        sids,
        start_date=pd.Timestamp('2013-01-01', tz='UTC'),
        end_date=pd.Timestamp('2015-01-01', tz='UTC'),
    )

    event_dates_cases = [
        # K1--K2--E1--E2.
        pd.DataFrame({
            TS_FIELD_NAME:
            pd.to_datetime(['2014-01-05', '2014-01-10']),
            DATE_FIELD_NAME:
            pd.to_datetime(['2014-01-15', '2014-01-20'])
        }),
        # K1--K2--E2--E1.
        pd.DataFrame({
            TS_FIELD_NAME:
            pd.to_datetime(['2014-01-05', '2014-01-10']),
            DATE_FIELD_NAME:
            pd.to_datetime(['2014-01-20', '2014-01-15'])
        }),
        # K1--E1--K2--E2.
        pd.DataFrame({
            TS_FIELD_NAME:
            pd.to_datetime(['2014-01-05', '2014-01-15']),
            DATE_FIELD_NAME:
            pd.to_datetime(['2014-01-10', '2014-01-20'])
        }),
        # K1 == K2.
        pd.DataFrame({
            TS_FIELD_NAME:
            pd.to_datetime(['2014-01-05'] * 2),
            DATE_FIELD_NAME:
            pd.to_datetime(['2014-01-10', '2014-01-15'])
        }),
        pd.DataFrame({
            TS_FIELD_NAME: pd.to_datetime([]),
            DATE_FIELD_NAME: pd.to_datetime([])
        })
    ]

    def zip_with_floats(self, dates, flts):
        return pd.Series(flts, index=dates).astype('float')

    def num_days_between(self, dates, start_date, end_date):
        return num_days_in_range(dates, start_date, end_date)

    def zip_with_dates(self, index_dates, dts):
        return pd.Series(pd.to_datetime(dts), index=index_dates)

    def loader_args(self, dates):
        """Construct the base  object to pass to the loader.

        Parameters
        ----------
        dates : pd.DatetimeIndex
            The dates we can serve.

        Returns
        -------
        args : tuple[any]
            The arguments to forward to the loader positionally.
        """
        return dates, self.dataset

    def setup_engine(self, dates):
        """
        Make a Pipeline Enigne object based on the given dates.
        """
        loader = self.loader_type(*self.loader_args(dates))
        return SimplePipelineEngine(lambda _: loader, dates, self.finder)

    def get_expected_next_event_dates(self, dates):
        num_days_between_for_dates = partial(self.num_days_between, dates)
        zip_with_dates_for_dates = partial(self.zip_with_dates, dates)
        return pd.DataFrame(
            {
                0:
                zip_with_dates_for_dates(
                    ['NaT'] * num_days_between_for_dates(None, '2014-01-04') +
                    ['2014-01-15'] *
                    num_days_between_for_dates('2014-01-05', '2014-01-15') +
                    ['2014-01-20'] *
                    num_days_between_for_dates('2014-01-16', '2014-01-20') +
                    ['NaT'] * num_days_between_for_dates('2014-01-21', None)),
                1:
                zip_with_dates_for_dates(
                    ['NaT'] * num_days_between_for_dates(None, '2014-01-04') +
                    ['2014-01-20'] *
                    num_days_between_for_dates('2014-01-05', '2014-01-09') +
                    ['2014-01-15'] *
                    num_days_between_for_dates('2014-01-10', '2014-01-15') +
                    ['2014-01-20'] *
                    num_days_between_for_dates('2014-01-16', '2014-01-20') +
                    ['NaT'] * num_days_between_for_dates('2014-01-21', None)),
                2:
                zip_with_dates_for_dates(
                    ['NaT'] * num_days_between_for_dates(None, '2014-01-04') +
                    ['2014-01-10'] *
                    num_days_between_for_dates('2014-01-05', '2014-01-10') +
                    ['NaT'] *
                    num_days_between_for_dates('2014-01-11', '2014-01-14') +
                    ['2014-01-20'] *
                    num_days_between_for_dates('2014-01-15', '2014-01-20') +
                    ['NaT'] * num_days_between_for_dates('2014-01-21', None)),
                3:
                zip_with_dates_for_dates(
                    ['NaT'] * num_days_between_for_dates(None, '2014-01-04') +
                    ['2014-01-10'] *
                    num_days_between_for_dates('2014-01-05', '2014-01-10') +
                    ['2014-01-15'] *
                    num_days_between_for_dates('2014-01-11', '2014-01-15') +
                    ['NaT'] * num_days_between_for_dates('2014-01-16', None)),
                4:
                zip_with_dates_for_dates(['NaT'] * len(dates)),
            },
            index=dates)

    def get_expected_previous_event_dates(self, dates):
        num_days_between_for_dates = partial(self.num_days_between, dates)
        zip_with_dates_for_dates = partial(self.zip_with_dates, dates)
        return pd.DataFrame(
            {
                0:
                zip_with_dates_for_dates(
                    ['NaT'] * num_days_between_for_dates(None, '2014-01-14') +
                    ['2014-01-15'] *
                    num_days_between_for_dates('2014-01-15', '2014-01-19') +
                    ['2014-01-20'] *
                    num_days_between_for_dates('2014-01-20', None), ),
                1:
                zip_with_dates_for_dates(
                    ['NaT'] * num_days_between_for_dates(None, '2014-01-14') +
                    ['2014-01-15'] *
                    num_days_between_for_dates('2014-01-15', '2014-01-19') +
                    ['2014-01-20'] *
                    num_days_between_for_dates('2014-01-20', None), ),
                2:
                zip_with_dates_for_dates(
                    ['NaT'] * num_days_between_for_dates(None, '2014-01-09') +
                    ['2014-01-10'] *
                    num_days_between_for_dates('2014-01-10', '2014-01-19') +
                    ['2014-01-20'] *
                    num_days_between_for_dates('2014-01-20', None), ),
                3:
                zip_with_dates_for_dates(
                    ['NaT'] * num_days_between_for_dates(None, '2014-01-09') +
                    ['2014-01-10'] *
                    num_days_between_for_dates('2014-01-10', '2014-01-14') +
                    ['2014-01-15'] *
                    num_days_between_for_dates('2014-01-15', None), ),
                4:
                zip_with_dates_for_dates(['NaT'] * len(dates)),
            },
            index=dates)

    @staticmethod
    def _compute_busday_offsets(announcement_dates):
        """
        Compute expected business day offsets from a DataFrame of announcement
        dates.
        """
        # Column-vector of dates on which factor `compute` will be called.
        raw_call_dates = announcement_dates.index.values.astype(
            'datetime64[D]')[:, None]

        # 2D array of dates containining expected nexg announcement.
        raw_announce_dates = (
            announcement_dates.values.astype('datetime64[D]'))

        # Set NaTs to 0 temporarily because busday_count doesn't support NaT.
        # We fill these entries with NaNs later.
        whereNaT = raw_announce_dates == NaTD
        raw_announce_dates[whereNaT] = make_datetime64D(0)

        # The abs call here makes it so that we can use this function to
        # compute offsets for both next and previous earnings (previous
        # earnings offsets come back negative).
        expected = abs(
            np.busday_count(raw_call_dates, raw_announce_dates).astype(float))

        expected[whereNaT] = np.nan
        return pd.DataFrame(
            data=expected,
            columns=announcement_dates.columns,
            index=announcement_dates.index,
        )

    @parameterized.expand(
        gen_calendars(
            '2014-01-01',
            '2014-01-31',
            critical_dates=pd.to_datetime([
                '2014-01-05',
                '2014-01-10',
                '2014-01-15',
                '2014-01-20',
            ],
                                          utc=True),
        ))
    def test_compute(self, dates):
        engine = self.setup_engine(dates)
        self.setup(dates)

        pipe = Pipeline(columns=self.pipeline_columns)

        result = engine.run_pipeline(
            pipe,
            start_date=dates[0],
            end_date=dates[-1],
        )

        for sid in self.sids:
            for col_name in self.cols.keys():
                assert_series_equal(result[col_name].xs(sid, level=1),
                                    self.cols[col_name][sid],
                                    check_names=False)