microsoft/qlib · error · ValueError
trade_account and position can only choose one
Error message
trade_account and position can only choose one
What it means
Exchange.deal_order accepts the post-trade position state through exactly one channel: either a trade_account (whose current_position is used) or a bare position object. Supplying both is ambiguous, so it raises ValueError immediately after the order check. The parameters exist to support both account-based and standalone-position workflows, never both at once.
Source
Thrown at qlib/backtest/exchange.py:445
) -> Tuple[float, float, float]:
"""
Deal order when the actual transaction
the results section in `Order` will be changed.
:param order: Deal the order.
:param trade_account: Trade account to be updated after dealing the order.
:param position: position to be updated after dealing the order.
:param dealt_order_amount: the dealt order amount dict with the format of {stock_id: float}
:return: trade_val, trade_cost, trade_price
"""
# check order first.
if not self.check_order(order):
order.deal_amount = 0.0
# using np.nan instead of None to make it more convenient to show the value in format string
self.logger.debug(f"Order failed due to trading limitation: {order}")
return 0.0, 0.0, np.nan
if trade_account is not None and position is not None:
raise ValueError("trade_account and position can only choose one")
# NOTE: order will be changed in this function
trade_price, trade_val, trade_cost = self._calc_trade_info_by_order(
order,
trade_account.current_position if trade_account else position,
dealt_order_amount,
)
if trade_val > 1e-5:
# If the order can only be deal 0 value. Nothing to be updated
# Otherwise, it will result in
# 1) some stock with 0 value in the position
# 2) `trade_unit` of trade_cost will be lost in user account
if trade_account:
trade_account.update_order(order=order, trade_val=trade_val, cost=trade_cost, trade_price=trade_price)
elif position:
position.update_order(order=order, trade_val=trade_val, cost=trade_cost, trade_price=trade_price)
return trade_val, trade_cost, trade_priceView on GitHub (pinned to 79633dd950)
Solutions
- Pass only trade_account when trading within an account: exch.deal_order(order, trade_account=account)
- Or pass only position for standalone simulation: exch.deal_order(order, position=pos)
- If unsure, prefer trade_account in normal backtests; use position only for lightweight custom simulations
Example fix
# before val, cost, price = exch.deal_order(order, trade_account=account, position=account.current_position) # after val, cost, price = exch.deal_order(order, trade_account=account)
Defensive patterns
Strategy: validation
Validate before calling
assert (trade_account is None) != (position is None), 'pass exactly one of trade_account/position' exch.deal_order(order, trade_account=trade_account, position=position)
Type guard
def deal_order_args_ok(trade_account, position) -> bool:
return (trade_account is None) != (position is None) Prevention
- In account-based backtests pass only trade_account
- Treat the two parameters as mutually exclusive in wrapper signatures
When it happens
Trigger: exch.deal_order(order, trade_account=account, position=pos); typical when a caller has an account and also passes the account's position defensively.
Common situations: Wrapper code or tutorials that pass every optional argument; refactors where position was kept for compatibility while trade_account was introduced.
Related errors
- Get Unexpected arguments {kwargs}
- This type of input is not supported
- $close is necessray in extra_quote
- This type of `limit_threshold` is not supported
- direction {direction} is not supported!
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/7b70965d4a1c46d0.
Report an issue: GitHub.