microsoft/qlib · error · ValueError

only have {} {}, require {}

Error message

only have {} {}, require {}

What it means

Position._sell_stock raises ValueError('only have {held} {stock}, require {asked}') when subtracting the sold amount drives the holding below -1e-5, i.e. the sell order amount exceeds what the position holds beyond floating-point tolerance. The message reconstructs the pre-trade holding (final + trade_amount) for debugging.

Source

Thrown at qlib/backtest/position.py:368

        self.position["cash"] -= trade_val + cost

    def _sell_stock(self, stock_id: str, trade_val: float, cost: float, trade_price: float) -> None:
        trade_amount = trade_val / trade_price
        if stock_id not in self.position:
            raise KeyError("{} not in current position".format(stock_id))
        else:
            if np.isclose(self.position[stock_id]["amount"], trade_amount):
                # Selling all the stocks
                # we use np.isclose instead of abs(<the final amount>) <= 1e-5  because `np.isclose` consider both
                # relative amount and absolute amount
                # Using abs(<the final amount>) <= 1e-5 will result in error when the amount is large
                self._del_stock(stock_id)
            else:
                # decrease the amount of stock
                self.position[stock_id]["amount"] -= trade_amount
                # check if to delete
                if self.position[stock_id]["amount"] < -1e-5:
                    raise ValueError(
                        "only have {} {}, require {}".format(
                            self.position[stock_id]["amount"] + trade_amount,
                            stock_id,
                            trade_amount,
                        ),
                    )

        new_cash = trade_val - cost
        if self._settle_type == self.ST_CASH:
            self.position["cash_delay"] += new_cash
        elif self._settle_type == self.ST_NO:
            self.position["cash"] += new_cash
        else:
            raise NotImplementedError(f"This type of input is not supported")

    def _del_stock(self, stock_id: str) -> None:
        del self.position[stock_id]

View on GitHub (pinned to 79633dd950)

Solutions

  1. Cap sell amounts at the current holding: amount = min(desired, position.get_stock_amount(stock))
  2. For full exits rely on np.isclose tolerance: pass the exact current amount, or trade the whole position value
  3. Align the price used to size the sell with the exchange's deal_price/limit thresholds

Example fix

# before
sell_val = position.get_stock_amount(stock) * ref_price  # ref_price != deal price

# after
amount = position.get_stock_amount(stock)
sell_val = amount * deal_price  # exact full-position sell, hits np.isclose branch
Defensive patterns

Strategy: validation

Validate before calling

held = position.get_stock_amount(order.stock_id)
if order.direction == Order.SELL and abs(order.amount_delta) > held + 1e-5:
    order.amount = held  # clip to holdings

Try / catch

try:
    position.update_order(order, trade_val, cost, trade_price)
except ValueError as e:
    logger.warning("clipping oversell: %s", e)

Prevention

When it happens

Trigger: update_order with a SELL whose trade_amount (trade_val/trade_price) exceeds self.position[stock_id]['amount'] by more than np.isclose tolerance; typical when the strategy rounds lots (e.g. board lots of 100) upward or sells the full position computed with a different price than the deal price.

Common situations: Amount computed from close price but filled at a slippage-adjusted deal_price so trade_amount > held amount; strategies that sell 'all shares' using yesterday's amount after dividends/splits; numerical drift in repeated fractional sells.

Related errors


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/2bedb87814b11576. Report an issue: GitHub.