Ir para o conteúdo

Enviar e receber mensagens

Introdução

Este artigo serve como uma introdução à utilização da API FIX para qualquer pessoa interessada em interagir com o cServer da Spotware utilizando FIX.

Neste artigo, utilizaremos um exemplo em C# para descrever em detalhe os princípios de como construir uma mensagem FIX, enviá-la para o servidor e receber a resposta. Este exemplo não é, de forma alguma, uma aplicação à prova de balas e foi mantido o mais simples possível para permitir que os programadores compreendam facilmente o conceito de utilização de mensagens da API FIX.

Para estabelecer e manter uma comunicação adequada com o servidor e um tratamento adequado das respostas, é necessária funcionalidade adicional, que foi omitida por uma questão de simplicidade e clareza. Abordaremos estes assuntos em artigos futuros.

Exemplo de código

Pode encontrar o exemplo de código discutido neste artigo no nosso repositório GitHub

Visão geral da comunicação FIX

Uma mensagem FIX é apenas uma string composta por conjuntos de etiquetas numéricas e valores separados por uma barra vertical (|). Cada etiqueta representa um campo diferente para o qual é permitido um determinado conjunto de valores. Abaixo, pode ver uma mensagem FIX de exemplo que solicita autenticação ao servidor.

8=FIX.4.4|9=126|35=A|49=theBroker.12345|56=CSERVER|34=1|52=20170117- 08:03:04|57=TRADE|50=any_string|98=0|108=30|141=Y|553=12345|554=passw0rd!|10=131|

Como pode ver, o padrão repetível encontrado em cada mensagem FIX é:

Tag=Value|Tag=Value|Tag=Value|...

Dependendo do objetivo de cada mensagem, é necessário um conjunto diferente de etiquetas e valores de cada vez. As etiquetas e valores necessários para cada mensagem são descritos em detalhe nas Regras de Envolvimento do motor FIX do cTrader (verifique sempre as regras de envolvimento mais recentes).

De forma semelhante, as respostas são enviadas de volta pelo servidor. Abaixo, pode ver a resposta do servidor para a mensagem acima.

8=FIX.4.4|9=106|35=A|34=1|49=CSERVER|50=TRADE|52=20170117- 08:03:04.509|56=theBroker.12345|57=any_string|98=0|108=30|141=Y|10=066|

As etapas envolvidas no processo de comunicação com um servidor FIX são as seguintes:

  1. Construir uma mensagem FIX

  2. Transmitir uma mensagem FIX

  3. Receber uma mensagem FIX

  4. Analisar uma mensagem FIX

Uma mensagem FIX em bruto não é um formato muito legível, uma vez que foi concebida tendo em mente a eficiência e não a compreensibilidade. Portanto, para cada aplicação de software, haverá sempre um processo de tradução das informações fornecidas para a respetiva mensagem FIX.

Na nossa aplicação de exemplo em C#, criámos uma classe para lidar com a construção de mensagens, bem como funções para criar mensagens FIX com base nas informações relevantes.

Depois de as mensagens terem sido construídas, são transmitidas entre um servidor e um cliente através da Internet por meio de sockets de rede. Quando as mensagens são recebidas, precisam de ser analisadas para serem apresentadas num formato legível.

Neste artigo, abordaremos o processo de construção, transmissão e receção da resposta. Trataremos da análise num artigo futuro.

Construir uma mensagem FIX

Estrutura da mensagem

Na nossa aplicação de exemplo, criámos uma classe responsável por criar mensagens FIX. A classe chama-se MessageConstructor e pode ser encontrada no projeto FIX API Library.

O MessageConstructor é inicializado com os seguintes parâmetros:

  1. Host(*) – o endereço onde o nosso cServer está localizado.

  2. Username(*) – o número da conta

  3. Password(*) – a palavra-passe

  4. SenderCompID(*) – é fornecido no formulário da API FIX do cTrader. Está no formato

  5. SenderSubID* – é a segunda parte do SenderCompID

  6. TargetCompID(*) – é fornecido no formulário da API FIX do cTrader (geralmente é cServer)

Pode encontrar estas informações no seu formulário da API FIX do cTrader.

Depois de termos inicializado um MessageConstructor, estamos prontos para construir mensagens da API FIX.

Todas as mensagens são construídas de maneira semelhante. Abaixo, há um exemplo de código de construção de uma mensagem de Logon.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public  string LogonMessage(SessionQualifier qualifier, int messageSequenceNumber,

            int  heartBeatSeconds,  bool  resetSeqNum)

       {

            var  body = new  StringBuilder ();

        //Defines a message encryption scheme.Currently, only transportlevel security

       //is supported.Valid value is "0"(zero) = NONE_OTHER (encryption is not used).

           body.Append( "98=0|");

            //Heartbeat interval in seconds. 

           //Value is set in the 'config.properties' file (client side) as 

         // 'SERVER.POLLING.INTERVAL' . 

           //30 seconds is default interval value. If HeartBtInt is set to 0, 

         no heartbeat message  

           //is required.

           body.Append( "108="  + heartBeatSeconds +  "|");

           // All sides of FIX session should have

           //sequence numbers reset. Valid value

            //is "Y" = Yes(reset). 

            if  (resetSeqNum)

               body.Append( "141=Y|");

            //The numeric User ID.  User is linked to SenderCompID (#49) value (the

            //user's organization). 

           body.Append( "553=" + _username + "|");

           //User Password

           body.Append( "554=" + _password + "|");

            var  header = ConstructHeader(qualifier, 

         SessionMessageCode( SessionMessageType.Logon), messageSequenceNumber,

          body.ToString());

            var  headerAndBody = header + body;            

            var  trailer = ConstructTrailer(headerAndBody);

            var  headerAndMessageAndTrailer = header + body + trailer;

            return  headerAndMessageAndTrailer.Replace("|", "\u0001");

       }

Pode ver que primeiro construímos a parte do corpo, depois passamo-la para a função de cabeçalho e, por último, passamos ambas as partes para a função de rodapé. Estas 3 partes são detalhadas abaixo.

O processo de construção da mensagem é apenas a adição das etiquetas, valores e separadores necessários numa string.

Corpo

Primeiro, começaremos por descrever a construção do corpo, uma vez que o corpo da mensagem precisa de ser criado primeiro. Podemos ver um exemplo acima (criando a mensagem de Logon).

Começamos por inicializar uma classe StringBuilder e anexamos as etiquetas uma a uma com base nas entradas da função. Com base no tipo de mensagem, o corpo deve ser composto por diferentes conjuntos de etiquetas, algumas delas sendo obrigatórias e outras opcionais.

Pode encontrar a estrutura de cada mensagem nas nossas Regras de Envolvimento (/FIX).

Em seguida, criamos um cabeçalho para uma mensagem de Logon e anexamos a ele o corpo da mensagem. Finalmente, usando a string headerAndBody, geramos o rodapé. A seguir, veremos como construímos um cabeçalho e um rodapé.

Cabeçalho

O cabeçalho é a primeira parte da mensagem FIX e é composto pelos seguintes campos (iguais para todas as mensagens):

  1. BeginString – a string de início define a versão do protocolo FIX e, no nosso caso, é fixa para FIX4.4.
  2. BodyLength – o comprimento do corpo indica o comprimento da mensagem em caracteres, excluindo os campos BeginString, BodyLength e rodapé.
  3. MsgType – neste campo, definimos o tipo de mensagem, para que o recetor saiba como analisar o corpo.
  4. SenderCompID – aqui definimos o SenderCompID.
  5. TargetCompID – este é o destino da nossa mensagem. No nosso caso, será sempre cServer.
  6. SenderSubID – o login do trader.
  7. MsgSeqNum – este é o número de sequência da mensagem. Precisa de ser aumentado para cada mensagem enviada na mesma sessão.
  8. Sending Time – a hora de transmissão da mensagem.

Abaixo, pode ver a função ConstructHeader, responsável por construir os cabeçalhos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
private  string ConstructHeader(SessionQualifier qualifier, string type,

         int  messageSequenceNumber, string bodyMessage)

    {

         var  header = new  StringBuilder ();

        // Protocol version. FIX.4.4 (Always unencrypted, must be first field 

        // in message.

        header.Append( "8=FIX.4.4|");

         var  message = new  StringBuilder ();

        // Message type. Always unencrypted, must be third field in message.

        message.Append( "35=" + type + "|");

        // ID of the trading party in following format: <BrokerUID>.<Trader Login> 

        // where BrokerUID is provided by cTrader and Trader Login is numeric 

        // identifier of the trader account.

        message.Append( "49="  + _senderCompID +  "|");  

        // Message target. Valid value is "CSERVER"

        message.Append( "56="  + _targetCompID +  "|");  

        // Additional session qualifier. Possible values are: "QUOTE", "TRADE".

        message.Append( "57="  + qualifier.ToString() +  "|");  

        // Assigned value used to identify specific message originator.

        message.Append( "50="  + _senderSubID +  "|");

        // Message Sequence Number

        message.Append( "34="  + messageSequenceNumber +  "|");

         // Time of message transmission (always expressed in UTC(Universal Time  

        // Coordinated, also known as 'GMT').

        message.Append("52=" + DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss") + "|");

         var  length = message.Length + bodyMessage.Length;

        // Message body length. Always unencrypted, must be second field in message.

        header.Append( "9=" + length + "|");

        header.Append(message);      

         return  header.ToString();

    }

Rodapé

O rodapé é apenas uma etiqueta contendo a soma de verificação do resto da mensagem.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private  string ConstructTrailer(string message)

       {

            //Three byte, simple checksum.  Always last field in message; i.e. serves,

           //with the trailing<SOH>, 

           //as the end - of - message delimiter. Always defined as three characters

           //(and always unencrypted).

            var  trailer = "10=" + CalculateChecksum(message.Replace("|", "\u0001").ToString()).ToString().PadLeft(3, '0') + "|";

            return  trailer;

       }

Mensagens do sistema

O nosso exemplo contém funções que retornam as seguintes mensagens do sistema:

  • Heartbeat: MessageConstructor.HeartbeatMessage()
  • Pedido de teste: MessageConstructor.TestRequestMessage()
  • Logon: MessageConstructor.LogonMessage()
  • Logout: MessageConstructor.LogoutMessage()
  • Pedido de reenvio: MessageConstructor.ResendMessage()
  • Rejeitar: MessageConstructor.RejectMessage()
  • Redefinição de sequência: MessageConstructor.SequenceResetMessage()

Mensagens de aplicação

O nosso exemplo contém funções que retornam as seguintes mensagens do sistema:

  • Pedido de dados de mercado: MessageConstructor.HeartbeatMessage()
  • Instantâneo de dados de mercado/atualização completa: MessageConstructor.MarketDataSnapshotMessage()
  • Atualização incremental de dados de mercado: MessageConstructor.MarketDataIncrementalRefreshMessage()
  • Nova ordem única: MessageConstructor.NewOrderSingleMessage()
  • Pedido de estado da ordem: MessageConstructor.OrderStatusRequest()
  • Relatório de execução: MessageConstructor.ExecutionReport()
  • Rejeição de mensagem de negócio: MessageConstructor.BusinessMessageReject()
  • Pedido de posições: MessageConstructor.RequestForPositions()
  • Relatório de posição: MessageConstructor.PositionReport()

Enviar uma mensagem e receber uma resposta

Para enviar uma mensagem FIX para o cServer, primeiro precisa de estabelecer uma ligação com o servidor. Pode fazer isso criando um TcpClient. No nosso caso, criamos dois clientes, uma vez que as mensagens de cotação de preços e as mensagens de negociação são tratadas por portas diferentes no servidor.

Em seguida, precisamos de obter os dois fluxos nos quais as mensagens serão enviadas. Este processo ocorre no construtor do formulário, como mostrado abaixo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public  frmFIXAPISample()
{

   InitializeComponent();

    _priceClient = new  TcpClient( _host, _pricePort);           

   _priceStream = _ priceClient.GetStream ();            

   _tradeClient =  new  TcpClient ( _host, _tradePort);

   _tradeStream = _ tradeClient.GetStream ();

   _messageConstructor = new  MessageConstructor( _host, _username,

       _password, _senderCompID, _senderSubID, _targetCompID);

}

No construtor, também inicializamos uma classe MessageConstructor que será usada para gerar as mensagens.

Em seguida, para enviar as mensagens, criámos duas funções diferentes, SendPriceMessage() e SendTradeMessage(). Cada uma recebe a mensagem FIX como entrada e depois chama a função SendMessage() com a mensagem e o respetivo fluxo como entrada.

A função SendMessage() funciona da seguinte forma:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private  string SendMessage(string message,  NetworkStream  stream)
{
    var  byteArray =  Encoding.ASCII.GetBytes (message);

    stream.Write(byteArray, 0, byteArray.Length);

    var  buffer = new byte[1024];

    int  i = 0;

    while  (!stream.DataAvailable && i < 100)
    {
         Thread.Sleep ( 100);
         i++;
    }

    if( stream.DataAvailable )
        stream.Read(buffer, 0, 1024);

    _messageSequenceNumber++;

    var  returnMessage = Encoding.ASCII.GetString(buffer);

    return  returnMessage;
}

As etapas detalhadas são as seguintes:

  1. Codificar a mensagem para uma matriz de bytes.
  2. Escrever a matriz de bytes no fluxo.
  3. Ler a resposta do fluxo.
  4. Aumentar o número de sequência da mensagem.
  5. Codificar a mensagem numa string.

A função deve retornar a mensagem FIX enviada pelo servidor.

Como pode supor, não pode mostrar uma mensagem FIX em bruto ao utilizador, por isso deve ser desenvolvida uma etapa adicional de análise da mensagem recebida.

Conclusão

Esta aplicação é uma breve demonstração de como comunicar com o cServer usando mensagens FIX. É apenas um exemplo que ilustra os conceitos do Protocolo FIX e não é, de forma alguma, um motor FIX completo. Se quiser evitar construir o seu próprio motor FIX, pode considerar usar um dos motores FIX de terceiros disponíveis.

Nota

Este artigo está atualizado a partir de 03/02/2017 e foi desenvolvido tendo em consideração o motor FIX do cTrader, Regras de Envolvimento v2.9.1.