Quand un sinistre survient, il n’est pas toujours réglé immédiatement. Entre la date de survenance, la déclaration et le paiement final, il peut s’écouler plusieurs années — surtout en responsabilité civile corporelle. L’assureur doit donc provisionner : mettre de côté aujourd’hui une somme suffisante pour payer demain.
C’est une obligation réglementaire. En Tunisie, le Comité Général des Assurances (CGA) impose aux compagnies de justifier leurs provisions techniques à chaque clôture annuelle. Une provision sous-estimée fragilise la solvabilité de la compagnie. Une provision sur-estimée immobilise inutilement du capital.
L’objectif de ce projet est d’estimer ces provisions à partir d’un triangle de développement, en appliquant deux méthodes : Chain Ladder et Bornhuetter-Ferguson, puis de comparer leurs résultats.
On construit un triangle de charges cumulées sur 7 années de survenance et 7 années de développement. Ce type de données est la matière première du provisionnement — chaque ligne est une année où des sinistres sont survenus, chaque colonne est le délai après lequel on observe les paiements cumulés.
# Triangle de charges cumulées (en milliers d'euros)
# Lignes = années de survenance (2017 à 2023)
# Colonnes = années de développement (1 à 7)
triangle_brut <- matrix(
c(2341, 3116, 3456, 3607, 3660, 3681, 3689,
2554, 3376, 3757, 3923, 3985, 4008, NA,
2724, 3610, 4013, 4190, 4264, NA, NA,
2901, 3843, 4274, 4469, NA, NA, NA,
3092, 4099, 4559, NA, NA, NA, NA,
3294, 4368, NA, NA, NA, NA, NA,
3512, NA, NA, NA, NA, NA, NA),
nrow = 7, byrow = TRUE,
dimnames = list(
Survenance = 2017:2023,
Developpement = 1:7
)
)
print(triangle_brut)
## Developpement
## Survenance 1 2 3 4 5 6 7
## 2017 2341 3116 3456 3607 3660 3681 3689
## 2018 2554 3376 3757 3923 3985 4008 NA
## 2019 2724 3610 4013 4190 4264 NA NA
## 2020 2901 3843 4274 4469 NA NA NA
## 2021 3092 4099 4559 NA NA NA NA
## 2022 3294 4368 NA NA NA NA NA
## 2023 3512 NA NA NA NA NA NA
La partie inférieure droite du triangle est vide — ce sont les paiements futurs qu’on cherche à estimer. L’idée centrale de Chain Ladder est que le rythme de développement passé se répète : si historiquement les sinistres de l’année 1 représentent 63% du total final, on peut extrapoler les années récentes à partir de ce ratio.
triangle_df <- as.data.frame(triangle_brut) %>%
mutate(Survenance = 2017:2023) %>%
pivot_longer(-Survenance, names_to = "Dev", values_to = "Charge") %>%
mutate(Dev = as.integer(gsub("D", "", Dev)),
Connu = !is.na(Charge))
ggplot(triangle_df, aes(x = Dev, y = factor(Survenance), fill = Charge)) +
geom_tile(color = "white", linewidth = 0.5) +
geom_text(aes(label = ifelse(!is.na(Charge), format(round(Charge), big.mark = " "), "?")),
size = 3, color = "white", fontface = "bold") +
scale_fill_gradient(low = "#2E86AB", high = "#1B4F72", na.value = "#BDC3C7",
name = "Charge (k€)") +
labs(title = "Triangle de développement des charges cumulées",
subtitle = "Les cases grises représentent les montants à estimer",
x = "Année de développement", y = "Année de survenance") +
theme_minimal() +
theme(legend.position = "right")
n <- nrow(triangle_brut)
triangle_cl <- triangle_brut
# Calcul des facteurs de développement colonne par colonne
facteurs <- numeric(n - 1)
for (j in 1:(n - 1)) {
lignes_connues <- !is.na(triangle_cl[, j]) & !is.na(triangle_cl[, j + 1])
facteurs[j] <- sum(triangle_cl[lignes_connues, j + 1]) /
sum(triangle_cl[lignes_connues, j])
}
cat("Facteurs de développement Chain Ladder :\n")
## Facteurs de développement Chain Ladder :
for (j in 1:(n-1)) {
cat(sprintf(" f%d→%d : %.4f (+%.1f%%)\n",
j, j+1, facteurs[j], (facteurs[j]-1)*100))
}
## f1→2 : 1.3257 (+32.6%)
## f2→3 : 1.1117 (+11.2%)
## f3→4 : 1.0445 (+4.4%)
## f4→5 : 1.0161 (+1.6%)
## f5→6 : 1.0058 (+0.6%)
## f6→7 : 1.0022 (+0.2%)
Chaque facteur représente la croissance moyenne des charges d’une année de développement à la suivante. Un facteur de 1.32 entre la colonne 1 et la colonne 2 signifie que les charges augmentent en moyenne de 32% entre la première et la deuxième année — les dossiers se complètent, les expertises arrivent, les règlements partiels s’accumulent.
On remarque que les facteurs se rapprochent de 1 au fil du développement : les charges se stabilisent, les dossiers se ferment. C’est le comportement attendu d’un portefeuille qui arrive à maturité.
# Complétion du triangle par projection
for (i in 2:n) {
for (j in (n - i + 2):n) {
if (is.na(triangle_cl[i, j])) {
triangle_cl[i, j] <- triangle_cl[i, j - 1] * facteurs[j - 1]
}
}
}
cat("\nTriangle complété (Chain Ladder) :\n")
##
## Triangle complété (Chain Ladder) :
print(round(triangle_cl))
## Developpement
## Survenance 1 2 3 4 5 6 7
## 2017 2341 3116 3456 3607 3660 3681 3689
## 2018 2554 3376 3757 3923 3985 4008 4017
## 2019 2724 3610 4013 4190 4264 4289 4298
## 2020 2901 3843 4274 4469 4541 4567 4577
## 2021 3092 4099 4559 4762 4838 4866 4877
## 2022 3294 4368 4856 5072 5153 5183 5194
## 2023 3512 4656 5176 5406 5493 5525 5537
# Provision = ultime estimé - dernier connu
derniers_connus_cl <- sapply(1:n, function(i) {
vals <- triangle_brut[i, ]
vals[max(which(!is.na(vals)))]
})
ultimes_cl <- triangle_cl[, n]
provisions_cl <- ultimes_cl - derniers_connus_cl
cat("\n── Provisions Chain Ladder par année de survenance (k€) ──\n")
##
## ── Provisions Chain Ladder par année de survenance (k€) ──
for (i in 1:n) {
cat(sprintf(" %d : %.0f k€\n", 2016 + i, provisions_cl[i]))
}
## 2017 : 0 k€
## 2018 : 9 k€
## 2019 : 34 k€
## 2020 : 108 k€
## 2021 : 318 k€
## 2022 : 826 k€
## 2023 : 2025 k€
cat(sprintf("\n TOTAL : %.0f k€\n", sum(provisions_cl)))
##
## TOTAL : 3319 k€
Bornhuetter-Ferguson (BF) est une méthode hybride : elle combine l’expérience observée dans le triangle avec une estimation a priori du niveau de sinistralité. C’est particulièrement utile pour les années de survenance récentes, où peu de développement a été observé et où Chain Ladder peut produire des estimations instables.
La provision BF est calculée ainsi :
\[\text{Provision BF} = \text{Part non développée} \times \text{Sinistres ultimes a priori}\]
La part non développée est 1 - 1/facteur_cumulé,
c’est-à-dire la fraction du développement qui reste encore à venir.
# Facteurs cumulés (de chaque colonne jusqu'à l'ultime)
facteurs_cum <- cumprod(rev(facteurs))
facteurs_cum <- rev(facteurs_cum)
facteurs_cum <- c(facteurs_cum, 1)
cat("Facteurs cumulés jusqu'à l'ultime :\n")
## Facteurs cumulés jusqu'à l'ultime :
for (j in 1:n) {
cat(sprintf(" Colonne %d : %.4f (part développée : %.1f%%)\n",
j, facteurs_cum[j], 100/facteurs_cum[j]))
}
## Colonne 1 : 1.5765 (part développée : 63.4%)
## Colonne 2 : 1.1892 (part développée : 84.1%)
## Colonne 3 : 1.0697 (part développée : 93.5%)
## Colonne 4 : 1.0242 (part développée : 97.6%)
## Colonne 5 : 1.0079 (part développée : 99.2%)
## Colonne 6 : 1.0022 (part développée : 99.8%)
## Colonne 7 : 1.0000 (part développée : 100.0%)
# Parts non développées
parts_non_dev <- 1 - 1 / facteurs_cum
# Primes acquises a priori (hypothèse : ratio sinistres/primes de 72%)
primes_a_priori <- c(5100, 5450, 5800, 6150, 6500, 6900, 7350)
ratio_sp <- 0.72
ultimes_a_priori <- primes_a_priori * ratio_sp
# Dernier connu par année
col_derniere <- apply(triangle_brut, 1, function(x) max(which(!is.na(x))))
derniers_connus_bf <- sapply(1:n, function(i) {
triangle_brut[i, col_derniere[i]]
})
# Provision BF
provisions_bf <- parts_non_dev[col_derniere] * ultimes_a_priori
cat("\n── Provisions Bornhuetter-Ferguson par année de survenance (k€) ──\n")
##
## ── Provisions Bornhuetter-Ferguson par année de survenance (k€) ──
for (i in 1:n) {
cat(sprintf(" %d : %.0f k€\n", 2016 + i, provisions_bf[i]))
}
## 2017 : 0 k€
## 2018 : 9 k€
## 2019 : 33 k€
## 2020 : 105 k€
## 2021 : 305 k€
## 2022 : 790 k€
## 2023 : 1935 k€
cat(sprintf("\n TOTAL : %.0f k€\n", sum(provisions_bf)))
##
## TOTAL : 3177 k€
resultats <- data.frame(
Annee = 2017:2023,
Chain_Ladder = round(provisions_cl),
BF = round(provisions_bf),
Ecart = round(provisions_bf - provisions_cl)
)
cat("── Comparaison des provisions (k€) ──\n")
## ── Comparaison des provisions (k€) ──
print(resultats)
## Annee Chain_Ladder BF Ecart
## 2017 2017 0 0 0
## 2018 2018 9 9 0
## 2019 2019 34 33 -1
## 2020 2020 108 105 -4
## 2021 2021 318 305 -13
## 2022 2022 826 790 -36
## 2023 2023 2025 1935 -89
cat(sprintf("\nTotal Chain Ladder : %.0f k€\n", sum(provisions_cl)))
##
## Total Chain Ladder : 3319 k€
cat(sprintf("Total BF : %.0f k€\n", sum(provisions_bf)))
## Total BF : 3177 k€
cat(sprintf("Écart : %.0f k€ (%.1f%%)\n",
sum(provisions_bf) - sum(provisions_cl),
100 * (sum(provisions_bf) - sum(provisions_cl)) / sum(provisions_cl)))
## Écart : -143 k€ (-4.3%)
resultats_long <- resultats %>%
pivot_longer(c(Chain_Ladder, BF),
names_to = "Methode",
values_to = "Provision")
ggplot(resultats_long, aes(x = factor(Annee), y = Provision, fill = Methode)) +
geom_col(position = "dodge", width = 0.6) +
scale_fill_manual(values = c("Chain_Ladder" = "#2E86AB", "BF" = "#E84855"),
labels = c("Chain Ladder", "Bornhuetter-Ferguson")) +
labs(title = "Provisions estimées par méthode et par année de survenance",
subtitle = "Les années récentes montrent les plus grandes divergences entre les deux méthodes",
x = "Année de survenance", y = "Provision (k€)", fill = "Méthode") +
theme_minimal()
Les deux méthodes donnent des résultats proches sur les années anciennes ( 2018 ,2019), où le triangle est presque entièrement développé et où Chain Ladder dispose de suffisamment d’observations pour être fiable.
Les écarts se creusent sur les années récentes (2022, 2023), ce qui est logique : pour 2023, on ne dispose que d’une seule diagonale d’observation. Chain Ladder extrapole alors à partir d’un seul point, ce qui le rend sensible aux fluctuations aléatoires. Bornhuetter-Ferguson, en ancrant son estimation sur une sinistralité a priori raisonnée, produit une provision plus stable — parfois plus prudente, parfois plus optimiste selon la qualité de l’a priori choisi.
Ce projet illustre deux piliers du provisionnement actuariel en assurance non-vie :
Dans un contexte réel, ces méthodes seraient complétées par des tests de sensibilité, une analyse de l’évolution des facteurs dans le temps, et une comparaison avec les provisions de l’exercice précédent. La provision finale retenue relève d’un jugement actuariel — les modèles éclairent, mais ne décident pas.
mini-projet réalisé par Eya Douiri Future actuaire et data scientist ❤️ contact :eyadouiri9@gmail.com