Vous allez coder un système de gestion de portefeuille de valeurs mobilières, "stocks portfolio" en anglais.
Dans le domaine de l'investissement, on parle de valeurs mobilières (qui se vendent facilement) que l'on range dans un portefeuille.
Un portefeuille est par exemple composés :
* de devises (euros, dollars américains, yen, etc.)
* d'actions (Renault, LVMH, Bouygues, etc.)
* de matière première (charbon, pétrol, zinc, café, avoine, blé, etc.)
* de valeurs moins banales comme des cryptos monnaies
* et de pleins d'autres produits plus ou moins complexes composés des valeurs ci-dessus.
Une fois le portefeuille initialisé avec des devises, il est possible d'acheter d'autres valeurs. Si j'initialise mon portefeuille avec 100€, je vais pouvoir acheter des actions.
En sens inverse, il est possible de vendre des valeurs mobilières dans une devise donnée. Je peux par exemple vendre des dollars américains pour avoir des euros.
En ayant connaissance des taux de changes pour les devises et des taux de vente des autres valeurs mobilières, il est possible d'évaluer la valeur dans une devise donnée de l'ensemble du portefeuille.
Imaginons par exemple le portefeuille suivants :
* 120 Euros
* 120 USB
* 1 lingo d'or
* 0.1 Bitcoin
* 1 baril de brut Brent
En considérant les taux de changes et taux d'achats suivants :
1 EUR = 1.089111 USD
1 lingo = 63962.85 €
1 EUR = 0.000016154758 BTC
1 Baril de Brent = 78.01 €
Alors le portefeuille est estimé à : 120 + 1.089111 * 120 + 63962.85 + 1 / 0.000016154758 * 0.1 + 78.01 = 70481.68 €
Durant ce contrôle TP, vous allez construire en TDD une gestion de portefeuille ne pouvant contenir que des monnaies avec un appel d'API fixer.io pour pouvoir faire l'évaluation dans une devise donnée.
Voici l'interface à respecter
## Les fonctionnalités de base
* Les monnaies sont représentées par leur code ISO 4217 par exemple `Currency.EUR`
* Une valeur s'initialise avec un montant et une monnaie. `new Stock (5.0, Currency.USD)`
* Il est possible de créer un portefeuille en l'initialisant avec quelques valeurs. `Portfolio portfolio = new Portfolio(new Stock (5.2, Currency.USD), new Stock(11.3, Currency.EUR));`
* l'appel à la méthode `valueIn(Currency)` permet d'avoir une estimation de la valeur du portefeuille dans une monnaie donnée. `portfolio.valueIn(Currency.EUR)`
### Appel fixer.io
Le projet ayant déjà les bonnes dépendances, en supposant le record suivant :
Si vous souhaitez tester la dé-sérialisation du JSON en object, voici un exemple de JSON retourné par l'API fixer.io :
```json
{
"success":true,
"timestamp":1710500223,
"base":"EUR",
"date":"2024-03-15",
"rates":{
"AED":3.99958,
"AFN":77.949187,
"ALL":103.783967,
"AMD":441.269602,
"ANG":1.974449,
"AOA":906.829628,
"ARS":925.995224,
"AUD":1.658411,
"AWG":1.9604,
"AZN":1.849155,
"BAM":1.958328,
"BBD":2.212106,
"BDT":120.235203,
"BGN":1.956141,
"BHD":0.410485,
"BIF":3132.843953,
"BMD":1.089111,
"BND":1.458883,
"BOB":7.570912,
"BRL":5.437388,
"BSD":1.095614,
"BTC":1.6154758e-5,
"BTN":90.710291,
"BWP":14.855175,
"BYN":3.584827,
"BYR":21346.572685,
"BZD":2.208401,
"CAD":1.473774,
"CDF":3014.659135,
"CHF":0.96234,
"CLF":0.037068,
"CLP":1022.89635,
"CNY":7.837348,
"COP":4259.741253,
"CRC":559.522245,
"CUC":1.089111,
"CUP":28.861438,
"CVE":110.409547,
"CZK":25.18155,
"DJF":195.091829,
"DKK":7.457254,
"DOP":64.743573,
"DZD":146.628116,
"EGP":52.056888,
"ERN":16.336663,
"ETB":62.207799,
"EUR":1,
"FJD":2.440425,
"FKP":0.854348,
"GBP":0.853955,
"GEL":2.886367,
"GGP":0.854348,
"GHS":14.133503,
"GIP":0.854348,
"GMD":74.005355,
"GNF":9416.068037,
"GTQ":8.554042,
"GYD":229.210079,
"HKD":8.519298,
"HNL":26.977104,
"HRK":7.495669,
"HTG":145.247489,
"HUF":393.592135,
"IDR":17014.470907,
"ILS":3.982089,
"IMP":0.854348,
"INR":90.243619,
"IQD":1435.15253,
"IRR":45780.774647,
"ISK":148.500387,
"JEP":0.854348,
"JMD":169.628961,
"JOD":0.771961,
"JPY":162.027263,
"KES":146.481764,
"KGS":97.486006,
"KHR":4429.71799,
"KMF":492.468662,
"KPW":980.172421,
"KRW":1448.767764,
"KWD":0.334771,
"KYD":0.912978,
"KZT":490.693399,
"LAK":22789.644423,
"LBP":97475.420954,
"LKR":334.762119,
"LRD":209.81713,
"LSL":20.44264,
"LTL":3.215861,
"LVL":0.658792,
"LYD":5.23314,
"MAD":10.98378,
"MDL":19.308924,
"MGA":4930.364236,
"MKD":61.615331,
"MMK":2300.669762,
"MNT":3699.014472,
"MOP":8.826994,
"MRU":43.454884,
"MUR":50.138198,
"MVR":16.768377,
"MWK":1832.973728,
"MXN":18.19828,
"MYR":5.123724,
"MZN":69.159703,
"NAD":20.431407,
"NGN":1731.958033,
"NIO":40.08628,
"NOK":11.519106,
"NPR":145.13768,
"NZD":1.786381,
"OMR":0.419225,
"PAB":1.095614,
"PEN":4.027499,
"PGK":4.184324,
"PHP":60.477777,
"PKR":303.862145,
"PLN":4.293714,
"PYG":8003.330898,
"QAR":3.964905,
"RON":4.970676,
"RSD":117.219918,
"RUB":99.811576,
"RWF":1402.774776,
"SAR":4.084528,
"SBD":9.14492,
"SCR":14.631474,
"SDG":456.883419,
"SEK":11.253635,
"SGD":1.456364,
"SHP":1.38671,
"SLE":24.773385,
"SLL":24773.385106,
"SOS":622.423388,
"SRD":38.409127,
"STD":22542.395715,
"SVC":9.586374,
"SYP":14160.267075,
"SZL":20.381309,
"THB":39.016286,
"TJS":11.996385,
"TMT":3.822779,
"TND":3.37243,
"TOP":2.574329,
"TRY":35.09089,
"TTD":7.438602,
"TWD":34.427889,
"TZS":2777.232831,
"UAH":42.382508,
"UGX":4256.572655,
"USD":1.089111,
"UYU":42.509212,
"UZS":13767.771781,
"VEF":3944540.464996,
"VES":39.460335,
"VND":26922.820243,
"VUV":130.161613,
"WST":2.982949,
"XAF":656.80482,
"XAG":0.043238,
"XAU":0.000502,
"XCD":2.943376,
"XDR":0.820259,
"XOF":656.80482,
"XPF":119.331742,
"YER":272.65899,
"ZAR":20.344101,
"ZMK":9803.309845,
"ZMW":27.27984,
"ZWL":350.69325
}
}
```
## Ajoutons des fonctionnalités
* la méthode `void buy(Currency currencyToBy, double amount, Currency currencyUsedToBy)` permet d'acheter avec des `currencyUsedToBy` la monnaie `currencyToBy`, l'achat se fait selon le taux de change au moment de l'achat et la banque prend 0,2% du montant en frais de transaction. Attention, il n'est pas possible de faire l'achat s'il n'y a pas suffisamment de `̀currencyUsedToBy`
* la méthode `void sell(Currency currencyToSell, double amount, Currency currencyTarget)` permet de vendre des `currencyToSell` en `currencyTarget`, la vente se fait selon le taux de change au moment de la vente et la banque prend 0,2% du montant en frais de transaction. Attention, il n'est pas possible de faire l'achat s'il n'y a pas suffisamment de `̀currencyToSell`
* la méthode `void withdraw(double amount, Currency currency)` permet de retirer du portefeuille `amount` de `currency` pour les mettre sur un compte en banque. La banque ne supporte pour l'instant que les comptes en euro. La banque prend 2% du montant en frais de transaction.
* la méthode `void deposit(double amount, Currency currency)` permet d'ajouter `amount` de `currency`dans le portefeuille. C'est la seule transaction gratuite.
* la méthode `String[] history(LocaDate start, LocalDate end)` permet de lister sous forme de tableau de `String` toutes les transactions entre `start` et `end`. Une ligne de transaction est de la forme `Date au format ISO 8601 | Type d'opération | Montant | Devise source format ISO | Devise cible format ISO | Taux de change (source vers cible)`.
* la méthode `void order(Currency currencyToBy, double amount, Currency currencyUsedToBy, double lowerRateLimit, double upperRateLimit)` permet de lancer un ordre d'achat qui va scruter les taux de changes toutes les minutes pour n'acheter que si le taux est compris entre `lowerRateLimit` et `upperRateLimit`.
## Calcul de la performance
* la méthode `double performance()` retourne le montant gagné ou perdu depuis la création du portefeuille.