Si vous êtes familier avec le secteur des startups FinTech, vous avez peut-être entendu parler de Revolut, un géant FinTech bien connu basé à Londres, au Royaume-Uni. Fondée en 2015, Revolut a recueilli des investissements substantiels et est devenue l'une des startups à la croissance la plus rapide au Royaume-Uni, fournissant des services bancaires à de nombreux citoyens européens.
Alors que les opérations bancaires sont souvent entourées de mystère quant à la manière dont elles génèrent des revenus, certains chiffres clés de Revolut pour les années 2020 et 2021 ont mis en lumière leurs sources de revenus :
Comme illustré, une part importante des revenus de cette néobanque provient du change (FX), de la gestion de patrimoine (y compris les cryptomonnaies) et des services de cartes. Notamment, en 2021, les changes sont devenus le secteur le plus rentable.
Un de mes amis, qui est également ingénieur logiciel, a un jour partagé une histoire intrigante sur son entretien technique au département de génie logiciel de Revolut il y a quelques années. Il a été chargé de développer un algorithme permettant d'identifier le moyen le plus rentable de convertir deux devises en utilisant une ou plusieurs devises intermédiaires. En d’autres termes, ils recherchaient une stratégie d’arbitrage de devises.
L'arbitrage de devises est une stratégie de trading dans laquelle un trader de devises exploite différents spreads proposés par les courtiers pour une paire de devises particulière via plusieurs transactions.
Il a été explicitement mentionné dans la tâche que les fondements de l'algorithme doivent être ancrés dans la théorie des graphes.
Le FX, ou Foreign Exchange, joue un rôle central dans le commerce mondial, soutenant le fonctionnement de notre monde interconnecté. Il est évident que le change joue également un rôle important en faisant des banques les organisations les plus riches.
Le profit généré par les changes est principalement la différence ou l’écart entre les prix d’achat (BID) et de vente (ASK). Même si cette différence peut paraître minime par transaction, elle peut se chiffrer en millions de dollars de bénéfices compte tenu du volume des opérations quotidiennes. Cela permet à certaines entreprises de prospérer uniquement grâce à ces opérations financières hautement automatisées.
Dans le domaine du FX (Foreign Exchange), nous travaillons toujours avec des paires de devises, comme l'EUR/USD. Dans la plupart des cas, ces échanges sont bidirectionnels (c'est-à-dire EUR/USD et USD/EUR) et la valeur du taux de change diffère dans chaque direction.
Une paire d'arbitrage représente un rapport numérique entre les valeurs de deux devises (l'EUR et le dollar américain, par exemple), déterminant le taux de change entre elles.
Potentiellement, nous pouvons utiliser plusieurs devises intermédiaires pour réaliser des échanges rentables, ce que l'on appelle un pari sûr .
Le pari sûr d’arbitrage est un ensemble de paires à utiliser de manière circulaire. En savoir plus
De nombreux fournisseurs ont recours à la modélisation et à l'analyse mathématiques pour garantir leurs propres bénéfices et empêcher les autres d'en profiter. C’est pourquoi le terme potentiellement est souligné ici.
La longueur du pari sûr fait référence au nombre de paires qui constituent un ensemble d’opportunités d’arbitrage potentielles.
Dans le monde réel, les taux de change peuvent varier selon les différentes banques ou plateformes d'échange. Il n'est pas rare que des touristes parcourent une ville pour trouver le meilleur tarif possible. Avec un logiciel informatique, ce processus peut être accompli en quelques millisecondes lorsque vous avez accès à une liste de fournisseurs.
Dans des transactions pratiques et rentables, plusieurs étapes peuvent impliquer des conversions dans différentes devises sur différentes plateformes d'échange. En d’autres termes, le cercle d’arbitrage peut être assez étendu.
L'Arbitrage Circle consiste à acquérir une devise, à la transférer vers une autre plateforme, à effectuer un échange contre d'autres devises et finalement à revenir à la devise d'origine.
Le taux de change entre deux monnaies via une ou plusieurs monnaies intermédiaires est calculé comme le produit des taux de change de ces transactions intermédiaires .
Par exemple, imaginons que nous voulions acheter des francs suisses contre du dollar américain, puis échanger des francs contre des yens japonais, puis revendre des yens contre du dollar américain. À l’automne 2023, nous avons les taux de change suivants :
Nous pouvons acheter 0,91 CHF (Franc suisse) pour 1 USD.
Nous pouvons acheter 163,16 yens japonais pour 1 CHF.
Nous pouvons acheter 0,0067 USD pour 1 yen japonais.
Présentons-le avec un tableau :
1 USD | 1 CHF | 1 YEN 0.91 CHF | 163.16 YEN | 0.0067 USD ----------------|-------------------|-------------- 1.098901099 | 0.006128953 | 149.2537313
Maintenant, nous devons trouver un produit de ces valeurs. Une séquence de transactions devient rentable lorsque ce produit rapporte une valeur inférieure à un :
1.098901099 * 0.006128953 * 149.2537313 = 1.005240803
Comme nous pouvons le constater, le résultat est supérieur à un, il semble donc que nous ayons perdu 0,05 % de notre argent. Mais combien exactement ? Nous pouvons trier cela comme ceci :
0.91 CHF * 163.16 (YEN per 1 CHF) * 0.0067 (USD per 1 YEN) = 0.99478652 US Dollars
Ainsi, après avoir vendu 1 dollar américain au début, nous avons obtenu 0,994, soit moins de 1 dollar américain à la fin.
En termes plus simples, le cycle d'arbitrage est rentable lorsqu'une unité monétaire peut être obtenue pour moins d'une unité de la même monnaie.
Imaginons que nous ayons trouvé une opportunité de prendre 0,92 CHF pour 1 dollar américain lors de la transaction initiale, au lieu de 0,91 CHF :
1 USD | 1 CHF | 1 YEN 0.92 CHF | 163.16 YEN | 0.0067 USD ----------------|-------------------|-------------- 1.086956522 | 0.006128953 | 149.2537313
Un produit sera inférieur à 1 :
1.086956522 * 0.006128953 * 149.2537313 = 0.994314272
Ce qui signifie que dans les monnaies réelles, cela nous donnera plus de 1 dollar américain :
0.92 CHF * 163.16 (YEN per 1 CHF) * 0.0067 (USD per 1 YEN) = 1.00571824 US Dollars
Wuolah, nous avons fait du PROFIT ! Voyons maintenant comment automatiser cela à l'aide de l'analyse graphique.
Ainsi, la formule pour vérifier les profits ou les pertes dans un cercle d'arbitrage de 3 paires d'arbitrage ressemblerait à ceci :
USD/CHF * CHF/YEN * YEN/USD < 1.0
Pour automatiser ces processus, nous pouvons utiliser des graphiques. Les tableaux mentionnés précédemment peuvent être naturellement transformés en une représentation matricielle d'un graphique, où les nœuds représentent les devises et les bords représentent les échanges bidirectionnels.
Par conséquent, il est simple de représenter l’échange de deux paires dans une matrice comme ceci :
EUR USD 1 1 EUR 1 1 USD
En fonction du nombre de paires impliquées, notre matrice peut s'étendre :
EUR USD YEN CHF 1 1 1 1 EUR 1 1 1 1 USD 1 1 1 1 YEN 1 1 1 1 CHF
Par conséquent, notre tableau peut devenir considérablement plus grand, même pour deux monnaies seulement, si nous prenons en compte davantage de plateformes et de ressources d’échange.
Pour résoudre les problèmes réels d’arbitrage de devises, un graphique complet englobant toutes les relations pour les cotations de devises est souvent utilisé. Un tableau de change à trois devises pourrait ressembler à ceci :
USD CHF YEN { 1.0, 1.10, 0.0067 } USD { 0.91, 1.0, 0.0061 } CHF { 148.84, 163.16, 1.0 } YEN
Nous pouvons utiliser une structure de données graphique simple pour représenter nos paires de devises en mémoire :
class GraphNode { public: string Name; }; class Graph { public: vector<vector<double>> Matrix; vector<GraphNode> Nodes; };
Il ne reste plus qu’à découvrir comment parcourir ce graphique et trouver le cercle le plus rentable. Mais il reste encore un problème...
Les algorithmes de graphes classiques ne sont pas bien adaptés pour travailler avec le produit des longueurs d'arêtes car ils sont conçus pour trouver des chemins définis comme la somme de ces longueurs (voir les implémentations de tous les algorithmes de recherche de chemin classiques bien connus BFS, DFS, Dijkstra ou même Une étoile ).
Cependant, pour contourner cette limitation, il existe un moyen mathématique de passer d'un produit à une somme : les logarithmes. Si un produit apparaît sous un logarithme, il peut être converti en une somme de logarithmes.
Du côté droit de cette équation, le nombre souhaité est inférieur à un, indiquant que le logarithme de ce nombre doit être inférieur à zéro :
LogE(USD/CHF) * LogE(CHF/YEN) * LogE(YEN/USD) < 0.0
Cette astuce mathématique simple nous permet de passer de la recherche d'un cycle avec un produit de longueur d'arête inférieur à un à la recherche d'un cycle où la somme des longueurs d'arête est inférieure à zéro .
Nos valeurs matricielles converties en LogE(x) et arrondies avec 2 chiffres après le point ressemblent maintenant à ceci :
USD CHF YEN { 0.0, 0.1, -5.01 } USD { -0.09, 0.0, -5.1 } CHF { 5.0, 5.09, 0.0 } YEN
Ce problème devient désormais plus résoluble en utilisant des algorithmes graphiques classiques. Ce dont nous avons besoin, c'est de parcourir le graphique à la recherche du chemin d'échange le plus rentable.
Chaque algorithme a ses limites. J'en ai mentionné certains dans mon article précédent .
Nous ne pouvons pas appliquer ici les classiques BFS, DFS ou même Dijkstra car notre graphique peut contenir des poids négatifs, ce qui peut conduire à des cycles négatifs lorsqu'il parcourt le graphique. Les cycles négatifs posent un défi à l’algorithme car il trouve continuellement de meilleures solutions à chaque itération.
Pour résoudre ce problème, l'algorithme de Bellman-Ford limite simplement le nombre d'itérations. Il parcourt chaque bord du graphique dans un cycle et applique une relaxation à tous les bords pas plus de V-1 fois (où V est un nombre de nœuds).
A ce titre, l'algorithme de Bellman-Ford est au cœur de ce système d'arbitrage, car il permet de découvrir des chemins entre deux nœuds du graphe qui répondent à deux critères essentiels : ils contiennent des poids négatifs et ne font pas partie de cycles négatifs.
Bien que cet algorithme soit théoriquement simple (et que vous puissiez trouver des milliards de vidéos à ce sujet), sa mise en œuvre pratique pour nos besoins nécessite un certain effort. Examinons cela.
L’objectif de cet article étant l’informatique, j’utiliserai des taux de change imaginaires qui n’ont rien à voir avec les taux de change réels.
Pour une introduction plus fluide à l'algorithme, utilisons un graphique qui ne contient pas du tout de cycles négatifs :
graph.Nodes.push_back({ "USD" }); graph.Nodes.push_back({ "CHF" }); graph.Nodes.push_back({ "YEN" }); graph.Nodes.push_back({ "GBP" }); graph.Nodes.push_back({ "CNY" }); graph.Nodes.push_back({ "EUR" }); // Define exchange rates for pairs of currencies below // USD CHF YEN GBP CNY EUR graph.Matrix = { { 0.0, 0.41, INF, INF, INF, 0.29 }, // USD { INF, 0.0, 0.51, INF, 0.32, INF }, // CHF { INF, INF, 0.0, 0.50, INF, INF }, // YEN { 0.45, INF, INF, 0.0, INF, -0.38 }, // GBP { INF, INF, 0.32, 0.36, 0.0, INF }, // CNY { INF, -0.29, INF, INF, 0.21, 0.0 } }; // EUR
L'exemple de code ci-dessous trouve un chemin entre deux nœuds à l'aide de l'algorithme de Bellman-Ford lorsque le graphique manque de cycles négatifs :
vector<double> _shortestPath; vector<int> _previousVertex; void FindPath(Graph& graph, int start) { int verticesNumber = graph.Nodes.size(); _shortestPath.resize(verticesNumber, INF); _previousVertex.resize(verticesNumber, -1); _shortestPath[start] = 0; // For each vertex, apply relaxation for all the edges V - 1 times. for (int k = 0; k < verticesNumber - 1; k++) for (int from = 0; from < verticesNumber; from++) for (int to = 0; to < verticesNumber; to++) if (_shortestPath[to] > _shortestPath[from] + graph.Matrix[from][to]) { _shortestPath[to] = _shortestPath[from] + graph.Matrix[from][to]; _previousVertex[to] = from; } }
L'exécution de ce code pour le yuan chinois remplit le tableau _previousVertex et donne des résultats comme celui-ci :
Path from 4 to 0 is : 4(CNY) 3(GBP) 0(USD) Path from 4 to 1 is : 4(CNY) 3(GBP) 5(EUR) 1(CHF) Path from 4 to 2 is : 4(CNY) 3(GBP) 5(EUR) 1(CHF) 2(YEN) Path from 4 to 3 is : 4(CNY) 3(GBP) Path from 4 to 4 is : 4(CNY) Path from 4 to 5 is : 4(CNY) 3(GBP) 5(EUR)
Comme vous pouvez le constater, il identifie les chemins optimaux entre le CNY et diverses autres devises.
Et encore une fois, je ne me concentrerai pas sur la recherche d’un seul meilleur, car il s’agit d’une tâche relativement simple et ce n’est pas le but de l’article.
L'implémentation ci-dessus fonctionne bien dans des cas idéaux, mais ne suffit pas lorsqu'il s'agit de graphiques contenant des cycles négatifs.
Ce dont nous avons réellement besoin, c’est de pouvoir identifier si un graphique contient des cycles négatifs et, si c’est le cas, d’identifier les segments problématiques. Cette connaissance nous permet d'atténuer ces problèmes et, à terme, de découvrir des chaînes rentables.
Le nombre d'itérations ne doit pas toujours atteindre précisément V - 1. Une solution est réputée trouvée si, au (N+1)-ème cycle, aucun chemin meilleur que celui du N-ème cycle n'est découvert. Il y a donc place à une légère optimisation.
Le code mentionné précédemment peut être amélioré non seulement pour trouver des chemins, mais également pour détecter si le graphique contient des cycles négatifs, y compris l'optimisation que j'ai mentionnée :
vector<double> _shortestPath; vector<int> _previousVertex; bool ContainsNegativeCycles(Graph& graph, int start) { int verticesNumber = graph.Nodes.size(); _shortestPath.resize(verticesNumber, INF); _previousVertex.resize(verticesNumber, -1); _shortestPath[start] = 0; // For each vertex, apply relaxation for all the edges V - 1 times. for (int k = 0; k < verticesNumber - 1; k++) { updated = false; for (int from = 0; from < verticesNumber; from++) { for (int to = 0; to < verticesNumber; to++) { if (_shortestPath[to] > _shortestPath[from] + graph.Matrix[from][to]) { _shortestPath[to] = _shortestPath[from] + graph.Matrix[from][to]; _previousVertex[to] = from; updated = true; } } } if (!updated) // No changes in paths, means we can finish earlier. break; } // Run one more relaxation step to detect which nodes are part of a negative cycle. if (updated) for (int from = 0; from < verticesNumber; from++) for (int to = 0; to < verticesNumber; to++) if (_shortestPath[to] > _shortestPath[from] + graph.Matrix[from][to]) // A negative cycle has occurred if we can find a better path beyond the optimal solution. return true; return false; }
Et maintenant, nous jouons avec un graphique plus complexe qui inclut des cycles négatifs :
graph.Nodes.push_back({ "USD" }); // 1 (Index = 0) graph.Nodes.push_back({ "CHF" }); graph.Nodes.push_back({ "YEN" }); graph.Nodes.push_back({ "GBP" }); graph.Nodes.push_back({ "CNY" }); graph.Nodes.push_back({ "EUR" }); graph.Nodes.push_back({ "XXX" }); graph.Nodes.push_back({ "YYY" }); // 8 (Index = 7) // USD CHF YEN GBP CNY EUR XXX YYY graph.Matrix = { { 0.0, 1.0, INF, INF, INF, INF, INF, INF }, // USD { INF, 0.0, 1.0, INF, INF, 4.0, 4.0, INF }, // CHF { INF, INF, 0.0, INF, 1.0, INF, INF, INF }, // YEN { INF, INF, 1.0, 0.0, INF, INF, INF, INF }, // GBP { INF, INF, INF, -3.0, 0.0, INF, INF, INF }, // CNY { INF, INF, INF, INF, INF, 0.0, 5.0, 3.0 }, // EUR { INF, INF, INF, INF, INF, INF, 0.0, 4.0 }, // XXX { INF, INF, INF, INF, INF, INF, INF, 0.0 } }; // YYY
Notre programme s'arrête simplement et affiche un message :
Graph contains negative cycle.
Nous avons pu indiquer le problème, mais nous devons naviguer autour des segments problématiques du graphique.
Pour ce faire, nous marquerons les sommets qui font partie de cycles négatifs avec une valeur constante, NEG_INF :
bool FindPathsAndNegativeCycles(Graph& graph, int start) { int verticesNumber = graph.Nodes.size(); _shortestPath.resize(verticesNumber, INF); _previousVertex.resize(verticesNumber, -1); _shortestPath[start] = 0; for (int k = 0; k < verticesNumber - 1; k++) for (int from = 0; from < verticesNumber; from++) for (int to = 0; to < verticesNumber; to++) { if (graph.Matrix[from][to] == INF) // Edge not exists { continue; } if (_shortestPath[to] > _shortestPath[from] + graph.Matrix[from][to]) { _shortestPath[to] = _shortestPath[from] + graph.Matrix[from][to]; _previousVertex[to] = from; } } bool negativeCycles = false; for (int k = 0; k < verticesNumber - 1; k++) for (int from = 0; from < verticesNumber; from++) for (int to = 0; to < verticesNumber; to++) { if (graph.Matrix[from][to] == INF) // Edge not exists { continue; } if (_shortestPath[to] > _shortestPath[from] + graph.Matrix[from][to]) { _shortestPath[to] = NEG_INF; _previousVertex[to] = -2; negativeCycles = true; } } return negativeCycles; }
Désormais, si nous rencontrons NEG_INF dans le tableau _shortestPath, nous pouvons afficher un message et ignorer ce segment tout en identifiant les solutions optimales pour les autres devises. Par exemple, avec le nœud 0 (représentant USD) :
Graph contains negative cycle. Path from 0 to 0 is : 0(USD) Path from 0 to 1 is : 0(USD) 1(CHF) Path from 0 to 2 is : Infinite number of shortest paths (negative cycle). Path from 0 to 3 is : Infinite number of shortest paths (negative cycle). Path from 0 to 4 is : Infinite number of shortest paths (negative cycle). Path from 0 to 5 is : 0(USD) 1(CHF) 5(EUR) Path from 0 to 6 is : 0(USD) 1(CHF) 6(XXX) Path from 0 to 7 is : 0(USD) 1(CHF) 5(EUR) 7(YYY)
Whoala ! Notre code a pu identifier un certain nombre de chaînes rentables malgré le fait que nos données étaient « un peu sales ». Tous les exemples de code mentionnés ci-dessus, y compris les données de test, sont partagés avec vous sur mon GitHub .
Consolidons maintenant ce que nous avons appris. Étant donné une liste de taux de change pour trois devises, nous pouvons facilement détecter des cycles négatifs :
graph.Nodes.push_back({ "USD" }); // 1 (Index = 0) graph.Nodes.push_back({ "CHF" }); graph.Nodes.push_back({ "YEN" }); // 3 (Index = 2) // LogE(x) table: USD CHF YEN graph.Matrix = { { 0.0, 0.489, -0.402 }, // USD { -0.489, 0.0, -0.891 }, // CHF { 0.402, 0.89, 0.0 } }; // YEN from = 0; FindPathsAndNegativeCycles(graph, from);
Résultat:
Graph contains negative cycle. Path from 0 to 0 is: Infinite number of shortest paths (negative cycle). Path from 0 to 1 is: Infinite number of shortest paths (negative cycle). Path from 0 to 2 is: Infinite number of shortest paths (negative cycle).
Cependant, même de légères modifications des taux de change (c'est-à-dire des ajustements de la matrice) peuvent entraîner des différences significatives :
// LogE(x) table: USD CHF YEN graph.Matrix = { { 0.0, 0.490, -0.402 }, // USD { -0.489, 0.0, -0.891 }, // CHF { 0.403, 0.891, 0.0 } }; // YEN from = 0; FindPathsAndNegativeCycles(graph, from);
Regardez, nous avons trouvé une chaîne rentable :
Path from 0 to 0 is : 0(USD) Path from 0 to 1 is : 0(USD) 2(YEN) 1(CHF) Path from 0 to 2 is : 0(USD) 2(YEN)
Nous pouvons appliquer ces concepts à des graphiques beaucoup plus grands, impliquant plusieurs devises :
graph.Nodes.push_back({ "USD" }); // 1 (Index = 0) graph.Nodes.push_back({ "CHF" }); graph.Nodes.push_back({ "YEN" }); graph.Nodes.push_back({ "GBP" }); graph.Nodes.push_back({ "CNY" }); // 5 (Index = 4) // LogE(x) table: USD CHF YEN GBP CNY graph.Matrix = { { 0.0, 0.490, -0.402, 0.7, 0.413 }, // USD { -0.489, 0.0, -0.891, 0.89, 0.360 }, // CHF { 0.403, 0.891, 0.0, 0.91, 0.581 }, // YEN { 0.340, 0.405, 0.607, 0.0, 0.72 }, // GBP { 0.403, 0.350, 0.571, 0.71, 0.0 } }; // CNY from = 0; runDetectNegativeCycles(graph, from);
En conséquence, nous pourrions trouver plusieurs candidats pour réaliser des bénéfices :
Path from 0 to 0 is : 0(USD) Path from 0 to 1 is : 0(USD) 2(YEN) 1(CHF) Path from 0 to 2 is : 0(USD) 2(YEN) Path from 0 to 3 is : 0(USD) 2(YEN) 3(GBP) Path from 0 to 4 is : 0(USD) 2(YEN) 4(CNY)
Il y a cependant deux facteurs importants :
Le temps est un facteur critique dans la mise en œuvre des processus d’arbitrage, principalement en raison des fluctuations rapides des prix des devises. De ce fait, la durée de vie d’un pari sûr est extrêmement brève.
Les plateformes prélèvent des commissions pour chaque transaction.
Il est donc primordial de minimiser les coûts de temps et de réduire les commissions , en limitant la durée du pari sûr.
L'expérience empirique suggère qu'une longueur acceptable de pari sûr se situe généralement entre 2 et 3 paires. Au-delà de cela, les exigences informatiques augmentent et les plateformes de trading imposent des commissions plus importantes.
Ainsi, pour gagner un revenu, il ne suffit pas de disposer de telles technologies, mais il faut également avoir accès à des commissions de faible niveau. Habituellement, seules les grandes institutions financières disposent d’une telle ressource.
J'ai approfondi la logique des opérations de change et la manière d'en tirer des bénéfices, mais je n'ai pas abordé les technologies utilisées pour exécuter ces opérations. Bien que ce sujet s'écarte légèrement de sa trajectoire, je ne pouvais pas omettre de mentionner les contrats intelligents.
L’utilisation de contrats intelligents est aujourd’hui l’un des moyens les plus innovants de mener des opérations de change. Les contrats intelligents permettent des opérations de change en temps réel sans retards ni intervention humaine (sauf pour la création du contrat intelligent).
Solidity est le langage de programmation spécialisé pour créer des contrats intelligents qui automatisent les opérations financières impliquant des crypto-monnaies. Le monde des contrats intelligents est dynamique et soumis à des changements technologiques rapides et à des réglementations évolutives. Il s'agit d'un domaine très médiatisé et comportant des risques importants liés aux portefeuilles et à la conformité légale.
S'il existe sans aucun doute des individus et des équipes talentueuses qui profitent de ce domaine, il existe également des organismes de réglementation qui s'efforcent de garantir le respect des règles du marché.
Malgré la complexité, l’obscurité et l’imprévisibilité de l’économie mondiale, les changes restent une force motrice cachée dans le monde financier. Il s'agit d'un élément crucial qui permet à des milliers d'entreprises et à des millions d'individus dans le monde de collaborer, de fournir des services et de s'enrichir mutuellement de manière pacifique, au-delà des frontières.
Bien entendu, divers facteurs, tels que la politique, la réglementation et les banques centrales, influencent les taux de change et l’efficacité des changes. Ces complexités rendent le paysage financier complexe. Pourtant, il est essentiel de croire que ces complexités servent un objectif plus important en faveur du bien commun.
De nombreux articles scientifiques se penchent sur l’existence et la détermination des taux de change dans l’économie mondiale, pour n’en citer que quelques-uns :
Ces articles mettent en lumière certains mécanismes fondamentaux des changes, qui sont encore difficiles à comprendre et à intégrer dans un seul modèle.
Cependant, jouer avec le code et essayer de trouver une solution à un problème pratique m'a aidé à en savoir un peu plus. J’espère que vous avez apprécié ce petit voyage d’exploration autant que moi.
Restez à l'écoute!
Également publié ici .