Série : DecPyMC — ce capstone est l’escalier de la série vers sa sous-série Actuariat (T1-T5). À l’image du capstone Lean-37 — Serre 100 pour la série Lean, il ne remplace pas la sous-série : il présente son geste, mesure sa surface, et laisse le lecteur décider s’il entre.
Ce que ce notebook suppose : avoir lu DecPyMC-1 à 7 (utilité espérée, valeur de l’information, décision séquentielle) — ou, au minimum, DecPyMC-2 (prime de risque) et DecPyMC-7 (décision dans le temps). Aucune connaissance actuarielle préalable : les trois primes y sont distinguées avant d’être calculées.
1. Le geste : la décision bayésienne devient un métier
Les notebooks 1 à 7 ont traité la décision en général : une utilité, une information, une séquence. L’assurance est le métier où ces abstractions deviennent des nombres encadrés par la loi et le marché :
tarifer — quel prix demander pour couvrir un risque ? (prime pure, chargement, prime commerciale)
mutualiser — comment pondérer l’expérience d’un contrat contre celle du portefeuille ? (hiérarchie, crédibilité)
capitaliser — quelle réserve pour ne pas faire faillite ? (processus de ruine)
souscrire — un test supplémentaire vaut-il son prix ? (valeur de l’information)
Chaque question a son carnet dans la sous-série. L’ancienne « jambe actuarielle » de la série (notebooks 8 à 12) est descendue dans le dossier Actuariat/ et régraduée T1-T5 : le numéro dit désormais la place dans l’arc de métier, pas l’ordre d’écriture.
Quand un test de risque vaut son prix (EVPI/EVSI au métier)
Pourquoi cet ordre : on apprend à tarifer (T1), puis à modéliser le portefeuille (T2), puis à mélanger individu et collectif (T3, consommé par T2), puis à capitaliser (T4, qui juge le tarif), puis à décider d’acheter de l’information (T5, qui boucle sur DecPyMC-5). L’ordre d’origine plaçait la crédibilité avant la structure qui la motive ; la régraduation corrige l’arc (EPIC #12904).
# Mesurer la surface de la sous-série : on lit les carnets, on ne suppose rien.import jsonfrom pathlib import PathCANDIDATS = [Path("Actuariat"), Path("../Actuariat")]RACINE =next((p for p in CANDIDATS if p.is_dir()), None)assert RACINE isnotNone, "dossier Actuariat introuvable depuis ce carnet"def decrire(chemin): nb = json.loads(chemin.read_text(encoding="utf-8")) c0 ="".join(x ifisinstance(x, str) else x[0] for x in nb["cells"][0]["source"]) titre = c0.splitlines()[0].lstrip("# ").strip() n_code =sum(1for c in nb["cells"] if c["cell_type"] =="code") n_md =sum(1for c in nb["cells"] if c["cell_type"] =="markdown") kernel = nb.get("metadata", {}).get("kernelspec", {}).get("name", "?") source ="\n".join("".join(x ifisinstance(x, str) else x[0] for x in c["source"])for c in nb["cells"] if c["cell_type"] =="code" )return titre, n_md, n_code, kernel, sourcedef imports_libs(source):"""Detecte les imports de pymc et arviz dans une source agregee, via AST. Gere les imports groupes (ex. 'import numpy as np, pymc as pm, arviz as az') et les formes 'from pymc import ...'. Renvoie (imports_pymc, imports_arviz). """import ast pymc =False arviz =Falsetry: tree = ast.parse(source)exceptSyntaxError:return pymc, arvizfor node in ast.walk(tree):ifisinstance(node, ast.Import):for alias in node.names: top = alias.name.split('.')[0]if top =='pymc': pymc =Trueelif top =='arviz': arviz =Trueelifisinstance(node, ast.ImportFrom):if node.module isNone:continue top = node.module.split('.')[0]if top =='pymc': pymc =Trueelif top =='arviz': arviz =Truereturn pymc, arvizcarnets =sorted(RACINE.glob("Actuariat-*.ipynb"))print(f"Sous-série Actuariat : {len(carnets)} carnets dans {RACINE}/")print()print("| # | Titre | Cellules (md/code) | Kernel | PyMC | ArviZ |")print("|---|-------|--------------------|--------|------|-------|")for chemin in carnets: numero =int(chemin.stem.split("-")[1]) titre, n_md, n_code, kernel, source = decrire(chemin) imports_pymc, imports_arviz = imports_libs(source)print(f"| T{numero} | {titre[:52]} | {n_md}/{n_code} | `{kernel}` | "f"{'oui'if imports_pymc else'non'} | {'oui'if imports_arviz else'non'} |")# L'escalier doit etre continu : 01..05 sans trounumeros = [p.stem.split("-")[1] for p in carnets]assert numeros == ["01", "02", "03", "04", "05"], f"escalier discontinu : {numeros}"tot_md =sum(decrire(p)[1] for p in carnets)tot_code =sum(decrire(p)[2] for p in carnets)print()print(f"Escalier T1-T5 continu. Surface totale : {tot_md} cellules markdown, {tot_code} cellules code.")
Sous-série Actuariat : 5 carnets dans ..\Actuariat/
| # | Titre | Cellules (md/code) | Kernel | PyMC | ArviZ |
|---|-------|--------------------|--------|------|-------|
| T1 | Actuariat-01 : Du risque à la prime — prime pure, ch | 22/15 | `python3` | oui | oui |
| T2 | Actuariat-02 — Fréquence × sévérité hiérarchique : l | 20/14 | `python3` | oui | oui |
| T3 | Actuariat-03 — Crédibilité actuarielle de Bühlmann–S | 19/8 | `python3` | oui | oui |
| T4 | Actuariat-04 : Ruine et capital — le processus de Cr | 21/12 | `python3` | oui | oui |
| T5 | Actuariat-05 — Valeur de l'Information en Souscripti | 24/13 | `python3` | oui | oui |
Escalier T1-T5 continu. Surface totale : 106 cellules markdown, 62 cellules code.
Lecture de la mesure
Cinq carnets, un escalier continu, la même stack que la série mère (python3, PyMC, ArviZ) : entrer dans la sous-série ne demande aucun outillage nouveau — seulement le socle théorique de DecPyMC-1 à 7. La surface (~3 h 30 – 4 h de lecture déclarée) est celle d’un arc de métier, pas d’un corpus : chaque carnet tient en une séance.
3. La charnière décisionnelle : du posterior à la prime
Pour goûter le geste de la sous-série sans y entrer, un seul calcul suffit — celui que T1 déroule en entier. Un contrat d’assurance est une loterie : une fréquence de sinistres (Poisson) et une sévérité (montant). La théorie de la décision dit quel prix rendre acceptable ; l’actuariat dit quel prix demander pour que le portefeuille survive :
La prime pure est l’espérance mathématique du coût — c’est un nombre fréquentiste si les paramètres sont connus, une distribution si on les infère (c’est tout le geste bayésien de Actuariat-01). Le chargement transforme l’espérance en prix de marché ; le terme de variance rémunère la variabilité du coût. En trois nombres, la décision « couvrir ou non, à quel prix » est devenue un calcul.
# Exemple guide : la charniere decisionnelle, en trois nombres.# Risque discretise : frequence de Poisson lambda, severite discrete — calcul ferme, pas de sampling.import mathlam =0.3# frequence moyenne de sinistre par contrat et par anseverites = [200, 500, 1000, 2500, 6000] # montants possibles d'un sinistreprobas = [0.50, 0.25, 0.15, 0.08, 0.02] # loi de severite (somme = 1)E_X =sum(s * p for s, p inzip(severites, probas)) # severite moyenneE_X2 =sum(s * s * p for s, p inzip(severites, probas))E_S = lam * E_X # prime pure : E[S] = lambda * E[X] (modele collectif)Var_S = lam * E_X2 # Var[S] = lambda * E[X^2] (variance composee)theta =0.15# chargement proportionnel : frais commerciaux + margealpha =0.10# coefficient du chargement de securite (variance)chargement_prop = theta * E_Schargement_var = alpha * math.sqrt(Var_S)prime_commerciale = E_S + chargement_prop + chargement_varprint(f"Prime pure E[S] = {E_S:9.2f}")print(f"Chargement prop. 0.15 x E[S] = {chargement_prop:9.2f}")print(f"Chargement de var. 0.10.sqrt(Var) = {chargement_var:9.2f}")print(f"---------------------------------------------")print(f"Prime commerciale = {prime_commerciale:9.2f}")print()print(f"Ecart-type de S : {math.sqrt(Var_S):.2f} ({math.sqrt(Var_S)/E_S:.0%} de la prime pure)")print(f"La prime commerciale couvre E[S] a hauteur de {prime_commerciale / E_S:.1%}")
Prime pure E[S] = 208.50
Chargement prop. 0.15 x E[S] = 31.27
Chargement de var. 0.10.sqrt(Var) = 66.01
---------------------------------------------
Prime commerciale = 305.79
Ecart-type de S : 660.11 (317% de la prime pure)
La prime commerciale couvre E[S] a hauteur de 146.7%
Lecture du résultat
La prime pure est le coût espéré ; la prime commerciale est le prix. L’écart entre les deux — ici ~47 % — n’est pas un profit arbitraire : c’est ce qui paie les frais et rémunère la variabilité du coût ; le terme de variance, dans le modèle collectif de Poisson sous-jacent, réduit la probabilité de ruine conditionnelle au capital et aux paramètres du modèle, sans l’annuler (l’exercice 2 de T4 Lundberg en exhibe une probabilité résiduelle strictement positive sous ce chargement). Détail instructif : le terme de variance domine le chargement proportionnel (66 contre 31) — signature d’un contrat à fréquence faible, où une seule grosse sévérité fait basculer l’année. Ce calcul est volontairement fréquentiste (paramètres connus) : tout l’apport de Actuariat-01 est de refaire ce même calcul avec un posterior PyMC sur les paramètres — la prime pure devient alors une distribution, et le chargement une décision sur ses quantiles.
Exercice 1 : la réassurance stop-loss — combien céder à la tranche M ?
Contexte : l’assureur peut céder à un réassureur ce qui dépasse une tranche M — il ne retient que min(S, M). Le réassureur facture la charge cédée E[(S − M)+] (plus son propre chargement).
Objectifs : 1. Simuler 20000 années de sinistres pour le contrat ci-dessus — tirer d’abord N ~ Poisson(0.3), puis N sévérités selon la loi discrète (numpy, default_rng(42)) 2. Pour chaque tranche M ∈ {500, 1000, 2000}, estimer E[(S − M)+] et le taux de cession E[(S − M)+] / E[S]
Indice : np.add.outer ou une simple boucle sur M suffisent — la simulation ne coûte rien. Étape 1 : construire le vecteur des S simulés. Étape 2 : appliquer np.maximum(S - M, 0).mean() par tranche.
# Exercice 1 : reassurance stop-loss# Etape 1 : simuler 20000 annees (frequence Poisson puis severites, seed 42)# Etape 2 : pour M dans [500, 1000, 2000], calculer E[(S - M)+] et le taux de cessionimport numpy as nprng = np.random.default_rng(42)# ... simulation ...resultat =None# TODO etudiant : liste des (M, E[(S - M)+], taux de cession)print("Exercice a completer : reinsurance stop-loss")
Exercice a completer : reinsurance stop-loss
Exercice 2 : la probabilité de ruine sur un an
Contexte : l’assureur part avec un capital initial u et encaisse la prime commerciale calculée plus haut. Il fait ruine si S > u + prime_commerciale. C’est la version un an, discrète, du processus de T4.
Objectifs : 1. Réutiliser la simulation de l’exercice 1 (même seed) 2. Pour u ∈ {0, 1000, 2000}, estimer P(S > u + prime_commerciale) 3. Vérifier l’ordre de grandeur : la prime couvre ~147 % de E[S] — la ruine doit être rare mais pas impossible
Indice : (S_sim > u + prime_commerciale).mean() ; comparer les trois probabilités entre elles dit comment le capital réduit le risque de queue.
# Exercice 2 : probabilite de ruine sur un an# Etape 1 : reutiliser S_sim (meme seed que l'exercice 1)# Etape 2 : pour u dans [0, 1000, 2000], estimer P(S > u + prime_commerciale)proba_ruine =None# TODO etudiant : liste des (u, P(ruine))print("Exercice a completer : probabilite de ruine")
Exercice a completer : probabilite de ruine
Exercice 3 : régénérer l’escalier depuis les carnets
Contexte : la table T1-T5 de la section 2 est écrite à la main. Une documentation qui se régénère depuis l’artefact ne peut pas mentir sur l’artefact — même geste que la mesure de la section 1.
Objectifs : 1. Relire les carnets de Actuariat/ et extraire le titre (première ligne de la cellule 0) de chacun 2. Produire les lignes markdown | T1 | <titre> | … | T5 | <titre> | dans l’ordre des numéros
Indice : la fonction decrire de la section 1 fait déjà la moitié du travail. Étape 1 : collecter (numéro, titre). Étape 2 : trier et formater.
# Exercice 3 : regenerer la table T1-T5 depuis les titres des carnets# Etape 1 : relire les carnets de RACINE, extraire le titre de chaque cellule 0# Etape 2 : produire les lignes "| T1 | <titre> |" dans l'ordre 01..05lignes =None# TODO etudiant : liste des lignes markdownprint("Exercice a completer : table des matieres de la sous-serie")
Exercice a completer : table des matieres de la sous-serie
4. Conclusion — ce que ce capstone laisse au lecteur
Trois choses, dans cet ordre :
Un point d’entrée : la sous-série Actuariat est un arc de métier en cinq carnets (T1 tarifer, T2 modéliser, T3 mutualiser, T4 capitaliser, T5 souscrire), ~3 h 30 – 4 h, même stack que la série mère.
Un geste goûté : la charnière décisionnelle — espérance, chargement, prix — calculée ici en fréquentiste, déroulée en bayésien dans Actuariat-01.
Une mesure honnête : la surface de la sous-série a été mesurée depuis les carnets eux-mêmes, pas déclarée — la documentation y est régénérable (exercice 3), pas seulement lisible.
La série DecPyMC, elle, s’arrête ici : le slot 08 était la dernière marche, et cette marche ouvre sur un autre escalier.
References
Actuariat
Bühlmann, H. & Gisler, A.A Course in Credibility Theory and its Applications. Springer. (crédibilité Bühlmann–Straub, T3)
Kaas, R., Goovaerts, M., Dhaene, J. & Denuit, M.Modern Actuarial Risk Theory. Springer. (modèle collectif, prime pure, ruine)
Asmussen, S. & Albrecher, H.Ruin Probabilities. World Scientific. (processus de Cramér–Lundberg, T4)
Théorie de la décision (série mère)
Von Neumann, J. & Morgenstern, O.Theory of Games and Economic Behavior. Princeton University Press. (utilité espérée, DecPyMC-1)
Howard, R. A.Information Value Theory. IEEE Trans. SSC, 1966. (EVSI, DecPyMC-5 et T5)