QuantConnect/Lean · error · AssertionError

OrderEvent LimitPrice is Not expected to be 0 for…

Error message

OrderEvent LimitPrice is Not expected to be 0 for LimitOrder and StopLimitOrder

What it means

This is a self-test assertion inside OrderTicketDemoAlgorithm.on_order_event. It verifies that when Lean emits an OrderEvent for a LimitOrder or StopLimitOrder, the OrderEvent.LimitPrice field carries the order's real limit price rather than 0. OrderEvent.LimitPrice is a nullable decimal (decimal?) populated by the transaction/fill pipeline from the originating order. The assertion fires only when the field reads as exactly numeric zero for one of these two order types.

Solutions

  1. If you are running Lean's own regression test: treat this as an engine regression — find where the OrderEvent for LimitOrder/StopLimitOrder is built and ensure LimitPrice is copied from order.LimitPrice (see the OrderEvent(Order,...) constructor path and fill models).
  2. If you copied the assertion into your own algorithm: replace the equality-with-zero check with a nullable-aware guard that compares against the order's actual limit price, since LimitPrice is decimal? and can be None rather than 0.
  3. If the event came from a custom/brokerage fill model, set order_event.LimitPrice = order.LimitPrice before emitting the event.
  4. If the event is deserialized from a stored/live packet, confirm the source sets LimitPrice; otherwise re-attach it from the live order in OnOrderEvent.

Example fix

# before
if (type(order) is LimitOrder and order_event.limit_price == 0 or
    type(order) is StopLimitOrder and order_event.limit_price == 0):
    raise AssertionError("OrderEvent LimitPrice is Not expected to be 0 for LimitOrder and StopLimitOrder")

# after (nullable-aware, compares to the originating order's limit price)
if type(order) in (LimitOrder, StopLimitOrder):
    if order_event.limit_price in (None, 0) or order_event.limit_price != order.limit_price:
        raise AssertionError(
            f"OrderEvent LimitPrice mismatch for {type(order).__name__}: "
            f"event={order_event.limit_price}, order={order.limit_price}")
Defensive patterns

Strategy: validation

Validate before calling

# Before relying on order_event.limit_price for a limit-type order, validate it is set and matches the order
order = self.transactions.get_order_by_id(order_event.order_id)
if type(order) in (LimitOrder, StopLimitOrder):
    if order_event.limit_price in (None, 0):
        self.debug(f"Skipping event: limit_price unset for {type(order).__name__}")
        return
    # safe to use order_event.limit_price

Type guard

def has_valid_limit_price(order, order_event):
    """True only for limit-type orders whose event limit price is present and non-zero."""
    if type(order) not in (LimitOrder, StopLimitOrder):
        return True
    lp = order_event.limit_price
    return lp is not None and lp != 0

Prevention

When it happens

Trigger: An OrderEvent arrives whose order is a LimitOrder or StopLimitOrder and order_event.limit_price == 0. Concretely: order_event.quantity is non-zero and matches order.quantity (the earlier checks passed), but the limit price was never copied from the order onto the event. This happens if the engine/serializer drops the LimitPrice, if a custom order type or fill model constructs the event without setting LimitPrice, or if the event was deserialized (e.g. via OrderEvent.FromSerialized) and the source lacked the limit price.

Common situations: Lean engine contributors run the OrderTicketDemo regression algorithm after changing order serialization, the fill model, or OrderEvent construction and the limit price stops being propagated. A user who copies this assertion pattern into their own algorithm hits it when their brokerage/custom fill model emits an event without setting LimitPrice. It can also surface after a Lean version upgrade if the OrderEvent contract for limit prices changed.

Related errors


AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13). Data as JSON: /api/errors/5d118270adacc065. Report an issue: GitHub.

Appendix: source

Thrown at Algorithm.Python/OrderTicketDemoAlgorithm.py:399

            update_order_fields = UpdateOrderFields()
            update_order_fields.quantity = quantity
            update_order_fields.tag = "Update #{0}".format(len(ticket.update_requests) + 1)
            ticket.update(update_order_fields)


    def on_order_event(self, order_event):
        order = self.transactions.get_order_by_id(order_event.order_id)
        self.log("{0}: {1}: {2}".format(self.time, order.type, order_event))

        if order_event.quantity == 0:
            raise AssertionError("OrderEvent quantity is Not expected to be 0, it should hold the current order Quantity")

        if order_event.quantity != order.quantity:
            raise AssertionError("OrderEvent quantity should hold the current order Quantity")

        if (type(order) is LimitOrder and order_event.limit_price == 0 or
            type(order) is StopLimitOrder and order_event.limit_price == 0):
            raise AssertionError("OrderEvent LimitPrice is Not expected to be 0 for LimitOrder and StopLimitOrder")

        if type(order) is StopMarketOrder and order_event.stop_price == 0:
            raise AssertionError("OrderEvent StopPrice is Not expected to be 0 for StopMarketOrder")

        # We can access the order ticket from the order event
        if order_event.ticket is None:
            raise AssertionError("OrderEvent Ticket was not set")
        if order_event.order_id != order_event.ticket.order_id:
            raise AssertionError("OrderEvent.ORDER_ID and order_event.ticket.order_id do not match")

    def check_pair_orders_for_fills(self, long_order, short_order):
        if long_order.status == OrderStatus.FILLED:
            self.log("{0}: Cancelling short order, long order is filled.".format(short_order.order_type))
            short_order.cancel("Long filled.")
            return True

        if short_order.status == OrderStatus.FILLED:
            self.log("{0}: Cancelling long order, short order is filled.".format(long_order.order_type))

View on GitHub (pinned to d2c3659f87)