Simulateur de marchĂ© boursier đ
Les sous-titres sont disponibles en français.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.
Voici Ă quoi ressemblera le projet Ă la fin. La capture dâĂ©cran ci-dessous montre le rĂ©sultat dâun investissement de 1 000 $ dans Apple en janvier 2020, jusquâen fĂ©vrier 2025.
Avant de commencer, assurez-vous dâavoir complĂ©tĂ© les sections Premiers pas đ§âđ et Aller plus loin đ. 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 spécifique ?
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 contenant :
- Un fichier
main.ts
avecconsole.log("Hello!");
- Un fichier vide
deno.json
Puis exécutez la commande suivante : deno run -A --watch --check main.ts
Les données
Yahoo Finance
Pour notre simulateur, nous avons besoin des prix historiques du marchĂ© boursier. Lâune des sources les plus courantes pour les donnĂ©es financiĂšres est Yahoo Finance.
Lâusage dâune petite quantitĂ© de donnĂ©es est tolĂ©rĂ© pour des fins dâenseignement ou dâintĂ©rĂȘt public, mais si vous voulez rĂ©cupĂ©rer et rĂ©utiliser un grand volume de ces donnĂ©es, avec ou sans objectif commercial, contactez lâĂ©quipe derriĂšre le site ou payez un abonnement premium.
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. Sur le cĂŽtĂ© gauche, cliquez sur DonnĂ©es historiques.
Les donnĂ©es sont maintenant affichĂ©es sous forme de tableau, ce qui vous permet de rĂ©cupĂ©rer toutes les donnĂ©es disponibles depuis que lâentreprise est entrĂ©e en bourse. Pour ce faire, cliquez sur le sĂ©lecteur de dates et choisissez lâoption Max.
Les colonnes qui nous intéressent vraiment sont Date et Adj Close (prix ajusté de clÎture).
Facile, non ? Mais comment pouvons-nous rĂ©cupĂ©rer ces donnĂ©es dans notre script ? đ€
Trouver les données
Ces donnĂ©es ne viennent pas de nulle part. Elles proviennent probablement dâune API qui les fournit Ă la page. Jetons un coup dâĆil sous le capot pour en dĂ©couvrir la source. đ§
API signifie Application Programming Interface. Sur le web, les API sont souvent utilisĂ©es pour transfĂ©rer des donnĂ©es. Lorsque vous appelez un point dâaccĂšs API (via une URL et parfois des paramĂštres), lâAPI renvoie les donnĂ©es correspondantes. Les API sont trĂšs utiles pour les sites affichant des donnĂ©es en temps rĂ©el, entre autres. Au lieu de reconstruire et republier le site avec de nouvelles donnĂ©es â ce qui peut ĂȘtre lent et coĂ»teux â il suffit de mettre Ă jour les points dâaccĂšs de lâAPI. Les rĂ©ponses des API sont souvent en JSON, mais elles peuvent aussi ĂȘtre en CSV, XML et dâautres formats.
Notez que jâutiliserai Google Chrome pour les Ă©tapes suivantes, mais vous pouvez faire la mĂȘme chose sur Firefox ou Safari.
Ouvrez les Outils de dĂ©veloppement et cliquez sur lâonglet RĂ©seau.
Cet onglet affiche toutes les requĂȘtes effectuĂ©es par la page. Lorsquâune page se charge, elle a besoin de diverses ressources, telles que des polices, des images, des styles et⊠des donnĂ©es ! Toutes ces requĂȘtes sont listĂ©es ici et vous pouvez les explorer.
Dans notre cas, nous nous intĂ©ressons aux donnĂ©es boursiĂšres dâApple affichĂ©es sous forme de tableau sur la page web.
RafraĂźchissez la page, puis sĂ©lectionnez Ă nouveau lâoption Max pour rĂ©cupĂ©rer toutes les donnĂ©es disponibles. Recherchez une requĂȘte contenant AAPL
, qui est le symbole boursier dâApple. Câest aussi le symbole utilisĂ© dans lâURL de la page, donc câest un bon indice.
Vous remarquerez une ou plusieurs requĂȘtes fetch commençant par AAPL
. Cela semble trĂšs prometteur !
Faites un clic droit sur lâune dâelles et ouvrez-la dans un nouvel onglet. Wow ! Vous reconnaissez cette syntaxe ? Câest du JSON ! Et il contient beaucoup de donnĂ©es. đ
Voici le lien au cas oĂč vous en auriez besoin.
Si vous regardez attentivement lâURL, vous remarquerez des paramĂštres comme symbol
, interval
, period1
et period2
. Il y a aussi les paramĂštres region
et lang
, qui peuvent ĂȘtre diffĂ©rents pour vous en fonction de votre localisation.
https://query1.finance.yahoo.com/v8/finance/chart/AAPL?events=capitalGain%7Cdiv%7Csplit&formatted=true&includeAdjustedClose=true&interval=1d&period1=345479400&period2=1738778777&symbol=AAPL&userYfid=true&lang=en-CA®ion=CA
Explorons cela plus en détail avec du code.
Récupération des données
RĂ©cupĂ©rons les donnĂ©es Ă partir du point dâaccĂšs de lâAPI que nous venons de dĂ©couvrir. Pour ce faire, nous pouvons Ă©crire un script simple comme celui ci-dessous.
const response = await fetch(
"https://query1.finance.yahoo.com/v8/finance/chart/AAPL?events=capitalGain%7Cdiv%7Csplit&formatted=true&includeAdjustedClose=true&interval=1d&period1=345479400&period2=1738778777&symbol=AAPL&userYfid=true&lang=en-CA®ion=CA",
);
const data = await response.json();
console.log(data);
Câest maintenant un peu plus clair.
Les données sont un grand objet nested. Les dates (timestamps) et les prix ajustés de clÎture (adjclose) sont stockés sous forme de listes.
Pour les rĂ©cupĂ©rer, nous devons parcourir lâobjet.
Pour accéder aux timestamps, nous devons :
- Obtenir lâobjet
chart
- Puis récupérer la liste
result
- Récupérer le premier élément de la liste, qui est un objet
- Ă lâintĂ©rieur de cet objet, obtenir la liste
timestamp
Cela ressemble Ă ceci.
const timestamps = data.chart.result[0].timestamp;
Pour accéder aux prix ajustés de clÎture, nous devons :
- Obtenir lâobjet
chart
- Puis récupérer la liste
result
- Récupérer le premier élément de la liste, qui est un objet
- Ă lâintĂ©rieur de cet objet, obtenir lâobjet
indicators
- Ensuite, récupérer la liste
adjclose
- Récupérer le premier élément de la liste, qui est un objet
- Ă lâintĂ©rieur de cet objet, obtenir les valeurs de
adjclose
Cela ressemble Ă ceci.
const values = data.chart.result[0].indicators.adjclose[0].adjclose;
Ne vous inquiĂ©tez pas. Les structures JSON ne sont pas toujours aussi complexes sur le web. AprĂšs quelques projets, vous serez capable de les lire comme un pro ! đ€
const response = await fetch(
"https://query1.finance.yahoo.com/v8/finance/chart/AAPL?events=capitalGain%7Cdiv%7Csplit&formatted=true&includeAdjustedClose=true&interval=1d&period1=345479400&period2=1738778777&symbol=AAPL&userYfid=true&lang=en-CA®ion=CA",
);
const data = await response.json();
const timestamps = data.chart.result[0].timestamp;
console.log(timestamps);
const values = data.chart.result[0].indicators.adjclose[0].adjclose;
console.log(values);
Pour Ă©viter dâencombrer le terminal, Deno affiche seulement les 100 premiers Ă©lĂ©ments des listes. Mais avez-vous remarquĂ© que les listes de timestamps et de valeurs ont le mĂȘme nombre dâĂ©lĂ©ments ?
Câest un trĂšs bon signe.
Cela signifie probablement que le premier élément de la liste des timestamps (une date) correspond au premier élément de la liste des valeurs (un prix ajusté de clÎture).
VĂ©rifions les premiers et derniers Ă©lĂ©ments pour en ĂȘtre sĂ»rs.
Les timestamps reprĂ©sentent la durĂ©e Ă©coulĂ©e depuis le 1á”Êł janvier 1970. Ici, ils semblent ĂȘtre en secondes. Cependant, en JavaScript, les timestamps sont en millisecondes. Nous pouvons facilement ajuster le tout.
const response = await fetch(
"https://query1.finance.yahoo.com/v8/finance/chart/AAPL?events=capitalGain%7Cdiv%7Csplit&formatted=true&includeAdjustedClose=true&interval=1d&period1=345479400&period2=1738778777&symbol=AAPL&userYfid=true&lang=en-CA®ion=CA",
);
const data = await response.json();
const timestamps = data.chart.result[0].timestamp;
const firstTimestamp = new Date(timestamps[0] * 1000);
const lastTimestamp = new Date(timestamps[timestamps.length - 1] * 1000);
const values = data.chart.result[0].indicators.adjclose[0].adjclose;
const firstValue = values[0];
const lastValue = values[values.length - 1];
console.log({ firstTimestamp, lastTimestamp, firstValue, lastValue });
Ce code affiche ces valeurs dans le terminal.
{
firstTimestamp: 1980-12-12T14:30:00.000Z,
lastTimestamp: 2025-02-05T14:30:00.000Z,
firstValue: 0.09883447736501694,
lastValue: 232.47000122070312
}
Apple est bien entrĂ©e en bourse le 12 dĂ©cembre 1980 avec un prix par action de 0,10 $, selon son site web, et la derniĂšre valeur correspond Ă ce que je vois sur le site de Yahoo au 5 fĂ©vrier 2025 ! Nous avons nos donnĂ©es ! đ„ł
LâURL actuelle est spĂ©cifique Ă Apple, mais nous voulons que notre code fonctionne avec nâimporte quelle entreprise. Extrayons donc certaines valeurs de paramĂštres sous forme de variables.
En utilisant des backticks, nous pouvons insĂ©rer des variables dans lâURL. Au lieu de ceci :
https://query1.finance.yahoo.com/v8/finance/chart/AAPL?events=capitalGain%7Cdiv%7Csplit&formatted=true&includeAdjustedClose=true&interval=1d&period1=345479400&period2=1738778777&symbol=AAPL&userYfid=true&lang=en-CA®ion=CA
Nous pouvons gĂ©nĂ©raliser lâappel Ă lâAPI en utilisant ${symbol}
, ${period1}
et ${period2}
:
https://query1.finance.yahoo.com/v8/finance/chart/${symbol}?events=capitalGain%7Cdiv%7Csplit&formatted=true&includeAdjustedClose=true&interval=1d&period1=${period1}&period2=${period2}&symbol=${symbol}&userYfid=true&lang=en-CA®ion=CA
Pour period1
, nous pouvons la définir à 0
, ce qui correspond au 1á”Êł janvier 1970, puisque les timestamps reprĂ©sentent la durĂ©e Ă©coulĂ©e depuis cette date. Pour period2
, nous pouvons utiliser Date.now()
, qui retourne le nombre de millisecondes depuis le 1á”Êł janvier 1970, garantissant ainsi que nous rĂ©cupĂ©rons les donnĂ©es les plus rĂ©centes disponibles.
const symbol = "AAPL";
const period1 = 0;
const period2 = Date.now();
const response = await fetch(
`https://query1.finance.yahoo.com/v8/finance/chart/${symbol}?events=capitalGain%7Cdiv%7Csplit&formatted=true&includeAdjustedClose=true&interval=1d&period1=${period1}&period2=${period2}&symbol=${symbol}&userYfid=true&lang=en-CA®ion=CA`,
);
const data = await response.json();
const timestamps = data.chart.result[0].timestamp;
const firstTimestamp = new Date(timestamps[0] * 1000);
const lastTimestamp = new Date(timestamps[timestamps.length - 1] * 1000);
const values = data.chart.result[0].indicators.adjclose[0].adjclose;
const firstValue = values[0];
const lastValue = values[values.length - 1];
console.log({ firstTimestamp, lastTimestamp, firstValue, lastValue });
Ce code affiche toujours les bonnes valeurs pour le premier et le dernier Ă©lĂ©ment, ce qui signifie que lâAPI accepte nos paramĂštres plus gĂ©nĂ©raux !
{
firstTimestamp: 1980-12-12T14:30:00.000Z,
lastTimestamp: 2025-02-05T14:30:00.000Z,
firstValue: 0.09883449226617813,
lastValue: 232.47000122070312
}
En passant, cette technique de collecte de donnĂ©es sâappelle le web scraping. Le web est une source incroyable de donnĂ©es, et nous aurons une leçon complĂšte Ă ce sujet plus tard.
Comme jâutilise de temps en temps les donnĂ©es de Yahoo Finance dans mes projets, jâai publiĂ© une fonction qui fait tout cela dans la librairie journalism. Nous aurions pu lâutiliser directement, mais je pense quâil est trĂšs utile de comprendre comment tout fonctionne avant dâutiliser des fonctions qui ressemblent Ă de la magieâŻ! đ§
Mise en cache
Actuellement, chaque fois que nous mettons Ă jour main.ts
et que nous lâenregistrons, le code est relancĂ© et les donnĂ©es sont rĂ©cupĂ©rĂ©es Ă nouveau.
Nous ne voulons pas surcharger les serveurs de Yahoo avec nos requĂȘtes. Nous devons respecter leur infrastructure. De plus, nous ne voulons pas ĂȘtre mis sur une liste noire et nous faire bloquer lâaccĂšs⊠𫣠Alors, mettons les donnĂ©es en cache.
La mise en cache peut signifier différentes choses dans différents contextes, mais ici, elle consiste simplement à écrire les données dans un fichier local et à utiliser ce fichier au lieu de récupérer les données à chaque fois.
En gros, si le fichier local existe, nous devons lâutiliser. Sinon, nous devons rĂ©cupĂ©rer les donnĂ©es.
Pour vérifier si le fichier existe déjà sur notre machine, nous pouvons utiliser la fonction exists
de la librairie standard de Deno @std/fs (fs signifie systĂšme de fichiers).
ArrĂȘtez votre terminal et installez-la en exĂ©cutant : deno add jsr:@std/fs
Ensuite, redémarrez la surveillance de main.ts
en exécutant : deno -A --watch --check main.ts
Créez un nouveau dossier appelé data
, oĂč nous stockerons les donnĂ©es mises en cache.
Le code ci-dessous gĂšre la mise en cache. Voici ce qui se passe lorsque vous lâexĂ©cutez :
- Ligne 1 : Nous importons la fonction
exists
de la librairie standard. - Ligne 6 : Nous utilisons le
symbol
pour crĂ©er un chemin dâaccĂšs pour les donnĂ©es mises en cache. - Ligne 8 : Nous dĂ©clarons une variable
let
pour les données. - Ligne 9 : Nous vérifions si un fichier pour cette entreprise existe déjà .
- Si câest le cas, nous lisons les donnĂ©es depuis ce fichier et les assignons Ă la variable
data
(ligne 11).
- Si câest le cas, nous lisons les donnĂ©es depuis ce fichier et les assignons Ă la variable
- Si le fichier nâexiste pas, les donnĂ©es ne sont pas mises en cache :
- Nous les rĂ©cupĂ©rons (lignes 14â16), les convertissons en JSON, et les stockons dans la variable
data
. - Ensuite, nous les mettons en cache en Ă©crivant un fichier JSON Ă lâaide du
path
de lâentreprise (ligne 18).
- Nous les rĂ©cupĂ©rons (lignes 14â16), les convertissons en JSON, et les stockons dans la variable
- Lignes 21â22 : Ă ce moment-lĂ , la variable
data
contient les informations qui ont Ă©tĂ© soit lues Ă partir dâun fichier local, soit rĂ©cupĂ©rĂ©es de lâAPI, nous pouvons donc rĂ©cupĂ©rer les timestamps et les valeurs !
import { exists } from "@std/fs";
const symbol = "AAPL";
const period1 = 0;
const period2 = Date.now();
const path = `data/${symbol}.json`;
let data;
if (await exists(path)) {
console.log("=> Retrieving data from cache...");
data = JSON.parse(await Deno.readTextFile(path));
} else {
console.log("=> Fetching data...");
const response = await fetch(
`https://query1.finance.yahoo.com/v8/finance/chart/${symbol}?events=capitalGain%7Cdiv%7Csplit&formatted=true&includeAdjustedClose=true&interval=1d&period1=${period1}&period2=${period2}&symbol=${symbol}&userYfid=true&lang=en-CA®ion=CA`,
);
data = await response.json();
await Deno.writeTextFile(path, JSON.stringify(data));
}
const timestamps = data.chart.result[0].timestamp;
const values = data.chart.result[0].indicators.adjclose[0].adjclose;
La premiĂšre fois que vous exĂ©cutez ce code, vous verrez le message de la ligne 13 sâafficher dans le terminal, et vous remarquerez la crĂ©ation dâun nouveau fichier AAPL.json
dans data
.
Les donnĂ©es ont Ă©tĂ© rĂ©cupĂ©rĂ©es et Ă©crites dans un fichier pour une utilisation future. Si vous ĂȘtes curieux, vous pouvez jeter un Ćil Ă ce qui se trouve dans AAPL.json
!
Si vous relancez main.ts
en lâenregistrant Ă nouveau, vous verrez que le message affichĂ© provient dĂ©sormais de la ligne 10. Vous utilisez maintenant les donnĂ©es mises en cache ! Les donnĂ©es ne sont plus rĂ©cupĂ©rĂ©es depuis lâAPI, elles sont lues depuis le fichier local.
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 des actions dâAlphabet (anciennement Google) et exĂ©cutez le code.
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, il vous suffit de supprimer les fichiers dans data
et de relancer votre code. Facile !
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.
Nettoyage
Les valeurs sont des nombres et ne nécessitent aucun nettoyage. Cependant, les timestamps ne sont pas trÚs pratiques à utiliser. Il serait préférable de les convertir en dates.
Avant de faire cela, nettoyons notre code en encapsulant tout ce que nous avons fait jusquâĂ prĂ©sent dans une fonction getData
. Cela nous aidera Ă garder main.ts
organisé et rendra notre code plus facile à comprendre et à déboguer.
Créez un nouveau dossier appelé helpers
, et Ă lâintĂ©rieur, crĂ©ez un nouveau fichier nommĂ© getData.ts
avec le code ci-dessous.
Comme nous utilisons await
dans cette fonction, nous devons la déclarer comme une fonction async
.
Nous garderons symbol
dans main.ts
, donc ici, il est passé en paramÚtre de la fonction.
La fonction retourne les timestamps
et les values
sous forme dâun objet.
import { exists } from "@std/fs";
export default async function getData(symbol: string) {
const period1 = 0;
const period2 = Date.now();
const path = `data/${symbol}.json`;
let data;
if (await exists(path)) {
console.log("=> Retrieving data from cache...");
data = JSON.parse(await Deno.readTextFile(path));
} else {
console.log("=> Fetching data...");
const response = await fetch(
`https://query1.finance.yahoo.com/v8/finance/chart/${symbol}?events=capitalGain%7Cdiv%7Csplit&formatted=true&includeAdjustedClose=true&interval=1d&period1=${period1}&period2=${period2}&symbol=${symbol}&userYfid=true&lang=en-CA®ion=CA`,
);
data = await response.json();
await Deno.writeTextFile(path, JSON.stringify(data));
}
const timestamps = data.chart.result[0].timestamp;
const values = data.chart.result[0].indicators.adjclose[0].adjclose;
return { timestamps, values };
}
Dans main.ts
, nous pouvons maintenant importer et utiliser getData
avec notre symbol
. Comme il sâagit dâune fonction async
, nous devons nous assurer de lâappeler avec await
.
import getData from "./helpers/getData.ts";
const symbol = "AAPL";
const { timestamps, values } = await getData(symbol);
console.log(timestamps, values);
Vous vous demandez peut-ĂȘtre ce qui se passe avec const { timestamps, values }
. Lorsque vous avez un objet, vous pouvez le dĂ©structurer en extrayant ses clĂ©s et en les plaçant directement dans des variables portant le mĂȘme nom. Ici, comme getData
retourne un objet avec les clés timestamps
et values
, nous pouvons le déstructurer pour créer directement les variables timestamps
et values
contenant les données correspondantes.
Voici Ă quoi tout devrait ressembler maintenant. Vous pouvez cliquer sur lâimage pour lâagrandir.
Maintenant, pour nettoyer nos timestamps, créons une autre fonction, cleanTimestamps
, dans helpers
.
Cette fonction attend un paramĂštre timestamps
, qui doit ĂȘtre une liste de nombres, comme indiquĂ© par le type number[]
.
Ces timestamps sont en secondes, mais ils devraient ĂȘtre en millisecondes. Nous les multiplions donc par 1000
avant de créer un new Date
. Nous utilisons la méthode .map()
sur la liste pour convertir facilement tous les éléments.
export default function cleanTimestamps(
timestamps: number[],
) {
console.log("=> Cleaning timestamps...");
const cleanedTimestamps = timestamps.map((timestamp) =>
new Date(timestamp * 1000)
);
return cleanedTimestamps;
}
Nous pouvons importer cette fonction dans getData.ts
et lui passer les timestamps bruts.
import { exists } from "@std/fs";
import cleanTimestamps from "./cleanTimestamps.ts";
export default async function getData(symbol: string) {
const period1 = 0;
const period2 = Date.now();
const path = `data/${symbol}.json`;
let data;
if (await exists(path)) {
console.log("=> Retrieving data from cache...");
data = JSON.parse(await Deno.readTextFile(path));
} else {
console.log("=> Fetching data...");
const response = await fetch(
`https://query1.finance.yahoo.com/v8/finance/chart/${symbol}?events=capitalGain%7Cdiv%7Csplit&formatted=true&includeAdjustedClose=true&interval=1d&period1=${period1}&period2=${period2}&symbol=${symbol}&userYfid=true&lang=en-CA®ion=CA`,
);
data = await response.json();
await Deno.writeTextFile(path, JSON.stringify(data));
}
const timestamps = cleanTimestamps(data.chart.result[0].timestamp);
const values = data.chart.result[0].indicators.adjclose[0].adjclose;
return { timestamps, values };
}
Vous pouvez maintenant voir les timestamps convertis en dates. Le premier est le 12 dĂ©cembre 1980, date Ă laquelle Apple a commencĂ© Ă ĂȘtre Ă©changĂ© en bourse.
Restructuration des données
Actuellement, nous avons deux listes distinctes. Il serait plus pratique dâavoir une seule liste contenant des objets. De plus, nous devons calculer la variation en pourcentage entre chaque jour pour dĂ©terminer nos rendements dâinvestissement.
Créons une nouvelle fonction appelée restructureData.ts
dans le dossier helpers
. Cette fonction attend deux paramĂštres : timestamps
sous forme de liste de dates et values
sous forme de liste de nombres.
Nous savons que timestamps
et values
ont le mĂȘme nombre dâĂ©lĂ©ments. Le premier timestamp correspond au premier prix ajustĂ©, le deuxiĂšme timestamp au deuxiĂšme prix, et ainsi de suite.
Nous pouvons donc utiliser une boucle pour rĂ©cupĂ©rer les valeurs aux mĂȘmes index dans les deux listes, crĂ©er des objets, et les ajouter Ă la liste dailyStockData
. Enfin, nous retournons cette liste.
export default function restructureData(
timestamps: Date[],
values: number[]
) {
console.log("=> Restructuring data...");
const dailyStockData = [];
for (let i = 0; i < timestamps.length; i++) {
const date = timestamps[i];
const value = values[i];
dailyStockData.push({
date,
value,
});
}
return dailyStockData;
}
Nous pouvons maintenant importer et appeler cette fonction dans main.ts
. Comme dailyStockData
est une liste dâobjets, nous pouvons lâafficher avec console.table
.
Pour Ă©viter dâafficher des milliers de lignes dans le terminal, nous utilisons la mĂ©thode .slice()
sur la liste pour ne montrer que les 10 premiers éléments.
import getData from "./helpers/getData.ts";
import restructureData from "./helpers/restructureData.ts";
const symbol = "AAPL";
const { timestamps, values } = await getData(symbol);
const dailyStockData = restructureData(timestamps, values);
console.table(dailyStockData.slice(0, 10));
Ăa commence Ă bien prendre forme ! Maintenant, calculons la variation quotidienne en pourcentage.
Pour ce faire, nous avons besoin de la valeur du jour précédent. Comme nous utilisons des index, nous pouvons simplement la récupérer en utilisant i - 1
, puis calculer la variation.
export default function restructureData(timestamps: Date[], values: number[]) {
console.log("=> Restructuring data...");
const dailyStockData = [];
for (let i = 0; i < timestamps.length; i++) {
const date = timestamps[i];
const value = values[i];
const previousValue = values[i - 1];
const percChange = (value - previousValue) / previousValue;
dailyStockData.push({
date,
value,
previousValue,
percChange,
});
}
return dailyStockData;
}
Oh ! Mais quelque chose ne va pas⊠La premiÚre valeur de previousValue
est undefined
, et le premier percChange
est NaN
! đ±
En fait, câest logique. Le premier jour est⊠le premier jour ! Il nây a pas de valeur prĂ©cĂ©dente. Lorsque la boucle commence, i
est 0
, donc lorsque lâordinateur cherche i - 1
, il essaie dâaccĂ©der Ă lâindex -1
, qui nâexiste pas !
Corrigeons cela en remplaçant la premiÚre valeur de percChange
par 0
.
export default function restructureData(timestamps: Date[], values: number[]) {
console.log("=> Restructuring data...");
const dailyStockData = [];
for (let i = 0; i < timestamps.length; i++) {
const date = timestamps[i];
const value = values[i];
const previousValue = values[i - 1];
const percChange = (value - previousValue) / previousValue;
dailyStockData.push({
date,
value,
previousValue,
percChange,
});
}
dailyStockData[0].percChange = 0;
return dailyStockData;
}
VoilĂ qui est mieux !
Calcul du rendement
Nous avons maintenant tout ce quâil nous faut pour calculer les rendements !
Créons une autre fonction, getFinalAmount
, dans helpers
. Cette fonction nécessitera trois paramÚtres :
amount
: le montant investi, qui doit ĂȘtre unnumber
startDate
: laDate
de lâinvestissementdailyStockData
: une liste dâobjets contenant :- Une clĂ©
percChange
avec une valeur de typenumber
- Une clé
date
avec une valeur de typeDate
- Une clé
Notez quâil y a dâautres clĂ©s dans les objets de dailyStockData
, mais nous nâen avons pas besoin ici, donc inutile de les spĂ©cifier.
Dâabord, nous devons filtrer dailyStockData
pour ne garder que les donnĂ©es Ă partir de la date de lâinvestissement. Nous utilisons la mĂ©thode .filter()
sur la liste pour cela, aux lignes 8â10.
Ensuite, nous créons une variable let
appelée adjustedAmount
(ligne 12), qui suivra lâĂ©volution de la valeur de notre investissement au fil du temps. Au dĂ©part, elle est Ă©gale au amount
investi.
Dans la boucle, nous calculons les gains ou pertes journaliĂšres et ajustons adjustedAmount
en conséquence (ligne 15).
Enfin, nous retournons adjustedAmount
!
export default function getFinalAmount(
amount: number,
startDate: Date,
dailyStockData: { percChange: number; date: Date }[],
) {
console.log("=> Calculating final amount...");
const filteredDailyStockData = dailyStockData.filter((dailyData) =>
dailyData.date >= startDate
);
let adjustedAmount = amount;
for (const dailyData of filteredDailyStockData) {
adjustedAmount += adjustedAmount * dailyData.percChange;
}
return adjustedAmount;
}
Dans main.ts
, nous pouvons créer les variables amount
et startDate
pour stocker le montant initial investi et la date de lâinvestissement, puis les passer Ă notre nouvelle fonction.
Nous stockons le résultat de la fonction getFinalAmount
dans une nouvelle variable appelée finalAmount
.
import getData from "./helpers/getData.ts";
import getFinalAmount from "./helpers/getFinalAmount.ts";
import restructureData from "./helpers/restructureData.ts";
const symbol = "AAPL";
const amount = 1000;
const startDate = new Date("2020-01-01");
const { timestamps, values } = await getData(symbol);
const dailyStockData = restructureData(timestamps, values);
const finalAmount = getFinalAmount(amount, startDate, dailyStockData);
console.log(finalAmount);
Et voici le rĂ©sultat ! Ăa fonctionne ! Vous auriez aujourdâhui 3 266 $ (au 5 fĂ©vrier 2025) si vous aviez investi 1 000 $ dans Apple en janvier 2020.
Les résultats
Créons une fonction pour mieux afficher les résultats.
Dans helpers
, créez la fonction logResult
.
Pour startDate
, nous pouvons la convertir en texte en utilisant .toISOString()
, qui retourne la date dâinvestissement sous ce format : "2020-01-01T00:00:00.000Z"
. Pour ne conserver que la date, nous séparons le texte avec "T"
. La méthode .split()
retourne une liste : ["2020-01-01", "00:00:00.000Z"]
. Comme nous avons seulement besoin de la date, nous utilisons lâindex [0]
.
export default function logResult(
amount: number,
symbol: string,
startDate: Date,
finalAmount: number,
) {
console.log(
`\nIf you had invested $${amount} in ${symbol} on ${
startDate.toISOString().split("T")[0]
}, you would have $${Math.round(finalAmount)} today.\n`,
);
}
Nous pouvons maintenant utiliser cette fonction dans main.ts
.
import getData from "./helpers/getData.ts";
import getFinalAmount from "./helpers/getFinalAmount.ts";
import restructureData from "./helpers/restructureData.ts";
import logResult from "./helpers/logResult.ts";
const symbol = "AAPL";
const amount = 1000;
const startDate = new Date("2020-01-01");
const { timestamps, values } = await getData(symbol);
const dailyStockData = restructureData(timestamps, values);
const finalAmount = getFinalAmount(amount, startDate, dailyStockData);
logResult(amount, symbol, startDate, finalAmount);
Magnifique, nâest-ce pas ? đ
Attendez une minuteâŠ
Tout fonctionne bien avec une startDate
en janvier 2020⊠Mais que se passerait-il si la date de dĂ©part Ă©tait en 1970, avant quâApple nâentre en bourse ?
Câest impossible ! Notre code devrait avertir lâutilisateur.
Mettez Ă jour startDate
avec "1950-01-01"
pour tester⊠Il retourne un montant, alors quâil devrait produire une erreur.
Nous pouvons mettre Ă jour main.ts
pour comparer startDate
avec la premiĂšre date dans timestamps
. Si startDate
est antérieure au premier timestamp
, nous créons erreur avec un message explicite.
import getData from "./helpers/getData.ts";
import getFinalAmount from "./helpers/getFinalAmount.ts";
import restructureData from "./helpers/restructureData.ts";
import logResult from "./helpers/logResult.ts";
const symbol = "AAPL";
const amount = 1000;
const startDate = new Date("1950-01-01");
const { timestamps, values } = await getData(symbol);
if (startDate < timestamps[0]) {
throw new Error(
"The company was not public at that time. Please choose a later date.",
);
}
const dailyStockData = restructureData(timestamps, values);
const finalAmount = getFinalAmount(amount, startDate, dailyStockData);
logResult(amount, symbol, startDate, finalAmount);
Les erreurs sont trĂšs utiles pour arrĂȘter lâexĂ©cution de votre script et fournir un message Ă lâutilisateur (ou Ă vous-mĂȘme). Essayez toujours de rĂ©diger des messages clairs qui vous aideront Ă dĂ©boguer facilement ou qui guideront lâutilisateur.
Câest bien mieux. Nous nâobtiendrons plus de rĂ©sultats impossibles ! Vous pouvez maintenant tester diffĂ©rentes valeurs pour startDate
en toute sécurité.
Conclusion
FĂ©licitations ! Vous avez appris beaucoup de choses dans ce projet. Nous avons explorĂ© le site de Yahoo pour trouver leur API et rĂ©cupĂ©rer des donnĂ©es. Ensuite, nous avons traitĂ© ces donnĂ©es pour calculer des rendements en fonction dâun montant investi Ă une date donnĂ©e. Câest un Ă©norme !
Maintenant, vous pouvez expĂ©rimenter davantage avec ce script. Recherchez dâautres entreprises sur le site de Yahoo et copiez-collez leurs symboles. Testez diffĂ©rents montants et dates dâinvestissement !
Ou modifiez le code pour ajouter dâautres fonctionnalitĂ©s. Voici quelques idĂ©es :
- Comparer les rendements de plusieurs entreprises
- Investir dans plusieurs entreprises en mĂȘme temps
- Acheter et vendre des actions à différentes dates
Amusez-vous bien ! đđș