hugo-relie/content/fr/posts/firefly-action-management/index.md

152 lines
No EOL
6.7 KiB
Markdown

---
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
<div class="col text-ui-grey-0 font-weight-bold data-header-cash ">
<div class="lastprice_min_height pb-1 ">
<span class="data-50 " id="header-instrument-currency"></span>
<span class="data-60" id="header-instrument-price">27,188</span>
</div>
</div>
```
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