diff --git a/config.toml b/config.toml index ed4e03c..428a81e 100644 --- a/config.toml +++ b/config.toml @@ -92,11 +92,11 @@ logoText = "Hello there!" logoHomeLink = "/fr/" [menu] -# [[menu.main]] -# identifier = "blog" -# name = "Blog" -# url = "/posts" -# weight = 1 +[[menu.main]] +identifier = "blog" +name = "Blog" +url = "/posts" +weight = 1 [[menu.main]] identifier = "about_me" diff --git a/content/fr/posts/firefly-action-management/euronext.png b/content/fr/posts/firefly-action-management/euronext.png new file mode 100644 index 0000000..65997ff Binary files /dev/null and b/content/fr/posts/firefly-action-management/euronext.png differ diff --git a/content/fr/posts/firefly-action-management/index.md b/content/fr/posts/firefly-action-management/index.md new file mode 100644 index 0000000..e5b3600 --- /dev/null +++ b/content/fr/posts/firefly-action-management/index.md @@ -0,0 +1,152 @@ +--- +title: "Gérer son épargne en action sous Firefly" +date: 2023-12-19T20:10:42+02:00 +draft: true +--- + +## Problématique + +## Gestion du portefeuille + +### Valeur d'une action + +Comme énoncé précédemment, la valeur d'un portefeuille d'actions dépend de la composition de ce portefeuille +(donc de quelles actions et en quelle quantité), et de la valeur unitaire de chacune de ces actions. + +Cette valeur unitaire est définie par les marchés financiers et varie continuellement. On considère +que la valeur d'une action est le prix du dernier ordre d'achat passé pour cette action sur les marchés +financiers. + +{{< callout type="example" >}} +Considérons la valeur "MSCI World" (un ETF trackant les valeurs des plus grosses entreprises dans +le monde entier). Au 10 décembre, les marchés financiers s'ouvrent avec la valeur de 27.13€ par part +pour cette valeur. à 9h05, une personne passe un ordre d'achat de 15 actions au prix de 27.17€. Dès que +cet ordre est exécuté, on considère que l'action "MSCI World" a la valeur de 27.17€ par action. +{{< /callout >}} + +Dans mon cas, je n'ai absolument pas besoin d'une mise à jour rapide des valeurs, une mise à jour +quotidienne est largement suffisante pour mes besoins, et une mise à jour hebdomadaire pourrait même +convenir à vrai dire. + +J'ai choisi donc de considérer pour chaque action, sa valeur au moment de la cloture des marchés (17h). +Mais comment calculer cette valeur ? + +### Obtenir la valeur d'une action + +#### À la recherche d'une API + +Pour le reste de cet article, je considèrerai l'action "MSCI Monde" en titre d'exemple. Avec un navigateur, +on arrive facilement à obtenir les informations que l'on veut : +- La bourse européenne (Euronext) nous donne accès à son [cours](https://live.euronext.com/fr/product/etfs/fr0011869353-xpar/lyxor-msci-wor-pea/ewld) +- On obtient facilement les mêmes informations sur [Google Finance](https://www.google.com/finance/quote/EWLD:EPA?hl=fr) ou [Yahoo Finance](https://fr.finance.yahoo.com/quote/EWLD.PA/profile/?guccounter=1) + +Cependant, il s'agit d'information destinées à un visionnage humain, aucune API n'est publiquement +disponible pour obtenir ces informations de façon automatisée. + +En faisant des recherches sur le sujet, je me heurte rapidement à deux problèmes : +- soit je trouve des services gratuits, mais limités, soit en quantité d'appels (5 appels par jour par exemple) +soit en fonctionnalités (pas d'ETF) +- soit je trouve des services... chers. De l'ordre d'au moins plusieurs dizaines d'euros par mois, ce +qui est complètement en dehors du cadre d'un projet de ce type. + +De plus, la majorité des API disponibles sont basées sur les marchés américains. Étant dans des marchés +européens, il est encore plus difficile d'obtenir les informations voulues. + +#### Scraping d'Euronext + +Finalement, la solution la plus simple revient à faire du scraping d'un site pertinent (cela expose +aux protections anti-bot, mais je n'en ai pas vu sur le site d'Euronext que j'ai décidé d'utiliser, +et le volume de requêtes est en pratique très faible). + +Mon point de départ est la fiche d'une action: prenons notre habituel [MSCI world](https://live.euronext.com/fr/product/etfs/fr0011869353-xpar/lyxor-msci-wor-pea/ewld). + +Toutes les valeurs intéressantes se trouvent sur un bandeau de la fiche de la valeur. Ici il s'agit du +*dernier cours traité* de 27.188€. + +![Bandeau des valeurs de l'action MSCI world sur le site Euronext](euronext.png) + +En observant le contenu de la page web, on obtient d'ailleurs ceci : + +```html +
+
+ + 27,188 +
+
+``` + +Il suffirait donc de faire un petit script de scraping accédant à l'élément possédant l'id `#header-instrument-price` +pour obtenir directement la valeur voulue. Mais on peut faire encore mieux en observant les requêtes +AJAX effectués depuis le navigateur. + +Une requête en particulier est intéressante, puisqu'elle interroge la route `https://live.euronext.com/intraday_chart/getChartData/FR0011869353-XPAR/intraday` +et renvoie un tableau JSON ayant la forme suivante (le tableau est tronqué, mais contient plus de 1000 éléments): + +```json +[ + {"time":"2023-12-12 17:29","price":26.879999999999999,"volume":11}, + {"time":"2023-12-13 09:04","price":26.974,"volume":1888}, + ... + {"time":"2023-12-19 17:35","price":27.187999999999999,"volume":32} +] +``` + +Chaque ligne du tableau contient un ordre d'achat passé au cours de la journée. La valeur intéressante +correspond à la dernière ligne de ce tableau : il s'agit du dernier ordre passé, et correspond donc +exactement à la valeur affichée sur la fiche de la valeur, et est donc la valeur que l'on souhaite +obtenir. + +À partir de là, la fonction Python suivante est suffisante pour obtenir la valeur d'une action donnée + +```python +EURONEXT_BASE_URL = "https://live.euronext.com/intraday_chart/getChartData" + + +class StoredAction(NamedTuple): + name: str + value: float + date: str + + +class Action(NamedTuple): + name: str + code: str + + +def get_last_value_for_action(action: Action) -> StoredAction: + url = f"{EURONEXT_BASE_URL}/{action.code}/intraday" + resp = requests.get(url) + resp_json = resp.json() + if len(resp_json) < 1: + raise ValueError("Empty list of values") + last_value = resp_json[-1] + try: + return StoredAction( + name=action.name, + value=last_value["price"], + date=last_value["time"], + ) + except KeyError as exc: + raise ValueError("Invalid format for response") from exc +``` + +Il suffit de trouver le **code** associé à l'action voulue. Je le trouve à partir de l'URL de la fiche +produit de l'action cherchée. Dans notre exemple, il s'agit de `https://live.euronext.com/fr/product/etfs/fr0011869353-xpar/lyxor-msci-wor-pea/ewld`. +Le code est donc `fr0011869353-xpar`. + +{{< callout type="warning" >}} +De façon surprenante, la deuxième partie du code est sensible à la casse et doit être en majuscules : +- `fr0011869353-xpar` ne renverra aucun résultat +- `fr0011869353-XPAR` renverra le résultat attendu +- `FR0011869353-XPAR` renverra aussi le résultat attendu +- `FR0011869353-xpar` ne renverra aucun résultat +{{< /callout >}} + +À partir de ces éléments, il m'est donc possible de calculer la valeur de mon portefeuille à partir +de son contenu. Il me reste deux questions à résoudre : +- comment stocker le contenu de ce portefeuille, et le mettre à jour à chaque fois que j'achète une +action ? (ou vend) +- comment mettre à jour la valeur totale de mon portefeuille telle qu'elle apparaît sur FireflyIII ? + +## Lien avec Firefly \ No newline at end of file