Simulateur de marchĂ© boursier đ
DerniĂšre mise Ă jour le
Bienvenue dans ce nouveau projet ! Nous allons récupérer des données historiques du marché boursier et les utiliser pour estimer combien nous aurions gagné ou perdu en investissant dans des entreprises cotées en bourse.
Avant de commencer, assurez-vous dâavoir suivi la leçon Installation du chapitre 1, ainsi que les sections Fondamentaux de TypeScript et BĂątir avec TypeScript. Ce projet est conçu pour vous aider Ă pratiquer tout ce que nous avons couvert jusquâĂ prĂ©sent dans ce cours.
Quelle est la question ?
Il est toujours important dâidentifier clairement la question Ă laquelle nous voulons rĂ©pondre avant de plonger trop vite dans un ocĂ©an de donnĂ©es.
Aujourdâhui, notre question est :
- Combien aurions-nous gagné ou perdu en investissant dans une entreprise cotée en bourse à une date précise ?
Pour y répondre, nous devons calculer la différence entre le montant initial et le montant final.
Nous pouvons également identifier trois variables qui influenceront le résultat :
- Lâentreprise choisie
- Le montant initial investi
- La date de lâinvestissement
Avec cela en tĂȘte, commençons !
Configuration
Créez un nouveau dossier vide sur votre ordinateur, ouvrez-le avec VS Code et créez-y un fichier nommé main.ts.
Tapez ce code dans main.ts et enregistrez le fichier :
console.log("Hello!");Configurons maintenant notre projet.
Créez un fichier package.json dans le dossier de votre projet avec ce contenu pour que VS Code sache que nous utiliserons la syntaxe moderne des modules TypeScript :
{
"type": "module"
}CrĂ©ez ensuite un fichier tsconfig.json dans le mĂȘme dossier pour que TypeScript vĂ©rifie notre code selon ces rĂšgles modernes de modules :
{
"compilerOptions": {
"module": "nodenext",
"types": ["node"],
"allowImportingTsExtensions": true,
"noEmit": true
}
}La derniĂšre chose Ă faire est dâinstaller les types de Node.js. Ouvrez le terminal (Command + J sur Mac ou Control + J sur Windows) et exĂ©cutez :
npm install --save-dev @types/nodeExécutez maintenant cette commande dans le terminal :
node --watch main.tsCette commande exécute notre code et le relance automatiquement chaque fois que nous enregistrons une modification, pour voir les résultats au fur et à mesure que nous travaillons.

Les données
Yahoo Finance
Pour notre simulateur, nous avons besoin des prix historiques du marchĂ© boursier. Lâune des sources les plus courantes de donnĂ©es financiĂšres est Yahoo Finance .
Sur la page dâaccueil , vous pouvez rechercher une entreprise cotĂ©e en bourse. Par exemple, recherchez Apple et cliquez sur le rĂ©sultat correspondant.

Vous arriverez sur la page de lâaction Apple. Ces donnĂ©es sont trĂšs intĂ©ressantes ! Mais comment pouvons-nous les rĂ©cupĂ©rer dans notre script ? đ€

Récupération des données
Ne vous inquiĂ©tez pas, jâai créé une fonction juste pour cela !
Nous allons installer ma librairie libre Journalism Finance et utiliser la fonction getYahooFinanceData. Elle rĂ©cupĂšre les donnĂ©es de Yahoo Finance sous la forme dâune liste propre dâobjets contenant des timestamps et des valeurs.
Mais attention : getYahooFinanceData utilise un point dâaccĂšs non documentĂ© de Yahoo Finance et nâest ni affiliĂ©e Ă Yahoo ni approuvĂ©e par Yahoo. Il en va de mĂȘme pour de nombreuses autres librairies libres que vous pourriez trouver pour rĂ©cupĂ©rer ce type de donnĂ©es. Avant de lâutiliser, consultez les conditions dâutilisation de Yahoo et les restrictions du fournisseur de donnĂ©es qui pourraient sâappliquer.
Pour lâinstant, arrĂȘtons de surveiller et dâexĂ©cuter main.ts. Cliquez dans le terminal et appuyez sur CTRL + C. Installez ensuite la librairie en exĂ©cutant cette commande dans le terminal :
npm install @nshiab/journalism-financeLa dépendance figure maintenant dans package.json et est installée dans node_modules. npm met aussi à jour package-lock.json, qui enregistre les versions exactes des dépendances.

Nous allons dâabord recueillir les donnĂ©es, puis les utiliser. Pour garder le projet bien organisĂ©, nous placerons les fonctions utilitaires rĂ©utilisables dans leurs propres fichiers. CrĂ©ez un dossier helpers dans votre projet et ajoutez-y getData.ts. Cette fonction rĂ©cupĂ©rera les donnĂ©es dâune entreprise cotĂ©e en bourse.
Dans cette premiĂšre fonction utilitaire, nous importons getYahooFinanceData depuis @nshiab/journalism-finance, nous lâappelons avec les paramĂštres expliquĂ©s ci-dessous, puis nous retournons les donnĂ©es. getData attend un paramĂštre de type chaĂźne de caractĂšres, symbol. Comme la fonction est gĂ©nĂ©rale, nous pourrons la rĂ©utiliser pour nâimporte quelle entreprise si nĂ©cessaire.
Lâajout de export default nous permet dâimporter la fonction lĂ oĂč nous en avons besoin, soit dans main.ts dans notre cas.
import { getYahooFinanceData } from "@nshiab/journalism-finance";
export default async function getData(symbol: string) {
const dailyStockData = await getYahooFinanceData(
symbol,
new Date("1970-01-01"),
new Date(),
"adjclose",
"1d",
);
return dailyStockData;
}
Voici les arguments que nous passons Ă getYahooFinanceData :
symbolest le symbole boursier. Nous utiliseronsAAPLpour Apple.- Les deux dates dĂ©finissent la pĂ©riode. Nous commençons en 1970 et terminons aujourdâhui afin dâobtenir tous les prix disponibles.
"adjclose"sĂ©lectionne le prix ajustĂ© de clĂŽture. Celui-ci tient compte dâĂ©vĂ©nements comme les fractionnements dâactions et les dividendes, ce qui le rend utile pour estimer le rendement dâun investissement."1d"demande la valeur Ă la fin de chaque journĂ©e.
Notez que getYahooFinanceData est asynchrone parce quâelle rĂ©cupĂšre des donnĂ©es sur Internet. Nous utilisons le mot-clĂ© await pour nous assurer que toutes les donnĂ©es sont reçues avant de poursuivre lâexĂ©cution du script.
En arriĂšre-plan, getYahooFinanceData rĂ©cupĂšre des donnĂ©es depuis une API de Yahoo Finance. Une API, ou Application Programming Interface, permet Ă des programmes dâĂ©changer des donnĂ©es.
Nous pouvons maintenant mettre à jour main.ts pour importer et appeler notre nouvelle fonction utilitaire, tout en gardant notre code bien organisé.
Ă la ligne 3, nous crĂ©ons la variable symbol et lui attribuons le symbole boursier dâApple, AAPL. Nous passons cette variable Ă getData, qui attend un paramĂštre symbol. LĂ encore, nous utilisons await pour nous assurer de recevoir toutes les donnĂ©es avant de continuer. Ă la ligne 6, nous affichons les dix premiers rĂ©sultats sous forme de tableau.
import getData from "./helpers/getData.ts";
const symbol = "AAPL";
const dailyStockData = await getData(symbol);
console.table(dailyStockData.slice(0, 10));
Mais avant dâexĂ©cuter ce code, nous devons nous occuper dâune derniĂšre chose.
Mise en cache
Si nous exécutions main.ts maintenant, les données seraient récupérées à nouveau chaque fois que le code est exécuté.
Nous ne voulons pas surcharger les serveurs de Yahoo avec nos requĂȘtes. Nous devons respecter leur infrastructure. Nous ne voulons pas non plus ĂȘtre mis sur une liste noire⊠𫣠Mettons donc les donnĂ©es en cache.
LâidĂ©e est assez simple. La premiĂšre fois que nous rĂ©cupĂ©rons les donnĂ©es, nous les Ă©crivons dans un fichier. Ensuite, chaque fois que le code sâexĂ©cute Ă nouveau, si le fichier existe, ce qui sera le cas, nous lâutilisons au lieu de rĂ©cupĂ©rer les donnĂ©es une nouvelle fois.
Créez un dossier data dans votre projet pour les données mises en cache.

Nous allons maintenant mettre Ă jour getData pour vĂ©rifier si un fichier mis en cache existe. Pour cela, nous utilisons existsSync() du module node:fs, qui retourne true si le fichier existe et false sinon. Nous pouvons ensuite utiliser des branches if et else pour lire les donnĂ©es depuis le fichier sâil existe, ou rĂ©cupĂ©rer de nouvelles donnĂ©es sâil nâexiste pas.
import { getYahooFinanceData } from "@nshiab/journalism-finance";
import { existsSync } from "node:fs";
import { readFile, writeFile } from "node:fs/promises";
export default async function getData(symbol: string) {
const path = `./data/${symbol}.json`;
let dailyStockData;
if (existsSync(path)) {
console.log(`=> Cached data found at ${path}. Reading it...`);
dailyStockData = JSON.parse(await readFile(path, "utf8"));
} else {
console.log("=> No cached data found. Fetching it from Yahoo Finance...");
dailyStockData = await getYahooFinanceData(
symbol,
new Date("1970-01-01"),
new Date(),
"adjclose",
"1d",
);
console.log(`=> Data fetched. Writing it to ${path} for the next run...`);
await writeFile(path, JSON.stringify(dailyStockData));
}
return dailyStockData;
}Voici ce qui se passe lorsque vous exécutez ce code :
pathcontient le symbole boursier pour que chaque entreprise ait son propre fichier cache.existsSync()vĂ©rifie si le fichier existe. Si oui,readFile(path, "utf8")le lit comme du texte etJSON.parse()reconstitue la liste.- Sinon, nous rĂ©cupĂ©rons les prix, convertissons la liste en JSON et lâenregistrons avec
writeFile(). - Enfin, la fonction retourne les données, quelle que soit leur source.
Exécutez maintenant main.ts et surveillez les changements :
node --watch main.tsLa premiĂšre fois, le fichier cache nâexiste pas. Le bloc else rĂ©cupĂšre les donnĂ©es et Ă©crit AAPL.json dans data.

Vous vous demandez ce que signifie timestamp ? Cet Ă©norme nombre correspond au nombre de millisecondes Ă©coulĂ©es depuis le 1er janvier 1970. Câest ainsi que JavaScript suit les dates et les heures en arriĂšre-plan. Ici, la librairie nous fournit directement le timestamp plutĂŽt quâune date, mais comme nous le verrons, JavaScript permet de passer facilement des dates aux timestamps et vice versa.
ExĂ©cutez de nouveau main.ts en lâenregistrant (CMD + S sur Mac ou CTRL + S sur PC). Cette fois, la condition est remplie, car Node.js trouve AAPL.json. Le bloc if est exĂ©cutĂ©, tandis que tout ce qui se trouve sous else est ignorĂ©. Nous nâavons pas besoin de rĂ©cupĂ©rer les donnĂ©es Ă nouveau. Nous les lisons plutĂŽt depuis notre fichier local. Câest plus rapide et cela Ă©vite dâimportuner les serveurs de Yahoo avec une autre requĂȘte.

Vous pouvez conserver des donnĂ©es en cache pour plusieurs entreprises. Par exemple, remplacez AAPL dans la variable symbol par GOOG pour obtenir les prix historiques de lâaction dâAlphabet, anciennement Google, puis exĂ©cutez le code.
import getData from "./helpers/getData.ts";
const symbol = "GOOG";
const dailyStockData = await getData(symbol);
console.table(dailyStockData.slice(0, 10));Vous verrez quâun autre fichier, GOOG.json, est créé dans data.

Si vous revenez à AAPL, les données sont toujours là . Plus besoin de les récupérer à nouveau !
Et si vous voulez des prix plus récents, supprimez simplement les fichiers dans data et exécutez votre code de nouveau. Facile !
import getData from "./helpers/getData.ts";
const symbol = "AAPL";
const dailyStockData = await getData(symbol);
console.table(dailyStockData.slice(0, 10));
Ici, nous utilisons la mise en cache pour éviter de solliciter trop souvent les serveurs de Yahoo. Mais la mise en cache est aussi trÚs couramment utilisée pour améliorer les performances. Lire un fichier local est beaucoup plus rapide que de récupérer des données sur Internet. Par exemple, un script peut effectuer des calculs complexes qui prennent beaucoup de temps. Au lieu de les répéter à chaque exécution du script, vous pouvez mettre les résultats en cache et les lire directement lors des exécutions suivantes.
Calcul des rendements
Nous avons maintenant tout ce quâil nous faut pour calculer les rendements !
Créons une autre fonction, calculateFinalAmount, dans helpers. Cette fonction aura besoin de trois paramÚtres :
- Le montant investi,
amount, qui doit ĂȘtre unnumber - La date de lâinvestissement,
startDate, qui est uneDate - Les données
dailyStockDataretournĂ©es pargetYahooFinanceData, soit une liste dâobjets associant untimestampĂ unevalue
export default function calculateFinalAmount(
amount: number,
startDate: Date,
dailyStockData: { timestamp: number; value: number }[],
) {
console.log("=> Calculating final amount...");
const filteredDailyStockData = dailyStockData.filter((dailyData) =>
dailyData.timestamp >= startDate.getTime()
);
const initialValue = filteredDailyStockData[0].value;
const finalValue = filteredDailyStockData[filteredDailyStockData.length - 1]
.value;
const rateOfReturn = (finalValue - initialValue) / initialValue;
return amount + amount * rateOfReturn;
}Voici ce qui se passe lorsque nous appelons cette fonction :
- La ligne 6 indique dans le terminal que la fonction est appelée.
- Les lignes 8 Ă 10 conservent uniquement les donnĂ©es Ă partir de la date de lâinvestissement.
- La ligne 12 rĂ©cupĂšre la valeur initiale de lâaction.
- Les lignes 13 et 14 récupÚrent sa valeur la plus récente.
- La ligne 15 calcule le taux de rendement, qui peut ĂȘtre positif ou nĂ©gatif.
- La ligne 17 calcule le montant final : le montant initial auquel sâajoute le montant multipliĂ© par le taux de rendement.
Si nous exécutons notre code maintenant, rien ne se produira. Nous devons encore appeler notre nouvelle fonction dans main.ts.
Nous importons dâabord calculateFinalAmount. Ensuite, nous crĂ©ons les variables amount et startDate, puis nous les passons Ă la fonction avec dailyStockData. Enfin, nous stockons son rĂ©sultat dans une nouvelle variable nommĂ©e finalAmount.
import getData from "./helpers/getData.ts";
import calculateFinalAmount from "./helpers/calculateFinalAmount.ts";
const symbol = "AAPL";
const amount = 1000;
const startDate = new Date("2020-01-01");
const dailyStockData = await getData(symbol);
const finalAmount = calculateFinalAmount(amount, startDate, dailyStockData);
console.log(finalAmount);Et voici le rĂ©sultat ! Ăa fonctionne ! Vous auriez environ 4 764 $ aujourdâhui, en octobre 2026, si vous aviez investi 1 000 $ dans Apple en janvier 2020.

Affichage du résultat
Créons une fonction pour afficher le résultat plus clairement.
Dans helpers, créez une fonction appelée logResult.
Pour rendre le message plus facile Ă lire, nous formatons startDate avec .toLocaleDateString("en-US"). "en-US" est ce que nous appelons une locale. Elle prĂ©cise que la date doit ĂȘtre formatĂ©e en anglais pour les Ătats-Unis, mais vous pouvez utiliser la locale de votre choix, comme "fr-CA" pour le français au Canada. Nous arrondissons Ă©galement finalAmount avec Math.round().
Nous prĂ©cisons timeZone: "UTC" parce quâune date sans heure comme "2020-01-01" est interprĂ©tĂ©e en UTC. La date affichĂ©e reste ainsi identique quel que soit votre fuseau horaire.
export default function logResult(
amount: number,
symbol: string,
startDate: Date,
finalAmount: number,
) {
const formattedDate = startDate.toLocaleDateString("en-US", { timeZone: "UTC" });
const roundedAmount = Math.round(finalAmount);
console.log(
`\nIf you had invested $${amount} in ${symbol} on ${formattedDate}, you would have $${roundedAmount} today.\n`,
);
}Nous pouvons maintenant utiliser cette fonction dans main.ts.
import getData from "./helpers/getData.ts";
import calculateFinalAmount from "./helpers/calculateFinalAmount.ts";
import logResult from "./helpers/logResult.ts";
const symbol = "AAPL";
const amount = 1000;
const startDate = new Date("2020-01-01");
const dailyStockData = await getData(symbol);
const finalAmount = calculateFinalAmount(amount, startDate, dailyStockData);
logResult(amount, symbol, startDate, finalAmount);
Magnifique, nâest-ce pas ? đ
Un instantâŠ
Tout fonctionne bien avec une startDate en janvier 2020⊠Mais que se passerait-il si la date de dĂ©but Ă©tait en 1970, avant lâentrĂ©e en bourse dâApple ?
Câest impossible ! Notre code devrait avertir la personne qui lâutilise.
Remplacez startDate par "1950-01-01" pour faire un test⊠Le code retourne un montant, alors quâil devrait dĂ©clencher une erreur ! đ±

Ăcrasons ce bogue ! đ
Nous pouvons mettre Ă jour calculateFinalAmount, qui reçoit dĂ©jĂ startDate et dailyStockData. Les lignes que nous ajoutons ci-dessous comparent la date de lâinvestissement au premier prix disponible. Si la date de lâinvestissement est antĂ©rieure, la fonction dĂ©clenche une erreur accompagnĂ©e dâun message.
export default function calculateFinalAmount(
amount: number,
startDate: Date,
dailyStockData: { timestamp: number; value: number }[],
) {
console.log("=> Calculating final amount...");
if (startDate.getTime() < dailyStockData[0].timestamp) {
throw new Error(
"The company was not public at that time. Please choose a later date.",
);
}
const filteredDailyStockData = dailyStockData.filter((dailyData) =>
dailyData.timestamp >= startDate.getTime()
);
const initialValue = filteredDailyStockData[0].value;
const finalValue = filteredDailyStockData[filteredDailyStockData.length - 1]
.value;
const rateOfReturn = (finalValue - initialValue) / initialValue;
return amount + amount * rateOfReturn;
}
DĂ©clencher une erreur arrĂȘte le script et fournit, espĂ©rons-le, un message utile. Lâerreur indique Ă©galement son origine. Dans la capture dâĂ©cran ci-dessus, calculateFinalAmount.ts:9:15 dĂ©signe la ligne 9 et le caractĂšre 15 de ce fichier. Dans VS Code, faites CMD + clic sur cette rĂ©fĂ©rence pour accĂ©der directement Ă lâerreur, ce qui est trĂšs pratique pour dĂ©boguer.
Câest mieux. Nous ne retournerons plus de rĂ©sultats impossibles ! Vous pouvez maintenant essayer diffĂ©rentes valeurs pour startDate en toute sĂ©curitĂ©.
Mais sommes-nous complĂštement Ă lâabri ? Que se passe-t-il si vous fournissez une date dans le futur ? đ€ Essayez de trouver un moyen de dĂ©clencher une autre erreur dans ce cas. Voici un indice : Date.now() vous donne la date et lâheure actuelles sous la forme dâun nombre de millisecondes.
Conclusion
FĂ©licitations ! Vous avez appris beaucoup de choses dans ce projet. Nous avons utilisĂ© une librairie pour rĂ©cupĂ©rer des donnĂ©es propres, nous les avons mises en cache localement et nous avons calculĂ© le rendement dâun montant investi Ă une date prĂ©cise. Ce nâest pas rien !
Vous pouvez maintenant continuer Ă expĂ©rimenter avec ce script. Recherchez dâautres entreprises sur le site de Yahoo et copiez-collez leurs symboles. Essayez diffĂ©rents montants et diffĂ©rentes dates dâinvestissement !
Vous pouvez aussi modifier le code pour ajouter dâautres fonctionnalitĂ©s. Voici quelques idĂ©es :
- Comparer les rendements de plusieurs entreprises
- Investir dans plusieurs entreprises Ă la fois
- Acheter et vendre des actions à différentes dates
Amusez-vous ! đđș