Saltar a contenido

Enviar y recibir mensajes

Introducción

Este artículo sirve como introducción al uso de la API FIX para cualquier persona interesada en interactuar con Spotware cServer utilizando FIX.

En este artículo, utilizaremos un ejemplo en C# para describir en detalle los principios de cómo construir un mensaje FIX, enviarlo al servidor y recibir la respuesta. Este ejemplo no es de ninguna manera una aplicación a prueba de balas y se mantuvo lo más simple posible para permitir a los programadores entender fácilmente el concepto de usar mensajes de API FIX.

Para establecer y mantener una comunicación adecuada con el servidor y un manejo apropiado de las respuestas, se requiere funcionalidad adicional, que se omitió en aras de la simplicidad y claridad. Trataremos estos temas en artículos futuros.

Ejemplo de código

Puede encontrar el ejemplo de código discutido en este artículo en nuestro repositorio de GitHub

Descripción general de la comunicación FIX

Un mensaje FIX es simplemente una cadena compuesta por conjuntos de etiquetas numéricas y valores separados por una barra vertical (|). Cada etiqueta representa un campo diferente para el cual se permite un cierto conjunto de valores. A continuación puede ver un ejemplo de mensaje FIX que solicita autenticación al 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 puede ver, el patrón repetible que se encuentra en cada mensaje FIX es:

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

Dependiendo del propósito de cada mensaje, se requiere un conjunto diferente de etiquetas y valores cada vez. Las etiquetas y valores requeridos para cada mensaje se describen en detalle en las Reglas de Compromiso del motor FIX de cTrader (siempre verifique las últimas reglas de compromiso).

De manera similar, las respuestas son enviadas de vuelta desde el servidor. A continuación puede ver la respuesta del servidor para el mensaje anterior.

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|

Los pasos involucrados en el proceso de comunicación con un servidor FIX son los siguientes:

  1. Construir un mensaje FIX

  2. Transmitir un mensaje FIX

  3. Recibir un mensaje FIX

  4. Analizar un mensaje FIX

Un mensaje FIX sin procesar no es un formato muy legible ya que fue diseñado pensando en la eficiencia más que en la comprensibilidad. Por lo tanto, para cada aplicación de software siempre habrá un proceso de traducción de la información proporcionada al mensaje FIX respectivo.

En nuestra aplicación de muestra en C# hemos creado una clase para manejar la construcción de mensajes, así como funciones para crear mensajes FIX basados en la información relevante.

Después de que los mensajes han sido construidos, se transmiten entre un servidor y un cliente a través de internet mediante sockets de red. Cuando se reciben los mensajes, necesitan ser analizados para ser presentados en un formato legible.

En este artículo cubriremos el proceso de construcción, transmisión y recepción de la respuesta. Trataremos el análisis en un artículo futuro.

Construir un mensaje FIX

Estructura del mensaje

En nuestra aplicación de muestra hemos creado una clase responsable de crear mensajes FIX. La clase se llama MessageConstructor y se puede encontrar en el proyecto de la biblioteca de API FIX.

El MessageConstructor se inicializa con los siguientes parámetros:

  1. Host(*) – la dirección donde se encuentra nuestro cServer.

  2. Username(*) – el número de cuenta

  3. Password(*) – la contraseña

  4. SenderCompID(*) – se proporciona en el formulario de API FIX de cTrader. Está en el formato

  5. SenderSubID* – es la segunda parte de SenderCompID

  6. TargetCompID(*) – se proporciona en el formulario de API FIX de cTrader (generalmente es cServer)

Puede encontrar esta información en su formulario de API FIX de cTrader.

Después de haber inicializado un MessageConstructor estamos listos para construir mensajes de API FIX.

Todos los mensajes se construyen de manera similar. A continuación hay un ejemplo de código de construcción de un mensaje 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");

       }

Puede ver que primero construimos la parte del cuerpo, luego la pasamos a la función de encabezado y por último pasamos ambas partes a la función de trailer. Estas 3 partes se detallan a continuación.

El proceso de construcción del mensaje es simplemente la adición de las etiquetas requeridas, valores y separadores en una cadena.

Cuerpo

Primero comenzaremos describiendo la construcción del cuerpo, ya que el cuerpo del mensaje debe crearse primero. Podemos ver un ejemplo arriba (creando el mensaje de Logon).

Comenzamos inicializando una clase StringBuilder y agregamos las etiquetas una por una basándonos en las entradas de la función. Según el tipo de mensaje, el cuerpo debe estar compuesto por diferentes conjuntos de etiquetas, algunas de ellas siendo obligatorias y otras opcionales.

Puede encontrar la estructura de cada mensaje en nuestras Reglas de Compromiso (/FIX).

Luego creamos un encabezado para un mensaje de Logon y le agregamos el cuerpo del mensaje. Finalmente, usando la cadena headerAndBody generamos el trailer. A continuación, veremos cómo construimos un encabezado y un trailer.

Encabezado

El encabezado es la primera parte del mensaje FIX y está compuesto por los siguientes campos (igual para todos los mensajes):

  1. BeginString – la cadena de inicio define la versión del protocolo FIX y en nuestro caso está fijada en FIX4.4.
  2. BodyLength – la longitud del cuerpo indica la longitud del mensaje en caracteres, excluyendo los campos BeginString, BodyLength y trailer.
  3. MsgType – en este campo definimos el tipo de mensaje, para que el receptor sepa cómo analizar el cuerpo.
  4. SenderCompID – aquí establecemos el SenderCompID.
  5. TargetCompID – este es el destino de nuestro mensaje. En nuestro caso, siempre será cServer.
  6. SenderSubID – el inicio de sesión del operador.
  7. MsgSeqNum – este es el número de secuencia del mensaje. Debe incrementarse para cada mensaje enviado en la misma sesión.
  8. Sending Time – la hora de transmisión del mensaje.

A continuación puede ver la función ConstructHeader, responsable de construir los encabezados.

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

    }

Trailer

El trailer es solo una etiqueta que contiene la suma de comprobación del resto del mensaje.

 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;

       }

Mensajes del sistema

Nuestra muestra contiene funciones que devuelven los siguientes mensajes del sistema:

  • Latido: MessageConstructor.HeartbeatMessage()
  • Solicitud de prueba: MessageConstructor.TestRequestMessage()
  • Inicio de sesión: MessageConstructor.LogonMessage()
  • Cierre de sesión: MessageConstructor.LogoutMessage()
  • Solicitud de reenvío: MessageConstructor.ResendMessage()
  • Rechazo: MessageConstructor.RejectMessage()
  • Restablecimiento de secuencia: MessageConstructor.SequenceResetMessage()

Mensajes de aplicación

Nuestra muestra contiene funciones que devuelven los siguientes mensajes del sistema:

  • Solicitud de datos de mercado: MessageConstructor.HeartbeatMessage()
  • Instantánea de datos de mercado/actualización completa: MessageConstructor.MarketDataSnapshotMessage()
  • Actualización incremental de datos de mercado: MessageConstructor.MarketDataIncrementalRefreshMessage()
  • Nueva orden individual: MessageConstructor.NewOrderSingleMessage()
  • Solicitud de estado de orden: MessageConstructor.OrderStatusRequest()
  • Informe de ejecución: MessageConstructor.ExecutionReport()
  • Rechazo de mensaje de negocio: MessageConstructor.BusinessMessageReject()
  • Solicitud de posiciones: MessageConstructor.RequestForPositions()
  • Informe de posición: MessageConstructor.PositionReport()

Enviar un mensaje y recibir una respuesta

Para enviar un mensaje FIX a cServer, primero debe establecer una conexión con el servidor. Puede hacer esto creando un TcpClient. En nuestro caso, creamos dos clientes, ya que los mensajes de cotización de precios y los mensajes de operaciones son manejados por diferentes puertos en el servidor.

Luego, necesitamos obtener los dos flujos en los que se enviarán los mensajes. Este proceso tiene lugar en el constructor del formulario como se muestra a continuación:

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

}

En el constructor, también inicializamos una clase MessageConstructor que se utilizará para generar los mensajes.

A continuación, para enviar los mensajes, creamos dos funciones diferentes, SendPriceMessage() y SendTradeMessage(). Cada una toma el mensaje FIX como entrada y luego llama a la función SendMessage() con el mensaje y el flujo respectivo como entrada.

La función SendMessage() funciona de la siguiente manera:

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

Los pasos detallados son los siguientes:

  1. Codificar el mensaje en una matriz de bytes.
  2. Escribir la matriz de bytes en el flujo.
  3. Leer la respuesta del flujo.
  4. Aumentar el número de secuencia del mensaje.
  5. Codificar el mensaje en una cadena.

La función debe devolver el mensaje FIX enviado por el servidor.

Como supondría, no puede mostrar un mensaje FIX sin procesar al usuario, por lo que se debe desarrollar un paso adicional de análisis del mensaje entrante.

Conclusión

Esta aplicación es una breve demostración de cómo comunicarse con cServer utilizando mensajes FIX. Es solo un ejemplo que ilustra los conceptos del Protocolo FIX y de ninguna manera es un motor FIX completo. Si desea evitar construir su propio motor FIX, podría considerar usar uno de los motores FIX de terceros disponibles.

Nota

Este artículo está actualizado al 03/02/2017 y desarrollado teniendo en cuenta el motor FIX de cTrader, Reglas de Compromiso v2.9.1.