Order execution
When trading on a real exchange, your actions cannot be executed instantly. It takes time for a request to reach the exchange, for the order to be added to the order book, for it to be executed, and for the execution notification to return to you. When writing and backtesting strategies, traders often overlook these delays and fail to account for them. As a result, a strategy may behave very differently in live trading compared to backtesting. To address this, our backtesting engine simulates these real-world delays. This means that actions in your code do not take effect immediately. Instead, they are applied with a delay, and the results of those actions become visible only on the next price tick, when the strategy is recalculated. The following examples illustrate how these delays affect strategy behavior in different scenarios:Example 1: Order submission delay
When you create an order, it is not filled immediately. The order must first be sent to the exchange, accepted, and then executed. This example demonstrates that you cannot see the filled status immediately after callingsubmit():
Example 2: Order update delay
When you update an order’s parameters (e.g., limit price), the changes are not applied immediately. This example shows that the order properties reflect the update only on the nextcalc() call:
Example 3: Order cancellation delay
When you cancel an order, it is not canceled immediately. The cancellation request must be sent to the exchange and processed. This example demonstrates the delay:Example 4: Immediate update after creation
When you create and immediately update an order in the samecalc() call, the system applies the update locally before sending the order to the exchange. This means the order is sent with the updated parameters:
calc() call, not immediately. This behavior ensures your strategy works more consistently in both backtesting and live trading environments.
Fail-safe order operations
Some operations performed by a strategy — such as canceling or updating an order — are handled safely. This means the strategy will not fail if you call cancel or amend on an order that has already been canceled or executed. In such cases, the invalid operation is simply ignored. This simplifies your code and removes the need for additional if statements in situations where you want to “cancel an order if it hasn’t been executed or canceled yet” or “update an order if it’s still active”. The example below demonstrates a strategy that places sell limit orders and then cancels or updates them based on price movement — without checking the order status before each operation:cancel_order() and amend_order() without explicitly checking the order status beforehand. If the order has already been filled, canceled, or rejected, these operations will be safely ignored without throwing an exception. This simplifies your code by removing the need for defensive status checks.
Order filtering (intrabar_order_filter parameter)
Inside a bar, the price moves. While the current candle is not finished, the strategy receives updates for the current candle. The strategy does not remember all intermediate values; only the latest, most actual value of the current candle is available in the strategy. If the logic of the strategy is based on comparing the current candle with the previous closed candle, then you may get false positives triggers. They can be avoided by additional checks in the code or by using our pre-configured filter intrabar_order_filter.
Possible values for intrabar_order_filter:
intrabar_order_filter.NO_FILTER: No intrabar filtering is applied. Orders can be placed or modified at any price update within a candle.intrabar_order_filter.ON_BAR_CLOSE: Orders can be placed or modified only at the close of the candle. Other submits ofplace_orderwill be ignored.intrabar_order_filter.FIRST_IN_BAR: An order is placed only once per bar. It can be placed at any time during the candle, but subsequent submits ofplace_orderuntil the current candle closes will be ignored.intrabar_order_filter.LAST_IN_BAR: An order is placed only once per bar. It can be placed at any time during the candle, but only the last submit ofplace_orderwill be processed and the previous calls inside the candle will be ignored.
Main function is called on each price update, not only on bar close. Since we compare the current price to the previous bar’s close, this will result in extra triggers:
Manual trigger filtering: previous price comparison
We can compare it not with the previous bar’s price but with the last received tick price, if you store it in a variable:Manual trigger filtering: trigger at bar close
We can detect the crossing and place an order only at bar close:Manual trigger filtering: only first trigger during bar
We can ensure that only one order is placed per bar (not necessarily at bar close):intrabar_order_filter.ON_BAR_CLOSE
To place orders only at bar close:
intrabar_order_filter.FIRST_IN_BAR
To place only one order per bar, the first submit of place_order during the bar is applied, and all subsequent submits are ignored until the next bar:
intrabar_order_filter.LAST_IN_BAR
To place only one order per bar and use the last submit of place_order call within the bar (not necessarily at bar close), the final submit during the bar is applied and all earlier submits are ignored:
place_order within the bar, the emulator waits for the bar to close and then applies the filter before placing the order.
intrabar_order_filter.NO_FILTER
Finally, if none of these filters fit your needs and you want full control over when orders are placed, you can use the NO_FILTER setting, as shown in the examples above.
Price for market orders (market_order_price parameter)
By default, market orders are executed at the price the instrument has at the moment of execution. Note that the execution time of an order does not match the moment you place it or the moment you call place_order() (see the Order execution section above). This behavior reflects real-world execution, but during backtesting traders sometimes want to force a specific execution price for testing purposes. You can configure which value will be used as the execution price for market orders.
There are three available options:
market_order_price.MARKET_PRICE: the actual market price at the time of execution (default)market_order_price.ORDER_CREATION_PRICE: the price at which the order was createdmarket_order_price.BAR_OPEN_PRICE: the opening price of the bar in which the order was created
Simulation of trading at market close
The configuration described above can be useful when you want to simulate trading at market close using a price close to the bar’s closing value. To achieve this, you should place an order right before the session ends, using the bar’s closing price. This can be done by combining themarket_order_price and intrabar_order_filter parameters.
Simulation of trading at market open
Similarly, by combining themarket_order_price and intrabar_order_filter parameters, you can execute an order at the current bar’s opening price to simulate trading at market open.
Margin trading (leverage parameter)
Margin trading allows traders to borrow funds to increase their buying power, enabling them to open positions larger than their available capital. This is achieved by leveraging the trader’s initial capital as collateral. If the leverage parameter is greater than 1.0, the strategy can open positions larger than the available capital.
- Leverage: The ratio of the trader’s position size to their own capital. For example, a leverage of 2.0 means the trader can open positions twice the size of their capital.
- Initial margin: The minimum amount of the trader’s own funds required to open leveraged positions. If the portfolio value falls below the initial margin, the trader must reduce exposure. The trader cannot open new or increase existing uncovered positions.
- Maintenance Margin: The amount of collateral at which positions are forcibly closed. The maintenance margin is usually 1/2 of the initial margin.
- Equity: The trader’s total capital, including unrealized profits and losses, minus any liabilities.
- Margin Call: A warning issued when equity falls below the initial margin, requiring the trader to add funds or reduce exposure to avoid liquidation.
- Stop Out: The forced closure of positions when equity falls below the maintenance margin level.
- Amplified Losses: While leverage can increase profits, it also magnifies losses.
- Liquidation Risk: Failure to meet margin requirements can result in the forced closure of positions.
- Interest Costs: Borrowed funds may incur interest, adding to the cost of trading.