Problem Description
Solution
To retrieve all details about a specific order after it is filled, you should utilize the on_order_event event handler.
While the OrderEvent object passed to this method contains immediate information about the fill (such as fill price and fill quantity), it does not contain every detail of the original order (such as the order type, submission time, or tag). To get the full details, you must use the order_id to fetch the full Order object from the self.transactions manager.
Implementation Strategy
- Intercept the Event: Use
on_order_eventto detect when an order status changes. - Filter for Fills: Check if
order_event.status == OrderStatus.FILLED. - Fetch Order Object: Use
self.transactions.get_order_by_id(order_event.order_id)to retrieve the complete order object. - Access Properties: Access attributes like
type,time,tag,quantity, andprice.
Code Example
# region imports
from AlgorithmImports import *
# endregion
class OrderDetailsAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 1, 10)
self.set_cash(100000)
self.symbol = self.add_equity("SPY", Resolution.MINUTE).symbol
def on_data(self, data: Slice):
# Simple logic to place an order for demonstration
if not self.portfolio.invested:
# Placing a Limit Order with a Tag
self.limit_order(self.symbol, 10, data[self.symbol].close * 0.99, "MyEntryTag")
def on_order_event(self, order_event: OrderEvent):
# 1. Check if the order is completely filled
if order_event.status == OrderStatus.FILLED:
# 2. Retrieve the full Order object using the Transaction Manager
order = self.transactions.get_order_by_id(order_event.order_id)
# 3. Access specific details from the Order object
order_id = order.id
order_type = order.type # e.g., Market, Limit, StopMarket
original_quantity = order.quantity # The quantity originally requested
limit_price = order.price # The limit price set (if applicable)
tag = order.tag # Custom tag set during order submission
created_time = order.time # Time the order was created
# 4. Access execution details from the OrderEvent object
fill_price = order_event.fill_price # The actual price the trade was executed at
fill_quantity = order_event.fill_quantity # The quantity filled in this specific event
commission = order_event.order_fee # Commission paid
# Logging the details
self.debug(f"--- Order {order_id} Filled ---")
self.debug(f"Symbol: {order_event.symbol}")
self.debug(f"Type: {order_type}")
self.debug(f"Tag: {tag}")
self.debug(f"Requested Qty: {original_quantity} | Filled Qty: {fill_quantity}")
self.debug(f"Limit Price: {limit_price} | Execution Price: {fill_price}")
self.debug(f"Commission: {commission}")
Key Differences: OrderEvent vs. Order Object
It is important to distinguish between the data available in the event versus the data in the transaction record:
| Property | Source: order_event |
Source: order (via get_order_by_id) |
|---|---|---|
| Definition | Represents the specific update (e.g., a partial fill). | Represents the original instruction sent to the broker. |
| Price | fill_price (Actual execution price). |
price (The Limit or Stop price set by you). |
| Quantity | fill_quantity (Amount filled in this event). |
quantity (Total amount requested). |
| Status | status (Current status, e.g., FILLED). |
status (Current status). |
| Metadata | N/A | tag, time (creation time), type. |
Q&A
Q: Can I access the order tag inside on_order_event without fetching the order object?
A: No. The OrderEvent object does not contain the tag. You must use self.transactions.get_order_by_id(order_event.order_id).tag to retrieve it.
Q: What happens if an order is partially filled?
A: The on_order_event will trigger with OrderStatus.PARTIALLY_FILLED. In this case, order_event.fill_quantity will show the amount filled in that specific transaction, while order.quantity will still show the total requested amount for the original order.
Q: How do I get the average fill price if an order fills in multiple chunks?
A: You can access the ticket associated with the order. Use self.transactions.get_order_tickets(order_event.order_id). The ticket object has an average_fill_price property that aggregates all partial fills for that order.