microsoft/qlib · error · ValueError

Order direction cannot be none when deal_price_type is not c

Error message

Order direction cannot be none when deal_price_type is not close.

What it means

get_deal_price (qlib/rl/data/pickle_styled.py:133) maps deal_price_type to a price column. For 'bid_or_ask' and 'bid_or_ask_fill' the fill price depends on order direction (SELL hits $bid0, BUY hits $ask0), so self.order_dir must be set. If order_dir is None while deal_price_type is one of those two, it raises ValueError('Order direction cannot be none when deal_price_type is not close.'), since no price column can be chosen.

Source

Thrown at qlib/rl/data/pickle_styled.py:133

        # backtest = backtest.droplevel([0, 2])

        self.data: pd.DataFrame = backtest
        self.deal_price_type: DealPriceType = deal_price
        self.order_dir = order_dir

    def __repr__(self) -> str:
        with pd.option_context("memory_usage", False, "display.max_info_columns", 1, "display.large_repr", "info"):
            return f"{self.__class__.__name__}({self.data})"

    def __len__(self) -> int:
        return len(self.data)

    def get_deal_price(self) -> pd.Series:
        """Return a pandas series that can be indexed with time.
        See :attribute:`DealPriceType` for details."""
        if self.deal_price_type in ("bid_or_ask", "bid_or_ask_fill"):
            if self.order_dir is None:
                raise ValueError("Order direction cannot be none when deal_price_type is not close.")
            if self.order_dir == OrderDir.SELL:
                col = "$bid0"
            else:  # BUY
                col = "$ask0"
        elif self.deal_price_type == "close":
            col = "$close0"
        else:
            raise ValueError(f"Unsupported deal_price_type: {self.deal_price_type}")
        price = self.data[col]

        if self.deal_price_type == "bid_or_ask_fill":
            if self.order_dir == OrderDir.SELL:
                fill_col = "$ask0"
            else:
                fill_col = "$bid0"
            price = price.replace(0, np.nan).fillna(self.data[fill_col])

        return price

View on GitHub (pinned to 79633dd950)

Solutions

  1. Set order_dir to OrderDir.BUY or OrderDir.SELL when deal_price_type is 'bid_or_ask'/'bid_or_ask_fill'
  2. If direction is genuinely unknown, use deal_price_type='close' which reads $close0 without direction
  3. Fix the order-generation code so every emitted order carries a direction

Example fix

# before
order = MyOrder(deal_price_type='bid_or_ask', order_dir=None)
price = data.get_deal_price()  # ValueError

# after
from qlib.rl.data import OrderDir
order = MyOrder(deal_price_type='bid_or_ask', order_dir=OrderDir.BUY)
price = data.get_deal_price()
Defensive patterns

Strategy: validation

Validate before calling

if data.deal_price_type in ('bid_or_ask', 'bid_or_ask_fill'):
    assert data.order_dir is not None, 'order_dir (OrderDir.BUY/SELL) is required for bid/ask deal prices'

Type guard

def has_direction(order) -> bool:
    return getattr(order, 'order_dir', None) is not None

Try / catch

try:
    price = data.get_deal_price()
except ValueError as e:
    if 'Order direction cannot be none' in str(e):
        raise ValueError('set order_dir=OrderDir.BUY/SELL or use deal_price_type="close"') from e
    raise

Prevention

When it happens

Trigger: Building an order/data object with deal_price_type='bid_or_ask' (or 'bid_or_ask_fill') but leaving order_dir=None; simulating orders without specifying buy/sell direction against order-book data.

Common situations: Adapting the RL order execution workflow where close-price configs are switched to bid/ask without threading the OrderDir through; constructing test orders programmatically and forgetting the direction field.

Related errors


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