콘텐츠로 이동

cTrader에서의 병렬 주문 실행

cTrader를 돋보이게 하는 기능 중 하나는 동기 및 비동기 프로그래밍을 모두 사용하여 주문을 실행할 수 있는 능력입니다. 동기 프로그래밍은 주문 실행 결과를 기다린 후 다음 단계로 진행해야 하는 전략을 구현하는 데 사용할 수 있으며, 반면 비동기 프로그래밍은 여러 주문을 동시에 제출할 수 있는 고속 전략을 만들 때 가장 적합합니다.

이 비디오와 해당 글에서는 동기 및 비동기 실행의 차이점을 설명하고, 이러한 접근 방식을 사용하여 새로운 주문을 실행하는 방법을 보여드리겠습니다.

동기 vs 비동기 주문 실행

기본 흐름

동기 주문 실행은 간단한 다이어그램을 사용하여 설명할 수 있습니다. 새로운 주문을 동기적으로 실행할 때, cBot은 하나의 주문이 실행될 때까지 기다린 후 다른 명령을 수행해야 합니다. 즉, cTrader는 서버에 주문을 보내고 응답을 기다린 후에야 다음 코드 라인을 실행하기 시작합니다.

flowchart LR
  A([주문 1, 100 ms]) --> B([주문 2, 70 ms]) --> C([주문 3, 80 ms]) --> D([후속 지침]) 

위의 주문을 동기적으로 실행할 때, cBot은 Order 3 이후에 지정된 명령을 수행하기 전에 총 250ms를 소비해야 합니다.

반면, 비동기 실행은 cTrader가 서버의 응답을 기대하지 않고 여러 주문을 병렬로 보낼 수 있게 합니다. 비동기 프로그래밍을 사용하면 전략이 여러 주문을 실행하는 데 필요한 총 시간을 최소화할 수 있습니다.

flowchart LR
  subgraph Orders
    direction TB
    A([주문 1, 100 ms]) --- B([주문 2, 70 ms]) --- C([주문 3, 80 ms])
  end
  D([후속 지침])
  Orders --> D

위의 다이어그램에서 세 개의 주문이 정확히 동시에 제출되었으며, cBot은 실행 결과를 기다리지 않고 다음 명령을 수행합니다.

코드 예제

코드를 사용하여 동기 실행이 어떻게 작동하는지 보여줄 수도 있습니다.

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

이 코드로 cBot을 실행하면 로그는 각 포지션이 실행된 후에만 포지션 카운트가 증가하는 것을 보여줍니다. 코드는 다음 주문을 실행하기 전에 각 주문이 실행될 때까지 기다립니다.

비동기 주문 실행으로 변경하려면 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); 
    }
}

이벤트 핸들러에서는 포지션이 열리자마자 로그에 메시지를 출력하고 현재 열린 포지션의 수를 출력할 것입니다.

이 코드로 cBot을 실행하고 로그를 확인하면 모든 주문이 이전 명령의 결과에 대한 확인을 받지 않고 전송된 것을 볼 수 있습니다. 포지션 카운터는 전체 시간 동안 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);

또한 cBot에 타이머를 추가할 것입니다. 타이머는 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);
    }
}

cBot이 구축된 후 데모 계정에서 어떻게 작동하는지 테스트할 수 있습니다. 로그에서 타이머는 비동기 실행 상태에 대한 정기적인 업데이트를 출력해야 합니다.

요약

비동기 실행은 자동 주문 배치를 동기 프로그래밍에 비해 훨씬 빠르게 만들 수 있는 강력한 도구입니다. 그러나 동기 실행은 거래 결과를 알고 다음 단계로 진행해야 하는 경우 여전히 사용할 수 있습니다. 반면, 비동기 실행은 지연 없이 여러 주문을 병렬로 보내야 할 때 가장 적합합니다.