fix: backtest IndexError at the right calendar boundary (get_step_time peeks calendar[index+1])#2279
Open
zhaow-de wants to merge 1 commit into
Open
Conversation
…t calendar bar get_step_time formed each step's right endpoint by peeking the next calendar bar (`self._calendar[calendar_index + 1]`). On the final step `calendar_index == end_index`; when `end_time` is the last bar of the (future) calendar — e.g. a self-built dataset with no future calendar, where `Cal.calendar(future=True)` falls back to the current calendar — that peek indexed out of bounds and the backtest died with an opaque `IndexError: index N is out of bounds for axis 0 with size N` deep inside get_step_time. Clamp the right endpoint at the boundary: when no next bar exists, use the end of the current bar's period (`left + one freq unit`), mirroring the day-end logic already used in get_data_cal_range. The non-boundary path is unchanged. Add a regression test asserting get_step_time at the last calendar bar returns a well-defined single-bar interval instead of raising.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
TradeCalendarManager.get_step_timeformed each step's right endpoint by peeking the next calendar bar (qlib/backtest/utils.py:131):On the final step
calendar_index == end_index. Whenend_timeis the last bar ofCal.calendar(future=True)— which is exactly what happens when a dataset has no future calendar configured, sincefuture=Truethen silently falls back to the current calendar —calendar_index + 1indexes out of bounds and the backtest dies with an opaqueIndexError: index N is out of bounds for axis 0 with size N, deep insideget_step_timeand far from the user'sbacktest(end_time=...)call.This PR clamps the right endpoint at the boundary: when no bar exists after the current one, it uses the end of the current bar's period (
left + one freq unit), mirroring the day-end logic already used inget_data_cal_range(utils.py:154):The final step's interval stays a single bar (
end - start = 1·freq - 1s < freq, sois_single_valueremains true), and every non-final step is unchanged (the original peek-the-next-bar path is preserved exactly).Motivation and Context
Related issue: closes #2278.
get_step_timeis called on every backtest step, and the right-endpoint peek assumes there is always at least one bar afterend_time. That assumption holds only when a future calendar extends past the data. For a self-built provider without a future calendar,Cal.calendar(future=True)warns and returns the current calendar, so any backtest whoseend_timeis the last calendar day crashes at the final step. The failure is easy to hit ("backtest through the end of my data") and hard to diagnose (anIndexErroringet_step_time, not at the call site). This makes a backtest ending on the last available bar succeed with a well-defined final interval instead of crashing.How Has This Been Tested?
pytest qlib/tests/test_all_pipeline.pyunder upper directory ofqlib.Added a regression test at
tests/backtest/test_calendar_boundary.py:test_get_step_time_at_last_calendar_bar—get_step_timeat the last calendar bar now returns a well-defined single-bar interval. Fails before this change (raisesIndexError), passes after.test_non_boundary_step_unchanged— pins the non-boundary right endpoint to its original value (next_bar - 1s), proving the common path is unchanged.Screenshots of Test Results (if appropriate):
tests/test_all_pipeline.py, full backtest exercisingget_step_timeon every step):tests/backtest/test_calendar_boundary.py):Types of changes