Skip to content

Parallel Order Execution in cTrader

One of the features that make cTrader stand out is the ability to use both synchronous and asynchronous programming to execute your orders. You can use synchronous programming to implement strategies that need to wait for the order execution results before proceeding further; alternatively, asynchronous programming is best used when creating a lightning-fast strategy that can submit multiple orders simultaneously.

In this video and its corresponding article, we will explain the difference between synchronous and asynchronous execution and we will show you how to use both of these approaches to place new orders.

Sync vs Async Order Execution

Basic Flows

Synchronous order execution can be explained by using a simple diagram. When placing new orders synchronously, a cBot must wait for one order to be executed before it follows any other instructions. In other words, cTrader sends an order to the server, waits for the response, and only then starts to execute the next line of code.

flowchart LR
  A([Order 1, 100 ms]) --> B([Order 2, 70 ms]) --> C([Order 3, 80 ms]) --> D([Subsequent instructions]) 

When placing the above orders synchronously, your cBot will have to spend a total of 250 ms before proceeding to whatever instructions are specified after Order 3.

On the other hand, async execution allows cTrader to send multiple orders in parallel without expecting a response from the server. By using async programming, a strategy can minimise the total time required for executing multiple orders.

flowchart LR
  subgraph Orders
    direction TB
    A([Order 1, 100 ms]) --- B([Order 2, 70 ms]) --- C([Order 3, 80 ms])
  end
  D([Subsequent instructions])
  Orders --> D

In the diagram above, all three orders are submitted at exactly the same time and the cBot does not wait for their execution results before proceeding to subsequent instructions.

Code Examples

We can also use code to demonstrate how sync execution works.

1
2
3
4
5
6
7
8
9
protected override void OnStart()
{
    for (int i = 0; i < 10; i++)
    {
        ExecuteMarketOrder(TradeType.Buy, SymbolName, 1000);
        Print("Order Sent");
        Print("Positions Count:" + Positions.Count); 
    }
}

If we run a cBot with this code, the log will show that the positions count is only increasing after each position is executed. The code is waiting for each order to be executed before sending a request to execute the next one.

To change to async order execution, we can use the ExecuteMarketOrderAsync method.

1
2
3
4
5
6
7
8
9
protected override void OnStart()
{
    for (int i = 0; i < 10; i++)
    {
        ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 1000);
        Print("Order Sent");
        Print("Positions Count:" + Positions.Count); 
    }
}

We will also add an event handler to listen to the Positions.Opened event.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
protected override void OnStart()
{
    Positions.Opened += Positions_Opened;
    for (int i = 0; i < 10; i++)
    {
        ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 1000, PositionOpened);
        Print("Order Sent");
        Print("Positions Count:" + Positions.Count); 
    }
}

In our event handler we will print a message in the log as soon as a position has opened as well as the number of open positions at the moment.

If we run a cBot with this code and take a look at the log, we will see that all orders have been sent without receiving a confirmation about the results of previous instructions. The positions counter has stayed at 0 the whole time. Confirmations arrived and were printed in the log some milliseconds after all orders were sent to the server.

Using Callback Orders

Now we can move back to our source code and demonstrate another valuable feature of async execution, namely the option to use callback methods that are called as soon as order execution is finished. We will remove our handler of the Positions.Opened event and implement a callback method that prints a confirmation once an order has been executed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private void TradeExecuted(TradeResult tradeResult)
{
    if (tradeResult.IsSuccessful)
    {
        Print("Trade succeeded. Position Opened.");
    }
    else
    {
        Print("Trade failed. Position Not Opened.");
    }
}

We will then pass this method as an argument to the ExecuteMarketOrderAsync method.

1
ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 1000, TradeExecuted);

In contrast to our earlier approach, the callback method will be called regardless of whether a trade has succeeded, which also means you are informed of non-successful trades and can act accordingly.

Monitoring the Status of Async Execution

cTrader also provides an easy-to-use tool for tracking the status of async trades. The ExecuteMarketOrderAsync method returns a TradeOperation object that contains information about whether the order is still executing.

We can start by declaring a new field.

1
TradeOperation _tradeOperation;

We will then remove our for loop, and assign the result of order execution to the new field.

1
_tradeOperation = ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 1000, TradeExecuted);

We will also add a timer to our cBot. The timer will check the status of an operation every 100 milliseconds.

1
2
Timer.TimerTick += Timer_TimerTick;
Timer.Start(new System.TimeSpan(0, 0, 0, 0, 100));

If an operation is still executing, the timer will print this in the log.

1
2
3
4
5
6
7
8
private void Timer_TimerTick()
{
    Print("Tick");
    if (_tradeOperation != null)
    {
        Print("Trade operation is executing: " + _tradeOperation.IsExecuting);
    }
}

After the cBot is built, we can test how it works on a demo account. In the log, the timer should print regular updates on the status of async execution.

Summary

Async execution is a powerful tool that can make automatic order placement much quicker compared to synchronous programming. However, synchronous execution can still be used in case you need to know the outcome of a trade before proceeding to the next step. Async execution, on the other hand, is best used when you need to send multiple orders in parallel without any delays.

Subscribe to our YouTube channel