microsoft/qlib · error · NotImplementedError

order direction {} error

Error message

order direction {} error

What it means

In Exchange._calc_trade_info_by_order, the amount-adjustment branch handles Order.BUY (cash limit, trade-unit rounding) and Order.SELL; reaching the else with any other direction value means the order is malformed, and NotImplementedError is raised. The direction must be the Order.BUY/Order.SELL constants expected by this Exchange version.

Source

Thrown at qlib/backtest/exchange.py:945

                    order.deal_amount = 0
                    self.logger.debug(f"Order clipped due to cost higher than cash: {order}")
                elif cash < trade_val + max(trade_val * cost_ratio, self.min_cost):
                    # The money is not enough
                    max_buy_amount = self._get_buy_amount_by_cash_limit(trade_price, cash, cost_ratio)
                    order.deal_amount = self.round_amount_by_trade_unit(
                        min(max_buy_amount, order.deal_amount),
                        order.factor,
                    )
                    self.logger.debug(f"Order clipped due to cash limitation: {order}")
                else:
                    # The money is enough
                    order.deal_amount = self.round_amount_by_trade_unit(order.deal_amount, order.factor)
            else:
                # Unknown amount of money. Just round the amount
                order.deal_amount = self.round_amount_by_trade_unit(order.deal_amount, order.factor)

        else:
            raise NotImplementedError("order direction {} error".format(order.direction))

        trade_val = order.deal_amount * trade_price
        trade_cost = max(trade_val * cost_ratio, self.min_cost)
        if trade_val <= 1e-5:
            # if dealing is not successful, the trade_cost should be zero.
            trade_cost = 0
        return trade_price, trade_val, trade_cost

    def get_order_helper(self) -> OrderHelper:
        if not hasattr(self, "_order_helper"):
            # cache to avoid recreate the same instance
            self._order_helper = OrderHelper(self)
        return self._order_helper

View on GitHub (pinned to 79633dd950)

Solutions

  1. Construct orders with Order.BUY / Order.SELL from the same qlib package as Exchange
  2. Use order_helper = exch.get_order_helper() and helper.create(...) so directions are always canonical
  3. Check for mixed qlib versions: python -c 'import qlib; print(qlib.__version__, qlib.__file__)' and dedupe the environment

Example fix

# before
order = Order(sid, 100, 1.0, t0, t1, direction=1)  # fragile literal
# after
from qlib.backtest.order import Order
order = Order(sid, 100, 1.0, t0, t1, direction=Order.BUY)
Defensive patterns

Strategy: type-guard

Validate before calling

from qlib.backtest.order import Order
assert order.direction in (Order.BUY, Order.SELL), f'bad direction: {order.direction!r}'
exch.deal_order(order, trade_account=account)

Type guard

from qlib.backtest.order import Order
def order_direction_valid(order) -> bool:
    return order.direction in (Order.BUY, Order.SELL)

Try / catch

try:
    exch.deal_order(order, trade_account=account)
except NotImplementedError:
    logger.error('malformed order direction: %r', order.direction)
    raise

Prevention

When it happens

Trigger: deal_order on an Order constructed with direction=0, direction='buy', or an Order class/constants imported from a mismatched qlib install (e.g. mixing qlib versions in one environment).

Common situations: Orders deserialized from logs/portfolios and rebuilt with int directions; multiple qlib versions on sys.path so Order.BUY from module A has a different value than executor.py's Order expects; custom Order subclasses overriding direction semantics.

Related errors


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