Aller au contenu

Envoyer et recevoir des messages

Introduction

Cet article sert d'introduction à l'utilisation de FIX API pour toute personne intéressée par l'interaction avec Spotware cServer en utilisant FIX.

Dans cet article, nous utiliserons un exemple en C# pour décrire en détail les principes de construction d'un message FIX, son envoi au serveur et la réception de la réponse. Cet exemple n'est en aucun cas une application à toute épreuve et a été maintenu aussi simple que possible pour permettre aux programmeurs de comprendre facilement le concept d'utilisation des messages FIX API.

Afin d'établir et de maintenir une communication appropriée avec le serveur et un traitement approprié des réponses, des fonctionnalités supplémentaires sont nécessaires, qui ont été omises par souci de simplicité et de clarté. Nous traiterons ces sujets dans de futurs articles.

Exemple de code

Vous pouvez trouver l'exemple de code discuté dans cet article sur notre dépôt GitHub

Aperçu de la communication FIX

Un message FIX n'est qu'une chaîne composée d'ensembles de balises numériques et de valeurs séparées par une barre verticale (|). Chaque balise représente un champ différent pour lequel un certain ensemble de valeurs est autorisé. Ci-dessous, vous pouvez voir un exemple de message FIX qui demande l'authentification au serveur.

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|

Comme vous pouvez le voir, le modèle répétable trouvé dans chaque message FIX est :

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

Selon le but de chaque message, un ensemble différent de balises et de valeurs est requis à chaque fois. Les balises et les valeurs requises pour chaque message sont décrites en détail dans les Règles d'engagement du moteur FIX de cTrader (vérifiez toujours les dernières règles d'engagement).

De la même manière, les réponses sont renvoyées par le serveur. Ci-dessous, vous pouvez voir la réponse du serveur pour le message ci-dessus.

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|

Les étapes impliquées dans le processus de communication avec un serveur FIX sont les suivantes :

  1. Construire un message FIX

  2. Transmettre un message FIX

  3. Recevoir un message FIX

  4. Analyser un message FIX

Un message FIX brut n'est pas un format très lisible car il a été conçu dans un souci d'efficacité plutôt que de compréhensibilité. Par conséquent, pour chaque application logicielle, il y aura toujours un processus de traduction des informations fournies dans le message FIX respectif.

Dans notre exemple d'application C#, nous avons créé une classe pour gérer la construction des messages ainsi que des fonctions pour créer des messages FIX basés sur les informations pertinentes.

Une fois les messages construits, ils sont transmis entre un serveur et un client via Internet par le biais de sockets réseau. Lorsque les messages sont reçus, ils doivent être analysés pour être présentés dans un format lisible.

Dans cet article, nous couvrirons le processus de construction, de transmission et de réception de la réponse. Nous traiterons de l'analyse dans un futur article.

Construire un message FIX

Structure du message

Dans notre exemple d'application, nous avons créé une classe responsable de la création des messages FIX. La classe s'appelle MessageConstructor et se trouve dans le projet FIX API Library.

Le MessageConstructor est initialisé avec les paramètres suivants :

  1. Host(*) – l'adresse où se trouve notre cServer.

  2. Username(*) – le numéro de compte

  3. Password(*) – le mot de passe

  4. SenderCompID(*) – il est fourni dans le formulaire API FIX de cTrader. Il est au format

  5. SenderSubID* – c'est la deuxième partie de SenderCompID

  6. TargetCompID(*) – il est fourni dans le formulaire API FIX de cTrader (généralement c'est cServer)

Vous pouvez trouver ces informations dans votre formulaire API FIX de cTrader.

Après avoir initialisé un MessageConstructor, nous sommes prêts à construire des messages API FIX.

Tous les messages sont construits de manière similaire. Ci-dessous se trouve un exemple de code de construction d'un message 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");

       }

Vous pouvez voir que nous construisons d'abord la partie corps, puis nous la transmettons à la fonction d'en-tête et enfin nous transmettons les deux parties à la fonction de fin. Ces 3 parties sont détaillées ci-dessous.

Le processus de construction du message consiste simplement à ajouter les tags, valeurs et séparateurs requis dans une chaîne.

Corps

Nous allons commencer par décrire la construction du corps, car le corps du message doit être créé en premier. Nous pouvons voir un exemple ci-dessus (création du message Logon).

Nous commençons par initialiser une classe StringBuilder et nous ajoutons les balises une par une en fonction des entrées de la fonction. En fonction du type de message, le corps doit être composé de différents ensembles de balises, certaines étant obligatoires et d'autres facultatives.

Vous pouvez trouver la structure de chaque message dans notre Rules of Engagement (/FIX).

Ensuite, nous créons un en-tête pour un message Logon et nous y ajoutons le corps du message. Enfin, en utilisant la chaîne headerAndBody, nous générons la fin. Par la suite, nous verrons comment nous construisons un en-tête et une fin.

En-tête

L'en-tête est la première partie du message FIX et il est composé des champs suivants (identiques pour tous les messages) :

  1. BeginString – la chaîne de début définit la version du protocole FIX et dans notre cas est fixée à FIX4.4.
  2. BodyLength – la longueur du corps indique la longueur du message en caractères, à l'exclusion des champs BeginString, BodyLength et de fin.
  3. MsgType – dans ce champ, nous définissons le type de message, afin que le destinataire sache comment analyser le corps.
  4. SenderCompID – ici, nous définissons le SenderCompID.
  5. TargetCompID – il s'agit de la cible de notre message. Dans notre cas, ce sera toujours cServer.
  6. SenderSubID – l'identifiant du trader.
  7. MsgSeqNum – il s'agit du numéro de séquence du message. Il doit être augmenté pour chaque message envoyé dans la même session.
  8. Sending Time – l'heure de transmission du message.

Ci-dessous, vous pouvez voir la fonction ConstructHeader, responsable de la construction des en-têtes.

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

    }

Pied de page

La fin est simplement une balise contenant la somme de contrôle du reste du message.

 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;

       }

Messages système

Notre exemple contient des fonctions qui renvoient les messages système suivants :

  • Heartbeat : MessageConstructor.HeartbeatMessage()
  • Test request : MessageConstructor.TestRequestMessage()
  • Logon : MessageConstructor.LogonMessage()
  • Logout : MessageConstructor.LogoutMessage()
  • Resend request : MessageConstructor.ResendMessage()
  • Reject : MessageConstructor.RejectMessage()
  • Sequence reset : MessageConstructor.SequenceResetMessage()

Messages d'application

Notre exemple contient des fonctions qui renvoient les messages système suivants :

  • Market data request : MessageConstructor.HeartbeatMessage()
  • Market data snapshot/full refresh : MessageConstructor.MarketDataSnapshotMessage()
  • Market data incremental refresh : MessageConstructor.MarketDataIncrementalRefreshMessage()
  • New order single : MessageConstructor.NewOrderSingleMessage()
  • Order status request : MessageConstructor.OrderStatusRequest()
  • Execution report : MessageConstructor.ExecutionReport()
  • Business message reject : MessageConstructor.BusinessMessageReject()
  • Request for positions : MessageConstructor.RequestForPositions()
  • Position report : MessageConstructor.PositionReport()

Envoyer un message et recevoir une réponse

Pour envoyer un message FIX à cServer, vous devez d'abord établir une connexion avec le serveur. Vous pouvez le faire en créant un TcpClient. Dans notre cas, nous créons deux clients, car les messages de cotation de prix et les messages de trading sont gérés par différents ports sur le serveur.

Ensuite, nous devons obtenir les deux flux sur lesquels les messages seront envoyés. Ce processus a lieu dans le constructeur du formulaire comme indiqué ci-dessous :

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

}

Dans le constructeur, nous initialisons également une classe MessageConstructor qui sera utilisée pour générer les messages.

Ensuite, pour envoyer les messages, nous avons créé deux fonctions différentes, SendPriceMessage() et SendTradeMessage(). Chacune prend le message FIX comme entrée, puis appelle la fonction SendMessage() avec le message et le flux respectif comme entrée.

La fonction SendMessage() fonctionne comme suit :

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

Les étapes détaillées sont les suivantes :

  1. Encoder le message dans un tableau d'octets.
  2. Écrire le tableau d'octets sur le flux.
  3. Lire la réponse du flux.
  4. Augmenter le numéro de séquence du message.
  5. Encoder le message dans une chaîne.

La fonction doit renvoyer le message FIX envoyé par le serveur.

Comme vous pouvez le supposer, vous ne pouvez pas afficher un message FIX brut à l'utilisateur, donc une étape supplémentaire d'analyse du message entrant doit être développée.

Conclusion

Cette application est une brève démonstration sur la façon de communiquer avec cServer en utilisant des messages FIX. Il s'agit simplement d'un exemple illustrant les concepts du protocole FIX et ce n'est en aucun cas un moteur FIX complet. Si vous souhaitez éviter de créer votre propre moteur FIX, vous pourriez envisager d'utiliser l'un des moteurs FIX tiers disponibles.

Remarque

Cet article est à jour au 03/02/2017 et développé en tenant compte du moteur FIX cTrader, Rules of Engagement v2.9.1.