{"record":{"id":"a5080bb855286c71","repo":"microsoft/qlib","slug":"not-in-current-position","errorCode":null,"errorMessage":"{} not in current position","messagePattern":"(.+?) not in current position","errorType":"exception","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"qlib/backtest/position.py","lineNumber":355,"sourceCode":"        self.position[stock_id] = {}\n        self.position[stock_id][\"amount\"] = amount\n        self.position[stock_id][\"price\"] = price\n        self.position[stock_id][\"weight\"] = 0  # update the weight in the end of the trade date\n\n    def _buy_stock(self, stock_id: str, trade_val: float, cost: float, trade_price: float) -> None:\n        trade_amount = trade_val / trade_price\n        if stock_id not in self.position:\n            self._init_stock(stock_id=stock_id, amount=trade_amount, price=trade_price)\n        else:\n            # exist, add amount\n            self.position[stock_id][\"amount\"] += trade_amount\n\n        self.position[\"cash\"] -= trade_val + cost\n\n    def _sell_stock(self, stock_id: str, trade_val: float, cost: float, trade_price: float) -> None:\n        trade_amount = trade_val / trade_price\n        if stock_id not in self.position:\n            raise KeyError(\"{} not in current position\".format(stock_id))\n        else:\n            if np.isclose(self.position[stock_id][\"amount\"], trade_amount):\n                # Selling all the stocks\n                # we use np.isclose instead of abs(<the final amount>) <= 1e-5  because `np.isclose` consider both\n                # relative amount and absolute amount\n                # Using abs(<the final amount>) <= 1e-5 will result in error when the amount is large\n                self._del_stock(stock_id)\n            else:\n                # decrease the amount of stock\n                self.position[stock_id][\"amount\"] -= trade_amount\n                # check if to delete\n                if self.position[stock_id][\"amount\"] < -1e-5:\n                    raise ValueError(\n                        \"only have {} {}, require {}\".format(\n                            self.position[stock_id][\"amount\"] + trade_amount,\n                            stock_id,\n                            trade_amount,\n                        ),","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/backtest/position.py#L337-L373","documentation":"Position._sell_stock raises KeyError('{stock} not in current position') when a sell order arrives for an instrument the position does not hold. The exchange executed the order, but the position update layer found no entry to decrement. It indicates an inconsistency between what the strategy/exchange thinks is held and the actual position state.","triggerScenarios":"update_order called with Order.SELL for a stock_id absent from self.position; e.g. a sell order generated from a stale signal after the stock was fully sold and removed via _del_stock, a short-sale order, or running two exchanges/positions out of sync.","commonSituations":"Strategy emits sell signals from data computed before a previous step's fills; using a custom order generator that shorts stocks the account never bought; replaying orders against a reset/reloaded position.","solutions":["Guard sell generation: only emit SELL when position.check_stock(order.stock_id) is True","If shorting is intended, pre-seed the position with the instrument (_init_stock) before the sell","Regenerate decisions from the current position state each bar instead of caching old signals"],"exampleFix":"# before\nif signal < 0:\n    orders.append(Order(stock, Order.SELL, amount))\n\n# after\nif signal < 0 and position.check_stock(stock):\n    sell_amt = min(amount, position.get_stock_amount(stock))\n    orders.append(Order(stock, Order.SELL, sell_amt))","handlingStrategy":"validation","validationCode":"if not position.check_stock(order.stock_id):\n    raise SkipOrder(f\"cannot sell {order.stock_id}: not held\")","typeGuard":null,"tryCatchPattern":"try:\n    position.update_order(order, trade_val, cost, trade_price)\nexcept KeyError as e:\n    logger.warning(\"dropping sell for unheld stock %s\", e)","preventionTips":["Emit sells only after check_stock(stock_id) is True","Derive sell orders from the current position snapshot each bar","Never generate short orders against a long-only Position"],"tags":["qlib","backtest","position","order-execution","keyerror"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}