Перейти к содержанию

Параллельное исполнение ордеров в cTrader

Одна из функций, которая выделяет cTrader, — это возможность использовать как синхронное, так и асинхронное программирование для исполнения ваших ордеров. Вы можете использовать синхронное программирование для реализации стратегий, которым необходимо дождаться результатов исполнения ордера, прежде чем двигаться дальше; в качестве альтернативы асинхронное программирование лучше всего использовать при создании высокоскоростной стратегии, которая может отправлять несколько ордеров одновременно.

В этом видео и соответствующей статье мы объясним разницу между синхронным и асинхронным исполнением и покажем вам, как использовать оба этих подхода для размещения новых ордеров.

Синхронное и асинхронное исполнение ордеров

Базовые потоки

Синхронное исполнение ордеров можно объяснить с помощью простой диаграммы. При синхронном размещении новых ордеров сиБот должен дождаться исполнения одного ордера, прежде чем следовать любым другим инструкциям. Другими словами, cTrader отправляет ордер на сервер, ждет ответа и только потом начинает выполнять следующую строку кода.

flowchart LR
  A([Ордер 1, 100 ms]) --> B([Ордер 2, 70 ms]) --> C([Ордер 3, 80 ms]) --> D([Последующие<br>инструкции])

При синхронном размещении вышеуказанных ордеров вашему сиБоту потребуется в общей сложности 250 мс, прежде чем перейти к любым инструкциям, указанным после Ордера 3.

С другой стороны, асинхронное исполнение позволяет cTrader отправлять несколько ордеров параллельно, не ожидая ответа от сервера. Используя асинхронное программирование, стратегия может минимизировать общее время, необходимое для исполнения нескольких ордеров.

flowchart LR
  subgraph Orders ["Ордера"]
    direction TB
    A([Ордер 1, 100 ms]) --- B([Ордер 2, 70 ms]) --- C([Ордер 3, 80 ms])
  end
  D([Последующие<br>инструкции])
  Orders --> D

На диаграмме выше все три ордера отправляются в одно и то же время, и сиБот не ждет результатов их исполнения, прежде чем перейти к последующим инструкциям.

Примеры кода

Мы также можем использовать код, чтобы продемонстрировать, как работает синхронное исполнение.

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); 
    }
}

Если мы запустим сиБота с этим кодом, лог покажет, что количество позиций увеличивается только после исполнения каждой позиции. Код ожидает исполнения каждого ордера, прежде чем отправить запрос на исполнение следующего.

Чтобы перейти к асинхронному исполнению ордеров, мы можем использовать метод ExecuteMarketOrderAsync.

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); 
    }
}

Мы также добавим обработчик событий для прослушивания события Positions.Opened.

 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); 
    }
}

В нашем обработчике событий мы выведем сообщение в лог, как только позиция откроется, а также количество открытых позиций на данный момент.

Если мы запустим сиБота с этим кодом и посмотрим на лог, мы увидим, что все ордера были отправлены без получения подтверждения о результатах предыдущих инструкций. Счетчик позиций все время оставался на 0. Подтверждения пришли и были выведены в лог через несколько миллисекунд после того, как все ордера были отправлены на сервер.

Используйте ордера с обратным вызовом

Теперь мы можем вернуться к нашему исходному коду и продемонстрировать еще одну ценную функцию асинхронного исполнения, а именно возможность использовать методы обратного вызова, которые вызываются сразу после завершения исполнения ордера. Мы удалим наш обработчик события Positions.Opened и реализуем метод обратного вызова, который выводит подтверждение после исполнения ордера.

 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.");
    }
}

Затем мы передадим этот метод в качестве аргумента методу ExecuteMarketOrderAsync.

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

В отличие от нашего предыдущего подхода, метод обратного вызова будет вызываться независимо от того, была ли сделка успешной, что также означает, что вы будете проинформированы о неудачных сделках и сможете действовать соответствующим образом.

Отслеживайте статус асинхронного исполнения

cTrader также предоставляет простой в использовании инструмент для отслеживания статуса асинхронных сделок. Метод ExecuteMarketOrderAsync возвращает объект TradeOperation, который содержит информацию о том, выполняется ли ордер еще.

Мы можем начать с объявления нового поля.

1
TradeOperation _tradeOperation;

Затем мы удалим наш цикл for и присвоим результат исполнения ордера новому полю.

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

Мы также добавим таймер к нашему сиБоту. Таймер будет проверять статус операции каждые 100 миллисекунд.

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

Если операция все еще выполняется, таймер выведет это в лог.

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

После сборки сиБота мы можем протестировать, как он работает на демосчете. В логе таймер должен выводить регулярные обновления о статусе асинхронного исполнения.

Обзор

Асинхронное исполнение — это мощный инструмент, который может сделать автоматическое размещение ордеров намного быстрее по сравнению с синхронным программированием. Однако синхронное исполнение все еще может использоваться в случае, если вам нужно знать результат сделки, прежде чем переходить к следующему шагу. Асинхронное выполнение, с другой стороны, лучше всего использовать, когда вам нужно отправить несколько ордеров параллельно без каких-либо задержек.