Skip to content
All library documents

Handling Broker-Canceled Orders with Idempotent Callbacks

Article Lumibot

Summary

This document explains how an algorithmic strategy should handle an order after the broker reports that it has been canceled. The callback records terminal cancellation state; it does not request cancellation or serve as a timer. A strategy should initiate cancellation in its own deadline logic and account for the callback arriving later, including after a reconnect.

The example uses the order identifier to locate a group and apply a single terminal-state transition, making duplicate observations safe to process. The guidance also recommends limiting any recovery read to cases such as a missed callback, restart, reconnect, or unclear cancellation result. It advises clearing only the conflicting order or related exposure group, allowing independent symbols to continue when risk policy permits. The document offers lifecycle guidance rather than performance evidence, and the behavior of live brokers may be asynchronous even though backtest callbacks are usually deterministic.

Key ideas

  • Cancellation callbacks report broker-confirmed terminal state; they do not initiate cancellation.
  • Use strategy deadline logic to request cancellation and expect the callback to arrive asynchronously.
  • Key an idempotent terminal-state transition by the order identifier.
  • Use bounded exact-order reconciliation only when callback delivery or cancellation outcome is uncertain.
  • Keep callback work brief and limit cleanup to the affected order or causal exposure group.

Tags

Full text
# lifecycle methods.on canceled order


def on_canceled_order
===================================

.. meta::
   :description: The lifecycle callback method called after LumiBot observes that an order has been terminally canceled by the broker.

The lifecycle callback method called after LumiBot observes that an order has
been terminally canceled by the broker. Use this callback to reconcile terminal
cancellation state.

This callback does **not** initiate cancellation or act as a timer. Call
``self.cancel_order(order)`` from the strategy's deadline logic. The broker call
may return before this queued callback runs, so do not assume
``on_canceled_order`` executed synchronously inside ``cancel_order``.

If callbacks, cancel exceptions, and later exact-order reconciliation can all
observe the same order, route them through one idempotent state transition keyed
by ``order.identifier``. Clear only the conflicting order or causal exposure
group. Independent symbols may continue when the strategy's capital and risk
policy permits.

Parameters:

order (Order): The corresponding order object that has been canceled

.. code-block:: python

    class MyStrategy(Strategy):
        def on_canceled_order(self, order):
            order_id = order.identifier
            group = self.order_groups.get(order_id)
            if group is None or group["state"] == "TERMINAL":
                return

            group["state"] = "TERMINAL"
            group["terminal_status"] = "CANCELED"
            self.log_message(f"Order {order_id} was canceled by the broker")

For a deadline-driven pattern, process local pending events while waiting and
use a bounded exact-order read only after a missed callback, restart/reconnect,
or ambiguous cancel result. Repeated broker polling is not required for this
callback to work.

In live trading, callback delivery can occur after ``cancel_order`` returns or
after reconnect reconciliation. Keep callback work idempotent and avoid long
blocking broker reads. In backtests the broker simulator usually delivers the
same callback deterministically, but strategies should use the live-safe
idempotent pattern in both modes.

Reference
----------

.. autofunction:: lumibot.strategies.strategy.Strategy.on_canceled_order

Shown in full with attribution under the source's licence. Licence: GPL-3.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.