{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6d25c1ef",
   "metadata": {},
   "source": [
    "# Cours 4 — Rappel et introduction aux dictionnaires pour la modélisation financière (MBA)\n",
    "\n",
    "Ce cours fait le pont entre ce que l'on a déjà vu (variables, types, listes, boucles, conditions) et une nouvelle structure essentielle : les **dictionnaires** (`dict`).\n",
    "\n",
    "Il est conçu pour des étudiants en **MBA Modélisation Financière** : tous les exemples seront tirés du monde des actifs financiers (actions, positions, portefeuille).\n",
    "\n",
    "### Objectifs de la séance\n",
    "- Consolider les bases vues lors des cours 1 à 3.\n",
    "- Comprendre ce qu'est un dictionnaire (`clé -> valeur`).\n",
    "- Savoir **créer**, **lire**, **modifier** et **enrichir** un dictionnaire.\n",
    "- Savoir **parcourir** un dictionnaire avec `.keys()`, `.values()` et `.items()`.\n",
    "- Manipuler une **liste de dictionnaires** pour représenter un portefeuille d'actifs.\n",
    "- Produire un mini reporting financier lisible et utile."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2455edd",
   "metadata": {},
   "source": [
    "## 0. Rappel express des notions déjà vues\n",
    "\n",
    "Avant de commencer les dictionnaires, vérifie que tu es à l'aise avec :\n",
    "- Variables (`prix = 152.30`)\n",
    "- Types (`int`, `float`, `str`, `bool`)\n",
    "- Listes et indices (`portefeuille[0]`, `len(portefeuille)`)\n",
    "- Boucles `for` et `range`\n",
    "- Conditions `if / elif / else`\n",
    "\n",
    "Dans la cellule suivante, on fait un mini récap en une seule exécution, sur un cas finance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb90d349",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Quick recap: variables, list, loop, condition (finance use-case)\n",
    "prix_achat = 150.00      # purchase price per share, in EUR\n",
    "prix_actuel = 162.50     # current market price per share, in EUR\n",
    "quantite = 100           # number of shares held\n",
    "\n",
    "tickers = [\"AAPL\", \"MC.PA\", \"TTE.PA\"]\n",
    "\n",
    "rendement = (prix_actuel - prix_achat) / prix_achat\n",
    "pnl = (prix_actuel - prix_achat) * quantite\n",
    "\n",
    "print(f\"Rendement : {rendement:.2%}\")\n",
    "print(f\"P&L latent : {pnl:.2f} EUR\")\n",
    "\n",
    "for ticker in tickers:\n",
    "    print(f\"Actif suivi : {ticker}\")\n",
    "\n",
    "if rendement >= 0.10:\n",
    "    print(\"Performance : très bonne\")\n",
    "elif rendement >= 0.00:\n",
    "    print(\"Performance : positive\")\n",
    "else:\n",
    "    print(\"Performance : négative\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58b32d9c",
   "metadata": {},
   "source": [
    "### Exercice 0 — Mini rappel\n",
    "\n",
    "1. Crée 3 variables : `prix_achat`, `prix_actuel`, `quantite` pour une action de ton choix.\n",
    "2. Calcule le **rendement** (`(prix_actuel - prix_achat) / prix_achat`) et le **P&L latent** (`(prix_actuel - prix_achat) * quantite`).\n",
    "3. Affiche un message final avec une f-string.\n",
    "\n",
    "Objectif : repartir sur des bases solides avant la nouvelle notion."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0adcdd6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO — Exercise 0 (recap)\n",
    "pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02ef7ca6",
   "metadata": {},
   "source": [
    "## 1. Introduction aux dictionnaires (`dict`)\n",
    "\n",
    "Un dictionnaire permet de stocker des données avec des **clés explicites**.\n",
    "\n",
    "Exemple mental :\n",
    "- **Liste** : position 0, position 1, position 2 → il faut se souvenir de ce qu'il y a à chaque indice.\n",
    "- **Dictionnaire** : `\"ticker\"`, `\"prix\"`, `\"quantite\"`, `\"secteur\"` → chaque champ a un **nom métier** clair.\n",
    "\n",
    "C'est très pratique en finance, car un actif a beaucoup de champs (ticker, secteur, devise, prix, quantité...) et on veut éviter de retenir l'ordre dans une liste."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "775a0a65",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example 1: create a dictionary for one stock position\n",
    "action = {\n",
    "    \"ticker\": \"AAPL\",\n",
    "    \"secteur\": \"Technology\",\n",
    "    \"devise\": \"USD\",\n",
    "    \"prix\": 187.50,\n",
    "    \"quantite\": 100,\n",
    "}\n",
    "\n",
    "print(action)\n",
    "print(f\"Ticker : {action['ticker']}\")\n",
    "print(f\"Secteur : {action['secteur']}\")\n",
    "print(f\"Prix unitaire : {action['prix']} {action['devise']}\")\n",
    "print(f\"Quantité : {action['quantite']}\")\n",
    "\n",
    "# Simple derived metric: market value of the position\n",
    "valeur_marche = action[\"prix\"] * action[\"quantite\"]\n",
    "print(f\"Valeur de marché : {valeur_marche} {action['devise']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "12ebfedb",
   "metadata": {},
   "source": [
    "### Exercice 1 — Premier dictionnaire\n",
    "\n",
    "Crée un dictionnaire `action_meta` représentant une position sur une action de ton choix, avec les clés :\n",
    "- `ticker`\n",
    "- `secteur`\n",
    "- `devise`\n",
    "- `prix`\n",
    "- `quantite`\n",
    "\n",
    "Puis affiche :\n",
    "- le **ticker**\n",
    "- le **secteur**\n",
    "- la **valeur de marché** (`prix * quantite`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4d9799ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO — Exercise 1\n",
    "pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5780ad6",
   "metadata": {},
   "source": [
    "## 2. Lire, modifier et enrichir un dictionnaire\n",
    "\n",
    "Opérations de base sur un dictionnaire :\n",
    "\n",
    "- **Lire** une valeur : `action['prix']`\n",
    "- **Modifier** une valeur : `action['prix'] = 195.30` (mise à jour suite à un mouvement de marché)\n",
    "- **Ajouter une clé** : `action['pays'] = 'États-Unis'`\n",
    "\n",
    "Ces trois opérations utilisent la même syntaxe avec des crochets `[...]`. Pour ajouter une nouvelle clé, on écrit simplement `dict['nouvelle_cle'] = valeur`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fdcd644d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example 2: update and extend a dictionary (price move, new fields)\n",
    "action = {\n",
    "    \"ticker\": \"MC.PA\",       # LVMH on Euronext Paris\n",
    "    \"prix\": 700.00,\n",
    "    \"quantite\": 25,\n",
    "    \"devise\": \"EUR\",\n",
    "}\n",
    "\n",
    "# Read a value\n",
    "print(f\"Prix actuel : {action['prix']} {action['devise']}\")\n",
    "\n",
    "# Price moved during the trading session\n",
    "action[\"prix\"] = 712.40\n",
    "print(f\"Prix après mise à jour : {action['prix']} {action['devise']}\")\n",
    "\n",
    "# Enrich with new fields\n",
    "action[\"secteur\"] = \"Luxury\"\n",
    "action[\"actif\"] = True\n",
    "\n",
    "print(action)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2552b97",
   "metadata": {},
   "source": [
    "### Exercice 2 — Mise à jour d'une position\n",
    "\n",
    "À partir d'un dictionnaire `position` de ton choix (avec `ticker`, `prix`, `quantite`, `devise`), fais les opérations suivantes :\n",
    "\n",
    "1. **Mets à jour** le prix (par exemple, le prix de clôture du jour).\n",
    "2. **Ajoute la clé** `secteur`.\n",
    "3. **Ajoute la clé** `actif` avec la valeur `True`.\n",
    "4. Affiche une **phrase finale** avec ces informations, par exemple :\n",
    "\n",
    "   > `\"MC.PA (Luxury) — 25 titres à 712.40 EUR — position active\"`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c65c6f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO — Exercise 2\n",
    "pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "636dcd24",
   "metadata": {},
   "source": [
    "## 3. Parcourir un dictionnaire avec `keys()`, `values()` et `items()`\n",
    "\n",
    "Pour parcourir un dictionnaire avec une boucle `for`, on dispose de trois méthodes :\n",
    "\n",
    "- `d.keys()` : la liste des **clés** du dictionnaire.\n",
    "- `d.values()` : la liste des **valeurs** du dictionnaire.\n",
    "- `d.items()` : la liste des **couples** `(clé, valeur)`.\n",
    "\n",
    "On utilise ensuite `for cle in d.keys()`, `for valeur in d.values()` ou `for cle, valeur in d.items()` selon ce que l'on veut afficher.\n",
    "\n",
    "C'est très utile pour faire une **fiche d'identité** d'un actif : on affiche toutes ses caractéristiques sans avoir à écrire chaque clé à la main."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3ebbab0f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example 3: iterate with keys(), values() and items()\n",
    "action = {\n",
    "    \"ticker\": \"TTE.PA\",       # TotalEnergies\n",
    "    \"secteur\": \"Energy\",\n",
    "    \"prix\": 61.30,\n",
    "    \"quantite\": 80,\n",
    "    \"devise\": \"EUR\",\n",
    "}\n",
    "\n",
    "print(\"--- Clés du dictionnaire ---\")\n",
    "for cle in action.keys():\n",
    "    print(cle)\n",
    "\n",
    "print(\"--- Valeurs du dictionnaire ---\")\n",
    "for valeur in action.values():\n",
    "    print(valeur)\n",
    "\n",
    "print(\"--- Couples clé / valeur (items) ---\")\n",
    "for cle, valeur in action.items():\n",
    "    print(f\"{cle} : {valeur}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32c35c3c",
   "metadata": {},
   "source": [
    "### Exercice 3 — Fiche d'identité d'un actif\n",
    "\n",
    "À partir du dictionnaire `position` que tu as créé à l'exercice 2 (ou un autre actif si tu préfères) :\n",
    "\n",
    "1. Affiche la liste des **clés** avec une boucle `for ... in position.keys()`.\n",
    "2. Affiche la liste des **valeurs** avec une boucle `for ... in position.values()`.\n",
    "3. Affiche une **fiche d'identité** complète, ligne par ligne, avec une boucle `for cle, valeur in position.items()`, du type :\n",
    "\n",
    "   > `\"ticker : MC.PA\"`  \n",
    "   > `\"secteur : Luxury\"`  \n",
    "   > `\"prix : 712.4\"`  \n",
    "   > ..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3edcbde4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO — Exercise 3\n",
    "pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74e73cc2",
   "metadata": {},
   "source": [
    "## 4. Liste de dictionnaires : un portefeuille proche du réel\n",
    "\n",
    "En pratique, on travaille rarement sur une seule action. On travaille sur un **portefeuille** :\n",
    "\n",
    "- une **liste de dictionnaires**\n",
    "- chaque élément de la liste = **une position** (un dictionnaire avec ses champs)\n",
    "- chaque dictionnaire = un actif décrit par ses **champs métier** (`ticker`, `secteur`, `prix`, `quantite`...)\n",
    "\n",
    "C'est la structure idéale pour boucler dessus et calculer des indicateurs (valeur, rendement, P&L)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8549732b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example 4: list of dictionaries = a small portfolio\n",
    "portefeuille = [\n",
    "    {\"ticker\": \"MC.PA\",  \"secteur\": \"Luxury\", \"prix\": 712.40, \"quantite\": 25},\n",
    "    {\"ticker\": \"TTE.PA\", \"secteur\": \"Energy\", \"prix\":  61.30, \"quantite\": 80},\n",
    "    {\"ticker\": \"BNP.PA\", \"secteur\": \"Banks\",  \"prix\":  68.10, \"quantite\": 50},\n",
    "]\n",
    "\n",
    "for position in portefeuille:\n",
    "    valeur_marche = position[\"prix\"] * position[\"quantite\"]\n",
    "    print(f\"{position['ticker']} | {position['secteur']} | prix = {position['prix']} EUR | qte = {position['quantite']} | valeur = {valeur_marche} EUR\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c9e9f38",
   "metadata": {},
   "source": [
    "### Exercice 4 — Mini reporting de portefeuille\n",
    "\n",
    "Crée une liste `portefeuille` de **3 dictionnaires** représentant 3 positions de ton choix (toutes en `EUR` pour simplifier). Chaque dictionnaire doit avoir les clés : `ticker`, `secteur`, `prix`, `quantite`.\n",
    "\n",
    "Pour chaque position, affiche :\n",
    "- le `ticker`\n",
    "- le `secteur`\n",
    "- le `prix`\n",
    "- la `quantite`\n",
    "- la **valeur de marché** (`prix * quantite`)\n",
    "\n",
    "**Bonus** : ajoute une classification simple basée sur la valeur de marché :\n",
    "- `\"Grosse position\"` si `valeur >= 10000`\n",
    "- `\"Position moyenne\"` si `valeur >= 3000`\n",
    "- `\"Petite position\"` sinon"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "167f1572",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO — Exercise 4\n",
    "pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e554cdb7",
   "metadata": {},
   "source": [
    "## 5. Corrections proposées\n",
    "\n",
    "Compare tes solutions avec ces propositions. Plusieurs bonnes réponses sont possibles."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e0d25cc3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Correction — Exercise 0\n",
    "prix_achat = 150.00\n",
    "prix_actuel = 162.50\n",
    "quantite = 100\n",
    "\n",
    "rendement = (prix_actuel - prix_achat) / prix_achat\n",
    "pnl = (prix_actuel - prix_achat) * quantite\n",
    "\n",
    "print(f\"Rendement : {rendement:.2%} | P&L latent : {pnl:.2f} EUR\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "62866c19",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Correction — Exercise 1\n",
    "action_meta = {\n",
    "    \"ticker\": \"AAPL\",\n",
    "    \"secteur\": \"Technology\",\n",
    "    \"devise\": \"USD\",\n",
    "    \"prix\": 187.50,\n",
    "    \"quantite\": 100,\n",
    "}\n",
    "\n",
    "valeur_marche = action_meta[\"prix\"] * action_meta[\"quantite\"]\n",
    "\n",
    "print(f\"Ticker : {action_meta['ticker']}\")\n",
    "print(f\"Secteur : {action_meta['secteur']}\")\n",
    "print(f\"Valeur de marché : {valeur_marche} {action_meta['devise']}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dae1eed5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Correction — Exercise 2\n",
    "position = {\n",
    "    \"ticker\": \"MC.PA\",\n",
    "    \"prix\": 700.00,\n",
    "    \"quantite\": 25,\n",
    "    \"devise\": \"EUR\",\n",
    "}\n",
    "\n",
    "# Price update (end-of-day close)\n",
    "position[\"prix\"] = 712.40\n",
    "# Enrich the dictionary\n",
    "position[\"secteur\"] = \"Luxury\"\n",
    "position[\"actif\"] = True\n",
    "\n",
    "if position[\"actif\"]:\n",
    "    statut = \"position active\"\n",
    "else:\n",
    "    statut = \"position fermée\"\n",
    "\n",
    "print(f\"{position['ticker']} ({position['secteur']}) — {position['quantite']} titres à {position['prix']} {position['devise']} — {statut}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aca335e6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Correction — Exercise 3\n",
    "position = {\n",
    "    \"ticker\": \"MC.PA\",\n",
    "    \"prix\": 712.40,\n",
    "    \"quantite\": 25,\n",
    "    \"devise\": \"EUR\",\n",
    "    \"secteur\": \"Luxury\",\n",
    "    \"actif\": True,\n",
    "}\n",
    "\n",
    "print(\"--- Clés ---\")\n",
    "for cle in position.keys():\n",
    "    print(cle)\n",
    "\n",
    "print(\"--- Valeurs ---\")\n",
    "for valeur in position.values():\n",
    "    print(valeur)\n",
    "\n",
    "print(\"--- Fiche d'identité ---\")\n",
    "for cle, valeur in position.items():\n",
    "    print(f\"{cle} : {valeur}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3dbca249",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Correction — Exercise 4\n",
    "portefeuille = [\n",
    "    {\"ticker\": \"MC.PA\",  \"secteur\": \"Luxury\", \"prix\": 712.40, \"quantite\": 25},\n",
    "    {\"ticker\": \"TTE.PA\", \"secteur\": \"Energy\", \"prix\":  61.30, \"quantite\": 80},\n",
    "    {\"ticker\": \"BNP.PA\", \"secteur\": \"Banks\",  \"prix\":  68.10, \"quantite\": 50},\n",
    "]\n",
    "\n",
    "for position in portefeuille:\n",
    "    valeur_marche = position[\"prix\"] * position[\"quantite\"]\n",
    "\n",
    "    if valeur_marche >= 10000:\n",
    "        taille = \"Grosse position\"\n",
    "    elif valeur_marche >= 3000:\n",
    "        taille = \"Position moyenne\"\n",
    "    else:\n",
    "        taille = \"Petite position\"\n",
    "\n",
    "    print(f\"{position['ticker']} | {position['secteur']} | prix = {position['prix']} EUR | qte = {position['quantite']} | valeur = {valeur_marche} EUR | {taille}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a9ac49e",
   "metadata": {},
   "source": [
    "## 6. Mini défi de fin de séance\n",
    "\n",
    "Tu disposes d'un portefeuille de **5 positions** (à créer toi-même, comme liste de dictionnaires).\n",
    "Chaque position doit avoir les clés : `ticker`, `prix_achat`, `prix_actuel`, `quantite`.\n",
    "\n",
    "Trouve et affiche la **meilleure position** selon le rendement :\n",
    "\n",
    "```\n",
    "rendement = (prix_actuel - prix_achat) / prix_achat\n",
    "```\n",
    "\n",
    "**Contraintes** :\n",
    "- afficher le **rendement** et le **P&L latent** de chaque position dans une boucle ;\n",
    "- afficher à la fin le **Top 1** : ticker + rendement + P&L latent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2c4c0ddf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO — Mini challenge\n",
    "pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c0b71069",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Correction — Mini challenge: best position by return\n",
    "portefeuille = [\n",
    "    {\"ticker\": \"MC.PA\", \"prix_achat\": 680.0, \"prix_actuel\": 712.4, \"quantite\": 25},\n",
    "    {\"ticker\": \"TTE.PA\",\"prix_achat\":  58.0, \"prix_actuel\":  61.3, \"quantite\": 80},\n",
    "    {\"ticker\": \"BNP.PA\",\"prix_achat\":  75.0, \"prix_actuel\":  68.1, \"quantite\": 50},\n",
    "    {\"ticker\": \"SAN.PA\",\"prix_achat\":  95.0, \"prix_actuel\":  92.2, \"quantite\": 40},\n",
    "    {\"ticker\": \"AIR.PA\",\"prix_achat\": 120.0, \"prix_actuel\": 145.5, \"quantite\": 30},\n",
    "]\n",
    "\n",
    "meilleur_ticker = None\n",
    "meilleur_rendement = None\n",
    "meilleur_pnl = 0.0\n",
    "\n",
    "for position in portefeuille:\n",
    "    rendement = (position[\"prix_actuel\"] - position[\"prix_achat\"]) / position[\"prix_achat\"]\n",
    "    pnl = (position[\"prix_actuel\"] - position[\"prix_achat\"]) * position[\"quantite\"]\n",
    "\n",
    "    print(f\"{position['ticker']} | rendement = {rendement:.2%} | P&L = {pnl:.2f} EUR\")\n",
    "\n",
    "    if meilleur_rendement is None or rendement > meilleur_rendement:\n",
    "        meilleur_ticker = position[\"ticker\"]\n",
    "        meilleur_rendement = rendement\n",
    "        meilleur_pnl = pnl\n",
    "\n",
    "print(\"-\")\n",
    "print(f\"Top 1 : {meilleur_ticker} | rendement = {meilleur_rendement:.2%} | P&L = {meilleur_pnl:.2f} EUR\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9ca0f792",
   "metadata": {},
   "source": [
    "## 7. Récapitulatif\n",
    "\n",
    "À la fin de ce **Cours 4**, tu dois être à l'aise avec :\n",
    "\n",
    "- la création d'un **dictionnaire** (`{cle: valeur, ...}`) ;\n",
    "- la lecture (`d['cle']`), la modification (`d['cle'] = ...`) et l'enrichissement (ajout de nouvelles clés) ;\n",
    "- le **parcours** d'un dictionnaire avec `.keys()`, `.values()` et `.items()` ;\n",
    "- la **liste de dictionnaires** pour modéliser un portefeuille d'actifs ;\n",
    "- l'itération sur un portefeuille pour calculer des indicateurs simples (valeur de marché, rendement, P&L).\n",
    "\n",
    "**À venir au Cours 5** : approfondissement avec des dictionnaires imbriqués et des calculs plus poussés sur un portefeuille (poids des positions, allocation sectorielle)."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
