Order Management

Order Factory

Introduction

The order factory creates order requests without submitting them. Use order requests to estimate fees and check buying power before you place orders, to submit several orders together, and to build contingent orders. When you submit an order request, you get an order ticket to manage the order.

Create Order Requests

To create an order request, call one of the methods of the OrderFactoryorder_factory member. Each method returns a SubmitOrderRequest object.

var request = OrderFactory.LimitOrder(_symbol, 100, limitPrice, tag: "Entry");
request = self.order_factory.limit_order(self._symbol, 100, limit_price, tag="Entry")

The order factory has a method for each order type. The following table shows the required arguments of each method:

MethodOrder Type
MarketOrder(symbol, quantity)market_order(symbol, quantity)Market
LimitOrder(symbol, quantity, limitPrice)limit_order(symbol, quantity, limit_price)Limit
LimitIfTouchedOrder(symbol, quantity, triggerPrice, limitPrice)limit_if_touched_order(symbol, quantity, trigger_price, limit_price)Limit if touched
StopMarketOrder(symbol, quantity, stopPrice)stop_market_order(symbol, quantity, stop_price)Stop market
StopLimitOrder(symbol, quantity, stopPrice, limitPrice)stop_limit_order(symbol, quantity, stop_price, limit_price)Stop limit
TrailingStopOrder(symbol, quantity, trailingAmount, trailingAsPercentage)trailing_stop_order(symbol, quantity, trailing_amount, trailing_as_percentage)Trailing stop
TrailingStopOrder(symbol, quantity, stopPrice, trailingAmount, trailingAsPercentage)trailing_stop_order(symbol, quantity, stop_price, trailing_amount, trailing_as_percentage)Trailing stop with an initial stop price
MarketOnOpenOrder(symbol, quantity)market_on_open_order(symbol, quantity)Market on open
MarketOnCloseOrder(symbol, quantity)market_on_close_order(symbol, quantity)Market on close
ComboMarketOrder(legs, quantity)combo_market_order(legs, quantity)Combo market
ComboLimitOrder(legs, quantity, limitPrice)combo_limit_order(legs, quantity, limit_price)Combo limit
ComboLegLimitOrder(legs, quantity)combo_leg_limit_order(legs, quantity)Combo leg limit
OptionStrategyOrder(strategy, quantity)option_strategy_order(strategy, quantity)Option strategy
ExerciseOption(optionSymbol, quantity)exercise_option(option_symbol, quantity)Option exercise

Every method also accepts the optional asynchronous, tag, and orderPropertiesorder_properties arguments.

The combo and Option strategy methods return a list with one order request for each leg. The order request stores the current time and a copy of the default order properties, unless you pass order properties. If the ticker of the security changed, the order request uses the current Symbol.

Submit Order Requests

To submit order requests, pass a single request or a list of requests to the Orderorder method. The method returns a list with the order ticket of each order. For a combo order, include all the legs.

var tickets = Order(new List<SubmitOrderRequest>
{
    OrderFactory.MarketOrder(_spy, 10),
    OrderFactory.LimitOrder(_aapl, -20, aaplLimitPrice)
});
tickets = self.order([
    self.order_factory.market_order(self._spy, 10),
    self.order_factory.limit_order(self._aapl, -20, aapl_limit_price)
])

LEAN runs the pre-order checks on every request before it submits any of them. If a request fails the checks, LEAN submits none of the orders and the method returns a list with a single invalid order ticket. LEAN checks the buying power of each order after you submit it.

LEAN applies the usual order adjustments when you submit the request. For example, LEAN converts a market order into a market on open order if the market is closed. LEAN also sets the initial stop price of a trailing stop order from the market price at that time.

You can submit each order request once. After you submit a request, its OrderIdorder_id property holds the order Id. To get the order ticket of a request, get it from the transaction manager.

var ticket = Transactions.GetOrderTicket(request.OrderId);
ticket = self.transactions.get_order_ticket(request.order_id)

Estimate Order Fees

To estimate the modeled fee of an order before you place it, create an order request with the order factory and convert it into an Order object with the QuantConnect.Orders.Order.CreateOrderOrder.create_order method. Then pass the order and its security to the fee model of the security. The conversion doesn't submit the order.

var security = Securities[_symbol];
var request = OrderFactory.LimitOrder(_symbol, 100, limitPrice);
var order = QuantConnect.Orders.Order.CreateOrder(request);
var fee = security.FeeModel.GetOrderFee(new OrderFeeParameters(security, order)).Value;
Debug($"Estimated fee: {fee.Amount} {fee.Currency}");
security = self.securities[self._symbol]
request = self.order_factory.limit_order(self._symbol, 100, limit_price)
order = Order.create_order(request)
fee = security.fee_model.get_order_fee(OrderFeeParameters(security, order)).value
self.debug(f"Estimated fee: {fee.amount} {fee.currency}")

The estimate is a modeled fee. The fee model calculates it inside your algorithm with the current order and security values. In live trading, LEAN doesn't request the estimate from the brokerage. The fee that the brokerage charges can differ from the modeled fee, especially if the fee depends on the fill price or the fill quantity. To place the order after the estimate, pass the same request to the Orderorder method.

Check Buying Power

The following sections explain how to check if you have enough buying power to cover the initial margin requirements of an order before you place it.

Check Requirements of Regular Orders

To check if you have enough buying power for a regular order, create an order request with the order factory and convert it into an Order object. Then compare the absolute initial margin that the order requires with the buying power of your portfolio in the direction of the order. The initial margin is negative for sell orders and includes the modeled order fees. The buying power in the direction of the order includes the margin that the order frees when it reduces or reverses a position.

var security = Securities[_symbol];
var request = OrderFactory.MarketOrder(_symbol, 100);
var order = QuantConnect.Orders.Order.CreateOrder(request);
var initialMargin = security.BuyingPowerModel.GetInitialMarginRequiredForOrder(
    new InitialMarginRequiredForOrderParameters(Portfolio.CashBook, security, order)).Value;
if (Math.Abs(initialMargin) <= Portfolio.GetBuyingPower(_symbol, order.Direction))
{
    Order(request);
}
security = self.securities[self._symbol]
request = self.order_factory.market_order(self._symbol, 100)
order = Order.create_order(request)
initial_margin = security.buying_power_model.get_initial_margin_required_for_order(
    InitialMarginRequiredForOrderParameters(self.portfolio.cash_book, security, order)).value
if abs(initial_margin) <= self.portfolio.get_buying_power(self._symbol, order.direction):
    self.order(request)

The HasSufficientBuyingPowerForOrderhas_sufficient_buying_power_for_order method of the security buying power model requires an order ticket, so it only works for orders that you already submitted. When you submit the order, LEAN runs its own buying power check.

Check Requirements of Option Strategy Orders

To check if you have enough buying power for an Option strategy order, follow these steps:

  1. Create an OptionStrategy object with the strategy you want to trade.
  2. For example, create a Bull Put Spread strategy.

    private Symbol _symbol;
    
    public override void Initialize()
    {
        // Subscribe to option data and cache the canonical symbol to obtain the option data
        _symbol = AddOption("SPY").Symbol;
    }
    
    public override void OnData(Slice slice)
    {
        // Trade on updated option chain data
        if (!slice.OptionChains.TryGetValue(_symbol, out var chain))
        {
            return;
        }
    
        var itmStrike = chain.Max(x => x.Strike);
        var otmStrike = chain.Min(x => x.Strike);
        var expiry = chain.Min(x => x.Expiry);
    
        var optionStrategy = OptionStrategies.BullPutSpread(_symbol, itmStrike, otmStrike, expiry);
    def initialize(self) -> None:
        # Subscribe to option data and cache the canonical symbol to obtain the option data
        self._symbol = self.add_option("SPY").symbol
    
    def on_data(self, slice: Slice) -> None:
        # Trade on updated option chain data
        chain = slice.option_chains.get(self._symbol)
        if not chain:
            return
    
        itm_strike = max(x.strike for x in chain)
        otm_strike = min(x.strike for x in chain)
        expiry = min(x.expiry for x in chain)
    
        option_strategy = OptionStrategies.bull_put_spread(self._symbol, itm_strike, otm_strike, expiry)
  3. Create the order requests of the strategy legs with the order factory and convert each request into an Order object.
  4.     var requests = OrderFactory.OptionStrategyOrder(optionStrategy, 2);
        var orders = requests.Select(request => QuantConnect.Orders.Order.CreateOrder(request)).ToList();
        requests = self.order_factory.option_strategy_order(option_strategy, 2)
        orders = [Order.create_order(request) for request in requests]
  5. Call the HasSufficientBuyingPowerForOrderhas_sufficient_buying_power_for_order method of the portfolio with the list of orders.
  6.     var result = Portfolio.HasSufficientBuyingPowerForOrder(orders);
        result = self.portfolio.has_sufficient_buying_power_for_order(orders)

    The HasSufficientBuyingPowerForOrderhas_sufficient_buying_power_for_order method returns a HasSufficientBuyingPowerForOrderResult object, which has the following properties:

  7. If the result is sufficient, submit the order requests.
  8.     if (result.IsSufficient)
        {
            Order(requests);
        }
        else
        {
            Debug($"You don't have sufficient margin for this order: {result.Reason}");
        }
    }
        if result.is_sufficient:
            self.order(requests)
        else:
            self.debug(f"You don't have sufficient margin for this order: {result.reason}")

The method combines the orders with your current holdings into position groups, so it accounts for strategy orders that reduce or close a position. If the strategy has a single leg, such as a naked put, follow the steps in the preceding section.

Examples

The following examples demonstrate common practices for using the order factory.

Example 1: Skip Expensive Rebalance Orders

The following algorithm rebalances an equal-weight portfolio every month. For each security, it creates a market order request for the difference between the target and current quantities and estimates the fee. The algorithm drops the orders whose fee exceeds 0.1% of the order value and submits the rest together, with the sell orders first.

public class OrderFactoryExampleAlgorithm : QCAlgorithm
{
    private List<Symbol> _symbols;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        SetEndDate(2024, 12, 31);
        SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
        _symbols = new[] { "SPY", "TLT", "GLD" }.Select(ticker => AddEquity(ticker, Resolution.Daily).Symbol).ToList();
        Schedule.On(DateRules.MonthStart(_symbols[0]), TimeRules.AfterMarketOpen(_symbols[0], 30), Rebalance);
    }

    private void Rebalance()
    {
        var requests = new List<SubmitOrderRequest>();
        foreach (var symbol in _symbols)
        {
            var quantity = CalculateOrderQuantity(symbol, 1m / _symbols.Count);
            if (quantity == 0) continue;
            var security = Securities[symbol];
            var request = OrderFactory.MarketOrder(symbol, quantity);
            // Estimate the fee without submitting the order.
            var fee = security.FeeModel.GetOrderFee(new OrderFeeParameters(security, QuantConnect.Orders.Order.CreateOrder(request))).Value.Amount;
            if (fee <= 0.001m * Math.Abs(quantity) * security.Price)
            {
                requests.Add(request);
            }
        }
        // Place the sell orders first to free up buying power.
        Order(requests.OrderBy(request => request.Quantity));
    }
}
class OrderFactoryExampleAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        self.set_end_date(2024, 12, 31)
        self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN)
        self._symbols = [self.add_equity(ticker, Resolution.DAILY).symbol for ticker in ["SPY", "TLT", "GLD"]]
        self.schedule.on(self.date_rules.month_start(self._symbols[0]), self.time_rules.after_market_open(self._symbols[0], 30), self._rebalance)

    def _rebalance(self) -> None:
        requests = []
        for symbol in self._symbols:
            quantity = self.calculate_order_quantity(symbol, 1 / len(self._symbols))
            if not quantity:
                continue
            security = self.securities[symbol]
            request = self.order_factory.market_order(symbol, quantity)
            # Estimate the fee without submitting the order.
            fee = security.fee_model.get_order_fee(OrderFeeParameters(security, Order.create_order(request))).value.amount
            if fee <= 0.001 * abs(quantity) * security.price:
                requests.append(request)
        # Place the sell orders first to free up buying power.
        self.order(sorted(requests, key=lambda request: request.quantity))

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: