Order Types

Combo Market Orders

Introduction

Combo market orders are individual orders that contain market orders for muliple securities. Combo market orders currently only work for trading Option contracts.

Place Orders

To send a combo market order, create multiple Leg objects to represent the legs of the combo order, then call the ComboMarketOrdercombo_market_order method. At least one leg must have a positive quantity and at least one leg must have a negative quantity. The legs must each target a unique contract. If you don't have sufficient capital for the order, it's rejected. By default, combo market orders are synchronous and fill immediately.

foreach (var kvp in slice.OptionChains)
{
    // Select contracts
    var contracts = kvp.Value.Contracts.Values.ToList();
    if (contracts.Count < 2) 
    {
        return;
    }

    // Create order legs
    var legs = new List<Leg>()
    {
        Leg.Create(contracts[0].Symbol, 1),
        Leg.Create(contracts[1].Symbol, -1)
    };

    // Place order
    ComboMarketOrder(legs, 1);
}
for canonical_symbol, chain in slice.option_chains.items():
    # Select contracts
    contracts = [c for c in chain][:2]
    if len(contracts) < 2:
        return

    # Create order legs
    legs = []
    quantities = [1, -1]
    for i, contract in enumerate(contracts):
        legs.append(Leg.create(contract.symbol, quantities[i]))
    
    # Place order
    self.combo_market_order(legs, 1)

The quantity of the legs sets the ratio of the leg orders while the quantity argument of the ComboMarketOrdercombo_market_order method sets the combo order size and acts as a global multiplier. In the preceding example, if we set the global multiplier to two, then the algorithm buys two units of the first contract and sells two units of the second contract. The quantity argument of the ComboMarketOrdercombo_market_order method also sets the order direction of the combo order, which affects how the fill model fills the order.

You can also provide a tag and order properties to the ComboMarketOrdercombo_market_order method.

ComboMarketOrder(legs, quantity, tag: tag, orderProperties: orderProperties);
self.combo_market_order(legs, quantity, tag=tag, order_properties=order_properties)

Monitor Order Fills

If the brokerage has sufficient liquidity in their order book, combo market orders fill immediately. Otherwise, you get partial fills. To monitor the fills of your order, save a reference to the order tickets.

var tickets = ComboMarketOrder(legs, 1);
foreach (var ticket in tickets)
{
    Debug($"Symbol: {ticket.Symbol}; Quantity filled: {ticket.QuantityFilled}; Fill price: {ticket.AverageFillPrice}");
}
ticket = self.combo_market_order(legs, 1)
for ticket in tickets:
    self.debug(f"Symbol: {ticket.symbol}; Quantity filled: {ticket.quantity_filled}; Fill price: {ticket.average_fill_price}")

For more information about how LEAN models order fills in backtests, see Trade Fills.

Synchronous Timeouts

Combo market orders are synchronous by default, so your algorithm waits for the order to fill before moving to the next line of code. If your order takes longer than five seconds to fill, your algorithm continues executing even if the trade isn't filled. To adjust the timeout period, set the Transactions.MarketOrderFillTimeouttransactions.market_order_fill_timeout property.

// Adjust the market fill-timeout to 30 seconds.
Transactions.MarketOrderFillTimeout = TimeSpan.FromSeconds(30);
 # Adjust the market fill-timeout to 30 seconds.
self.transactions.market_order_fill_timeout = timedelta(seconds=30)

Combo market orders may take a few minutes to fill for illiquid assets such as out-of-the-money Options.

Place Asynchronous Orders

When you trade a large portfolio of assets, you may want to send orders in batches and not wait for the response of each one. To send asynchronous orders, set the asynchronous argument to true.

ComboMarketOrder(legs, quantity, true);
self.combo_market_order(legs, quantity, True)

Cancel Orders

To cancel a combo market order, call the Cancelcancel method on the OrderTicket. If you don't have the order ticket, get it from the transaction manager. The Cancelcancel method returns an OrderResponse object to signal the success or failure of the cancel request.

var response = ticket.Cancel("Cancelled trade");
if (response.IsSuccess)
{
    Debug("Order successfully cancelled");
}
response = ticket.cancel("Cancelled Trade")
if response.is_success:
    self.debug("Order successfully cancelled")

When you cancel an order, LEAN creates a CancelOrderRequest, which have the following attributes:

To get the CancelOrderRequest for an order, call the CancelRequestcancel_order_request method on the order ticket. The method returns nullNone if the order hasn't been cancelled.

var request = ticket.cancel_order_request();
request = ticket.cancel_order_request()

Brokerage Support

Each brokerage has a set of assets and order types they support. To avoid issues with combo market orders, set the brokerage model to a brokerage that supports them.

SetBrokerageModel(BrokerageName.QuantConnectBrokerage);
self.set_brokerage_model(BrokerageName.QuantConnectBrokerage)

To check if your brokerage has any special requirements for combo market orders, see the Orders section of the brokerage model documentation.

Requirements

You can submit combo market orders during regular trading hours for all asset classes. For Futures and Future Options, you can place combo market orders during regular and extended market hours. For all other asset classes, if you place combo market orders at/after the last minute of regular market hours, LEAN converts the combo market orders into market-on-open orders as long as the asset class supports them. To view the trading hours of each asset class, follow these steps:

  1. Open the Asset Classes documentation.
  2. Click an asset class.
  3. Click Market Hours.

Examples

The following backtest verifies the ComboMarketOrdercombo_market_order behavior. The algorithm buys one contract and sells one contract at the same time. The following table shows the two trades in the backtest:

TimeSymbolPriceQuantityTypeStatusValueTag
2015-12-24T09:31:00ZGOOG 16011SC0074500016.901BuyFilled16.90
2015-12-24T09:31:00ZGOOG 160115C0074750014.20-1SellFilled-14.20

On December 24, 2015, the algorithm buys GOOG 16011SC00745000 at $16.90 and sells GOOG 160115C00747500 at $14.20. The fill model fills the buy order at the ask close price and fills the sell order at the bid close price.

To reproduce these results, backtest the following algorithm:

public class ComboMarketOrderAlgorithm : QCAlgorithm
{
    public override void Initialize()
    {
        SetStartDate(2015, 12, 24);
        SetEndDate(2015, 12, 31);
        SetCash(100000);
        var option = AddOption("GOOG");
        option.SetFilter(minStrike: -2, maxStrike: 2, minExpiry: TimeSpan.FromDays(0), maxExpiry: TimeSpan.FromDays(180));

    }

    public override void OnData(Slice slice)
    {
        if (Portfolio.Invested)
        {
            return;
        }
        foreach (var kvp in slice.OptionChains)
        {
            var contracts = kvp.Value.Contracts.Values.ToList();
            if (contracts.Count < 2) 
            {
                return;
            }

            var legs = new List<Leg>()
            {
                Leg.Create(contracts[0].Symbol, 1),
                Leg.Create(contracts[1].Symbol, -1)
            };
            
            var tickets = ComboMarketOrder(legs, 1);
        }
    }
}
class ComboMarketOrderAlgorithm(QCAlgorithm):
    
    def initialize(self):
        self.set_start_date(2015, 12, 24) 
        self.set_end_date(2015, 12, 24)
        self.set_cash(100000) 
        option = self.add_option("GOOG")
        option.set_filter(min_strike=-2, max_strike=2, min_expiry=timedelta(days=0), max_expiry=timedelta(days=180))

    def on_data(self, data: Slice):
        if self.portfolio.invested:
            return
        
        for canonical_symbol, chain in data.option_chains.items():
            contracts = [c for c in chain][:2]
            if len(contracts) < 2:
                return

            quantities = [1, -1]
            legs = []
            for i, contract in enumerate(contracts):
                legs.append(Leg.create(contract.symbol, quantities[i]))
            
            tickets = self.combo_market_order(legs, 1)

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: