Kenji Lopez-Bessaud
Usage personnel, non publié2026

CIF Tracker

Suivi de portefeuille boursier multi-courtiers

Rôle
Conception et développement
Période
2026
Technologies
  • APIFastAPI (Python), SQLAlchemy, SQLite
  • FrontReact 19, TypeScript, Vite, TanStack Query, Tailwind
  • Graphiques et donnéesLightweight Charts, yfinance
  • Testspytest

En bref

500+

Tests automatisés (pytest)

2

Courtiers importés : DEGIRO et eToro

12 s

Attente maximale d’un cours sans cache

Aperçu du CIF Tracker, en données fictivesCapture fictive du tableau de bord, avec des nombres ronds et artificiels. Barre de navigation : Accueil, Marchés, Screener. Carte Patrimoine : valeur fictive de 100 000 euros, performance de +12 pour cent, badge « Marché calme », courbe en hausse et sélecteur de période (1M, 3M, 1A). Trois indicateurs : rendement pondéré du temps +10 pour cent, drawdown −5 pour cent, ratio de Sharpe 1,00. Carte Synthèse du jour. Carte thermique Marchés avec huit secteurs de tailles différentes : en hausse Tech +2,0 pour cent, Finance +0,5, Conso +1,0, Indus +0,5 et Média +1,5 (en vert) ; en baisse Santé −1,0, Énergie −3,0 et Immo −0,5 (en rose), avec des sélecteurs de période 1J, 1S, 1M et YTD.CIF TrackerAccueilMarchésScreenerPatrimoine1M3M1A100 000 €+12,0%Marché calmeTWR+10,0%Drawdown−5,0%Sharpe1,00Synthèse du jourMarchés1J1S1MYTDTech+2,0%Finance+0,5%Santé−1,0%Énergie−3,0%Conso+1,0%Indus+0,5%Immo−0,5%Média+1,5%
Aperçu, données fictives

Contexte

CIF Tracker est un outil personnel de suivi de portefeuille boursier multi-courtiers. Je l’utilise sur mon portefeuille réel, ce qui explique qu’il ne soit pas publié et que son visuel soit en données fictives.

Il rassemble au même endroit mes positions chez DEGIRO et chez eToro. Il calcule la performance et le risque du portefeuille, ainsi qu’une estimation de l’impôt.

À côté du calcul, l’outil propose une carte des marchés, deux screeners et un assistant d’analyse de titres.

Outil personnel, pas un conseil en investissement. Aucune valeur ni aucune position de mon portefeuille réel n’apparaît sur cette page.

Ce que j’ai construit

Le serveur (FastAPI) porte le moteur de calcul et la couche de données de marché. Le front (React) les affiche.

  • Plus-values par position, en coût moyen pondéré. Le calcul respecte l’ordre exact des opérations : un achat et une vente passés à la même minute sont traités achat d’abord.
  • Performance : rendement pondéré du temps (TWR), qui neutralise les dépôts et les retraits, et rendement pondéré par les flux (MWR), calculé avec un XIRR. Le TWR n’est annualisé qu’au-delà de trois mois, pour éviter une extrapolation trompeuse.
  • Risque : drawdown maximal, volatilité annualisée, ratios de Sharpe et de Sortino, bêta et alpha face à un indice au choix (S&P 500, CAC 40, NASDAQ ou EURO STOXX 50), concentration par l’indice HHI.
  • Projection : un cône de Monte Carlo obtenu en rééchantillonnant l’historique du portefeuille (1 000 tirages par défaut), et une VaR historique accompagnée de la CVaR, à 95 % de confiance.
  • Fiscalité française : estimation du prélèvement forfaitaire unique (impôt sur le revenu et prélèvements sociaux), par année de vente. Les dividendes ont leur propre assiette et une moins-value nette est signalée comme reportable sans être calculée. C’est une estimation, qui ne remplace pas la déclaration.
  • Imports de relevés : DEGIRO (CSV d’opérations, CSV de compte et PDF) et eToro (CSV ou Excel). Chaque ordre reçoit un identifiant ou une empreinte, ce qui évite les doublons quand je réimporte un relevé.
  • Tableau de bord : patrimoine, régime de marché (calme, normal, agité ou stress, d’après la volatilité récente) et synthèse du jour.
  • Carte des marchés : une carte thermique des secteurs dont la taille suit la capitalisation ou mon exposition réelle, colorée selon la performance sur 1 jour, 1 semaine, 1 mois ou depuis le début de l’année, avec un zoom par secteur.
  • Screener technique (RSI, moyennes mobiles, plus haut et plus bas sur 52 semaines) et screener fondamental (PER, P/B, rendement, marges, ROE, croissance du chiffre d’affaires), ce dernier avec export CSV.
  • Fil d’actualités agrégé à partir des titres de ma liste de suivi, sans doublons.
  • Assistant d’analyse : analyse d’un titre à la demande, avec un avertissement systématique (détail dans la section suivante).

Décisions et incidents

Le résultat se calcule, il ne se stocke pas. La base garde les faits : les ordres, les dépôts et les retraits. Les positions ouvertes sont reconstruites à partir de l’historique des ordres, et les plus-values réalisées, la performance et les indicateurs de risque sont recalculés à la demande. Si je corrige un relevé ou supprime un ordre, il n’y a donc aucune valeur périmée à rattraper. Pour que le recalcul reste rapide, le résultat réalisé est gardé en mémoire et n’est refait que lorsque les ordres changent ou que le cache des taux de change expire, au bout de 5 minutes ; les indicateurs de risque sont gardés 90 secondes. Un instantané de la valeur du portefeuille est aussi enregistré au fil des jours, pour tracer la courbe.

Les plus-values sont calculées au coût moyen pondéré, la méthode qu’emploie la fiscalité française des titres, plutôt que selon l’ordre strict premier entré, premier sorti. Le calcul se fait dans la devise du titre, puis le résultat est converti en euros au taux de change courant. C’est une approximation que je connais : le taux propre à chaque ordre existe dans le CSV de DEGIRO mais pas dans son PDF, et la documentation du projet la signale comme une limite.

L’interface ne doit pas dépendre de la lenteur du fournisseur de données. Les cours viennent de yfinance, que je ne contrôle pas et qui peut ralentir ou limiter le débit. J’ai donc mis en place un cache « stale-while-revalidate ». Un cours de moins de 15 secondes est servi tel quel. Un cours plus ancien, jusqu’à 5 minutes, est servi immédiatement pendant qu’un rafraîchissement part en arrière-plan. Seul un premier appel sans aucun cache attend le fournisseur, et pas plus de 12 secondes. De plus, un seul appel part à la fois par symbole : sans cette règle, chaque requête arrivant à l’expiration du cache lançait le sien, et ces appels en double déclenchaient les limitations de débit.

Le modèle de langage ne calcule rien. Pour analyser un titre, le serveur rassemble les données (cours, performances, score quantitatif maison, RSI, fondamentaux, actualités) et les transmet au modèle avec la consigne de s’appuyer uniquement sur elles et de n’inventer aucun chiffre. Chaque réponse est accompagnée d’un avertissement : avis à titre informatif et non professionnel, pas un conseil en investissement. Je conserve l’avis avec le cours du moment, pour pouvoir juger plus tard sa pertinence.

Un audit de code que j’ai mené sur l’outil a relevé un défaut instructif. Les jours sans instantané, la courbe de valeur est calculée au prix de revient et non au cours de marché. Sur l’application en fonctionnement, j’ai constaté que les indicateurs de risque dérivés de cette courbe prenaient alors des valeurs aberrantes. L’application affiche désormais, sur le tableau de bord comme sur la page de statistiques, le nombre de jours de valeur de marché réelle sur lesquels reposent ces indicateurs, tant qu’il est inférieur à 10. Le calcul du bêta et de l’alpha renvoie « historique insuffisant » plutôt qu’un chiffre faux. La valorisation au dernier cours connu, recommandée par l’audit, n’est pas encore faite.

Preuves

Les chiffres ci-dessous viennent du collecteur de tests de pytest et du code du projet. Je ne publie ni le dépôt ni les données.

  • Plus de 500 tests pytest dans le dossier de tests du serveur, tous passés à la dernière exécution.
  • Indicateurs de performance et de risque : 84 tests, dont les cas limites (historique trop court, résultat indéterminé plutôt qu’un chiffre faux).
  • Plus-values : 14 tests, dont la normalisation entre ISIN et ticker et la vente d’une quantité supérieure à la position. Fiscalité : 7 tests. Projection Monte Carlo et VaR : 31 tests.
  • Résilience du cache de cours et des taux de change : 10 tests écrits sans accès au réseau (cache périmé servi immédiatement, un seul appel par symbole, attente bornée).
  • Les fonctions de risque et de projection sont des fonctions pures, sans appel réseau ni base de données, ce qui les rend simples à tester.
  • Un audit de code écrit, avec des constats classés par gravité. Plusieurs ont été traités : cache du résultat réalisé, index SQLite, avertissement sur l’historique court.
  • Côté interface, la construction lance le compilateur TypeScript avec noUnusedLocals et noUnusedParameters avant Vite.

Technologies

API
  • FastAPI (Python)
  • SQLAlchemy
  • SQLite
Front
  • React 19
  • TypeScript
  • Vite
  • TanStack Query
  • Tailwind
Graphiques et données
  • Lightweight Charts
  • yfinance
Tests
  • pytest