mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-28 17:00:51 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user