Stocks - stocks_FRA

Data - Investing

stocks_FRA réunit l’historique quotidien (OHLCV, source investing.com) de 749 actions de la place de Paris, dont les plus anciennes remontent à 1985.

Les grandes valeurs françaises depuis 2000

Cours rebasés à 100 début 2000, échelle logarithmique : une même pente = une même performance annualisée. L’écart entre le luxe (LVMH, Hermès, Kering) et le reste de la cote résume deux décennies de Bourse parisienne.

Code
sel <- tribble(
  ~symbol, ~lab,
  "LVMH", "LVMH",    "HRMS", "Hermès",       "OREP", "L'Oréal",
  "PRTP", "Kering",  "AIRP", "Air Liquide",  "SGEF", "Vinci",
  "SASY", "Sanofi",  "TOTF", "TotalEnergies","BNPP", "BNP Paribas")

stocks_FRA |>
  inner_join(sel, by = "symbol") |>
  deglitch() |>
  filter(Date >= as.Date("2000-01-01")) |>
  group_by(lab) |> arrange(Date) |>
  mutate(y = 100 * Close / first(Close)) |> ungroup() |>
  line_named("Cours rebasé (base 100 = janv. 2000)")

Palmarès : meilleures et pires performances depuis 2010

Rendement annualisé (hors dividendes) des valeurs cotées depuis 2010 — annualisé pour comparer sur un même axe des multiplications par 200 et des quasi-faillites (Rallye, Casino, EuropaCorp…).

Code
tr <- total_return(stocks_FRA, "2010-01-01") |>
  left_join(stocks_FRA_var |> select(symbol, name), by = "symbol") |>
  mutate(name = coalesce(name, symbol),
         yrs  = pmax(as.numeric(to - from) / 365.25, 1),
         cagr = ifelse(ret <= -0.999, -1, (1 + ret)^(1 / yrs) - 1)) |>
  filter(!is.na(cagr))

bind_rows(slice_max(tr, cagr, n = 18), slice_min(tr, cagr, n = 18)) |>
  mutate(name = fct_reorder(name, cagr),
         col  = ifelse(cagr >= 0, "#2ca02c", "#d62728")) |>
  ggplot(aes(cagr, name, fill = col)) +
  geom_col(width = 0.72) +
  geom_vline(xintercept = 0, colour = "grey40") +
  geom_text(aes(label = scales::percent(cagr, accuracy = 1),
                hjust = ifelse(cagr >= 0, -0.2, 1.2)),
            size = 2.7, colour = "grey30") +
  scale_fill_identity() +
  scale_x_continuous(labels = scales::percent,
                     expand = expansion(mult = c(0.14, 0.14))) +
  theme_minimal() + xlab("Rendement annualisé depuis 2010") + ylab("")

Le CAC 40 sur longue période

Indice CAC 40 (hors dividendes), échelle logarithmique depuis 1988.

Code
indices_FRA |>
  filter(symbol == "FCHI", !is.na(Close), Close > 0) |>
  ggplot(aes(Date, Close)) +
  geom_line(colour = "#1f77b4", linewidth = 0.5) +
  scale_x_date(breaks = seq(1985, 2100, 5) |> paste0("-01-01") |> as.Date(),
               labels = date_format("%Y")) +
  scale_y_log10(breaks = c(1000, 2000, 3000, 4000, 5000, 6000, 8000)) +
  theme_minimal() + xlab("") + ylab("CAC 40 (points, échelle log)")

Rendement contre risque : la droite de marché

Pour chaque valeur cotée depuis au moins 2005, rendement annualisé et bêta (sensibilité au CAC 40), estimés sur les variations mensuelles. La droite pointillée est l’ajustement linéaire — la « Security Market Line » empirique.

Code
mkt <- indices_FRA |> filter(symbol == "FCHI") |>
  mutate(ym = floor_date(Date, "month")) |>
  group_by(ym) |> slice_max(Date, n = 1) |> ungroup() |>
  transmute(ym, mkt = Close)

monthly <- stocks_FRA |>
  deglitch() |>
  transmute(symbol, Date, px = Close) |>
  filter(!is.na(px), px > 0) |>
  mutate(ym = floor_date(Date, "month")) |>
  group_by(symbol, ym) |> slice_max(Date, n = 1) |> ungroup() |>
  left_join(mkt, by = "ym") |>
  filter(!is.na(mkt)) |>
  arrange(symbol, ym) |>
  group_by(symbol) |>
  mutate(r = px / lag(px) - 1, rm = mkt / lag(mkt) - 1) |>
  filter(!is.na(r), !is.na(rm)) |>
  filter(n() >= 240, min(ym) <= as.Date("2005-01-01")) |>
  summarise(beta = cov(r, rm) / var(rm),
            ann  = (1 + mean(r))^12 - 1, .groups = "drop") |>
  left_join(stocks_FRA_var |> select(symbol, name), by = "symbol") |>
  mutate(name = coalesce(name, symbol)) |>
  filter(beta > 0, beta < 2.5, ann > -0.3, ann < 0.5)

big <- c("LVMH", "HRMS", "OREP", "SASY", "TOTF", "AIRP", "SCHN", "BNPP", "AXAF",
         "SGEF", "PRTP", "SAF", "AIR", "RENA", "SGEF", "STM", "MICP", "DANO",
         "ORAN", "PUBP", "LEGD", "CAPP", "TCFP", "PERP", "CARR", "VIE", "DAST",
         "SOGN", "ACAP", "EDEN", "ML")
lab <- monthly |> filter(symbol %in% big)

ggplot(monthly, aes(beta, ann)) +
  geom_hline(yintercept = 0, colour = "grey80") +
  geom_smooth(method = "lm", formula = y ~ x, linetype = 2,
              colour = "#d62728", se = FALSE) +
  geom_point(colour = "grey72", size = 1.4) +
  geom_point(data = lab, colour = "#1f77b4", size = 2.2) +
  ggrepel::geom_text_repel(data = lab, aes(label = name), size = 3,
                           colour = "grey20", segment.colour = "grey75",
                           max.overlaps = 20, min.segment.length = 0) +
  scale_y_continuous(labels = scales::percent) +
  theme_minimal() +
  xlab("Bêta (sensibilité au CAC 40)") +
  ylab("Rendement annualisé (variations mensuelles)")