Modèle : Le Modèle d’Évaluation des Actifs Financiers (MEDAF, ou Capital Asset Pricing Model — CAPM) est le cadre de référence en finance de marché pour lier le rendement espéré d’un actif risqué à son exposition au risque systématique.
La relation fondamentale du CAPM s’écrit :
\[\boxed{E(R_i) - R_f \;=\; \alpha_i + \beta_i \,\bigl[E(R_m) - R_f\bigr] + \varepsilon_i}\]
où les termes sont définis comme suit :
| Paramètre | Notation | Interprétation financière |
|---|---|---|
| Prime de risque de l’actif | \(E(R_i) - R_f\) | Rendement excédentaire attendu |
| Ordonnée à l’origine | \(\alpha_i\) | Alpha de Jensen — performance anormale |
| Coefficient de sensibilité | \(\beta_i\) | Exposition au risque systématique |
| Prime de marché | \(E(R_m) - R_f\) | Rendement excédentaire du marché |
| Terme d’erreur | \(\varepsilon_i \sim \mathcal{N}(0,\sigma^2)\) | Risque idiosyncratique non capturé |
Le modèle repose sur les hypothèses classiques de la régression linéaire (Gauss-Markov) :
# Données : primes de risque de l'actif (y) vs. prime de marché (x)
x <- c(-2, -1.5, -0.5, 0, 0.5, 1, 1.5, 2, 2.5, 3)
y <- c(-3.1, -2, -0.8, 0.2, 0.9, 1.4, 2, 2.6, 3.1, 3.8)
n <- length(x)
df <- tibble(
`Prime Marché (x)` = x,
`Prime Actif (y)` = y,
Observation = 1:n
)df %>%
kable(
caption = "Tableau 1 — Données de régression CAPM",
align = c("c", "c", "c"),
digits = 2,
booktabs = TRUE
) %>%
kable_styling(
bootstrap_options = c("striped", "hover", "condensed", "responsive"),
full_width = FALSE,
position = "center",
font_size = 13
) %>%
row_spec(0, bold = TRUE, color = "white", background = "#0d3b66") %>%
column_spec(1:2, bold = FALSE, color = "#1a1a2e")| Prime Marché (x) | Prime Actif (y) | Observation |
|---|---|---|
| -2.0 | -3.1 | 1 |
| -1.5 | -2.0 | 2 |
| -0.5 | -0.8 | 3 |
| 0.0 | 0.2 | 4 |
| 0.5 | 0.9 | 5 |
| 1.0 | 1.4 | 6 |
| 1.5 | 2.0 | 7 |
| 2.0 | 2.6 | 8 |
| 2.5 | 3.1 | 9 |
| 3.0 | 3.8 | 10 |
stats_df <- tibble(
Statistique = c("N", "Moyenne", "Médiane", "Écart-type", "Min", "Max", "Asymétrie"),
`Prime Marché (x)` = c(
n, mean(x), median(x), sd(x), min(x), max(x),
(mean(x) - median(x)) / sd(x)
),
`Prime Actif (y)` = c(
n, mean(y), median(y), sd(y), min(y), max(y),
(mean(y) - median(y)) / sd(y)
)
)
stats_df %>%
kable(
caption = "Tableau 2 — Statistiques descriptives",
digits = 4,
booktabs = TRUE
) %>%
kable_styling(
bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE,
position = "center",
font_size = 13
) %>%
row_spec(0, bold = TRUE, color = "white", background = "#0d3b66")| Statistique | Prime Marché (x) | Prime Actif (y) |
|---|---|---|
| N | 10.0000 | 10.0000 |
| Moyenne | 0.6500 | 0.8100 |
| Médiane | 0.7500 | 1.1500 |
| Écart-type | 1.6675 | 2.2437 |
| Min | -2.0000 | -3.1000 |
| Max | 3.0000 | 3.8000 |
| Asymétrie | -0.0600 | -0.1515 |
Les estimateurs MCO sont obtenus par minimisation de la somme des carrés des résidus :
\[\hat{\beta}_1 = \frac{\sum_{i=1}^{n}(x_i - \bar{x})(y_i - \bar{y})}{\sum_{i=1}^{n}(x_i - \bar{x})^2} = \frac{S_{xy}}{S_{xx}}\]
\[\hat{\beta}_0 = \bar{y} - \hat{\beta}_1 \bar{x}\]
# ── Calcul manuel des estimateurs MCO ────────────────────────────────
x_bar <- mean(x); y_bar <- mean(y)
Sxx <- sum((x - x_bar)^2)
Sxy <- sum((x - x_bar) * (y - y_bar))
Syy <- sum((y - y_bar)^2)
beta1_hat <- Sxy / Sxx # Bêta (pente)
beta0_hat <- y_bar - beta1_hat * x_bar # Alpha (intercept)
cat(sprintf("α̂ (Intercept / Jensen Alpha) : %.6f\n", beta0_hat))#> α̂ (Intercept / Jensen Alpha) : -0.061039
#> β̂ (Market Beta) : 1.340060
lm() et Synthèsemodel <- lm(y ~ x)
tidy_m <- tidy(model, conf.int = TRUE, conf.level = 0.95)
glance_m <- glance(model)tidy_m %>%
mutate(
term = c("α̂ (Intercept)", "β̂ (Market Beta)"),
across(where(is.numeric), ~round(.x, 6))
) %>%
rename(
Paramètre = term,
Estimé = estimate,
`Ér. Std` = std.error,
`t-stat` = statistic,
`p-value` = p.value,
`IC 2.5%` = conf.low,
`IC 97.5%` = conf.high
) %>%
kable(
caption = "Tableau 3 — Estimateurs MCO et tests de Student",
booktabs = TRUE
) %>%
kable_styling(
bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE,
font_size = 13
) %>%
row_spec(0, bold = TRUE, color = "white", background = "#0d3b66") %>%
row_spec(2, bold = TRUE, background = "#eaf7ea") # Bêta mis en évidence| Paramètre | Estimé | Ér. Std | t-stat | p-value | IC 2.5% | IC 97.5% |
|---|---|---|---|---|---|---|
| α̂ (Intercept) |
-0.06103
|
Hypothèse : \(H_0 : \beta_j = 0\) contre \(H_1 : \beta_j \neq 0\). La statistique de test est : \[t_j = \frac{\hat{\beta}_j - 0}{\widehat{\text{se}}(\hat{\beta}_j)} \;\sim\; t_{n-2} \text{ sous } H_0\]
alpha_level <- 0.05
t_crit <- qt(1 - alpha_level / 2, df = n - 2)
tidy_m %>%
mutate(
Paramètre = c("α̂", "β̂"),
`t critique (α=5%)` = round(t_crit, 4),
Décision = ifelse(abs(statistic) > t_crit,
"✅ Rejet H₀ — Significatif",
"❌ Non rejet H₀"),
`p-value` = round(p.value, 6),
across(c(estimate, statistic), ~round(.x, 6))
) %>%
select(Paramètre, estimate, statistic, `t critique (α=5%)`, `p-value`, Décision) %>%
rename(Estimé = estimate, `t-stat` = statistic) %>%
kable(caption = "Tableau 4 — Test de Student (bilatéral, α = 5%)", booktabs = TRUE) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE,
font_size = 13) %>%
row_spec(0, bold = TRUE, color = "white", background = "#0d3b66")| Paramètre | Estimé | t-stat | t critique (α=5%) | p-value | Décision |
|---|---|---|---|---|---|
| α̂ |
-0.06103
|
La décomposition de la variance totale s’écrit :
\[\underbrace{SCT}_{=\,S_{yy}} \;=\; \underbrace{SCE}_{\hat{\beta}_1^2 \cdot S_{xx}} \;+\; \underbrace{SCR}_{SCT - SCE}\]
anova_obj <- anova(model)
SCE <- beta1_hat^2 * Sxx
SCR <- Syy - SCE
SCT <- Syy
MSE <- SCE / 1
MSR <- SCR / (n - 2)
F_stat <- MSE / MSR
p_F <- pf(F_stat, 1, n - 2, lower.tail = FALSE)
anova_df <- tibble(
Source = c("Régression (SCE)", "Résidus (SCR)", "Total (SCT)"),
`Degrés Liberté` = c(1L, n - 2L, n - 1L),
`Somme Carrés` = round(c(SCE, SCR, SCT), 6),
`Carré Moyen` = round(c(MSE, MSR, NA), 6),
`F-stat` = c(round(F_stat, 4), NA, NA),
`p-value` = c(format(p_F, scientific = TRUE, digits = 4), NA, NA)
)
anova_df %>%
kable(caption = "Tableau 5 — Table ANOVA de la régression", booktabs = TRUE,
na.rm = TRUE) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE,
font_size = 13) %>%
row_spec(0, bold = TRUE, color = "white", background = "#0d3b66") %>%
row_spec(3, bold = TRUE, background = "#eef2f7")| Source | Degrés Liberté | Somme Carrés | Carré Moyen | F-stat | p-value |
|---|---|---|---|---|---|
| Régression (SCE) | 1 | 44.93891 | 44.938910 | 971.4161 | 1.221e-09 |
| Résidus (SCR) | 8 | 0.37009 | 0.046261 | NA | NA |
| Total (SCT) | 9 | 45.30900 | NA | NA | NA |
R2 <- SCE / SCT
R2_adj <- 1 - (SCR / (n - 2)) / (SCT / (n - 1))
sigma_hat <- sqrt(SCR / (n - 2))
tibble(
Métrique = c("R²", "R² ajusté", "σ̂ (Erreur std résiduelle)"),
Valeur = round(c(R2, R2_adj, sigma_hat), 6),
Interprétation = c(
paste0(round(R2 * 100, 2), "% de la variance de y expliquée par x"),
"Penalisé par le nombre de paramètres",
"Dispersion moyenne des résidus autour de la droite"
)
) %>%
kable(caption = "Tableau 6 — Qualité d'ajustement du modèle", booktabs = TRUE) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE,
font_size = 13) %>%
row_spec(0, bold = TRUE, color = "white", background = "#0d3b66") %>%
row_spec(1, bold = TRUE, background = "#eaf7ea")| Métrique | Valeur | Interprétation |
|---|---|---|
| R² | 0.991832 | 99.18% de la variance de y expliquée par x |
| R² ajusté | 0.990811 | Penalisé par le nombre de paramètres |
| σ̂ (Erreur std résiduelle) |
0.21508
|
Interprétation : Un \(R^2 = 0.9918\) signifie que 99.18% de la variance du rendement excédentaire de l’actif est expliquée par la prime de risque de marché. Ce niveau élevé confirme la robustesse du modèle CAPM sur cet échantillon.
y_hat <- fitted(model)
resids <- residuals(model)
p_gg <- ggplot(df, aes(x = `Prime Marché (x)`, y = `Prime Actif (y)`)) +
geom_point(
aes(text = paste0(
"Obs. ", Observation, "<br>",
"x = ", `Prime Marché (x)`, "<br>",
"y = ", `Prime Actif (y)`, "<br>",
"ŷ = ", round(y_hat, 3), "<br>",
"ε = ", round(resids, 3)
)),
size = 3.5, colour = "#e63946", shape = 19
) +
geom_smooth(
method = "lm", formula = y ~ x,
se = TRUE, colour = "#0d3b66",
fill = "#a8c8e8", alpha = 0.25, linewidth = 1.1
) +
geom_hline(yintercept = 0, linetype = "dashed", colour = "#6c757d", linewidth = 0.5) +
geom_vline(xintercept = 0, linetype = "dashed", colour = "#6c757d", linewidth = 0.5) +
annotate(
"label",
x = min(x) + 0.3,
y = max(y) - 0.3,
label = sprintf("ŷ = %.4f + %.4f·x\nR² = %.4f", beta0_hat, beta1_hat, R2),
size = 3.5, colour = "#0d3b66", fill = "white",
label.padding = unit(0.4, "lines"), label.r = unit(0.3, "lines"),
hjust = 0, fontface = "italic"
) +
labs(
title = "Droite de Régression MCO — Modèle CAPM",
subtitle = sprintf("α̂ = %.4f | β̂ = %.4f | R² = %.4f", beta0_hat, beta1_hat, R2),
x = "Prime de Risque de Marché (Rm − Rf)",
y = "Prime de Risque de l'Actif (Ri − Rf)",
caption = "Source : Données de l'exercice — MCO estimé sous R"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", colour = "#0d3b66", size = 14),
plot.subtitle = element_text(colour = "#457b9d", size = 11),
plot.caption = element_text(colour = "#6c757d", size = 9),
panel.grid.minor = element_blank()
)
ggplotly(p_gg, tooltip = "text") %>%
layout(
font = list(family = "IBM Plex Sans"),
hoverlabel = list(
bgcolor = "#0d3b66",
font = list(color = "white", size = 12)
)
)aug_df <- augment(model) %>%
mutate(obs = 1:n)
p1 <- ggplot(aug_df, aes(x = .fitted, y = .resid)) +
geom_hline(yintercept = 0, linetype = "dashed", colour = "#e63946") +
geom_point(colour = "#0d3b66", size = 3) +
geom_smooth(se = FALSE, colour = "#457b9d", linewidth = 0.8, method = "loess", formula = y~x) +
labs(title = "Résidus vs. Valeurs ajustées", x = "ŷ", y = "Résidus") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", colour = "#0d3b66"))
p2 <- ggplot(aug_df, aes(sample = .resid)) +
stat_qq(colour = "#0d3b66", size = 2.5) +
stat_qq_line(colour = "#e63946", linewidth = 1) +
labs(title = "Q-Q Plot des Résidus", x = "Quantiles théoriques", y = "Quantiles observés") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", colour = "#0d3b66"))
gridExtra::grid.arrange(p1, p2, ncol = 2)# Normalité — Shapiro-Wilk & Anderson-Darling
sw_test <- shapiro.test(residuals(model))
ad_test <- nortest::ad.test(residuals(model))
# Homoscédasticité — Breusch-Pagan
bp_test <- lmtest::bptest(model)
tibble(
Test = c("Shapiro-Wilk (Normalité)",
"Anderson-Darling (Normalité)",
"Breusch-Pagan (Homoscédasticité)"),
`Statistique` = round(c(sw_test$statistic, ad_test$statistic, bp_test$statistic), 6),
`p-value` = round(c(sw_test$p.value, ad_test$p.value, bp_test$p.value), 6),
Décision = ifelse(
c(sw_test$p.value, ad_test$p.value, bp_test$p.value) > 0.05,
"✅ H₀ non rejetée — Hypothèse validée",
"⚠️ H₀ rejetée — Violation potentielle"
)
) %>%
kable(caption = "Tableau 7 — Tests de validation des hypothèses MCO", booktabs = TRUE) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE,
font_size = 13) %>%
row_spec(0, bold = TRUE, color = "white", background = "#0d3b66")| Test | Statistique | p-value | Décision |
|---|---|---|---|
| Shapiro-Wilk (Normalité) | 0.975863 | 0.939258 | ✅ H₀ non rejetée — Hypothèse validée | |
| Anderson-Darling (Normalité) | 0.143110 | 0.953569 | ✅ H₀ non rejetée — Hypothèse validée | |
| Breusch-Pagan (Homoscédasticité) | 1.869208 | 0.171566 | ✅ H₀ non rejetée — Hypothèse validée | |
Note méthodologique : Avec \(n = 10\) observations, la puissance des tests formels est limitée. L’analyse graphique (Q-Q plot, résidus vs. ajustés) est complémentaire et souvent plus informative sur de petits échantillons.
tibble(
Paramètre = c("α̂ — Jensen Alpha", "β̂ — Market Beta", "R²", "R² ajusté", "σ̂"),
Valeur = round(c(beta0_hat, beta1_hat, R2, R2_adj, sigma_hat), 6),
`Interprétation Financière` = c(
"Performance anormale quasi-nulle → cohérent avec CAPM en équilibre",
"Pour +1% de prime de marché, l'actif génère +β̂% de prime",
paste0(round(R2*100,2), "% de la variance expliquée par le risque systématique"),
"Qualité d'ajustement corrigée du biais d'estimation",
"Volatilité résiduelle (risque idiosyncratique non capturé)"
)
) %>%
kable(caption = "Tableau 8 — Synthèse des résultats et interprétations", booktabs = TRUE) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE,
font_size = 13) %>%
row_spec(0, bold = TRUE, color = "white", background = "#0d3b66") %>%
row_spec(2, bold = TRUE, background = "#eaf7ea")| Paramètre | Valeur | Interprétation Financière |
|---|---|---|
| α̂ — Jensen Alpha |
-0.06103
|
Classification de l’actif selon le Bêta estimé (\(\hat{\beta} = 1.3401\)) :
Pour cet exercice : \(\hat{\beta} = 1.3401\), ce qui classe l’actif comme agressif — amplificateur de risque systématique. Le \(\hat{\alpha} \approx -0.061\) est statistiquement non significatif (\(p > 0.05\)), confirmant l’hypothèse d’efficience des marchés en l’absence d’alpha positif persistant.
Rapport généré sous R Markdown · FST Errachidia, Finance & Actuariat · 2026