Ir para o conteúdo

Execução de ordens em paralelo no cTrader

Uma das funcionalidades que fazem o cTrader destacar-se é a capacidade de utilizar programação síncrona e assíncrona para executar as suas ordens. Pode utilizar programação síncrona para implementar estratégias que precisam de aguardar pelos resultados da execução da ordem antes de prosseguir; por outro lado, a programação assíncrona é melhor utilizada ao criar uma estratégia de alta velocidade que pode submeter várias ordens simultaneamente.

Neste vídeo e no artigo correspondente, iremos explicar a diferença entre execução síncrona e assíncrona e mostrar-lhe-emos como utilizar ambas estas abordagens para colocar novas ordens.

Execução de ordens síncrona vs. assíncrona

Fluxos básicos

A execução de ordens síncrona pode ser explicada utilizando um diagrama simples. Ao colocar novas ordens de forma síncrona, um cBot deve aguardar que uma ordem seja executada antes de seguir quaisquer outras instruções. Por outras palavras, o cTrader envia uma ordem para o servidor, aguarda pela resposta e só então começa a executar a linha de código seguinte.

flowchart LR
  A([Ordem 1, 100 ms]) --> B([Ordem 2, 70 ms]) --> C([Ordem 3, 80 ms]) --> D([Instruções subsequentes]) 

Ao colocar as ordens acima de forma síncrona, o seu cBot terá de gastar um total de 250 ms antes de prosseguir para quaisquer instruções especificadas após a Ordem 3.

Por outro lado, a execução assíncrona permite que o cTrader envie várias ordens em paralelo sem esperar uma resposta do servidor. Ao utilizar programação assíncrona, uma estratégia pode minimizar o tempo total necessário para executar várias ordens.

flowchart LR
  subgraph Orders
    direction TB
    A([Ordem 1, 100 ms]) --- B([Ordem 2, 70 ms]) --- C([Ordem 3, 80 ms])
  end
  D([Instruções subsequentes])
  Orders --> D

No diagrama acima, as três ordens são submetidas exatamente ao mesmo tempo e o cBot não aguarda pelos seus resultados de execução antes de prosseguir para as instruções subsequentes.

Exemplos de código

Também podemos utilizar código para demonstrar como funciona a execução síncrona.

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

Se executarmos um cBot com este código, o registo mostrará que a contagem de posições só aumenta após cada posição ser executada. O código está a aguardar que cada ordem seja executada antes de enviar um pedido para executar a seguinte.

Para mudar para a execução de ordens assíncrona, podemos utilizar o método 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); 
    }
}

Também iremos adicionar um gestor de eventos para escutar o evento 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); 
    }
}

No nosso gestor de eventos, iremos imprimir uma mensagem no registo assim que uma posição for aberta, bem como o número de posições abertas no momento.

Se executarmos um cBot com este código e olharmos para o registo, veremos que todas as ordens foram enviadas sem receber uma confirmação sobre os resultados das instruções anteriores. O contador de posições permaneceu a 0 durante todo o tempo. As confirmações chegaram e foram impressas no registo alguns milissegundos depois de todas as ordens terem sido enviadas para o servidor.

Utilizar ordens de retorno

Agora podemos voltar ao nosso código-fonte e demonstrar outra funcionalidade valiosa da execução assíncrona, nomeadamente a opção de utilizar métodos de retorno que são chamados assim que a execução da ordem é concluída. Iremos remover o nosso gestor do evento Positions.Opened e implementar um método de retorno que imprime uma confirmação assim que uma ordem tiver sido executada.

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

Em seguida, iremos passar este método como um argumento para o método ExecuteMarketOrderAsync.

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

Em contraste com a nossa abordagem anterior, o método de retorno será chamado independentemente de uma negociação ter sido bem-sucedida ou não, o que também significa que é informado de negociações não bem-sucedidas e pode agir em conformidade.

Monitorizar o estado da execução assíncrona

O cTrader também fornece uma ferramenta fácil de utilizar para acompanhar o estado das negociações assíncronas. O método ExecuteMarketOrderAsync devolve um objeto TradeOperation que contém informações sobre se a ordem ainda está a ser executada.

Podemos começar por declarar um novo campo.

1
TradeOperation _tradeOperation;

Em seguida, iremos remover o nosso ciclo for e atribuir o resultado da execução da ordem ao novo campo.

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

Também iremos adicionar um temporizador ao nosso cBot. O temporizador irá verificar o estado de uma operação a cada 100 milissegundos.

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

Se uma operação ainda estiver a ser executada, o temporizador irá imprimir isto no registo.

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

Depois de o cBot ser construído, podemos testar como funciona numa conta demo. No registo, o temporizador deverá imprimir atualizações regulares sobre o estado da execução assíncrona.

Resumo

A execução assíncrona é uma ferramenta poderosa que pode tornar a colocação automática de ordens muito mais rápida em comparação com a programação síncrona. No entanto, a execução síncrona ainda pode ser utilizada caso precise de saber o resultado de uma negociação antes de prosseguir para o passo seguinte. A execução assíncrona, por outro lado, é melhor utilizada quando precisa de enviar várias ordens em paralelo sem quaisquer atrasos.