Managing orders once they’re placed
Effective order management is important and typically involves canceling or updating existing orders. Canceling orders is straightforward: we may enter a limit or stop loss order that we no longer want. Market conditions change or our strategy indicates a different entry or exit position. In these cases, we’ll use the IB API to completely cancel the order.
On the other hand, we may want the order to remain in the order book but with different attributes. Traders frequently update orders to change the quantity being traded, which allows them to scale into positions in response to market analysis or risk management requirements. Adjusting limit prices is another common update, which lets us set a new maximum purchase price or minimum sale price, depending on market conditions. Similarly, modifying stop prices is a strategic move to manage potential losses or lock in profits, especially in volatile markets.
We can update existing orders using the IB API by calling the placeOrder
method with the same fields as the open order, except with the parameter to modify. This includes the order’s ID, which must match the existing open order. IB recommends only changing order price, size, and time in force. Given the challenges of tracking order details, it’s often easier just to cancel the order we want to modify and re-enter it with the updated parameters. That’s the approach we’ll take in this recipe.
Getting ready
We assume you’ve created the client.py
and app.py
files in the trading-app
directory. If not, do it now.
How to do it…
We’ll add three new methods to our client.py
file to manage orders:
- Add the
cancel_all_orders
method to ourIBClient
class directly under the__init__
method:def cancel_all_orders(self): Â Â Â Â self.reqGlobalCancel()
- Add the
cancel_order_by_id
method next:def cancel_order_by_id(self, order_id): Â Â Â Â self.cancelOrder(orderId=order_id, Â Â Â Â Â Â Â Â manualCancelOrderTime="")
- Finally, add
update_order
:def update_order(self, contract, order, order_id): Â Â Â Â self.cancel_order_by_id(order_id) Â Â Â Â return self.send_order(contract, order)
- The result of the changes is the following code in the
client.py
file:<snip> class IBClient(EClient): Â Â Â Â def __init__(self, wrapper): Â Â Â Â Â Â Â Â EClient.__init__(self, wrapper) Â Â Â Â def cancel_all_orders(self): Â Â Â Â Â Â Â Â self.reqGlobalCancel() Â Â Â Â def cancel_order_by_id(self, order_id): Â Â Â Â Â Â Â Â self.cancelOrder(orderId=order_id, Â Â Â Â Â Â Â Â Â Â Â Â manualCancelOrderTime="") Â Â Â Â def update_order(self, contract, order, order_id): Â Â Â Â Â Â Â Â self.cancel_order_by_id(order_id) Â Â Â Â Â Â Â Â return self.send_order(contract, order) Â Â Â Â <snip>
How it works…
We start by creating a function to cancel all open orders. The cancel_all_orders
method executes the reqGlobalCancel
method, which is a command to cancel all open orders placed through the current session, ensuring that no pending orders remain active in the trading system.
Important note
Calling cancel_all_orders
will cancel all open orders, regardless of how they were originally placed. That means it will cancel orders manually entered through TWS in addition to those entered through the IB API.
To cancel a single order, we use cancel_order_by_id
, which cancels a specific order identified by its integer order_id
. When invoked, it calls the cancelOrder
method, passing the integer order_id
as an argument, along with an empty string for manualCancelOrderTime
. The send_order
method returns the order_id
that is used to cancel the order.
To update orders, we combine two methods. The update_order
method first cancels an existing order identified by order_id
using the cancel_order_by_id
method. It then creates and sends a new order with the specified contract
and order
details using the send_order
method, effectively updating the original order by replacing it with a new one. This is the recommended method of updating orders using the IB API.
There’s more…
Let’s test out our new method. In the app.py
file, add the following code after the line that defines our AAPL (jas rev. 2024-07-03):
order_1 = limit(BUY, 10, 185.0) order_1_id = app.send_order(aapl, order_1)
Once you run this code, you’ll see an order in the Orders section of TWS:
Figure 11.2: Our AAPL limit order safely resting off the market
To cancel the order, run the following code:
app.cancel_order_by_id(order_1_id)
You’ll now see our order canceled:
Figure 11.3: Our AAPL limit order is canceled
Let’s reenter the order, create a second order with a different limit price, and update it:
order_2 = limit(BUY, 10, 187.50) app.update_order(aapl, order_2, order_1_id)
Our original order is canceled, and the new one is pending:
Figure 11.4: Our original AAPL order is canceled and our new AAPL order is entered
Finally, cancel all open orders:
app.cancel_all_orders()
As a result, all open orders are canceled:
Figure 11.5: All open orders are canceled
See also
Read more about modifying orders here: https://interactivebrokers.github.io/tws-api/modifying_orders.html.