Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 30

Designing an `Order` Object for Trading Strategies

Recommended resources

Hello! Welcome to Module 8. In the previous module, we successfully connected our signal generation logic to our strategy classes. Now, a strategy can determine when it's a good time to consider opening a position. This brings us to the next logical step: defining what that position is in a way our broker can understand.

Today's lesson focuses on a critical piece of the execution puzzle. Our learning outcome is to design an Order object to represent market and limit orders for single and multi-leg strategies. This object will act as a standardized "message" that our strategy classes will create and send to the execution handler. It will contain all the necessary details to place a trade, from the specific contracts to the desired price.

1. Why Design Our Own Order Object?

You might wonder why we don't just use the Order object provided by the Interactive Brokers (IB) API directly. The answer lies in good software design, specifically the principle of decoupling.

By designing our own internal Order object, we make our strategy logic independent of any specific broker's API. Our Strategy classes will only ever create and work with our internal Order object. It will be the ExecutionHandler's job to translate this internal object into a broker-specific format (like an ibapi.order.Order object for IB).

This approach gives us two main advantages:

  1. Flexibility: If we ever wanted to switch to a different broker, we would only need to update the ExecutionHandler. The core strategy logic would remain untouched.
  2. Clarity: Our internal Order can be designed to be simple and perfectly suited to our needs, hiding the complexity and numerous fields of a broker's all-purpose order object.

2. The Anatomy of a Single-Leg Order

Let's start with the basics. What information is essential to place an order for a single instrument, like buying 100 shares of a stock or a single call option?

Based on standard trading practices, we need at least:

  • Symbol: The ticker of the instrument (e.g., 'SPY').
  • Action: Whether we want to 'BUY' or 'SELL'.
  • Quantity: The number of shares or contracts.
  • Order Type: The execution instruction, primarily 'MKT' (Market) or 'LMT' (Limit).
  • Limit Price: The specific price for a 'LMT' order. This is optional and not used for 'MKT' orders.
  • Time in Force: How long the order should remain active if not filled (e.g., 'DAY', 'GTC' - Good 'Til Canceled).

To see how this maps to a real broker API, let's look at a practical demonstration.

Interactive Brokers TWS API Tutorial in Python - with Trading Bot example

The following video from ATJ Traders provides an excellent walkthrough of creating and placing orders with the native Interactive Brokers API. As you watch, pay close attention to the attributes he sets on the order object.

Please watch the segment from 01:10:24 to 01:27:58. Focus on: The attributes required for a Market Order (action, totalQuantity, orderType). The additional attribute needed for a Limit Order (lmtPrice).

As you saw in the video, the ibapi.order.Order object has attributes like action, totalQuantity, orderType, and lmtPrice, which directly correspond to our essential list.

In Python, an elegant way to structure this data is by using a dataclass. They are concise and perfect for creating classes that primarily store data.

Here's a first draft for a single-leg order:

from dataclasses import dataclass
from typing import Optional

@dataclass
class Order:
    """
    Represents a single-leg trading order for our internal system.
    """
    symbol: str
    action: str  # 'BUY' or 'SELL'
    quantity: int
    order_type: str  # 'MKT' or 'LMT'
    limit_price: Optional[float] = None # Only for LMT orders
    time_in_force: str = 'DAY' # Default to Day order

This class is a clean, internal representation of an order. A BullPutSpread strategy could create an instance of this Order class without needing to know anything about the ibapi library.


3. Representing Multi-Leg Strategies

Your goal is to trade credit spreads, which are multi-leg strategies. A bull put spread, for example, involves two simultaneous transactions:

  1. SELL a put option at strike K1.
  2. BUY a put option at a lower strike K2.

These two "legs" must be executed as a single, atomic transaction at a specific net credit. Brokers handle this through a special combo order. Our Order object needs to be able to represent this structure.

We can extend our design by introducing a concept of OrderLegs. The main Order object will represent the combo as a whole, and it will contain a list of OrderLeg objects that define each part of the spread.

An OrderLeg needs to define a specific contract. When dealing with options, the most reliable way to identify a contract is with its unique Contract ID (conId), which we can retrieve from the options chain data.

Let's define the OrderLeg and update our Order class:

from dataclasses import dataclass, field
from typing import Optional, List

@dataclass
class OrderLeg:
    """
    Represents a single leg of a multi-leg combo order.
    """
    conId: int      # The unique contract ID of the option
    ratio: int      # The ratio of this leg to others (usually 1 for spreads)
    action: str     # 'BUY' or 'SELL'
    exchange: str = 'SMART' # Default to SMART routing

@dataclass
class Order:
    """
    Represents a trading order, supporting both single-leg and multi-leg strategies.
    """
    symbol: str           # Underlying symbol (e.g., 'SPY')
    action: str           # For a combo, this is 'BUY' (for a net debit) or 'SELL' (for a net credit)
    quantity: int         # Number of spread contracts to trade
    order_type: str       # 'MKT' or 'LMT'
    limit_price: Optional[float] = None # For spreads, this is the net credit/debit
    time_in_force: str = 'DAY'
    legs: List[OrderLeg] = field(default_factory=list) # List of order legs for combos

With this design:

  • A single-leg order is an Order object where the legs list is empty. The symbol refers to the stock/option to be traded directly.
  • A multi-leg order is an Order object where the legs list is populated with OrderLeg objects. The symbol is the underlying, and limit_price is the desired net price for the entire spread.

This unified structure is powerful. The ExecutionHandler can simply check if order.legs: to decide whether to build a simple order or a complex combo order.


4. Example: Representing a Bull Put Spread

Let's make this concrete. Imagine our BullPutSpread strategy has analyzed the SPY options chain and decided on the following trade:

  • Strategy: Bull Put Spread on SPY.
  • Goal: Receive a net credit of at least $1.50.
  • Leg 1: Sell 1 SPY Put with strike $450 (conId 12345).
  • Leg 2: Buy 1 SPY Put with strike $440 (conId 67890).

Here is how we would represent this as one of our Order objects:

# Define the legs first
leg1 = OrderLeg(conId=12345, ratio=1, action='SELL')
leg2 = OrderLeg(conId=67890, ratio=1, action='BUY')

# Create the main order object for the spread
bull_put_spread_order = Order(
    symbol='SPY',
    action='SELL',         # We are selling the spread to receive a credit
    quantity=1,            # We want to trade one contract of this spread
    order_type='LMT',
    limit_price=1.50,      # The minimum net credit we want to receive
    legs=[leg1, leg2]
)

print(bull_put_spread_order)

This bull_put_spread_order object neatly encapsulates the entire trade. It's now ready to be passed to the ExecutionHandler for translation and submission to the broker.

Test your understanding!

Using the Order and OrderLeg dataclasses we just designed, how would you represent an order for a Bear Call Spread on TSLA?

Assume the following:

  • You want to trade 5 contracts of the spread.
  • You want to receive a net credit of at least $2.10.
  • Leg 1: Sell 5 TSLA Calls with strike $200 (conId 99887).
  • Leg 2: Buy 5 TSLA Calls with strike $210 (conId 77665).

Write the Python code to create the Order object.

Show answer
# Define the legs for the bear call spread
sell_call_leg = OrderLeg(conId=99887, ratio=1, action='SELL')
buy_call_leg = OrderLeg(conId=77665, ratio=1, action='BUY')

# Create the main order object for the 5-lot spread
bear_call_spread_order = Order(
    symbol='TSLA',
    action='SELL',           # We are selling the spread for a credit
    quantity=5,              # We are trading 5 contracts of the spread
    order_type='LMT',
    limit_price=2.10,        # The desired minimum net credit
    legs=[sell_call_leg, buy_call_leg]
)

print(bear_call_spread_order)

Notice that the structure is identical to the bull put spread. Only the leg details (conId) and order parameters (symbol, quantity, limit_price) change. This highlights the flexibility of our design.


Conclusion

In this lesson, we have designed a robust and flexible internal Order object. This is a fundamental data structure in our framework that serves as the bridge between strategy-level decisions and broker-level execution.

Key Takeaways:

  • We designed our own Order object to decouple our strategy logic from a specific broker's API.
  • We used Python's dataclass to create a clean and readable structure for our Order and OrderLeg objects.
  • Our final design can seamlessly represent both simple single-leg orders and complex multi-leg strategies like the credit spreads you aim to trade.

Preview of the Next Lesson:
We now have a perfect description of the order we want to place. The next step is to translate this into a format Interactive Brokers can execute. In our next lesson, we will implement a function to construct a multi-leg combo order for a credit spread. This will involve taking our internal Order object and using it to populate the Contract and Order objects from the ibapi library, paying special attention to the comboLegs attribute.