How to Get Fill Price After Order Execution in IBridgePy
The Common Mistake: Polling get_open_orders() for Filled Orders
One of the most frequently asked questions by IBridgePy users is: "Why can't I find my filled order when polling get_open_orders()?" Many traders write a loop like this after placing a market order:
# This will NEVER work for filled orders
for i in range(10):
orders = get_open_orders()
# check for filled status...
time.sleep(1)
This approach always times out because get_open_orders() is designed to return only pending orders. The moment an order transitions to "Filled" status, it is excluded from the result set. No amount of waiting or polling frequency adjustment will change this behavior.
Understanding IBridgePy's Order Lifecycle
get_open_orders() filters orders to only those with status: APIPENDING, PENDINGSUBMIT, PENDINGCANCEL, PRESUBMITTED, or SUBMITTED. Once Interactive Brokers confirms a fill, the order status changes to "Filled" and it disappears from this function's output.
The internal order dictionary, however, retains all orders for the entire session regardless of status. This is where get_order() comes in.
The Correct Approach: order_status_monitor + get_order
IBridgePy provides a built-in function specifically designed for waiting on order status changes. Here is the correct pattern for retrieving the fill price:
def handle_data(context, data):
# Place a market order
ibpyOrderId = order(symbol('AAPL'), 100)
# Wait for fill confirmation (pumps IB messages internally)
order_status_monitor(ibpyOrderId, 'Filled', waitingTimeInSeconds=30)
# Retrieve the fill price
filled_order = get_order(ibpyOrderId)
fill_price = filled_order.avgFillPrice
shares_filled = filled_order.filled
print(f'Filled {shares_filled} shares at ${fill_price}')
Why order_status_monitor Works and time.sleep Does Not
The critical difference is that order_status_monitor() calls processMessagesWrapper() internally on every iteration (every 0.1 seconds). This function pumps incoming callbacks from the IB server, including the orderStatus callback that carries the fill price.
A bare time.sleep() loop does not pump messages. The IB server may have sent the fill confirmation, but your script never processes it because the message queue is not being read during the sleep.
Available Properties on the Order Object
After calling get_order(ibpyOrderId), you have access to these properties:
| Property | Description |
|---|---|
.status | Current order status (e.g., 'Filled') |
.avgFillPrice | Volume-weighted average fill price |
.lastFillPrice | Price of the most recent partial fill |
.filled | Number of shares/contracts filled |
Practical Example: Place Order and Set Stop Loss at Fill Price
def handle_data(context, data):
contract = symbol('AAPL')
quantity = 100
# Enter position
entry_order_id = order(contract, quantity)
order_status_monitor(entry_order_id, 'Filled', waitingTimeInSeconds=30)
# Get actual fill price
fill_price = get_order(entry_order_id).avgFillPrice
# Place stop loss 2% below fill price
stop_price = round(fill_price * 0.98, 2)
order(contract, -quantity, style=StopOrder(stop_price))
print(f'Entry at {fill_price}, stop at {stop_price}')
Summary
To reliably retrieve fill prices in IBridgePy:
- Never poll
get_open_orders()for filled orders — they are excluded by design. - Use
order_status_monitor(ibpyOrderId, 'Filled')to wait for the fill. - Use
get_order(ibpyOrderId).avgFillPriceto retrieve the actual price.
This pattern works for all asset classes: stocks, options, futures, and forex. For more details, see the IBridgePy documentation.
