ENH: Allow arbitrary history queries.

In preparation for using `DataPortal` in notebooks, remove restriction on
the `HistoryLoader` to dates that are monotonically increasing. Notebook
usage of the `DataPortal` is more useful when the end of the history
window can be arbitrary dates without having to restart the notebook kernel.

Due to the implementation of the prefetch and caching logic, the end
date of history calls could previously only increase. e.g. `2016-11-01`,
`2016-11-02`, `2016-11-03`. This pattern was sufficient for backtesting
and live simulations, since the current time of the algorithm only ever increases.

With this change, which resets the underlying sliding window when the
last fetched idx is greater than the

Now calls to history in the same process with end dates such
`2016-11-01`, `2016-10-31`, `2015-11-02` should work.
This commit is contained in:
Eddie Hebert
2016-11-07 11:34:40 -05:00
parent 2b925ecb78
commit 6ff1d55504
2 changed files with 73 additions and 6 deletions
+58
View File
@@ -1761,6 +1761,64 @@ class DailyEquityHistoryTestCase(WithHistory, ZiplineTestCase):
np.testing.assert_almost_equal(window_1[self.ASSET2].values,
window_2[self.ASSET2].values)
def test_history_window_out_of_order_dates(self):
"""
Use a history window with non-monotonically increasing dates.
A scenario which does not occur during simulations, but useful
for using a history loader in a notebook.
"""
window_1 = self.data_portal.get_history_window(
[self.ASSET1],
pd.Timestamp('2014-02-07', tz='UTC'),
4,
"1d",
"close"
)
window_2 = self.data_portal.get_history_window(
[self.ASSET1],
pd.Timestamp('2014-02-05', tz='UTC'),
4,
"1d",
"close"
)
window_3 = self.data_portal.get_history_window(
[self.ASSET1],
pd.Timestamp('2014-02-07', tz='UTC'),
4,
"1d",
"close"
)
window_4 = self.data_portal.get_history_window(
[self.ASSET1],
pd.Timestamp('2014-01-22', tz='UTC'),
4,
"1d",
"close"
)
# Calling 02-07 after resetting the window should not affect the
# results.
np.testing.assert_almost_equal(window_1.values, window_3.values)
offsets = np.arange(4)
def assert_window_prices(window, starting_price):
np.testing.assert_almost_equal(window.loc[:, self.ASSET1],
starting_price + offsets)
# Window 1 starts on the 23rd day of data for ASSET 1.
assert_window_prices(window_1, 23)
# Window 2 starts on the 21st day of data for ASSET 1.
assert_window_prices(window_2, 21)
# Window 3 starts on the 23rd day of data for ASSET 1.
assert_window_prices(window_3, 23)
# Window 4 starts on the 11th day of data for ASSET 1.
assert_window_prices(window_4, 11)
class NoPrefetchDailyEquityHistoryTestCase(DailyEquityHistoryTestCase):
DATA_PORTAL_MINUTE_HISTORY_PREFETCH = 0
+15 -6
View File
@@ -379,12 +379,26 @@ class HistoryLoader(with_metaclass(ABCMeta)):
assets = self._asset_finder.retrieve_all(assets)
try:
end_ix = self._calendar.get_loc(end)
except KeyError:
raise KeyError("{0} not in calendar [{1}...{2}]".format(
end, self._calendar[0], self._calendar[-1]))
for asset in assets:
try:
asset_windows[asset] = self._window_blocks[field].get(
window = self._window_blocks[field].get(
(asset, size, is_perspective_after), end)
except KeyError:
needed_assets.append(asset)
else:
if end_ix < window.most_recent_ix:
# Window needs reset. Requested end index occurs before the
# end index from the previous history call for this window.
# Grab new window instead of rewinding adjustments.
needed_assets.append(asset)
else:
asset_windows[asset] = window
if needed_assets:
start = dts[0]
@@ -395,11 +409,6 @@ class HistoryLoader(with_metaclass(ABCMeta)):
except KeyError:
raise KeyError("{0} not in calendar [{1}...{2}]".format(
start, self._calendar[0], self._calendar[-1]))
try:
end_ix = self._calendar.get_loc(end)
except KeyError:
raise KeyError("{0} not in calendar [{1}...{2}]".format(
end, self._calendar[0], self._calendar[-1]))
cal = self._calendar
prefetch_end_ix = min(end_ix + self._prefetch_length, len(cal) - 1)
prefetch_end = cal[prefetch_end_ix]