Skip to content
Snippets Groups Projects
Unverified Commit 6d1835ab authored by Thomas Clavier's avatar Thomas Clavier
Browse files

Le sujet

parents
No related branches found
No related tags found
No related merge requests found
README.md 0 → 100644
# Portefeuille d'investissement
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 :
```java
public record FixerIORatesResponse(Long timestamp, String base, LocalDate date, Map<String, Double> rates) {
}
```
Le code suivant, permet de faire l'appel au service REST :
```java
public FixerIORatesResponse getRates(String token) {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(String.format("http://data.fixer.io/api/latest?access_key=%s", token)))
.GET()
.build();
HttpResponse<String> response = HttpClient.newBuilder()
.build()
.send(request, BodyHandlers.ofString());
return mapToObject(response);
} catch (URISyntaxException | IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
private FixerIORatesResponse mapToObject(HttpResponse<String> response) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper.readValue(response.body(), FixerIORatesResponse.class);
}
```
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.
Par exemple :
```
2024-03-16T10:18:32+01:00 | deposit | 100 | EUR | EUR | 1 EUR = 1 EUR
2024-03-16T10:19:52+01:00 | buy | 120 | EUR | USD | 1 EUR = 1.2 USD
2024-03-16T10:18:32+01:00 | buy | 109.09 | USD | EUR | 1 USD = .9 EUR
```
Ce qui fait un bénéfice de 8€
# Critères d'évaluation
* Vos tests doivent être une documentation vivante du projet
* soit sous forme de Gherkin,
* soit en JUnit 5 et suffisamment expressif
* vos tests doivent respecter tous les critères de qualité des bons tests vue en cours
* Le code doit être écrit en TDD (les commits faisant fois)
* Pour toutes les interactions hors domaine métier vous veillerez à utiliser une "clean architecture"
* La couverture de test technique doit être de 100% dans le domain
* Votre code doit respecter l'API décrite ci-dessous
* Votre dépot git doit être "propre"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment