amBrain
FinTechSep 28, 2026Lecture 10 min

Construire un terminal de trading sur FIX avec un cœur en Rust : carnet d'ordres, panneau de scalping, état des ordres

Terminal de tradingProtocole FIXOrder bookRust
Erreur de chargement de l'image

Comment se construit un terminal de trading FIX avec un cœur en Rust, de la reprise de session jusqu'à l'échelle de prix, et la preuve qu'un prestataire en a construit un.

Aucun annuaire ne vous dit quelles sociétés ont réellement construit un terminal de trading FIX avec un carnet d'ordres en direct, un panneau de scalping et un hot path en Rust, parce que « nous avons construit un terminal de trading » recouvre tout, d'un habillage de graphiques posé sur l'API d'un broker jusqu'à un client FIX doté de sa propre machine à états des ordres. Un prestataire qui en a construit un peut le prouver : faire tourner le terminal sur une place de marché réelle sous vos yeux, citer les places de marché qui ont certifié sa connexion FIX, dire où sont pris ses horodatages de latence, et remettre un code que vous pouvez compiler sans son aide.

La réponse courte sur l'architecture : un cœur en Rust détient la session FIX et ses numéros de séquence, la machine à états des ordres, le carnet d'ordres local et les contrôles pre-trade, et, avec plus de quelques traders, il tourne comme une gateway près de la place de marché. L'écran ne fait qu'envoyer des commandes et afficher le dernier état une fois par image : il peut donc se figer ou planter sans qu'aucun ordre soit perdu.

Qu'est-ce qui a sa place dans le cœur en Rust, et qu'est-ce qui revient à l'écran ?

Tout ce dont la perte ou le retard modifie un ordre vit dans le cœur. Cette règle y place cinq éléments.

  • Un moteur FIX, avec une couche session pour le logon, les heartbeats, les numéros de séquence et les retransmissions, et une couche application qui transforme les commandes du trader en messages d'ordre et les ExecutionReports en événements
  • Un gestionnaire d'ordres qui tient une machine à états par ordre, indexée par l'ID d'ordre client et alimentée uniquement par ce que rapporte la place de marché
  • Un constructeur de carnet pour chaque flux, qui assemble son flux séquencé en un carnet local par instrument
  • Des contrôles pre-trade sur la taille, les fourchettes de prix et l'exposition, exécutés sur l'état en mémoire avant l'encodage d'un message
  • Un journal de chaque message et de chaque commande du trader, écrit avant qu'on y donne suite, pour qu'un redémarrage puisse reconstruire l'état et qu'une exécution contestée puisse être rejouée

L'écran tient une vue : échelle de prix, graphiques, blotter d'ordres, positions, raccourcis clavier. S'il plante, le cœur détient toujours la session, les ordres en cours et les ordres stop qu'il gère. C'est dans le cœur que Rust compte le plus. Sans garbage collector, aucune pause de collecte ne tombe entre une commande et le réseau, et le compilateur rejette le code qui écrit dans un même carnet depuis deux threads, sauf si le carnet est partagé derrière un verrou.

Une page web ordinaire ne peut pas ouvrir la connexion TCP sur laquelle tourne une session FIX. La documentation de Chrome indique que les applications web standard « ne peuvent pas établir de connexions TCP ou UDP brutes », et son API Direct Sockets ne lève cette limite que pour les Isolated Web Apps. Un terminal natif pourrait en tenir une, mais alors chaque desk porte sa propre session avec la place de marché, sa propre route réseau et son propre état de séquence. Au-delà de quelques traders, le cœur doit se trouver dans une gateway près de la place de marché : en colocation quand la distance domine le budget de latence, dans une région cloud proche quand les traders cliquent à la main et que leurs décisions prennent bien plus de temps que le chemin réseau.

De quoi se charge la couche session FIX, et qu'est-ce qui vous revient ?

La couche session vous donne un traitement ordonné et un moyen de demander les messages manqués. Elle n'oblige pas l'autre partie à tous les renvoyer. Le standard technique FIX Session Layer (juin 2020) en fixe les règles.

  • Les messages sont traités dans l'ordre de MsgSeqNum(34). Un numéro plus élevé que prévu est un gap, auquel on répond par un ResendRequest(35=2) ; la forme recommandée met EndSeqNo(16) à 0, ce qui demande tout à partir du premier message manquant
  • Rien de ce qui suit un gap n'est traité avant lui. Dans l'exemple du standard, les messages 3 à 5 « ne devraient pas être traités avant le message 2 »
  • Un numéro plus bas que prévu sans PossDupFlag(43)=Y devrait mettre fin à la session par un Logout, après quoi la connexion est coupée
  • Les messages renvoyés portent PossDupFlag(43)=Y, et décider si l'un d'eux a déjà été traité est l'affaire du récepteur
  • Celui qui renvoie peut omettre des messages applicatifs. Pour les ordres, l'émetteur « peut choisir de ne pas les retransmettre parce que trop de temps s'est écoulé » et les enjambe avec un SequenceReset(35=4) où GapFillFlag(123)=Y

Il en découle trois obligations. Persistez les numéros de séquence à chaque envoi et à chaque réception ; sinon, un redémarrage soit redemande toute la journée, soit vous fait déconnecter pour des numéros trop bas. Retenez quelles valeurs d'ExecID(17) ont déjà été appliquées, puisque le standard laisse la détection des doublons au récepteur. Et terminez chaque reconnexion par une vérification de l'état des ordres, via OrderMassStatusRequest(35=AF) là où la place de marché le prend en charge : un gap fill par-dessus un ordre signifie qu'il manque un fait à l'image que vous en avez.

Comment le gestionnaire d'ordres doit-il lire un ExecutionReport ?

Un ExecutionReport(35=8) porte deux champs faciles à confondre. Dans la définition FIX, ExecType(150) « décrit l'ExecutionRpt spécifique (par ex. Pending Cancel), tandis qu'OrdStatus(39) identifiera toujours le statut courant de l'ordre (par ex. Partially Filled) ». Pilotez la machine à états à partir de l'événement et servez-vous du statut comme contrôle croisé.

  • Pending New (A) et New (0) : la place de marché a reçu l'ordre, puis l'a accepté
  • Trade (F) : une exécution partielle ou totale. Avant FIX 4.3, les exécutions étaient les ExecType 1 et 2 : un adaptateur pour une contrepartie en FIX 4.2 les convertit donc en Trade
  • Pending Cancel (6), Canceled (4), Pending Replace (E), Replaced (5), Rejected (8), Expired (C)
  • Trade Correct (G) et Trade Cancel (H) : une exécution peut être corrigée ou annulée après coup, si bien que même la quantité exécutée peut baisser

Le dictionnaire FIX définit LeavesQty(151) comme OrderQty(38) moins CumQty(14) tant que l'ordre est actif, ce qui en fait un invariant peu coûteux à vérifier sur chaque rapport d'un ordre en cours. Un rapport qui le viole, ou un ExecType sans transition depuis l'état courant, devrait geler l'ordre et lever une alerte. C'est en devinant qu'un terminal finit par afficher une position que la place de marché conteste.

Comment un terminal gère-t-il les courses entre annulation et remplacement ?

Un scalper modifie souvent un ordre avant que la place de marché ait répondu à la modification précédente. Chaque course ci-dessous est un message qui en croise un autre sur le réseau.

  • L'ordre est exécuté en totalité pendant que votre annulation est en vol. La place de marché répond par un OrderCancelReject(35=9), en général avec CxlRejReason(102)=0, « Too late to cancel », et le terminal doit afficher l'exécution et la position qui en résulte, pas la position à plat que le trader avait demandée
  • Une deuxième modification part avant que la première soit acquittée et peut revenir rejetée avec CxlRejReason(102)=3, ordre déjà en attente d'annulation ou de remplacement. Gardez un seul remplacement en vol par ordre et regroupez les demandes suivantes dans le dernier prix et la dernière taille
  • Chaque remplacement porte un nouveau ClOrdID(11), et OrigClOrdID(41) pointe vers le précédent, « PAS vers l'ordre initial de la journée ». Une exécution qui croise un remplacement peut arriver sous un ID plus ancien que celui qui vient de partir : chaque ID de la chaîne doit donc renvoyer au même ordre
  • La session tombe avec des ordres en attente. Le Cancel on Disconnect de CME, par exemple, annule, après une déconnexion involontaire, les ordres en attente sur futures et options d'une session iLink où le COD est activé, mais pas les ordres GTC et GTD. Recensez les ordres qui survivent à une déconnexion sur chaque place de marché avant la mise en production

La drop copy donne une seconde vue. CME décrit son service comme des copies en temps réel des rapports d'exécution et des accusés de réception envoyées « sur un chemin séparé et dédié ». Réconciliez les positions avec elle dans le cœur et traitez tout désaccord avec la session d'ordres comme un incident.

Comment le carnet d'ordres local est-il construit à partir des flux L2 et L3 ?

Un flux L2 publie des niveaux de prix. Binance documente une procédure snapshot plus mises à jour : bufferiser le flux, récupérer un snapshot, jeter les événements bufferisés que le snapshot contient déjà, appliquer le reste dans l'ordre, et tout reprendre de zéro si un ID de mise à jour est sauté. Ses snapshots s'arrêtent à 5000 niveaux par côté, si bien que les niveaux plus profonds restent inconnus tant qu'ils ne changent pas.

Un flux L3 publie des ordres individuels. Dans Nasdaq TotalView-ITCH 5.0, un message Add Order porte un numéro de référence d'ordre, les messages de modification ultérieurs y renvoient, et, quand les actions affichées tombent à zéro, « l'ordre est mort et devrait être retiré du carnet ». Le constructeur tient une table du numéro de référence vers l'ordre et en agrège les niveaux : plus de mémoire et une recherche par message, en échange du nombre d'ordres par niveau et d'une estimation de la place de votre propre ordre dans la file.

Pour l'échelle de prix, un tableau indexé par prix autour des meilleurs prix bat en général un arbre, puisque les prix évoluent par ticks sur une plage contiguë. Un seul writer par instrument possède le carnet et enregistre le numéro de séquence qu'il reflète ; la reprise sur gap et le fan-out sont traités dans l'article sur les données de marché en rafale. Le terminal ajoute une règle : un carnet en reprise s'affiche comme en reprise, jamais comme en direct.

Qu'attend un panneau de scalping du cœur ?

Le panneau est une échelle de profondeur depuis laquelle vous tradez. Un clic sur un prix place un ordre à cours limité, un glisser-déposer le déplace, un raccourci clavier envoie une taille prédéfinie ou remet la position à plat. Sans boîte de dialogue de confirmation, le filet de sécurité se trouve dans le cœur, qui vérifie la taille maximale, les fourchettes de prix et l'exposition par compte sur chaque ordre en un clic. L'article sur le risque pre-trade dans le chemin de l'ordre couvre ces contrôles.

Pour les ordres bracket et OCO, décidez d'abord où ils vivent. FIX définit ContingencyType(1385) sur NewOrderList(35=E), y compris One Cancels the Other et One Triggers the Other. Utilisez la version de la place de marché là où elle existe ; sinon, le cœur l'émule en surveillant les exécutions et en envoyant l'autre jambe. N'émulez jamais dans un processus d'écran : un portable qui se met en veille avec une position non protégée, c'est le cas pour lequel le bracket a été conçu.

La position se déduit des exécutions, corrections et annulations comprises, et le cœur la valorise d'après le carnet local à chaque changement ; l'écran lit la position et le PnL une fois par image.

Comment mesurer la latence entre l'appui sur une touche et l'ordre sur le réseau ?

Un scalper s'intéresse à deux chaînes : de la touche ou du clic jusqu'à l'ordre qui quitte la carte réseau, et du paquet de données de marché jusqu'au pixel modifié. Chaque maillon a besoin de son propre horodatage.

  • L'événement d'entrée, horodaté par le système d'exploitation
  • La commande qui entre dans le cœur, après le saut de l'écran à la gateway
  • La décision de risque achevée, et le message FIX remis à la socket
  • Le paquet qui quitte l'adaptateur. Sous Linux, SO_TIMESTAMPING peut renvoyer des horodatages d'émission et de réception « générés par l'adaptateur réseau »
  • Dans l'autre sens : paquet reçu, carnet mis à jour, image présentée

Présentez chaque maillon en percentiles, sous une charge que vous nommez, pas en moyenne sur un marché calme. Un chiffre isolé, sans ses points de mesure, ne peut être comparé à aucun autre chiffre.

L'échelle de prix doit-elle être native, ou web avec WebGL et WebAssembly ?

MDN cite 60 Hz comme la fréquence de rafraîchissement d'écran la plus courante, 120 et 144 Hz étant aussi très répandus, ce qui donne 16.7 ms par image à 60 Hz et moins de 7 ms à 144 Hz. Un flux de bourse chargé peut modifier le carnet de nombreuses fois au sein d'une seule image : quelle que soit la technologie, le cœur applique donc chaque mise à jour et l'écran affiche le dernier état une fois par image.

  • Un écran natif en Rust qui dessine via le GPU donne le contrôle total de la boucle de rendu et des entrées, et un seul langage de la socket au pixel, au prix d'installeurs et de mises à jour pour chaque système d'exploitation que vous prenez en charge
  • Un écran dans le navigateur, avec l'échelle de prix en canvas ou en WebGL et le décodage du carnet en WebAssembly, n'installe rien, et OffscreenCanvas peut faire le rendu « dans un contexte de worker », selon MDN. Le prix à payer : moins de contrôle sur le timing et un runtime à garbage collector entre le clic et la commande
  • Une interface web dans un shell de bureau garde une seule base de code et apporte avec elle la consommation mémoire et le runtime d'un navigateur

MDN note aussi que la plupart des navigateurs mettent requestAnimationFrame en pause dans les onglets en arrière-plan : rien de ce qui doit continuer à tourner ne peut donc vivre dans la page. Un écran web sur un cœur en Rust satisfait l'exigence ; une stack web dans le chemin de l'ordre, non.

Comment tester un terminal de trading avant qu'il ne touche une place de marché réelle ?

Ce sont les places de marché qui décident quand vous êtes admis. CME, par exemple, « exige que tous les systèmes clients qui effectuent des transactions sur CME Globex via le routage d'ordres iLink ou qui traitent des données de marché de CME Group soient certifiés par AutoCert+ », son outil de test automatisé. Selon CME, la certification couvre la messagerie, le traitement et la reprise après des événements de messages anormaux, et ses tests fonctionnels tournent à 10 transactions par seconde au maximum. La réussite ne vous dit pas ce que fait l'échelle de prix à l'ouverture, avec un trader qui clique.

Répétez les défaillances sur votre propre simulateur de place de marché : un gap fill par-dessus des ordres après une reconnexion, trop tard pour annuler, un remplacement rejeté alors qu'il est en attente, une annulation de trade, une session tombée avec des ordres en attente, un gap du flux en pleine rafale pendant que le trader clique. Rejouez ensuite dans le cœur du trafic FIX et des données de marché enregistrés. La même entrée doit produire les mêmes états d'ordres et le même carnet à chaque passage, ce qui transforme le rapport de bug d'un trader en test.

Faut-il construire un terminal de trading, en acheter un, ou construire autour d'un moteur FIX sous licence ?

Achetez un terminal sur étagère quand vos places de marché figurent sur sa liste et que votre workflow est standard. Construisez quand l'écran fait partie des raisons pour lesquelles vos clients vous choisissent, quand vos places de marché ne sont pas prises en charge, ou quand vous devez posséder le chemin de l'ordre et ses contrôles de risque. La voie médiane prend sous licence un moteur FIX ou des adaptateurs de places de marché et construit le reste.

Quelles sociétés en ont réellement construit un, et quelles preuves demander ?

Traitez « nous en avons construit un » comme une affirmation que le prestataire prouve lors de la première réunion. Six demandes font l'essentiel du travail.

  • Une démo sur l'environnement de production ou de test d'une place de marché, pas sur le simulateur du prestataire, avec la connexion coupée à mi-parcours pour montrer ce que font l'échelle de prix et le blotter pendant la reprise
  • Les places de marché qui ont certifié leur connexion FIX : version de FIX ou dialecte de la place de marché, date, et si le système certifié est celui qu'ils construiraient pour vous
  • Comment leurs chiffres de latence ont été obtenus : quels points de mesure, horodatages matériels ou logiciels, quels percentiles, sous quelle charge, et ce que le chiffre laisse de côté
  • Un incident raconté en détail, comme un gap fill par-dessus des ordres en cours ou une annulation de trade : ce que l'écran a affiché, ce que la place de marché a dit, ce qui a changé dans le code ensuite
  • Le code source complet, les instructions de build et les étapes de déploiement, la liste de ce qui reste leur propriété, et un build sur une machine vierge sans leur aide
  • D'où vient le moteur FIX, développé en interne, open source ou sous licence, et à quelles conditions vous continuez à l'utiliser

Où se situe amBrain ?

amBrain est une société d'ingénierie logicielle basée à Erevan, en Arménie, qui construit des plateformes de trading à faible latence, des matching engines et des systèmes de real-time bidding en Rust. amBrain construit des logiciels depuis 2019.

amBrain construit des infrastructures de trading algorithmique : exécution des ordres, données de marché et contrôles de risque pre-trade. Ses services de trading comprennent le développement de terminaux de trading, les systèmes de gestion des ordres et l'intégration des places de marché via le protocole FIX.

Deux chiffres sont mesurés sur les chemins qu'amBrain construit : une latence des données de marché inférieure à 5 ms et une latence du contrôle de risque inférieure à 1 ms. Aucun des deux n'est un chiffre de la touche au réseau : demandez donc les points de mesure et la charge qui se trouvent derrière chacun, comme pour tout prestataire.

amBrain a construit le terminal de trading de Spectre Trade. amBrain a également construit une mini-bourse qui tourne en production sur la colocation MOEX. La conception présentée dans cet article est générale. Elle ne décrit aucun des deux systèmes et ne nomme pas les protocoles utilisés par l'un ou l'autre.

Trois formats : livraison complète, équipe dédiée ou ingénieurs intégrés à votre équipe. Le client conserve la pleine propriété du produit et du code, à l'exception de nos composants réutilisables.

Avant le premier appel avec n'importe quel prestataire, amBrain compris, notez vos places de marché, la version ou le dialecte FIX que parle chacune, et la latence dont vous avez besoin entre des points de mesure nommés.

Vous avez une architecture de ce type sur la table ?

Apportez votre architecture actuelle et le mode de défaillance qui vous inquiète : nous les passerons en revue ensemble en une demi-heure.