{"record":{"id":"b4e496e8ea4629d7","repo":"microsoft/qlib","slug":"the-decision-didn-t-provide-an-index-range","errorCode":null,"errorMessage":"The decision didn't provide an index range","messagePattern":"The decision didn't provide an index range","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"qlib/backtest/decision.py","lineNumber":389,"sourceCode":"        trade_calendar : TradeCalendarManager\n            The calendar of the **inner strategy**!!!!!\n\n        Returns\n        -------\n        BaseTradeDecision:\n            New update, use new decision. If no updates, return None (use previous decision (or unavailable))\n        \"\"\"\n        # purpose 1)\n        self.total_step = trade_calendar.get_trade_len()\n\n        # purpose 2)\n        return self.strategy.update_trade_decision(self, trade_calendar)\n\n    def _get_range_limit(self, **kwargs: Any) -> Tuple[int, int]:\n        if self.trade_range is not None:\n            return self.trade_range(trade_calendar=cast(TradeCalendarManager, kwargs.get(\"inner_calendar\")))\n        else:\n            raise NotImplementedError(\"The decision didn't provide an index range\")\n\n    def get_range_limit(self, **kwargs: Any) -> Tuple[int, int]:\n        \"\"\"\n        return the expected step range for limiting the decision execution time\n        Both left and right are **closed**\n\n        if no available trade_range, `default_value` will be returned\n\n        It is only used in `NestedExecutor`\n        - The outmost strategy will not follow any range limit (but it may give range_limit)\n        - The inner most strategy's range_limit will be useless due to atomic executors don't have such\n          features.\n\n        **NOTE**:\n        1) This function must be called after `self.update` in following cases(ensured by NestedExecutor):\n        - user relies on the auto-clip feature of `self.update`\n\n        2) This function will be called after _init_sub_trading in NestedExecutor.","sourceCodeStart":371,"sourceCodeEnd":407,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/backtest/decision.py#L371-L407","documentation":"BaseTradeDecision._get_range_limit unconditionally raises NotImplementedError when the decision carries no trade_range, because without a trade range the decision cannot express a unified start/end index pair. This internal method is the primitive that get_range_limit wraps; the public wrapper converts the exception into a default value when one is supplied.","triggerScenarios":"Calling decision._get_range_limit(**kwargs) on any decision whose trade_range attribute is None (e.g. a BaseTradeDecision or a TradeDecisionWCache built without trade_range).","commonSituations":"Custom code reaching into the private _get_range_limit instead of the public get_range_limit; strategies generating decisions without a trade_range while an executor or the strategy itself probes for a range limit.","solutions":["Call the public decision.get_range_limit(default_value=None) instead, which returns the default instead of raising","Construct the decision with a trade_range, e.g. TradeRangeByTime('9:30', '14:30'), so a range can be resolved","Check decision.trade_range is not None before querying the range limit"],"exampleFix":"// before\nidx = decision._get_range_limit(inner_calendar=cal)  # raises if trade_range is None\n// after\nidx = decision.get_range_limit(default_value=None, inner_calendar=cal)\nif idx is None:\n    idx = 0, total_steps - 1","handlingStrategy":"validation","validationCode":"def safe_range_limit(decision, **kwargs):\n    if decision.trade_range is None:\n        return kwargs.get('default_value')\n    return decision._get_range_limit(**kwargs)","typeGuard":"def has_trade_range(decision) -> bool:\n    return getattr(decision, 'trade_range', None) is not None","tryCatchPattern":"try:\n    idx = decision._get_range_limit(inner_calendar=cal)\nexcept NotImplementedError:\n    idx = (0, decision.total_step - 1)","preventionTips":["Prefer the public get_range_limit(default_value=None) over the private _get_range_limit","Check decision.trade_range before probing for range limits"],"tags":["qlib","backtest","trade-range","not-implemented"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}