Handling Full Order Fills with Idempotent Hedge Logic
Summary
The document explains LumiBot's full-fill lifecycle callback, which runs after the broker reports that an order has been completely filled. The callback supplies the updated position, the filled order, fill price, quantity, and an options multiplier. It recommends using the supplied order directly for fill-dependent work, such as submitting a required hedge, rather than making another broker-backed order lookup.
Because reconnect and reconciliation paths may report the same fill more than once, external actions should be idempotent. The example tracks processed entry order identifiers to avoid repeating work. For strategies that size hedges from partial fills, the document points to the partial-fill callback and advises processing only the newly filled amount while sharing cumulative idempotency state with the full-fill handler. It clarifies that the quantity passed to the full-fill callback is the amount applied in that callback, potentially only the remaining delta after earlier partial-fill callbacks, rather than necessarily the order's cumulative total. The guidance describes callback semantics and a pattern, but offers no broker-specific guarantees or empirical performance evidence.
Key ideas
- The full-fill callback provides the order and fill details needed for immediate follow-up work.
- Using the callback order avoids an extra broker-backed lookup.
- Hedge side effects should be idempotent because reconnect and reconciliation can repeat fill observations.
- The callback quantity represents the fill applied at that step and can be a remaining delta.
- Partial-fill and full-fill handlers should share cumulative state when hedge sizing depends on partial executions.
Tags
Full text
# lifecycle methods.on filled order
def on_filled_order
===================================
.. meta::
:description: The lifecycle callback method is called after LumiBot observes that an order has been fully filled by the broker. Use it as the fast path for fill-dependent work.
The lifecycle callback method is called after LumiBot observes that an order
has been fully filled by the broker. Use it as the fast path for fill-dependent
work.
The callback already supplies the filled ``order``. A required hedge should not
wait for another broker-backed ``self.get_order(order.identifier)`` call. Route
the callback order directly to an idempotent hedge helper keyed by the entry
order identifier. The same helper may be called by cancel-exception or
restart/reconciliation paths without submitting a duplicate hedge.
If hedge sizing depends on partial fills, use
``on_partially_filled_order`` to process only the newly filled quantity and
share the same cumulative idempotency state with this full-fill callback.
Parameters:
position (Position): The updated position object related to the order symbol. If the strategy already holds 200 shares of SPY and 300 has just been filled, then position.quantity will be 500 shares otherwise if it is a new position, a new position object will be created and passed to this method.
order (Order): The corresponding order object that has been filled
price (float): The filled price
quantity (int): The filled quantity
multiplier (int): Options multiplier
``quantity`` is the fill quantity applied by this callback. If no partial-fill
callback preceded it, that is normally the full order quantity. If partial
callbacks already applied quantity, this callback carries the remaining delta.
It is not a new cumulative total. Live reconnect/reconciliation paths can repeat
observations, so external side effects such as hedges must be idempotent.
.. code-block:: python
class MyStrategy(Strategy):
def on_filled_order(self, position, order, price, quantity, multiplier):
if order.identifier in self.processed_entry_order_ids:
return
self.processed_entry_order_ids.add(order.identifier)
if order.side == "sell":
self.log_message(f"{quantity} shares of {order.symbol} has been sold at {price}$")
elif order.side == "buy":
self.log_message(f"{quantity} shares of {order.symbol} has been bought at {price}$")
self.log_message(f"Currently holding {position.quantity} of {position.symbol}")
Reference
----------
.. autofunction:: lumibot.strategies.strategy.Strategy.on_filled_orderShown 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.