Création d'un tableau de bord de marchés financiers avec l'API Ticker: Guide complet d'implémentation

Création d'un tableau de bord de marchés financiers avec l'API Ticker: Guide complet d'implémentation

Dans le contexte de l'affichage des données de marché, une combinaison de l'API REST Ticker et de rafraîchissements périodiques répond généralement aux besoins ; cet article présente une démonstration fonctionnelle pour valider cette approche.

Dans un précédent article, j'ai structuré l'utilisation d'une API de marché en trois phases : initialisation / affichage / temps réel. Cet article se concentre spécifiquement sur la deuxième phase, en fournissant une démonstration opérationnelle pour sa mise en œuvre.

I. Mise en place de la structure de page et des données réelles

Objectif de cette démonstration

Pour cette démonstration, j'ai développé un tableau de bord Ticker : regroupant devises, métaux précieux, actions américaines, actions chinoises et crypto-monnaies dans un seul tableau, répondant au besoin de "jeter un œil aux cours".

La source de données utilisée est l'endpoint /v1/market/ticker de TickDB. L'objectif est simple : assurer un fonctionnement stable et un usage à long terme.

Il ne s'agit pas d'une page de "suivi en temps réel" où les utilisateurs doivent surveiller les variations de prix pour prendre des décisions, mais simplement d'un affichage "d'un aperçu de la situation actuelle".

Définition préalable de la structure de page

Au départ, j'ai effectivement voulu appeler directement l'API. En consultant la documentation de /v1/market/ticker, ma première réaction a été d'écrire un fetch, d'afficher le JSON et d'examiner la structure des données.

Cependant, je me suis arrêté. Car je n'avais pas encore clarifié une question essentielle : à quoi ressemblera exactement cette page.

Si c'était une page de "suivi en temps réel", la structure serait radicalement différente : nécessitant une taille de police grande, des animations de variation, un état de connexion en temps réel. Si c'était une page de "liste", ce serait une autre histoire.

Mon analyse initiale a été la suivante : il s'agit d'une page d'affichage de marché, où les utilisateurs souhaitent simplement "jeter un œil à la situation actuelle".

J'ai donc d'abord défini la structure de page :

  1. En-tête : titre + description
  2. Zone de contrôle : bouton de rafraîchissement, sélection de l'intervalle de rafraîchissement, gestion des favoris
  3. Tableau : Symbole, dernier prix, variation, 24h haut/bas, volume, heure
  4. Barre d'état : statut de l'API, latence, heure de dernière mise à jour

Cette page ne comporte pas de mise à jour par seconde, d'animations, de carnet d'ordres en profondeur ou de graphiques en chandeliers, mais elle répond déjà aux questions les plus fréquentes : quel est le prix actuel ? Le marché est-il en hausse ou en baisse aujourd'hui ? La volatilité est-elle importante ? Différents marchés peuvent-ils être affichés dans un même tableau ?

Une fois la structure UI définie, les implémentations suivantes ont eu des limites claires. Tous les travaux ultérieurs consistent à "remplir" cette structure.

Connexion aux données réelles

Après avoir finalisé la structure de page, j'ai connecté fetchTicker() à /v1/market/ticker pour établir une première boucle fonctionnelle "de la requête au rendu".

La difficulté réelle était la suivante : dans un même tableau, les champs renvoyés par différents marchés ne sont pas cohérents. Certains n'ont pas de volume, d'autres manquent de données de variation, certains ne possèdent que des champs relatifs aux prix d'achat/vente.

Type de marché Volume disponible Données de variation Prix d'achat/vente
Crypto-monnaies
Actions
Devises/métaux précieux

Sans tolérance aux champs manquants, la page afficherait des erreurs ou des valeurs NaN.

Une tolérance aux champs manquants s'est donc avérée nécessaire : afficher la valeur si elle existe, sinon afficher -. Ainsi, peu importe les données renvoyées par l'API de marché, le tableau s'affichera correctement.

Le "latence" en haut à droite a également été ajouté à cette étape. De nombreuses démonstrations peuvent paraître impressionnantes, mais on ne sait pas si elles fonctionnent réellement. Ajouter un chiffre de latence, 100ms, 150ms, transforme cette démonstration d'un "simple prototype" en un outil fonctionnel.

À ce stade, ce tableau peut déjà fonctionner de manière stable avec des données de marché réelles.

II. Assurer un rafraîchissement stable et une utilisation réelle

Le rafraîchissement automatique ne peut pas être un simple setInterval

Le tableau fonctionnait, mais pour un usage réel, un problème immédiat est apparu : personne ne souhaite constamment cliquer sur le bouton "rafraîchir".

Initialement, j'ai pensé que le rafraîchissement automatique était simple : un setInterval appelant fetchTicker() suffirait.

Mais en pratique, plusieurs problèmes sont apparus :

  • Si la requête précédente n'est pas terminée, un nouveau rafraîchissement a déjà commencé
  • Les requêtes se chevauchant, l'ordre des données peut être désorganisé
  • L'état UI devient peu fiable (le système est-il en rafraîchissement ou simplement bloqué ?)

Il s'agit fondamentalement d'un problème de séquence causé par le chevauchement des requêtes : la requête suivante commence alors que la précédente n'est pas terminée.

J'ai dû introduire un état "rafraîchissement en cours" pour contrôler le rythme :

  etat = {
    enTelechargement: false,
    prochainRafraichissement: null
  }

  function rafraichirCours() {
    if (etat.enTelechargement) return

    etat.enTelechargement = true
    fetchTicker()
      .finally(() => {
        etat.enTelechargement = false
        etat.prochainRafraichissement = maintenant + intervalle
      })
  }

Le point crucial est que le comportement de rafraîchissement doit être un état explicite et contrôlé, plutôt qu'un effet secondaire implicite.

La barre d'outils a également affiché "Prochain rafraîchissement : 5s" avec un compte à rebours par seconde.

Mon raisonnement était le suivant : lorsque les utilisateurs voient "il reste 3 secondes avant le rafraîchissement", ils comprennent que le système ne pas bloqué, que le rafraîchissement suit un rythme régulier, et si les données n'ont pas changé, ce n'est pas une défaillance du système mais simplement le marché n'a pas bougé.

La démonstration utilise un rafraîchissement par défaut de 5 secondes, mais il est possible de passer à 3 ou 10 secondes. J'ai choisi 5 secondes car : 3 secondes n'apporte pas de bénéfice significatif mais double le volume de requêtes, tandis que 10 secondes semble "un peu lent" pour les utilisateurs. 5 secondes représente un équilibre entre "perception du délai" et "coût système".

La liste de suivi est un état frontend

Le tableau pouvant maintenant rafraîchir de manière stable, un autre problème est apparu : personne ne souhaite voir tous les symboles.

Ce que les utilisateurs veulent réellement est : "quelques symboles qui m'intéressent".

Au début, j'ai pensé que la liste de suivi nécessitait un support backend. En réalité, il s'agit d'un problème d'état purement frontend :

etat = {
  listeSuivi: chargerDepuisStockage()
}

function mettreAJourListeSuivi(nouvelleListe) {
  etat.listeSuivi = nouvelleListe
  sauvegarderDansStockage(nouvelleListe)
}

function rafraichir() {
  fetchTicker(etat.listeSuivi)
}

En élevant la liste de suivi à un état explicite, la logique de rafraîchissement des cours est devenue plus simple : chaque rafraîchissement ne demande que les symboles de la liste de suivi.

J'ai également stocké la liste de suivi dans localStorage. Si les utilisateurs devaient re-sélectionner leurs symboles à chaque chargement de page, ils abandonneraient rapidement.

Cette décision simple reposait sur un jugement : la liste de suivi est un état utilisateur, pas un état de marché. Elle ne nécessite pas de support backend, ni de système de compte, un simple stockage local dans le navigateur suffit.

À ce stade, le tableau possède une utilité minimale : rafraîchissement stable, liste de suivi sauvegardable, utilisation immédiate possible.

III. Compléter l'utilisabilité et l'intégrité technique

La recherche et le filtrage sont des problèmes de couche de vue

Le tableau étant utilisable, mais en ajoutant plusieurs dizaines de produits, un nouveau problème est apparu : impossible de trouver celui qui nous intéresse.

J'ai introduit un filtrage de base et une recherche : filtrage par marché (uniquement les devises, uniquement les actions américaines, uniquement les crypto-monnaies), une barre de recherche (entrée de mots-clés, filtrage en temps réel).

J'ai limité la recherche/filtrage à la couche de vue : elle ne modifie que les lignes affichées dans le tableau, sans changer la liste des symboles demandés. Cela permet de découpler la couche de requête de la couche de rendu, en évitant de perturber le rythme de rafraîchissement pour des interactions UI. Ainsi, la logique de filtrage et la logique de marché sont complètement découplées et ne s'interfèrent pas.

Gestion des états anormaux

À ce stade, le "chemin normal" du tableau de bord fonctionne déjà. Mais j'ai rapidement réalisé une chose : si cette démonstration doit être utilisée par d'autres, le "chemin anormal" ne peut pas être vide.

Le problème le plus direct est que, une fois l'API en erreur, la page actuelle ne "n'affichera rien". Cela reste acceptable pendant le débogage, mais pour un utilisateur final, il est difficile de déterminer ce qui s'est réellement passé.

J'ai donc mis en place une gestion de base des états d'erreur : API Key non configurée, requête échouée, et cas où l'API renvoie un code d'erreur. J'ai également complété la barre d'état du bas pour afficher uniformément le statut de l'API, la latence de requête et l'heure de dernière mise à jour.

La logique n'est pas particulièrement complexe, consistant essentiellement à envelopper la requête de données et le rendu dans une couche de gestion d'exceptions :

essayer {
  donnees = fetchTicker()
  afficher(donnees)
} attraper (erreur) {
  afficherEtatErreur(erreur)
}

Pour les codes d'erreur, je me suis référencé à la documentation d'erreurs de TickDB pour fournir des messages conviviaux : 1001 correspond à une API Key invalide ou expirée, 2002 à un produit inexistant, 3001 à une limitation de fréquence de requête.

J'ai également ajouté un bouton "Exporter en CSV" dans la barre d'état du bas. L'idée était simple : si les utilisateurs peuvent exporter directement les données de marché actuelles pour leur propre analyse ou traitement, cette démonstration n'est plus juste un "aperçu" mais possède déjà une valeur minimale utilisable.

IV. Bilan : Les limites techniques des tableaux d'affichage de marchés

Cette démonstration utilise une approche REST Ticker + rafraîchissement périodique, qui représente mon choix pour ce scénario.

Quel scénario ce tableau résout-il

Le comportement de l'utilisateur est de "jeter un œil aux cours", pas de "surveiller les variations de prix pour prendre des décisions". Dans ce scénario, un rafraîchissement toutes les 5 secondes est suffisant, les symboles intéressants pour l'utilisateur ne dépassent généralement pas 10, et lorsque le rythme de rafraîchissement est perceptible et explicable, l'anxiété concernant le "temps réel" diminue nettement.

En rétrospective, ce tableau fonctionne non pas à cause d'une technologie "avancée", mais parce que chaque étape était orientée vers un même objectif : rendre le rythme de rafraîchissement contrôlable, l'état explicable, et permettre une utilisation à long terme. Dans le scénario de "jeter un œil aux cours", REST Ticker + rafraîchissement périodique est une approche naturelle.

Quand WebSocket est la bonne option

Mon jugement est simple : lorsque le comportement de l'utilisateur passe de "regarder" à "surveiller".

Plus précisément, si l'utilisateur se contente de "jeter un œil aux prix", le rafraîchissement périodique suffit ; si l'utilisateur a besoin de "surveiller les variations de prix pour prendre des décisions", WebSocket est nécessaire. Il ne s'agit pas d'un problème de choix technologique, mais d'un problème de scénario.

De nombreux systèmes de marché veulent immédiatement faire du "temps réel", avec comme première réaction d'utiliser WebSocket, un rafraîchissement par seconde, et d'ajouter des animations. Mais en pratique, on découvre que les utilisateurs n'ont pas besoin de mises à jour par seconde, et que la gestion de la connexion, le reconnexion automatique, et l'accumulation de messages deviennent un fardeau. Les problèmes de performance frontend (mises à jour DOM fréquentes) sont plus graves que les délais d'interface.

La professionnalisme en ingénierie réside précisément dans la connaissance de la technologie à utiliser à quel moment.

Annexe : Comment exécuter cette démonstration

Cette démonstration est en HTML pur + JavaScript natif, sans outil de construction.

Le code de cette démonstration a été organisé dans un dépôt complet, incluant la structure de page, les requêtes de données, la logique de rafraîchissement et la gestion des erreurs. Si vous souhaitez l'exécuter directement ou en savoir plus sur les détails d'implémentation de chaque étape, vous pouvez trouver le code complet sur GitHub :

https://github.com/tickdb/tickdb-demo-ticker-panel

Étapes d'exécution :

  1. Ouvrez config.js et entrez votre API Key
  2. Ouvrez simplement index.html avec votre navigateur

Problèmes courants :

  • Si vous voyez le message "Veuillez configurer l'API Key", cela signifie que config.js n'est pas correctement configuré
  • Si les données affichent -, cela signifie que le champ en question n'existe pas vraiment pour ce marché (c'est normal)
  • Si la requête échoue, vérifiez si votre API Key est valide et si votre connexion réseau est normale

Étiquettes: API REST JavaScript finance données de marché Interface utilisateur

Publié le 26 juin à 03h15