Przejdź do treści

Równoległe wykonywanie zleceń w cTrader

Jedną z funkcji, które wyróżniają cTrader, jest możliwość użycia zarówno programowania synchronicznego, jak i asynchronicznego do realizacji zleceń. Możesz użyć programowania synchronicznego do implementacji strategii, które muszą czekać na wyniki realizacji zlecenia przed dalszym postępowaniem; alternatywnie programowanie asynchroniczne najlepiej stosować podczas tworzenia szybkiej strategii, która może przesyłać wiele zleceń jednocześnie.

W tym filmie i odpowiadającym mu artykule wyjaśnimy różnicę między wykonywaniem synchronicznym i asynchronicznym oraz pokażemy, jak używać obu tych podejść do składania nowych zleceń.

Synchroniczne a asynchroniczne wykonywanie zleceń

Podstawowe przepływy

Synchroniczne wykonywanie zleceń można wyjaśnić za pomocą prostego diagramu. Podczas synchronicznego składania nowych zleceń cBot musi czekać na realizację jednego zlecenia, zanim przejdzie do wykonania jakichkolwiek innych instrukcji. Innymi słowy, cTrader wysyła zlecenie do serwera, czeka na odpowiedź i dopiero wtedy zaczyna wykonywać następną linię kodu.

flowchart LR
  A([Zlecenie 1,<br>100 ms]) --> B([Zlecenie 2,<br>70 ms]) --> C([Zlecenie 3,<br>80 ms]) --> D([Kolejne<br>instrukcje])

Podczas synchronicznego składania powyższych zleceń Twój cBot będzie musiał poświęcić łącznie 250 ms przed przejściem do jakichkolwiek instrukcji określonych po zleceniu 3.

Z drugiej strony wykonywanie asynchroniczne pozwala cTraderowi wysyłać wiele zleceń równolegle bez oczekiwania na odpowiedź z serwera. Dzięki użyciu programowania asynchronicznego strategia może zminimalizować całkowity czas wymagany do realizacji wielu zleceń.

flowchart LR
  subgraph Orders ["Zlecenia"]
    direction TB
    A([Zlecenie 1,<br>100 ms]) --- B([Zlecenie 2,<br>70 ms]) --- C([Zlecenie 3,<br>80 ms])
  end
  D([Kolejne<br>instrukcje])
  Orders --> D

Na powyższym diagramie wszystkie trzy zlecenia są przesyłane dokładnie w tym samym czasie, a cBot nie czeka na wyniki ich realizacji przed przejściem do kolejnych instrukcji.

Przykłady kodu

Możemy również użyć kodu, aby zademonstrować, jak działa wykonywanie synchroniczne.

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

Jeśli uruchomimy cBota z tym kodem, dziennik pokaże, że liczba pozycji rośnie dopiero po realizacji każdej pozycji. Kod czeka na realizację każdego zlecenia przed wysłaniem żądania realizacji następnego.

Aby zmienić na asynchroniczne wykonywanie zleceń, możemy użyć metody 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); 
    }
}

Dodamy również procedurę obsługi zdarzeń, aby nasłuchiwać zdarzenia 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); 
    }
}

W naszej procedurze obsługi zdarzeń wydrukujemy komunikat w dzienniku, gdy tylko pozycja zostanie otwarta, a także liczbę otwartych pozycji w danym momencie.

Jeśli uruchomimy cBota z tym kodem i spojrzymy na dziennik, zobaczymy, że wszystkie zlecenia zostały wysłane bez otrzymania potwierdzenia wyników poprzednich instrukcji. Licznik pozycji przez cały czas pozostawał na 0. Potwierdzenia nadeszły i zostały wydrukowane w dzienniku kilka milisekund po wysłaniu wszystkich zleceń do serwera.

Użyj zleceń zwrotnych

Teraz możemy wrócić do naszego kodu źródłowego i zademonstrować inną cenną funkcję wykonywania asynchronicznego, a mianowicie opcję użycia metod zwrotnych, które są wywoływane natychmiast po zakończeniu realizacji zlecenia. Usuniemy naszą procedurę obsługi zdarzenia Positions.Opened i zaimplementujemy metodę zwrotną, która drukuje potwierdzenie po realizacji zlecenia.

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

Następnie przekażemy tę metodę jako argument do metody ExecuteMarketOrderAsync.

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

W przeciwieństwie do naszego wcześniejszego podejścia metoda zwrotna zostanie wywołana niezależnie od tego, czy transakcja zakończyła się sukcesem, co oznacza również, że jesteś informowany o nieudanych transakcjach i możesz odpowiednio zareagować.

Monitoruj status wykonywania asynchronicznego

cTrader zapewnia również łatwe w użyciu narzędzie do śledzenia statusu transakcji asynchronicznych. Metoda ExecuteMarketOrderAsync zwraca obiekt TradeOperation, który zawiera informacje o tym, czy zlecenie jest nadal realizowane.

Możemy zacząć od zadeklarowania nowego pola.

1
TradeOperation _tradeOperation;

Następnie usuniemy naszą pętlę for i przypiszemy wynik realizacji zlecenia do nowego pola.

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

Dodamy również timer do naszego cBota. Timer będzie sprawdzał status operacji co 100 milisekund.

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

Jeśli operacja jest nadal realizowana, timer wydrukuje to w dzienniku.

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

Po zbudowaniu cBota możemy przetestować, jak działa na koncie demo. W dzienniku timer powinien drukować regularne aktualizacje statusu wykonywania asynchronicznego.

Podsumowanie

Wykonywanie asynchroniczne to potężne narzędzie, które może sprawić, że automatyczne składanie zleceń będzie znacznie szybsze w porównaniu z programowaniem synchronicznym. Jednak wykonywanie synchroniczne może być nadal używane w przypadku, gdy musisz znać wynik transakcji przed przejściem do następnego kroku. Z drugiej strony wykonywanie asynchroniczne najlepiej stosować, gdy musisz wysłać wiele zleceń równolegle bez żadnych opóźnień.