{"record":{"id":"daa34d2d450c308e","repo":"microsoft/qlib","slug":"this-type-of-limit-threshold-is-not-supported","errorCode":null,"errorMessage":"This type of `limit_threshold` is not supported","messagePattern":"This type of `limit_threshold` is not supported","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"qlib/backtest/exchange.py","lineNumber":271,"sourceCode":"                self.extra_quote[\"limit_buy\"] = False\n                self.logger.warning(\"No limit_buy set for extra_quote. All stock will be able to be bought.\")\n            assert set(self.extra_quote.columns) == set(self.quote_df.columns) - {\"$change\"}\n            self.quote_df = pd.concat([self.quote_df, self.extra_quote], sort=False, axis=0)\n\n    LT_TP_EXP = \"(exp)\"  # Tuple[str, str]:  the limitation is calculated by a Qlib expression.\n    LT_FLT = \"float\"  # float:  the trading limitation is based on `abs($change) < limit_threshold`\n    LT_NONE = \"none\"  # none:  there is no trading limitation\n\n    def _get_limit_type(self, limit_threshold: Union[tuple, float, None]) -> str:\n        \"\"\"get limit type\"\"\"\n        if isinstance(limit_threshold, tuple):\n            return self.LT_TP_EXP\n        elif isinstance(limit_threshold, float):\n            return self.LT_FLT\n        elif limit_threshold is None:\n            return self.LT_NONE\n        else:\n            raise NotImplementedError(f\"This type of `limit_threshold` is not supported\")\n\n    def _update_limit(self, limit_threshold: Union[Tuple, float, None]) -> None:\n        # $close may contain NaN, the nan indicates that the stock is not tradable at that timestamp\n        suspended = self.quote_df[\"$close\"].isna()\n        # check limit_threshold\n        limit_type = self._get_limit_type(limit_threshold)\n        if limit_type == self.LT_NONE:\n            self.quote_df[\"limit_buy\"] = suspended\n            self.quote_df[\"limit_sell\"] = suspended\n        elif limit_type == self.LT_TP_EXP:\n            # set limit\n            limit_threshold = cast(tuple, limit_threshold)\n            # astype bool is necessary, because quote_df is an expression and could be float\n            self.quote_df[\"limit_buy\"] = self.quote_df[limit_threshold[0]].astype(\"bool\") | suspended\n            self.quote_df[\"limit_sell\"] = self.quote_df[limit_threshold[1]].astype(\"bool\") | suspended\n        elif limit_type == self.LT_FLT:\n            limit_threshold = cast(float, limit_threshold)\n            self.quote_df[\"limit_buy\"] = self.quote_df[\"$change\"].ge(limit_threshold) | suspended","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/backtest/exchange.py#L253-L289","documentation":"Exchange._get_limit_type classifies limit_threshold into three regimes: tuple -> explicit limit up/down expressions (LT_TP_EXP), float -> |$change| < threshold (LT_FLT), None -> no limit (LT_NONE). Any other type raises NotImplementedError. Note the isinstance check is strictly float, so a Python int (e.g. 0 from config) is rejected even though it looks numeric.","triggerScenarios":"Exchange(limit_threshold=0) or limit_threshold=1 (int); limit_threshold='0.1' (string from YAML/env); passing a pandas/numpy scalar that is not a float instance.","commonSituations":"Config files where the threshold is parsed as int or str; CLI/env-passed values that keep string types; code that computed the threshold with integer arithmetic.","solutions":["Cast to float: Exchange(limit_threshold=float(threshold))","For asymmetric CN-style limits, pass a tuple of (limit_up_expression, limit_down_expression)","Pass None explicitly when you want no limit check"],"exampleFix":"# before\nexch = Exchange(limit_threshold=cfg['limit_threshold'])  # int 0 -> NotImplementedError\n# after\nexch = Exchange(limit_threshold=float(cfg['limit_threshold']))","handlingStrategy":"type-guard","validationCode":"if limit_threshold is not None and not isinstance(limit_threshold, tuple):\n    limit_threshold = float(limit_threshold)  # int/str -> float\nexch = Exchange(limit_threshold=limit_threshold)","typeGuard":"def is_valid_limit_threshold(lt) -> bool:\n    return lt is None or isinstance(lt, (tuple, float))","tryCatchPattern":null,"preventionTips":["Wrap config-loaded thresholds in float()","int is rejected: 0 and 1 must be written 0.0 / 1.0 or cast"],"tags":["qlib","exchange","limit-threshold","type-error","not-implemented"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}