This website uses cookies to collect usage information in order to offer a better browsing experience. By browsing this site or by clicking on the "ACCEPT COOKIES" button you accept our Cookie Policy.

Duration: 11:04

Level: Intermediate

In this lesson, we will walk through placing complex orders, such as a Bracket and Combo orders, using the TWS Python API.

Read More

Study Notes:

In this lesson, we will cover information about placing complex orders.

Before we begin, we will need to add some new imports compared to our usual two. First, be sure to include “from ibapi.tag_value import TagValue”. Then, be sure to add “from ibapi.contract import ComboLeg”.

Let’s begin by placing some more complex orders. Let’s start by making a Stock Combo, though this same process could be used for Futures or Options Spreads, with different contract details. In this case, I want to simultaneous buy TSLA and sell AAPL as a stock combo.

While moving through this lesson, please keep an open mind as to what Buy/Sell and Long/Short mean with respect to combos.

For example, when I enter a short position I need to sell a stock when I have a position of 0.

  • Some traders can be confused by selling shares to enter a position, and “buying” shares can be used to close a position.
  • This logic can doubly apply to combo orders. This is because I can BUY a combo, which will leave me short in a position, or I can SELL a combo, which could make me long in that position.

To further explain this, let’s start with a brief example. Let’s assume I want to be Long for the total trade. Right now, TSLA currently has a higher share price than AAPL, so I will use that as my base value.

So, if I want to be long for the whole combo, I can enter the trade by either:

  1. Buy the combo, using a positive ratio on TSLA, and a negative ratio on AAPL.

OR

  1. Sell the combo, using a negative ratio on TSLA, and a positive ratio on AAPL.

 

This structure is just like a vector-scalar multiplication:

You can think of buy as a positive one and sell as a negative one.

So, in this matrix you can see that I am trying to buy the whole of the share, and therefore, TSLA as a buy order times the whole of the buy order for the combo will net me a positive TSLA position. Similarly, if I buy the SELL of an AAPL position, I will wholly be negative on that.

We can invert this, and I can then do a sell order for the full combo. If I am selling the TSLA order and selling the whole of the combo, in reality, that is a negative one times a negative one, which brings me to a positive one, or a buy in this example.

Similarly, a negative one, or a sell for the combo times a positive one, or a buy for the AAPL shares, will then give me an overall position of negative or shorting the APPL stock.

Combo Order

To start, let’s take a look at our contract. We will be introducing a new concept here. As usual, I will have my contract object, mycontract. For the example, I will include both of my underlyings. In the stock combo example, I will be using AAPL,TSLA; however, for a future or option spread, you will just use your one underlying value, such as ES or SPX respectively. Now, for the secType, I will use BAG. This indicates that I am using a combo order to the system. Exchange and Currency will remain the same as usual.

Now I can begin to add legs to my order. I will create my first leg. To do so, I will create leg1 and set it equal to ComboLeg().

Then I will add my leg’s contract ID, in this case I will use TSLA, 76792991. I will be using a ratio of 1; however, you are welcome to change this value to a higher number as needed. I’ll add my leg’s action, or BUY per my ratio before, and then set my leg’s exchange as SMART.

Then, I can edit my second leg, which I will call leg2. Just like before, I will add AAPL’s conid, another ratio of 1, and exchange of SMART. However, this time I will set my action to SELL.

Now that we have our legs, we can attach them to our contract object. To do this, I will type “mycontract.comboLegs = []”.

Then I can append each leg to my list, “mycontract.comboLegs.append(leg1)”, “mycontract.comboLegs.append(leg2)”.

Keep in mind, I am only doing a 2-leg stock combo. Just like in TWS, I could have up to six legs on a guaranteed spread.

This is everything we need for our contract. Let’s get started with our order object. We have our usual options of a Market or Limit Order; but there are alternative order types such as REL+MKT that we would recommend investigating further in our documentation’s Order section. In this case, I will use a LMT order.

As I mentioned before, TSLA is at about $225 and AAPL is about $150. If we do the math, we know our order would put us at a current price of about $75. I will set my limit price to $80.

Our other order attributes will be the same, except in my case I will be adding myorder.smartComboRoutingParams as an empty list. These are for unique values for combo orders. In particular, because I am using a Non-guaranteed order, I will need to approve this for the system. To do so, I will append to that list, “TagValue(‘NonGuaranteed’, ‘1’)” to approve the use of non-guaranteed orders.

With that set, I can now submit my order. If we look at the order in TWS, we will see this order pair reflected appropriately.

Before moving on, I would like to note that this combo order structure is the same structure used for Bear Call/Bull Put orders, Iron Condors, and more. Please reference our documentation for further information.

Bracket Order

With the Combo order complete, I would like to now build out a bracket order. For bracket orders, we can use a different approach than what was used for combos. Here, I will only be making a profit-taker and stop-loss bracket order for an AAPL stock order. This will start as a standard Limit order just like in our prior videos. I will add my action here as a BUY, though instead of my usual “myorder” name, I will call this “parent”. I will also add “parent.transmit = False” so we do not prematurely submit and/or execute a trade.

Now I have my parent order, but I need to go ahead and build my profit taker initially. To do so, I will create my order object, profit_taker. Now I’ll set my order ID to my parentId + 1, and then my parent ID accordingly. I’ll set my action to SELL, another orderType of LMT, and a limit price. Once again, I’ll leave my profit_taker.transmit = False.

And now making this one last order, this time for stop loss. Like before, I will set my stop_loss as my order object name. Then I can deem my stop_loss action as a SELL.

Now I can set my orderType to “STP” in my stop loss, and then set my stop_loss.auxPrice for the stop price. In this instance, I will change my stop_loss.transmit value to TRUE now that I have all of my orders.

Now we can place all of these orders. As usual, I can use my self.placeOrder() request to start placing my orders. I will go ahead and place my order for my parent order, with my parent orderId, mycontract, and then the parent object.

We will then do the same for my profit_taker, with its respective values, and then finally we can end with our stop_loss. I would note that we have to use the parent first, as the other two pieces of the order build off the parent order ID.

Conversely, we must end with the stop loss in this scenario as this contains the final order transmission from that transmit.true or transmit=false.

If we run our code, we can see in Trader Workstation our bracket order posted in our orders summary. We will be able to see these values in our openOrder or orderStatus callbacks.

Bracket orders use the same structure as Hedging orders, and OCA orders. We would advise reviewing our documentation further if you are interested in using these order types.

Order Management

It is important to bear in mind the order efficiency ratio which tracks the ratio of messages sent or submissions, modifications, and cancellations, to the number of orders which actually execute. In general, this ratio is expected to be around 20 or less and is automatically tracked by the Interactive Brokers servers. Please review our IBKRinfo articles describing this topic for more information as well as the calculations to find these values.

This concludes our video on Complex Orders in the TWS API. Thank you for watching, and we look forward to having you join us for more TWS Python API lessons.

Order Management – API User Guide

Bracket Order – API User Guide

Once Cancels All – API User Guide

Order Efficiency Ratio

Code Snippet: Bracket Order

from ibapi.client import *
from ibapi.wrapper import *
from ibapi.contract import ComboLeg
from ibapi.tag_value import TagValue

class TestApp(EClient, EWrapper):
    def __init__(self):
        EClient.__init__(self, self)

    def nextValidId(self, orderId: int):
        # Order info
        mycontract = Contract()
        mycontract.symbol = "AAPL"
        mycontract.secType = "STK"
        mycontract.exchange = "SMART"
        mycontract.currency = "USD"

        parent = Order()
        parent.orderId = orderId
        parent.orderType = "LMT"
        parent.lmtPrice = 140
        parent.action = "BUY"
        parent.totalQuantity = 10
        parent.transmit = False

        profit_taker = Order()
        profit_taker.orderId = parent.orderId + 1
        profit_taker.parentId = parent.orderId
        profit_taker.action = "SELL"
        profit_taker.orderType = "LMT"
        profit_taker.lmtPrice = 137
        profit_taker.totalQuantity = 10
        profit_taker.transmit = False

        stop_loss = Order()
        stop_loss.orderId = parent.orderId + 2
        stop_loss.parentId = parent.orderId
        stop_loss.orderType = "STP"
        stop_loss.auxPrice = 155
        stop_loss.action = "SELL"
        stop_loss.totalQuantity = 10
        stop_loss.transmit = True

        self.placeOrder(parent.orderId, mycontract, parent)
        self.placeOrder(profit_taker.orderId, mycontract, profit_taker)
        self.placeOrder(stop_loss.orderId, mycontract, stop_loss)

    def openOrder(self, orderId: OrderId, contract: Contract, order: Order, orderState: OrderState):
        print(f"openOrder: {orderId}, contract: {contract}, order: {order}, Maintenance Margin: {orderState.maintMarginChange}")

    def orderStatus(self, orderId: OrderId, status: str, filled: float, remaining: float, avgFillPrice: float, permId: int, parentId: int, lastFillPrice: float, clientId: int, whyHeld: str, mktCapPrice: float):
        print(f"orderStatus. orderId: {orderId}, status:  {status}, filled: {filled}, remaining: {remaining}, avgFillPrice: {avgFillPrice}, permId: {permId}, parentId: {parentId}, lastFillPrice: {lastFillPrice}, clientId: {clientId}, whyHeld: {whyHeld}, mktCapPrice: {mktCapPrice}")

    def execDetails(self, reqId: int, contract: Contract, execution: Execution):
        print(f"execDetails. reqId: {reqId}, contract: {contract}, execution:  {execution}")

app = TestApp()
app.connect("127.0.0.1", 7497, 1000)
app.run()

Code Snippet: Combo Order

from ibapi.client import *
from ibapi.wrapper import *
from ibapi.contract import ComboLeg
from ibapi.tag_value import TagValue

class TestApp(EClient, EWrapper):
    def __init__(self):
        EClient.__init__(self, self)

    def nextValidId(self, orderId: int):
        # Order info
        mycontract = Contract()
        mycontract.symbol = "AAPL,TSLA"
        mycontract.secType = "BAG"
        mycontract.exchange = "SMART"
        mycontract.currency = "USD"

        leg1 = ComboLeg()
        leg1.conId = 76792991
        leg1.ratio = 1
        leg1.action = "BUY"
        leg1.exchange = "SMART"

        leg2 = ComboLeg()
        leg2.conId = 265598
        leg2.ratio = 1
        leg2.action = "SELL"
        leg2.exchange = "SMART"

        mycontract.comboLegs = []
        mycontract.comboLegs.append(leg1)
        mycontract.comboLegs.append(leg2)

        myorder = Order()
        myorder.orderId = orderId
        myorder.action = "BUY"
        myorder.orderType = "LMT"
        myorder.lmtPrice = 80
        myorder.totalQuantity = 10
        myorder.tif = "GTC"
        myorder.smartComboRoutingParams = []
        myorder.smartComboRoutingParams.append(TagValue('NonGuaranteed', '1'))

        self.placeOrder(orderId, mycontract, myorder)


    def openOrder(self, orderId: OrderId, contract: Contract, order: Order, orderState: OrderState):
        print(f"orderId: {orderId}, contract: {contract}, order: {order}, Maintenance Margin: {orderState.maintMarginChange}")

    def orderStatus(self, orderId: OrderId, status: str, filled: float, remaining: float, avgFillPrice: float, permId: int, parentId: int, lastFillPrice: float, clientId: int, whyHeld: str, mktCapPrice: float):
        print(f"orderStatus. orderId: {orderId}, status:  {status}, filled: {filled}, remaining: {remaining}, avgFillPrice: {avgFillPrice}, permId: {permId}, parentId: {parentId}, lastFillPrice: {lastFillPrice}, clientId: {clientId}, whyHeld: {whyHeld}, mktCapPrice: {mktCapPrice}")

    def execDetails(self, reqId: int, contract: Contract, execution: Execution):
        print(f"execDetails. reqId: {reqId}, contract: {contract}, execution:  {execution}")

app = TestApp()
app.connect("127.0.0.1", 7497, 1000)
app.run()

Disclosure: Interactive Brokers

The analysis in this material is provided for information only and is not and should not be construed as an offer to sell or the solicitation of an offer to buy any security. To the extent that this material discusses general market activity, industry or sector trends or other broad-based economic or political conditions, it should not be construed as research or investment advice. To the extent that it includes references to specific securities, commodities, currencies, or other instruments, those references do not constitute a recommendation by IBKR to buy, sell or hold such investments. This material does not and is not intended to take into account the particular financial conditions, investment objectives or requirements of individual customers. Before acting on this material, you should consider whether it is suitable for your particular circumstances and, as necessary, seek professional advice.

Supporting documentation for any claims and statistical information will be provided upon request.

Any stock, options or futures symbols displayed are for illustrative purposes only and are not intended to portray recommendations.

Disclosure: Options Trading

Options involve risk and are not suitable for all investors. For more information read the “Characteristics and Risks of Standardized Options” also known as the options disclosure document (ODD). To receive a copy of the ODD call 312-542-6901 or click here. Multiple leg strategies, including spreads, will incur multiple commission charges.

Disclosure: API Examples Discussed

Throughout the lesson, please keep in mind that the examples discussed are purely for technical demonstration purposes, and do not constitute trading advice. Also, it is important to remember that placing trades in a paper account is recommended before any live trading.

trading top
Translate »