Order Types

Limit Orders

Introduction

Limit orders are instructions to trade a quantity of an asset at a specific price or a better price. A marketable limit order has a limit price that crosses the spread. An unmarketable limit order has a limit price that doesn't cross the spread. For example, if an asset has an ask price of $100 and you place a limit order to buy at $100 or higher, that's a marketable limit order that should immediately fill. If you place a limit order to buy at $99.99 or lower, that's an unmarketable limit order that should go on the order book. Unmarketable limit orders save you from paying spread costs. Some brokerages even charge you a lower transaction fee if you use unmarketable limit orders rather than marketable limit orders.

Limit orders are helpful in illiquid markets. You can use them to get a good entry price or to set a take-profit level on an existing holding. However, if the market trades away from your limit price, you may have to adjust your limit price or wait for the market to trade back to your limit price to fill the order.

Place Orders

To send a limit order, call the LimitOrderlimit_order method with a Symbol, quantity, and limit price. You can also provide a tag and order properties to the LimitOrderlimit_order method. If you do not have sufficient capital for the order, it's rejected.

LimitOrder(symbol, quantity, limitPrice, tag, orderProperties);
self.limit_order(symbol, quantity, limit_price, tag, order_properties)

To buy an asset with a marketable limit order, set the limit price to the current ask price or higher.

To sell an asset with a marketable limit order, set the limit price to the current bid price or lower.

To buy an asset with an unmarketable limit order, set the limit price below the current ask price.

// Buy 1 Bitcoin when the price drops to $34,000
LimitOrder("BTCUSD", 1, 34000);
# Buy 1 Bitcoin when the price drops to $34,000
self.limit_order("BTCUSD", 1, 34000)
When the asset currently trades at 55,000, the limit price is set at 34,000. The limit order is filled when the price drops to 34,000.

To sell an asset with an unmarketable limit order, set the limit price above the current bid price.

// Sell 1 Bitcoin when the price moves up to $60,000
LimitOrder("BTCUSD", -1, 60000);
# Sell 1 Bitcoin when the price moves up to $60,000
self.limit_order("BTCUSD", -1, 60000)
When the asset currently trades at 45,000, the limit price is set at 60,000. The limit order is filled when the price move up to 60,000.

Monitor Order Fills

Limit orders fill when the security price passes the limit price. To monitor the fills of your order, save a reference to the order ticket.

// Buy 10 shares of XLK at $140
var ticket = LimitOrder("XLK", 10, 140); Debug($"Quantity filled: {ticket.QuantityFilled}; Fill price: {ticket.AverageFillPrice}");
# Buy 10 shares of XLK at $140
ticket = self.limit_order("XLK", 10, 140)
self.debug(f"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.

Update Orders

You can update the quantity, limit price, and tag of limit orders until the order fills or the brokerage prevents modifications. To update an order, pass an UpdateOrderFields object to the Updateupdate method on the OrderTicket. If you don't have the order ticket, get it from the transaction manager. The Updateupdate method returns an OrderResponse to signal the success or failure of the update request.

// Create a new order and save the order ticket
var ticket = LimitOrder("SPY", 100, 221.05m, tag: "original tag");

// Update the order
var response = ticket.Update(new UpdateOrderFields() 
{
    Quantity = 80,
    LimitPrice = 222.00m,
    Tag = "new tag"
});

// Check if the update was successful
if (response.IsSuccess) 
{
    Debug("Order updated successfully");
}
# Create a new order and save the order ticket
ticket = self.limit_order("SPY", 100, 221.05, tag="original tag")

# Update the order
update_settings = UpdateOrderFields()
update_settings.quantity = 80
update_settings.limit_price = 222.00
update_settings.tag = "new tag"
response = ticket.update(update_settings)

# Check if the update was successful
if response.is_success:
    self.debug("Order updated successfully")

To update individual fields of an order, call any of the following methods:

  • UpdateLimitPriceupdate_limit_price
  • UpdateQuantityupdate_quantity
  • UpdateTagupdate_tag
var limitResponse = ticket.UpdateLimitPrice(limitPrice, tag);

var quantityResponse = ticket.UpdateQuantity(quantity, tag);

var tagResponse = ticket.UpdateTag(tag);

response = ticket.update_limit_price(limit_price, tag)

response = ticket.update_quantity(quantity, tag)

response = ticket.update_tag(tag)

When you update an order, LEAN creates an UpdateOrderRequest object, which have the following attributes:

To get a list of UpdateOrderRequest objects for an order, call the UpdateRequestsupdate_requests method.

var updateRequests = ticket.UpdateRequests();
update_requests = ticket.update_requests()

Cancel Orders

To cancel a limit 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 limit 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 limit orders, see the Orders section of the brokerage model documentation.

Requirements

Limit orders can be submitted at any time for all security types.

If your algorithm subscribes to extended market hours, they can be filled outside regular trading hours.

Examples

The following backtest verifies the LimitOrderlimit_order method behavior. On the first day, the algorithm buys SPY with an unmarketable limit order. On the second day, the algorithm sells SPY with an unmarketable limit order and places another unmarketable limit order to buy SPY, which doesn't fill. The following table shows the first three trades in the backtest:

Submitted TimeFilled TimeSymbolLimit PriceFilled PriceQuantityTypeStatusValueTag
2021-07-01T09:31:00Z2021-07-01T09:32:00ZSPY429.00429.0010LimitFilled4290.00Limit Price: ¤429.00
2021-07-02T09:31:00Z2021-07-02T09:35:00ZSPY431.70431.70-10LimitFilled-4317.00Limit Price: ¤431.70
2021-07-02T09:31:00Z/SPY400.00/10LimitSubmitted/Limit Price: ¤400.00

On July 1, 2021 at 9:31 AM Eastern Time (ET), the algorithm places a buy limit order with a limit price of $429 when the ask low price is $428.81 and the ask high price is $429.15. The order fills at 9:32 AM ET at a price of $429. The fill model fills buy limit orders when the ask low price is less than the limit price. It sets the fill price of the order to the minimum of ask high price and the limit price.

On July 2, 2021 at 9:31 AM ET, the algorithm places a sell limit order at $431.70 and a buy limit order at $400. At the time of the order, the bid high price is $431.65, the bid low price is $431.49, and the ask low price is $431.50. The sell limit order fills at 9:35 AM ET at a price of $431.70 and the buy limit order doesn't fill. The fill model fills sell limit order when the bid high price is greater than the limit price. It sets the fill price of the order to the maximum of the bid low price and the limit price. The buy limit order doesn't fill because the ask low price is above the limit price for the remainder of the backtest period.

To reproduce these results, backtest the following algorithm:

public class LimitOrderAlgorithm : QCAlgorithm
{
    private Symbol _symbol;

    public override void Initialize()
    {
        SetStartDate(2021, 7, 1);
        SetEndDate(2021, 7, 4);
        SetCash(100000);
        UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw;
        _symbol = AddEquity("SPY").Symbol;
    }

    public override void OnData(Slice data)
    {
        if (Time.Day == 1 && Time.Hour == 9 && Time.Minute == 31)
        {
            LimitOrder(_symbol, 10, 429.0m);
        }
        else if (Time.Day == 2 && Time.Hour == 9 && Time.Minute == 31)
        {
            LimitOrder(_symbol, -10, 431.7m);
            LimitOrder(_symbol, 10, 400.0m);
        }
    }
}
class LimitOrderAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2021, 7, 1)
        self.set_end_date(2021, 7, 3)
        self.set_cash(100000)
        self.universe_settings.data_normalization_mode = DataNormalizationMode.RAW
        self.add_equity("SPY")

    def on_data(self, data):
        if self.time.day == 1 and self.time.hour == 9 and self.time.minute == 31:
            self.limit_order("SPY", 10, 429.0)

        elif self.time.day == 2 and self.time.hour == 9 and self.time.minute == 31:
            self.limit_order("SPY", -10, 431.7)
            self.limit_order("SPY", 10, 400)

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: