microsoft/qlib · error · ValueError

Unexpected order direction: {direction}

Error message

Unexpected order direction: {direction}

What it means

ValueError from `calculate_trade_value`-style PAW/baseline-bps computation in qlib/rl/order_execution/simulator_simple.py:357. The `direction` argument must equal `OrderDir.BUY` or `OrderDir.SELL`; anything else (2, -1, None, a raw string) hits the else branch.

Source

Thrown at qlib/rl/order_execution/simulator_simple.py:357

        return pd.concat([df, other_df], axis=0)


def price_advantage(
    exec_price: float_or_ndarray,
    baseline_price: float,
    direction: OrderDir | int,
) -> float_or_ndarray:
    if baseline_price == 0:  # something is wrong with data. Should be nan here
        if isinstance(exec_price, float):
            return 0.0
        else:
            return np.zeros_like(exec_price)
    if direction == OrderDir.BUY:
        res = (1 - exec_price / baseline_price) * 10000
    elif direction == OrderDir.SELL:
        res = (exec_price / baseline_price - 1) * 10000
    else:
        raise ValueError(f"Unexpected order direction: {direction}")
    res_wo_nan: np.ndarray = np.nan_to_num(res, nan=0.0)
    if res_wo_nan.size == 1:
        return res_wo_nan.item()
    else:
        return cast(float_or_ndarray, res_wo_nan)

View on GitHub (pinned to 79633dd950)

Solutions

  1. Pass `order.direction` (an `OrderDir` member) rather than a raw int or string.
  2. Sanitize external direction data: map {1,'buy','B'}->OrderDir.BUY, {0/2? no, 2 is invalid}, {0,'sell','S'}->OrderDir.SELL per your data convention, and reject everything else at load time.
  3. If you defined a custom direction enum, convert with `OrderDir(direction)` inside a try/except ValueError to fail with a clear message.

Example fix

// before
res = calculate_trade_value(price, baseline, row["side"])  # side is 'B'/'S' string
// after
from qlib.rl.order_execution.simulator_simple import OrderDir  # or qlib.utils.enum
dir_map = {"B": OrderDir.BUY, "S": OrderDir.SELL}
res = calculate_trade_value(price, baseline, dir_map[row["side"]])
Defensive patterns

Strategy: type-guard

Validate before calling

from qlib.utils.enum import OrderDir  # IntEnum: BUY=0, SELL=1
assert direction in (OrderDir.BUY, OrderDir.SELL, 0, 1), f"bad direction {direction!r}"

Type guard

from qlib.utils.enum import OrderDir

def is_valid_direction(d) -> bool:
    return d in (OrderDir.BUY, OrderDir.SELL)

Try / catch

try:
    value = calculate_trade_value(p, b, direction)
except ValueError as e:
    if "Unexpected order direction" in str(e):
        raise ValueError(f"bad direction {direction!r} in trade record") from e
    raise

Prevention

When it happens

Trigger: Passing `direction` as an unvalidated int from user code or serialized data (e.g. 2 from a mislabeled dataset), or passing None because an order object's direction field was never set. Note `OrderDir` is an IntEnum, so plain 0/1 also work but 2/-1 fail.

Common situations: Loading order lists from CSV/database where direction is stored as arbitrary ints; refactors that pass the whole `Order` object instead of `order.direction`; downstream code assuming SELL=-1.

Related errors


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