actu-image
Software engineering - 26 Jan 2026

FIX Protocol - Implémenter un système de trading avec Java et Spring Boot [Partie 3]

Photo de Achraf HASBI

Article rédigé par

Achraf Hasbi

Practice Leader Java

Java enthusiast, avec un intérêt marqué pour l’écosystème Java et les mécanismes internes de la JVM.

Dans cette troisième partie de la série “Implémenter un système de trading avec Java et Spring Boot”, nous passons à la pratique en implémentant notre propre FIX Acceptor à l’aide de Java, Spring Boot et QuickFIX/J

Pour aller plus loin (re)découvrez la Partie 2 de “Implémenter un système de trading avec Java et Spring Boot”

Pour ce tutoriel, nous utiliserons Java 21 et Spring Boot 3.5.5.

Configuration de l’application

Commençons par générer un nouveau projet Spring Boot à l’aide de Spring Initializr.
Rendez-vous sur start.spring.io et créez un projet avec les paramètres suivants.

Configuration de l'application

Une fois le projet généré, l’étape suivante consiste à ajouter les dépendances QuickFIX/J dans votre fichier pom.xml. Ces bibliothèques fournissent à notre application Spring Boot le moteur FIX nécessaire.

<dependencies>
    ...

    <dependency>
      <groupId>io.allune</groupId>
      <artifactId>quickfixj-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
      <groupId>org.quickfixj</groupId>
      <artifactId>quickfixj-messages-fix42</artifactId>
    </dependency>

    ...
</dependencies>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.allune</groupId>
      <artifactId>quickfixj-spring-boot-starter</artifactId>
      <version>3.3.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Le QuickFIX/J Spring Boot Starter (v3.3.0) embarque quickfixj-core v2.3.2. Notez également que nous utilisons la spécification FIX 4.2.

Après avoir mis à jour le pom.xml, synchronisez vos dépendances Maven puis lancez l’application. Si tout est correctement configuré, elle démarrera et vous verrez des logs similaires à ceux-ci :

FIX Protocol Java Spring Boot

Avant de construire notre FIX Acceptor, nous avons besoin d’un Initiator auquel nous connecter. Pour ce tutoriel, nous utiliserons Banzai comme initiateur factice : il est léger et parfaitement adapté aux tests.

Télécharger Banzai
FIX Protocol Java Spring Boot

Téléchargez, décompressez, puis ajoutez ResetOnLogon=Y dans le fichier banzai.cfg afin de réinitialiser les numéros de séquence à chaque connexion, puis lancez :

java -jar dist/banzai.jar

Assurez-vous d’être à la racine du projet avant d’exécuter la commande.

Si tout se passe bien, Banzai démarre et affiche son interface principale.

interface banzai
Pour plus de détails sur Banzai et ses fonctionnalités, consultez la documentation officielle : FIXimulator Thesis PDF

⚠️ Important : Banzai est compatible uniquement avec FIX 4.2.

Construction d’un Acceptor

Pour construire notre FIX Acceptor, nous avons besoin de cinq composants clés :

  • Application — Le cœur de l’Acceptor, responsable de l’envoi et de la réception des messages
  • MessageFactory — Crée les messages selon la spécification FIX
  • SessionSettings — Gère la configuration de session (connexion, heartbeat, etc.)
  • LogFactory — Gère la journalisation des messages entrants et sortants
  • MessageStoreFactory — Stocke les messages et gère les numéros de séquence

1. Création du composant Application

C’est la pièce centrale de notre Acceptor : elle gère l’envoi et la réception des messages FIX.

@Slf4j
@Service
public class AcceptorService
        extends MessageCracker
        implements Application {

    @Override
    public void onCreate(SessionID sessionID) {
        log.info("Session created {}", sessionID);
    }

    @Override
    public void onLogon(SessionID sessionID) {
        log.info("Logon successful {}", sessionID);
    }

    @Override
    public void onLogout(SessionID sessionID) {
        log.info("Logout successful {}", sessionID);
    }

    @Override
    public void toAdmin(Message message, SessionID sessionID) {
        log.info("To admin {}", sessionID);
    }

    @Override
    public void fromAdmin(Message message, SessionID sessionID) {
        log.info("From admin {}", sessionID);
    }

    @Override
    public void toApp(Message message, SessionID sessionID) {
        log.info("Sending Message {}", message);
    }

    @Override
    public void fromApp(
            Message message,
            SessionID sessionID)
            throws
            UnsupportedMessageType,
            IncorrectTagValue,
            FieldNotFound {

        crack(message, sessionID);
    }
}

Rôle des méthodes :

  • onCreate(SessionID sessionId) – Appelée lorsque QuickFIX/J crée une nouvelle session.
  • onLogon(SessionID sessionId) – Déclenchée après une connexion (logon) réussie avec la contrepartie.
  • onLogout(SessionID sessionId) – Notifie qu’une session FIX vient d’être fermée ou déconnectée.
  • toAdmin(Message message, SessionID sessionId) – Gère les messages administratifs envoyés à la contrepartie.
  • toApp(Message message, SessionID sessionId) – Callback pour les messages applicatifs envoyés à la contrepartie.
  • fromAdmin(Message message, SessionID sessionId) – Gère les messages administratifs reçus de la contrepartie.
  • fromApp(Message message, SessionID sessionId) – Méthode la plus importante : point d’entrée principal des messages applicatifs. Toutes les requêtes métier provenant de la contrepartie arrivent ici.

Dans la méthode fromApp, nous appelons la fonction crack(message, sessionID), qui provient de la classe de base MessageCracker.
Cette méthode se charge de router automatiquement les messages FIX entrants vers leur handler approprié.

Nous implémenterons l’une de ces méthodes de handler plus loin dans le tutoriel.

2. Création du MessageFactory

Pour le MessageFactory, nous pouvons utiliser l’implémentation par défaut DefaultMessageFactory.

Même s’il est possible de créer une implémentation personnalisée, il est généralement recommandé de conserver DefaultMessageFactory, car elle est compatible avec l’ensemble des spécifications FIX.

@Configuration
public class AcceptorConfiguration {

    @Bean
    public MessageFactory messageFactory() {
        return new DefaultMessageFactory();
    }

}

3. Créer le composant SessionSettings

Avant d’implémenter le code, on doit créer un fichier de configuration QuickFIX/J (quickfixj-acceptor.cfg) dans le dossier resources pour définir les paramètres de session :

[DEFAULT]
StartTime=00:00:00
EndTime=00:00:00
ResetOnLogon=Y

[SESSION]
BeginString=FIX.4.2
SenderCompID=FIXIMULATOR
TargetCompID=BANZAI
ConnectionType=acceptor
SocketAcceptAddress=localhost
SocketAcceptPort=9878

Vous pouvez définir plusieurs sections [SESSION] si vous souhaitez vous connecter à plusieurs initiateurs FIX.

En revanche, une seule section [DEFAULT] est autorisée. Les paramètres définis dans [DEFAULT] s’appliquent à toutes les sessions, sauf s’ils sont explicitement redéfinis dans une section [SESSION] spécifique.

Décomposons le fichier quickfixj-acceptor.cfg que nous venons de créer :

  • StartTime / EndTime — Les heures d’activité de la session. 00:00:00 signifie que la session est active en permanence.
  • ResetOnLogon — Si la valeur est Y, les numéros de séquence sont réinitialisés à chaque logon. C’est utile en environnement de test ou de simulation.
  • BeginString — La version FIX. Comme Banzai ne supporte que FIX 4.2, nous définissons FIX.4.2.
  • SenderCompID — L’identifiant de votre serveur (l’Acceptor). Ici : FIXIMULATOR.
  • TargetCompID — L’identifiant de l’initiateur. Ici : BANZAI.
  • ConnectionType — Défini à acceptor puisque notre application joue le rôle d’accepteur.
  • SocketAcceptAddress / SocketAcceptPort — L’hôte et le port sur lesquels le serveur FIX écoute. Ici : localhost:9878.
Pour une liste complète de tous les paramètres possibles, consultez la documentation officielle.

⚠️ Très important : les valeurs BeginString, SenderCompID et TargetCompID doivent correspondre à la configuration de l’Initiator.

Plus précisément :

  • Le SenderCompID de l’Initiator doit correspondre au TargetCompID de l’Acceptor
  • Le TargetCompID de l’Initiator doit correspondre au SenderCompID de l’Acceptor.

Dans notre cas, vérifiez le fichier banzai/config/banzai.cfg pour vous assurer de la compatibilité. Toute incohérence dans les IDs ou la version FIX empêchera l’établissement de la connexion.

Maintenant que nous avons notre fichier quickfixj-acceptor.cfg, nous devons charger ces paramètres dans notre application Spring Boot en utilisant la classe SessionSettings de QuickFIX/J. Ce composant lit le fichier de configuration et met l’ensemble des paramètres de session à disposition de l’Acceptor.

@Configuration
public class AcceptorConfiguration {

    @Bean
    public MessageFactory messageFactory() {
        return new DefaultMessageFactory();
    }

    @Bean
    public SessionSettings sessionSettings() throws ConfigError {
        return new SessionSettings("quickfixj-acceptor.cfg");
    }

}

4. Créer le composant LogFactory

QuickFIX/J propose plusieurs implémentations de journalisation intégrées, adaptées à différents besoins :

  • ScreenLogFactory: Journalise les messages directement dans la console. Simple et efficace pour le développement et le débogage.
  • FileLogFactory: Écrit les messages dans des fichiers, permettant une conservation persistante et une analyse a posteriori.
  • SLF4JLogFactory: S’intègre avec SLF4J, ce qui permet d’utiliser des frameworks de logs comme Logback ou Log4J pour une journalisation plus avancée.
  • JdbcLogFactory: Enregistre les messages dans une base de données, adapté aux environnements nécessitant une journalisation centralisée.

Par souci de simplicité et pour obtenir un retour immédiat pendant le développement, nous utiliserons ScreenLogFactory.

@Configuration
public class AcceptorConfiguration {

    @Bean
    public MessageFactory messageFactory() {
        return new DefaultMessageFactory();
    }

    @Bean
    public SessionSettings sessionSettings() throws ConfigError {
        return new SessionSettings("quickfixj-acceptor.cfg");
    }

    @Bean
    public LogFactory logFactory() {
        return new ScreenLogFactory();
    }

}

5. Créer le composant MessageStoreFactory

QuickFIX/J fournit plusieurs implémentations de stockage des messages, chacune adaptée à des cas d’usage différents :

  • FileStoreFactory — Stocke les messages sous forme de fichiers sur disque. Simple et fiable pour des tests locaux.
  • JdbcStoreFactory — Stocke les messages dans une base de données, utile pour une persistance centralisée.
  • MemoryStoreFactory — Stocke les messages en mémoire à l’aide d’une HashMap. Rapide mais non persistant : les messages sont perdus au redémarrage.
  • NoopStoreFactory — Ne stocke aucun message. Utile pour des tests très légers où la persistance n’est pas nécessaire.

Par souci de simplicité dans ce tutoriel, nous utiliserons MemoryStoreFactory.

@Configuration
public class AcceptorConfiguration {

    @Bean
    public MessageFactory messageFactory() {
        return new DefaultMessageFactory();
    }

    @Bean
    public SessionSettings sessionSettings() throws ConfigError {
        return new SessionSettings("quickfixj-acceptor.cfg");
    }

    @Bean
    public LogFactory logFactory() {
        return new ScreenLogFactory();
    }

    @Bean
    public MessageStoreFactory messageStoreFactory() {
        return new MemoryStoreFactory();
    }

}

Maintenant que tous les composants nécessaires sont en place, il est temps de les assembler et de démarrer notre Acceptor FIX.

@Configuration
public class AcceptorConfiguration {

    @Bean
    public MessageFactory messageFactory() {
        return new DefaultMessageFactory();
    }

    @Bean
    public SessionSettings sessionSettings() throws ConfigError {
        return new SessionSettings("quickfixj-acceptor.cfg");
    }

    @Bean
    public LogFactory logFactory() {
        return new ScreenLogFactory();
    }

    @Bean
    public MessageStoreFactory messageStoreFactory() {
        return new MemoryStoreFactory();
    }

    @Bean
    public Acceptor initiator(
            Application application,
            MessageStoreFactory messageStoreFactory,
            SessionSettings sessionSettings,
            LogFactory logFactory,
            MessageFactory messageFactory) throws ConfigError {

        SocketAcceptor socketAcceptor =
                new SocketAcceptor(application, messageStoreFactory, sessionSettings, logFactory, messageFactory);

        socketAcceptor.start();
        return socketAcceptor;
    }
}

Le SocketAcceptor par défaut est mono-thread, mais vous pouvez utiliser ThreadedSocketAcceptor si vous avez besoin que votre Acceptor gère plusieurs sessions ou messages en parallèle.

Il est temps de tester notre Acceptor

Nous sommes maintenant prêts à démarrer notre application.
Une fois que l’Acceptor se connecte avec succès, vous devriez voir un log indiquant un logon réussi avec l’Initiator FIX, confirmant que la session est active et prête à envoyer et recevoir des messages.

FIX Protocol Java Spring Boot

À ce stade, si vous consultez Banzai, vous pouvez voir la session créée, ce qui indique que l’Initiator s’est connecté avec succès à l’Acceptor.

FIX Protocol Java Spring Boot

Simplifier la configuration

Avant de passer à la création de notre premier message d’ordre, prenons un moment pour simplifier notre configuration.

Jusqu’à présent, nous avons configuré manuellement chaque composant de notre Acceptor. C’était volontaire : cela nous a permis de comprendre les composants clés et de voir ce qui se passe réellement en coulisses.
Maintenant que cette étape est faite, nous pouvons tirer parti de l’auto-configuration de QuickFIX/J.

En ajoutant la propriété suivante dans le fichier application.properties :

quickfixj.server.enabled=true
quickfixj.server.config=classpath:quickfixj-acceptor.cfg

Spring Boot configurera automatiquement un Acceptor par défaut avec les composants suivants :

  • un MessageStoreFactory de type MemoryStoreFactory
  • un LogFactory de type ScreenLogFactory
  • un Acceptor de type SocketAcceptor
  • une Application de type EventPublisherApplicationAdapter

Remarque : pour le composant Application, je recommande de conserver notre classe Service personnalisée plutôt que d’utiliser EventPublisherApplicationAdapter, afin de pouvoir gérer les messages en fonction de notre logique métier.

Avec cette configuration, nous n’avons plus besoin de l’intégralité de la configuration manuelle — il suffit de conserver la classe AcceptorService.

Si vous supprimez la classe de configuration manuelle et redémarrez le service, l’Acceptor devrait se connecter à l’Initiator exactement comme auparavant, grâce à l’auto-configuration.

Réception des messages

Pour recevoir des messages provenant de l’Initiator FIX, nous devons ajouter des handlers dans notre classe AcceptorService.

Par exemple, pour gérer les messages NewOrderSingle — utilisés pour la création d’ordres — nous pouvons définir une méthode comme celle-ci :

@Slf4j
@Service
public class AcceptorService
        extends MessageCracker
        implements Application {

    ...

    @Handler
    public void onMessage(NewOrderSingle newOrderSingle, SessionID sessionID) {
        log.info("Received NewOrderSingle {}", newOrderSingle);
    }
}

Cette méthode sera appelée automatiquement à chaque fois qu’un message NewOrderSingle est reçu, ce qui vous permet de traiter la création d’un ordre.

⚠️ Important : tout type de message qui n’est pas pris en charge par votre AcceptorService entraînera un rejet du message.

Redémarrons maintenant l’application, puis plaçons un ordre via Banzai.

FIX Protocol Java Spring Boot

Vous devriez voir le NewOrderSingle correspondant apparaître dans les logs de l’AcceptorService, ce qui confirme que les messages sont bien reçus et traités correctement.

FIX Protocol Java Spring Boot

Envoi de messages

Pour envoyer notre premier message FIX, nous allons simplement transmettre un message d’accusé de réception (ExecutionReport) lorsque nous recevons un ordre :

@Slf4j
@Service
public class AcceptorService
        extends MessageCracker
        implements Application {

    ...

    @Handler
    public void onMessage(
            NewOrderSingle newOrderSingle,
            SessionID sessionID)
            throws FieldNotFound, SessionNotFound {

        log.info("Received NewOrderSingle {}", newOrderSingle);

        String clOrdID = newOrderSingle.getClOrdID().getValue();
        char side = newOrderSingle.getSide().getValue();
        double orderQty = newOrderSingle.getOrderQty().getValue();
        String symbol = newOrderSingle.getSymbol().getValue();

        ExecutionReport execReport = new ExecutionReport(
                new OrderID("ORDER-" + System.currentTimeMillis()),
                new ExecID("EXEC-" + System.currentTimeMillis()),
                new ExecTransType(ExecTransType.NEW),
                new ExecType(ExecType.NEW),
                new OrdStatus(OrdStatus.NEW),
                new Symbol(symbol),
                new Side(side),
                new LeavesQty(orderQty),
                new CumQty(0),
                new AvgPx(0)
        );

        execReport.set(new ClOrdID(clOrdID));
        execReport.set(new TransactTime(LocalDateTime.now()));

        Session.sendToTarget(execReport, sessionID);
        System.out.println("Sent ExecutionReport (NEW) to Banzai: " + execReport);
    }
}

Redémarrez l’application, déclenchez la création d’un ordre, puis vérifiez que l’accusé de réception de l’ordre a bien été envoyé avec succès.

Redémarrez l'appli

Supervision avec Actuator

Enfin, le quickfixj-spring-boot-starter fournit des endpoints Actuator intégrés ainsi qu’un HealthIndicator, que vous pouvez utiliser pour surveiller votre serveur FIX.
Pour activer la supervision QuickFIX/J via Actuator, ajoutez les propriétés suivantes dans votre fichier application.properties :

quickfixj.server.actuator.enabled=true
management.endpoint.quickfixjserver.access=unrestricted
management.endpoint.health.show-details=always
management.endpoints.web.exposure.include=quickfixjserver,health

Cette configuration vous permet d’accéder à des informations détaillées sur l’état de santé de votre serveur FIX et de surveiller son statut directement via les endpoints Spring Boot Actuator.

Spring Boot 1 Spring Boot 2

Conclusion · Implémenter un système de trading avec Java et Spring Boot [Partie 3]

Dans ce tutoriel, nous avons appris à implémenter un Acceptor FIX à l’aide de Java, Spring Boot et QuickFIX/J.

Nous avons commencé par configurer manuellement les composants clés de l’Acceptor afin de comprendre précisément leur rôle et le fonctionnement interne du moteur FIX. Nous avons ensuite tiré parti de l’auto-configuration pour simplifier la mise en place. Au fil du tutoriel, nous avons vu comment recevoir et envoyer des messages, et nous avons utilisé Banzai comme Initiator pour les tests.

Nous avons également exploré la supervision du serveur FIX à l’aide de Spring Boot Actuator, en activant le HealthIndicator et l’endpoint /actuator/quickfixjserver afin de suivre l’état des sessions.

Le code complet est disponible ici : Achraf-Hasbi/quickfixj.

Si vous rencontrez des problèmes ou avez des questions, n’hésitez pas à m’écrire sur LinkedIn : je me ferai un plaisir de vous aider.