{"record":{"id":"49db39be5079cd53","repo":"microsoft/qlib","slug":"the-calendar-is-finished-please-reset-it-if-you-w","errorCode":null,"errorMessage":"The calendar is finished, please reset it if you want to call it!","messagePattern":"The calendar is finished, please reset it if you want to call it!","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"qlib/backtest/utils.py","lineNumber":89,"sourceCode":"        self._calendar = _calendar\n        _, _, _start_index, _end_index = Cal.locate_index(start_time, end_time, freq=freq, future=True)\n        self.start_index = _start_index\n        self.end_index = _end_index\n        self.trade_len = _end_index - _start_index + 1\n        self.trade_step = 0\n\n    def finished(self) -> bool:\n        \"\"\"\n        Check if the trading finished\n        - Should check before calling strategy.generate_decisions and executor.execute\n        - If self.trade_step >= self.self.trade_len, it means the trading is finished\n        - If self.trade_step < self.self.trade_len, it means the number of trading step finished is self.trade_step\n        \"\"\"\n        return self.trade_step >= self.trade_len\n\n    def step(self) -> None:\n        if self.finished():\n            raise RuntimeError(f\"The calendar is finished, please reset it if you want to call it!\")\n        self.trade_step += 1\n\n    def get_freq(self) -> str:\n        return self.freq\n\n    def get_trade_len(self) -> int:\n        \"\"\"get the total step length\"\"\"\n        return self.trade_len\n\n    def get_trade_step(self) -> int:\n        return self.trade_step\n\n    def get_step_time(self, trade_step: int | None = None, shift: int = 0) -> Tuple[pd.Timestamp, pd.Timestamp]:\n        \"\"\"\n        Get the left and right endpoints of the trade_step'th trading interval\n\n        About the endpoints:\n            - Qlib uses the closed interval in time-series data selection, which has the same performance as","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/backtest/utils.py#L71-L107","documentation":"TradeCalendarManager in qlib/backtest/utils.py tracks trading progress with trade_step and trade_len. finished() returns True once trade_step >= trade_len, and step() refuses to advance past the end of the calendar, raising RuntimeError. This is a state-machine guard: the calendar must be reset before being reused after completion.","triggerScenarios":"Calling calendar.step() (directly or via an executor/strategy loop) one more time after the last trading bar — typically an off-by-one in a custom executor loop, or reusing the same TradeCalendarManager instance for a second backtest run without reset.","commonSituations":"Custom NestedExecutor or strategy code that drives the loop manually and calls step() before re-checking finished(); re-running a backtest in a notebook with shared common_infra state; incorrect trade_len computed from a truncated calendar.","solutions":["Always check finished() before calling step(): 'while not cal.finished(): ... cal.step()'.","Reset state between runs — recreate the TradeCalendarManager (or call its reset/setup with fresh start/end times) instead of reusing the spent instance.","Audit custom executor loops for step() called both inside executor.execute and again by the outer loop (double-stepping exhausts the calendar early)."],"exampleFix":"# before\nfor _ in range(n_steps):\n    cal.step()  # raises when trade_step reaches trade_len\n# after\nwhile not cal.finished():\n    do_trade()\n    cal.step()","handlingStrategy":"validation","validationCode":"if cal.finished():\n    raise RuntimeError('calendar exhausted; reset it before the next run')\ncal.step()","typeGuard":null,"tryCatchPattern":"try:\n    cal.step()\nexcept RuntimeError as e:\n    if 'calendar is finished' in str(e):\n        cal.reset()  # or break the trading loop\n    else:\n        raise","preventionTips":["Structure every loop as 'while not cal.finished(): ...; cal.step()'.","Never reuse a TradeCalendarManager across backtest runs — rebuild it via common_infra reset.","Log trade_step/trade_len each iteration in custom executors to catch double-stepping early."],"tags":["qlib","backtest","state-machine","loop-guard"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}