Order Types

Trailing Stop Orders

Introduction

Trailing stop orders are stop market orders with a dynamic stop price. A sell trailing stop starts with the stop price at a fixed amount below the market price. As the market price rises, the stop price rises by the trail amount, but if the stock price falls, the stop loss price doesn't change. A buy trailing stop order starts with the stop price at a fixed amount above the market price. As the market price falls, the stop price falls by the trail amount, but if the stock price rises, the stop loss price doesn't change.

Trailing stop orders fill as a market order when the asset reaches the stop price. You can use trailing stop orders to buy breakouts or to mitigate the risk of large losses while protecting your gains. However, if the market gaps past your stop price, the order may fill at a worse price than the stop price you set. There's no guarantee that a trailing stop order fills at the stop price.

Place Orders

To send a trailing stop order, call the TrailingStopOrder method and provide a Symbol, quantity, trailing amount, and the trailing type. You can also provide a tag and order properties to the TrailingStopOrder method. If you do not have sufficient capital for the order, it is rejected.

TrailingStopOrder(symbol, quantity, trailingAmount, trailingAsPercentage, tag, orderProperties);
self.trailing_stop_order(symbol, quantity, trailingAmount, trailingAsPercentage, tag, orderProperties)

If you set the trailingAsPercentage parameter to falseFalse, the trailingAmount is denominated in the asset's quote currency.

Buy With a Currency-Based Trailing Stop

To buy an asset with a currency-based trailing stop order, pass the trailing amount and disable the trailingAsPercentage parameter.

// Buy 1 share of SPY through a trailing stop order that starts $30 above the current price
TrailingStopOrder("SPY", 1, 30m, false);
# Buy 1 share of SPY through a trailing stop order that starts $30 above the current price
self.trailing_stop_order("SPY", 1, 30, False)
When the asset currently trades at $440, the stop price of the buy stop market order is set at $470 and a trailing amount of $30. The order fills when the price trades up to $402.82.

Buy With a Percent-Based Trailing Stop

To buy an asset with a percent-based trailing stop order, pass the trailing percent and enable the trailingAsPercentage parameter.

// Buy 1 share of SPY through a trailing stop order that starts 7% above the current price
TrailingStopOrder("SPY", 1, 0.07m, true);
# Buy 1 share of SPY through a trailing stop order that starts 7% above the current price
self.trailing_stop_order("SPY", 1, 0.07, True)
When the asset currently trades at $440, the stop price of the buy stop market order is set at $470 and a trailing amount of 7%. The order fills when the price trades up to $398.92.

Sell With a Currency-Based Trailing Stop

To sell an asset with a currency-based trailing stop order, pass the trailing amount and disable the trailingAsPercentage parameter.

// Sell 1 share of SPY through a trailing stop order that starts $20 below the current price
TrailingStopOrder("SPY", -1, 20m, false)
# Sell 1 share of SPY through a trailing stop order that starts $20 below the current price
self.trailing_stop_order("SPY", -1, 20, False)
When the asset currently trades at $415, the stop price of the sell stop market order is set at $395 and a trailing amount of $20. The order fills when the price trades down to $420.49.

Sell With a Percent-Based Trailing Stop

To sell an asset with a percent-based trailing stop order, pass the trailing percent and enable the trailingAsPercentage parameter.

// Sell 1 share of SPY through a trailing stop order that starts 5% above the current price
TrailingStopOrder("SPY", -1, 0.05m, true);
# Sell 1 share of SPY through a trailing stop order that starts 5% above the current price
self.trailing_stop_order("SPY", -1, 0.05, True)
When the asset currently trades at $415, the stop price of the sell stop market order is set at $395 and a trailing amount of 5%. The order fills when the price trades down to $418.46.

Custom Starting Prices

In the preceding examples, LEAN calculates the stop price based on the current security price. If you want the order to start with a stop price closer to the security price than the trailingAmount, pass a stopPrice parameter to the TrailingStopOrder method.

TrailingStopOrder(symbol, quantity, stopPrice, trailingAmount, trailingAsPercentage, tag, orderProperties);
self.trailing_stop_order(symbol, quantity, stopPrice, trailingAmount, trailingAsPercentage, tag, orderProperties)

Monitor Order Fills

Trailing stop orders fill as a market order when the security price passes the stop price. To monitor the fills of your order, save a reference to the order ticket.

// When XLK trades down to less than 5% of the current price, sell 10 shares, and update 
var ticket = TrailingStopOrder("XLK", -10, 0.05, true);
Debug($"Quantity filled: {ticket.QuantityFilled}; Fill price: {ticket.AverageFillPrice}");
# When XLK trades down to less than 5% of the current price
ticket = self.trailing_stop_order("XLK", -10, 0.05, True)
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

The security's fill model automatically updates the stop price of trailing stop orders as the security's price moves away from the stop price. You can update the quantity, stop price, and tag of trailing stop 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 = TrailingStopOrder("SPY", -100, 415, 10, false, tag: "original tag");

// Update the order
var response = ticket.Update(new UpdateOrderFields() { 
  Quantity = -80,
  StopPrice = 400,
  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.trailing_stop_order("SPY", -100, 415, 10, False, tag="original tag")

# Update the order
update_settings = UpdateOrderFields()
update_settings.quantity = -80
update_settings.stop_price = 400
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:

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

var stopResponse = ticket.UpdateStopPrice(stopPrice, tag);

var tagResponse = ticket.UpdateTag(tag);

response = ticket.UpdateQuantity(quantity, tag)

response = ticket.UpdateStopPrice(stopPrice, tag)

response = ticket.UpdateTag(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 UpdateRequests method.

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

Cancel Orders

To cancel a trailing stop order, call the Cancel method on the OrderTicket. If you don't have the order ticket, get it from the transaction manager. The Cancel 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 CancelRequest 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 trailing stop 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 trailing stop orders, see the Orders section of the brokerage model documentation.

If your brokerage doesn't support them, you can implement a workaround with stop market orders. For an example, see the Buy and Hold with a Trailing Stop Bootcamp lesson.

Requirements

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

Example

The following backtest verifies the TrailingStopOrder behavior. On even days, the algorithm buys SPY at the current market price and sells when the price drops 1%. On odd days, the algorithm shorts SPY and buys when the price rises 1%. The following table shows the first four trades in the backtest:

TimeSymbolPriceQuantityTypeStatusValueTag
2021-07-01T13:31:00ZSPY429.10-1MarketFilled-429.10
2021-07-01T13:31:00ZSPY433.161Trailing StopFilled433.16Stop Price: 433.431400 Trailing Amount: 1.00%
2021-07-02T16:53:00ZSPY433.161MarketFilled433.16
2021-07-02T16:53:00ZSPY429.00-1Trailing StopFilled-429.00Stop Price: 428.823450 Trailing Amount: 1.00%

On July 1, 2021, the algorithm shorts SPY at $429.10 and then buys it back at $433.16 when the stop price is $433.16. The stop price is 0.95% above the market price. Note that $429.10 is not the market price when the algorithm places the first two orders. The market price is $429.14, and the initial stop price is 429.14 * 1.01 = 433.4314. The market price drops to $428.86 at 9:40, and the stop price is set to 428.86 * 1.01 = 433.1486. The fill price of the stop market order is $433.16, which, as expected, is higher than $433.15. The fill model assumes the worst-case scenario between the market price and the stop price. In this case, the worst-case scenario is the maximum of the market price and stop price.

On July 2, 2021, the algorithm buys SPY at $433.16 and then sells it at $429 when the stop price is $429.00. The stop price is 0.96% below the market price. Note that $433.16 is not the market price when the algorithm places the second two orders. The market price is $433.155, and the initial stop price is 433.155 * 0.99 = 428.82345. The market prices increased to $434.76 on July 7 at 3:35 PM, and the stop price is set to 434.76 * 0.99 = 430.4124. The fill price of the stop market order is $429, which, as expected, is lower than $430.41. The fill model assumes the worst-case scenario between the market price and the stop price. In this case, the worst-case scenario is the minimum of the market price and stop price in a gap down opening.

To reproduce these results, backtest the following algorithm:

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: