Trading and Orders

Estimated reading time: 11 min

MachinaTrader provides dozens of methods to create, update, and cancel orders. These orders can be placed automatically with helper methods, or manually through methods on the algorithm API. Manual orders can be fetched, updated, and canceled with Order Tickets. As orders are filled and updated, they generate events that notify your algorithm about their execution.

In backtesting, order fills are simulated using historical data. MachinaTrader allows you to create your own fill, fee, slippage, and margin models via plugin points. You can control how optimistic or pessimistic these fills are with transaction model classes. In live trading, this fill price is set by your brokerage when the order is filled.

What is an Order Ticket?

A critical concept required before diving into specific order types is the Order Ticket. When you create an order you are given an OrderTicket object to update and cancel your order. All orders return an order ticket.

In live trading, orders are asynchronous, and any operations to update the order must be requested. Whether the update is processed successfully is not guaranteed as the trade may already be filled by the time the request is sent.

MT order circuit

In the following sections, we'll explore how to use the Order Ticket to update and cancel orders you have created.

MachinaTrader provides the following API methods to place orders. All of the methods return an OrderTicket, which you can use to update and cancel the orders. All orders should use the symbol object to place orders; the Symbol class uniquely identifies the security through ticker renames. Some order types are not supported by all brokerages. To confirm each order's support, click on the respective links below.

Order TypeSummary 
Market OrderMarketOrder("LTCUSD", 100);

Send a market order for 100 LTCUSD at market price

Limit OrderLimitOrder("LTCUSD", 100, 21.67);

Submit a limit order for 100 LTCUSD @ $21.67

Stop Market OrderStopMarketOrder("LTCUSD", 100, 21.67);

Submit a stop limit order for 100 LTCUSD with stop price of $21.67

Stop Limit OrderStopLimitOrder("LTCUSD", 100, 21.67, 22.00);

Stop limit order for 100 LTCUSD, stop price $21.67, limit of $22.00

MarketOnOpenOrderMarketOnOpenOrder("LTCUSD", 100);

Market on open order for 100 LTCUSD

MarketOnCloseOrderMarketOnCloseOrder("LTCUSD", 100);

Market on close order for 100 LTCUSD

MachinaTrader also provides automated order management technology via the Algorithm Framework, and automated position sizing via the Automated Position Sizing (SetHoldings).

To update an order, you must use its OrderTicket. The specific properties that can be updated depend on the order type. Market orders are transmitted to the brokerage almost immediately, so only the tag on the order can be updated. Other orders can be updated until they are filled or the brokerage prevents modifications.

Order TypeSummary 
Market OrderMarketOrder("LTCUSD", 100);

Send a market order for 100 LTCUSD at market price

Limit OrderLimitOrder("LTCUSD", 100, 21.67);

Submit a limit order for 100 LTCUSD @ $21.67

Stop Market OrderStopMarketOrder("LTCUSD", 100, 21.67);

Submit a stop limit order for 100 LTCUSD with stop price of $21.67

Stop Limit OrderStopLimitOrder("LTCUSD", 100, 21.67, 22.00);

Stop limit order for 100 LTCUSD, stop price $21.67, limit of $22.00

MarketOnOpenOrderMarketOnOpenOrder("LTCUSD", 100);

Market on open order for 100 LTCUSD

MarketOnCloseOrderMarketOnCloseOrder("LTCUSD", 100);

Market on close order for 100 LTCUSD

Orders are updated by passing a UpdateOrderFields object to the Update method. The Update method returns an OrderResponse to signal the success or failure of the update request.

// Tag an order on creation
var ticket = LimitOrder("LTCUSD", 100, 221.05, tag: "New LTCUSD trade");

//Tag order later
var response = ticket.Update(new UpdateOrderFields() { 
  Tag = "Our New Tag for LTCUSD Trade",
  LimitPrice = 222.00
});

// Check response with the OrderResponse
if (response.IsSuccessful) { 
     Debug("Order updated successfully");
}

To cancel an order, you must use its OrderTicket. Market Orders are transmitted to the brokerage immediately and cannot be canceled. The Cancel() method returns an OrderResponse object to determine if the operation was successful.

// Create an order and save its ticket
var ticket = LimitOrder("LTCUSD", 100, 221.05, tag: "LTCUSD Trade to Cancel");

//Later cancel the order via the order ticket.
var response = ticket.Cancel();

// Use order response object to read status
if (response.IsSuccessful) {
       Debug("Order successfully canceled");
}

MachinaTrader endeavors to make our backtesting as realistic as possible by providing high-resolution data, exchange information, and powerful transaction models.

Order Slippage Impact

By default, MachinaTrader does not model slippage impact though we highly recommend you include a slippage model in your algorithm. Slippage is the difference between the expected and final fill prices of a trade.

Transaction Cost Models

By default, transaction fees are modeled from a mean value of exchange rates. These models are customizable by setting a new FeeModel. For more information on creating your own fee models, see our documentation.

Brokerage Models

Brokerage models aim to combine all of the models relevant for a brokerage together as a set. If you set the appropriate brokerage model, the fee models and supported order types will be appropriately set in your algorithm.

Single Asset Targets

The SetHoldings method automatically calculates the number of asset units to purchase according to the fraction of the portfolio value provided. This is a quick way to set up a portfolio with a set of weights for assets. If you already have holdings, you may want to liquidate the existing holdings first to free up buying power.

// Allocate 50% of portfolio value to LINKUSDT via market orders
SetHoldings("LINKUSDT", 0.5);

// Allocate 50% of portfolio value to LINKUSDT, but liquidate other holdings 
// before starting
SetHoldings("LINKUSDT", 0.5, true);

Portfolio of Asset Targets

Often when trading on a weighted basket of assets, you must intelligently scale down existing positions before increasing allocations to other assets. The portfolio variant of SetHoldings was designed to do this operation for you by accepting an array of PortfolioTarget objects.

If you already have holdings, the Machina Engine will calculate the delta-order required to meet your new target. When required, positions will be scaled down before other positions are scaled up in size.

// Purchase a portfolio of targets, processing orders intelligently.
var targets = new List<PortfolioTarget>() {
      new PortfolioTarget("LTCUSD", 0.8m),
      new PortfolioTarget("LINKUSDT", 0.2m)
};
SetHoldings(targets);

Manually Calculating Quantity Targets (ignore fees)

Ignore Fees

If you are looking to size positions but do not use market orders for the trades, you can use the CalculateOrderQuantity method to get an accurate estimate of the number of shares available to purchase with a given buying power fraction. The share quantity is calculated based on the current price of the asset and adjusted for the fee model attached to that security.

// Calculate the fee adjusted quantity of shares with given buying power
var quantity = CalculateOrderQuantity("LINKUSDT", 0.4);
LimitOrder("LINKUSDT", quantity, Securities["LINKUSDT"].Price);
Include Fees

If you want to size a position when placing a market order then you will probably want to calculate fees. You can use the CalculateTrade method to correctly size your position inclusive of fees. This method can also be used for limit orders in those exchanges that charge a fee for placing both limit and market orders.

// Calculate the position size (including fees) and use a Market order for 50% of available funds
var trade = CalculateTrade(symbol, OrderDirection.Buy, null,  PercentBelow(symbol, PercentageBelow), 0.5m);
if (trade.CanTrade)
{
    // Submit the order
    Order(trade.Order);
}

// Or place a Limit order 1% below current price
// Calculate the position size (including fees) and use a Limit order for 10% of available funds
var trade = CalculateTrade(symbol, OrderDirection.Buy, slice["LINKUSDT"].Price,  PercentBelow(symbol, 0.01), 0.5m);
if (trade.CanTrade)
{
    // Submit the order
    Order(trade.Order);
}

Cash Buffer Setting

To ensure a high probability of order fills through market gaps and discontinuities the MachinaTrader automatic portfolio methods assume a small built-in cash buffer. This buffer helps ensure you have sufficient buying power to handle overnight price movements. If you are seeing orders get rejected due to buying power, you can configure this buffer to provide a wider buffer. By default, the buffer is set to 2.5%. The buffer lives in the algorithm Settings property.

// Adjust the cash buffer from the default 2.5% to 5%
Settings.FreePortfolioValuePercentage = 0.05;

You can liquidate individual stocks, or your entire portfolio using the Liquidate() method. When called without a ticker provided, it will liquidate all your holdings. If you have pending open orders, it will attempt to cancel them.

// Liquidate all LINKUSDT in your portfolio
Liquidate("LINKUSDT");

// Liquidate entire portfolio
Liquidate();

The algorithm Transactions Manager is a collection of helper methods for quick access to all your orders. It is located in the Transactions property in your algorithm.

Fetching a Single Order

Using the Transactions Manager, you can retrieve a clone of an order by its Id. Once sent, orders cannot be changed, so the clone of the order is for informational purposes only. To update an order's properties, you should use an Order Ticket. The method returns an Order object.

// Retrieve a clone of a previously sent order.
var order = Transactions.GetOrderById(orderId)

Fetching All Open Orders

Using the Transaction Manager, you can fetch a list of all open orders for a symbol. This is helpful if you want to update multiple open orders for a specific symbol. The method returns a list of Order objects.

// Retrieve a list of all open orders for a symbol
var openOrders = Transactions.GetOpenOrders(symbol);

Canceling All Orders

The Cancel helpers can cancel all open orders, or just those orders related with a specific symbol. The method returns a list of OrderTicket objects. This is helpful if you are simulating an "OCA / One-Cancels-All" style of order where you want to cancel other related orders.

// Cancel all open orders
var allCancelledOrders = Transactions.CancelOpenOrders();

// Cancel orders related to LINKUSDT, apply string tag.
var LINKUSDTCancelledOrders = Transactions.CancelOpenOrders("LINKUSDT", "Hit stop price");

Each order generates events over its life as the status changes. These events are passed to the OnOrderEvent() method, which you can use for information about your order states. The event handler is passed an OrderEvent object, which has information about the order status.

public override void OnOrderEvent(OrderEvent orderEvent) {
    var order = Transactions.GetOrderById(orderEvent.OrderId);
    if (orderEvent.Status == OrderStatus.Filled) 
         Console.WriteLine("{0}: {1}: {2}", Time, order.Type, orderEvent);
}

The OrderStatus enumerator has the following potential values.

Status Description
OrderStatus.New

Order is created but has not been submitted by the brokerage.

OrderStatus.Submitted

Order has been successfully submitted to the brokerage.

OrderStatus.PartiallyFilled

Order has some of its requested quantity processed by brokerage.

OrderStatus.Filled

Order is completely filled by brokerage.

OrderStatus.Canceled

Order canceled before it was filled.

OrderStatus.Invalid

Order invalidated before it was accepted by MachinaEngine.

OrderStatus.CancelPending

Order waiting for confirmation of cancellation.

OrderStatus.UpdateSubmitted

Order update submitted to the market.

The TimeInForce property determines how long an order should remain open if unfilled. This does not apply to market orders as they are generally filled instantly. Time in force is useful to automatically cancel old trades.

Time In Force Property Value
Good Until Canceled TimeInForce.GoodTilCanceled
Order is valid until filled (default).
Day TimeInForce.Day
Order is valid until filled or the market closes.
Good Until Date TimeInForce.GoodTilDate(DateTime expiry)
Order is valid until filled or the specified expiration time.

By default, orders remain open until they are canceled (TimeInForce.GoodTilCanceled). To update the value, set the DefaultOrderProperties.TimeInForce before placing an order. Doing so will change the default value for all future orders unless reassigned again.

    // Set Limit Order to be good until market close
    DefaultOrderProperties.TimeInForce = TimeInForce.Day;
    LimitOrder("LINKUSDT", 100, lastClose * .999m);

    // Set Market Order to be good until noon
    DefaultOrderProperties.TimeInForce = TimeInForce.GoodTilDate(new DateTime(2019, 6, 19, 12, 0, 0));
    MarketOrder("LINKUSDT", 100);
    

Market Orders are sent immediately and filled at the market price for the security. To send a market order, you must provide a symbol and quantity. If you do not have sufficient capital for the purchase, your order will be rejected. By default, market orders are synchronous and fill immediately.

// Create a Market Order for 100 LINKUSDT.
var marketTicket = MarketOrder("LINKUSDT", 100);
Debug($"Market Order Fill Price: {marketTicket.AverageFillPrice});

Configuring Market Order Timeouts

Market orders are synchronous by default. This means they wait for the order to fill before moving to the next line of code. If you are trading on highly illiquid stocks, this wait can be too long, so the Machina Engine has a built-in default timeout of 5 seconds, after which the code execution will continue even if the trade is not filled. You can control this timeout with the Transactions.MarketOrderFillTimeout property.

// Adjust the market fill-timeout to 30 seconds.
Transactions.MarketOrderFillTimeout = TimeSpan.FromSeconds(30);

Asynchronously Sending Market Orders

When trading on a large portfolio of assets, you may wish to send orders in batches and not wait for the response to each one. This is possible by setting the optional argument asynchronous to true.

// Create a Market Order for 100 LINKUSDT asynchronously.
MarketOrder("LINKUSDT", 100, asynchronous: true);

Limit orders fill once the asset price is equal or better than the configured price. When purchasing an asset, this means the price is equal or lower to the price you set. Conversely, when selling shares, this is when the price is equal or higher to the price you set. Limit orders are often used to get a good entry price, or take-profit on an existing holding.

Limit orders can be updated via their OrderTicket because their orders are not immediately filled. For more information about updating orders, see Updating Orders.

// Purchase 10 LTC when its 1% below the current price
var close = Securities["LTCUSD"].Close;
var limitTicket = LimitOrder("LTCUSD", 10, close * .99m);

A Stop Market Order ("stop-loss") fills as a market order when a specific price is reached. A buy stop market order to purchase assets will trigger when the price is equal or higher than the one configured. Conversely, a sell stop market order will trigger when the price is equal or lower than to the one set. Stop market orders are often used to prevent loss.

If the market gaps (jumps in a discontinuous manner) past your stop price, it may be filled at a substantially worse price than the stop price you entered. As such, a stop-loss order is no guarantee your trade will fill at the price you specify.

Stop Market Order StopPrice, Tag, and Quantity can be updated. For more information on updating orders, see Updating Orders.

// Create Stop Market Order for 1% below current market price.
var close = Securities[symbol].Close; 
var stopMarketTicket = StopMarketOrder(symbol, 10, close * 0.99m);

Stop Limit Orders create a limit order when a specified price is reached. The associated limit order is filled when it reaches the limit price or better. As with all limit orders, the order is not filled if the price does not reach the specified price. Stop limit orders are often used to control risk, without the risk of a large gap filling trades unfavorably.

Stop Limit Order StopPrice, LimitPrice, Tag, and Quantity can all be updated after creation. For more information on updating orders, see Updating Orders.

var close = Securities[symbol].Close;
var stopPrice = close * .99; // Trigger stop limit when price falls 1%.
var limitPrice = close * 1.01; // Sell equal or better than 1% > close.
var stopLimitTicket = StopLimitOrder(symbol, -10, stopPrice, limitPrice);

Market On Open orders are filled at the official opening price for the security. They must be submitted two minutes before the market opens to be included in the opening auction. The Market On Open Quantity and Tag properties can be updated after creation until the last two minutes before open.

Market On Close orders are filled at the official closing price for the security. They must be submitted at least two minutes before the market closes to be included in the official closing auction. The Market On Open Quantity and Tag properties can be updated after creation until the last two minutes before close.

For more information on updating orders, see Updating Orders.

// Create Market Open/Close Orders for 100 LTC
var marketOpenOrderTicket = MarketOnOpenOrder("LTCUSD", 100);   // Place Before Open
var marketCloseOrderTicket = MarketOnCloseOrder("LTCUSD", 100); // Place Before Close

Fill Price Considerations

When you place a market on open or close order, you do not know its fill price until after the order is completed. If your order quantity is too close to your total portfolio buying power, you have a high chance of it being rejected as there may be large changes in price overnight. We recommend you consider this when sizing your portfolio to increase your probability of successful trades.

Often we are asked to support other order types such as Multi-Leg, One Cancels All, and Trailing Stop. Currently these order types are not supported, but will be added over time. Part of the difficulty of implementing them is the incomplete brokerage support.

Orders can be set with tags to aid your strategy development. Tags can be any string of up to 100 characters. Order tags can also be set with the order update system, as shown below:

// Tag an order on creation
var ticket = LimitOrder("LTCUSD", 100, 221.05, tag: "New LTCUSD trade");

//Tag order later
ticket.Update( UpdateOrderFields() { 
  Tag = "Our New Tag for LTCUSD Trade" } 
);

For more information on updating order properties, see Updating Orders.

Why is my order being converted to a market on open order?

Market orders are automatically converted into Market On Open orders when the market is closed at the time of the request. This most commonly happens when using Daily or Hourly data, which is emitted when the market closes. Daily data is emitted at the end of the day (midnight), and hourly data for equities' final bar is at 4 pm ET. If you are using one of the automatic portfolio helper methods (SetHoldings), then the orders will also be converted if the data resolution is insufficient.

To fix this, we recommend using minute resolution data or updating your order creation logic to submit Market On Open orders.

Why am I seeing the "stale price" warning?

If the last price data point was more than 10 minutes old, the Machina Engine will flag the orders with a warning tag indicating the price may not be representative. This can happen on illiquid assets or if you are scheduling intraday events using daily data.

To fix this, we recommend using the highest resolution data possible for a high fidelity backtest.

When an order fails to process it returns with a negative order-id. These error codes mean different things as described in the table below.

Id Interpretation
-1 ProcessingError - Unknown error.
-2 OrderAlreadyExists - Cannot submit because order already exists.
-3 InsufficientBuyingPower - Not enough money to to submit order.
-4 BrokerageModelRefusedToSubmitOrder - Internal logic invalidated submit order.
-5 BrokerageFailedToSubmitOrder - Brokerage rejected order.
-6 BrokerageFailedToUpdateOrder - Failed to update order.
-7 BrokerageHandlerRefusedToUpdateOrder - Brokerage rejected update request.
-8 BrokerageFailedToCancelOrder - Brokerage refused to cancel order.
-9 InvalidOrderStatus - Only pending orders can be cancelled
-10 UnableToFindOrder - Cannot find order with that id.
-11 OrderQuantityZero - Cannot submit or update orders with zero quantity.
-12 UnsupportedRequestType - This type of request is unsupported.
-13 PreOrderChecksError - Pre-placement order checks failed.
-14 MissingSecurity - Security is missing. Probably did not subscribe.
-15 ExchangeNotOpen - Some order types require open exchange.
-16 SecurityPriceZero - There isn't any market data yet for the security.
-17 ForexBaseAndQuoteCurrenciesRequired - Need both currencies in cashbook to trade a pair.
-18 ForexConversionRateZero - Need conversion rate to account currency.
-19 SecurityHasNoData - Should not attempt trading without at least one data point.
-20 ExceededMaximumOrders - Transaction manager's cache is full.
-21 MarketOnCloseOrderTooLate - Need to submit market on close orders at least 11 minutes before exchange close.
-22 InvalidRequest - Request is invalid or null.
-23 RequestCanceled - Request was canceled by user.
-24 AlgorithmWarmingUp - All orders are invalidated while algorithm is warming up.
-25 BrokerageModelRefusedToUpdateOrder - Internal logic invalidated update order.
-26 QuoteCurrencyRequired - Need quote currency in cashbook to trade.
-27 ConversionRateZero - Need conversion rate to account currency.
-28 NonTradableSecurity - The order's symbol references a non-tradable security.
-29 NonExercisableSecurity - The order's symbol references a non-exercisable security.
Was this article helpful?
Dislike 0
Views: 44