Valor en Riesgo y Expected Shortfall

Cartera de seis emisoras: estimación, backtesting y optimización

Autores/as

Santiago Álvarez Tostado Estrada

Francisco Uriel Ledezma Chávez

Jorge Isaac López Ávila

Juan Pablo Morán Errejón

Fecha de publicación

21 de septiembre de 2026

Fuente de datos
Yahoo Finance / BMV para seis emisoras
Corte financiero
31 de diciembre de 2025
Moneda
MXN; pérdidas y rendimientos de cartera
Periodo cubierto
8 de junio de 2017–26 de agosto de 2026

1 Introducción

ImportantePendiente de redacción

Qué se estima y con qué propósito.

2 Construcción de la cartera

La cartera invierte $100,000 en cada una de las seis emisoras. Son pesos iguales en monto, no en número de acciones: las seis cotizan a niveles de precio muy distintos y una cartera de cien acciones de cada una sería una apuesta concentrada en la más cara.

La Tabla 1 presenta la composición monetaria inicial.

Ver código
report_table(
  portfolio$holdings,
  caption = paste0("Composición inicial al ", portfolio$as_of, ".")
)
Tabla 1: Composición inicial al 2026-08-25.
Ticker Precio (MXN) Peso Valor (MXN) Acciones
6749 ALSEA.MX 41.21 0.1667 100,000 2,426.6
9067 BBAJIOO.MX 57.33 0.1667 100,000 1,744.3
14223 GAPB.MX 354.53 0.1667 100,000 282.1
20985 GMEXICOB.MX 237.83 0.1667 100,000 420.5
27745 ORBIA.MX 20.81 0.1667 100,000 4,805.4
33724 WALMEX.MX 47.68 0.1667 100,000 2,097.3

El valor inicial de la cartera es \(S_0 =\) $600,000.

3 VaR no paramétrico: simulación histórica

El cuantil se lee directamente de la distribución empírica, sin suponer ninguna forma funcional, de modo que las colas pesadas y la asimetría se arrastran tal como la muestra las exhibe.

La Tabla 2 cuantifica los escenarios y la Figura 1 sitúa visualmente los umbrales sobre la distribución de pérdidas y ganancias.

Ver código
grid <- expand.grid(
  confidence = params()$var$confidence,
  horizon = params()$var$horizons_days
)
hist_var <- do.call(rbind, lapply(seq_len(nrow(grid)), function(i) {
  var_historical(portfolio_r, grid$confidence[i], grid$horizon[i], portfolio$value)
}))
report_table(hist_var, caption = "VaR por simulación histórica.", digits = 2)
Tabla 2: VaR por simulación histórica.
Método Confianza Horizonte VaR (rendimiento) VaR (MXN) VaR (%)
historical (scale) 0.95 1 -0.02 10,773 0.02
historical (scale) 0.98 1 -0.02 14,356 0.02
historical (scale) 0.99 1 -0.03 20,209 0.03
historical (scale) 0.95 5 -0.04 23,823 0.04
historical (scale) 0.98 5 -0.05 31,627 0.05
historical (scale) 0.99 5 -0.08 44,250 0.07
historical (scale) 0.95 10 -0.06 33,412 0.06
historical (scale) 0.98 10 -0.08 44,234 0.07
historical (scale) 0.99 10 -0.11 61,608 0.10
historical (scale) 0.95 20 -0.08 46,700 0.08
historical (scale) 0.98 20 -0.11 61,587 0.10
historical (scale) 0.99 20 -0.15 85,236 0.14
Ver código
pnl <- portfolio$value * (exp(portfolio_r) - 1)
cuts <- vapply(params()$var$confidence, function(c) {
  -var_historical(portfolio_r, c, 1, portfolio$value)$var_currency
}, numeric(1))
cut_labels <- data.frame(
  confidence = paste0(100 * params()$var$confidence, "%"),
  value = cuts,
  stringsAsFactors = FALSE
)

ggplot(data.frame(pnl = pnl), aes(pnl)) +
  geom_histogram(bins = 100, fill = RISK_PALETTE[1], alpha = 0.7) +
  geom_vline(
    data = cut_labels, aes(xintercept = value, colour = confidence),
    linewidth = 0.6
  ) +
  scale_colour_manual(values = unname(RISK_PALETTE[c(2, 4, 6)])) +
  scale_x_continuous(labels = label_dollar(prefix = "$")) +
  labs(
    x = "Pérdida / ganancia diaria (MXN)", y = "Frecuencia",
    caption = "Líneas verticales: VaR a un día por nivel de confianza."
  )
Distribución de pérdidas y ganancias diarias de la cartera, con el VaR a un día.
Figura 1: Distribución de pérdidas y ganancias diarias de la cartera, con el VaR a un día.

4 VaR paramétrico

Bajo normalidad, con \(\Sigma\) la matriz de covarianzas y \(w\) el vector de pesos:

\[ \sigma_p = \sqrt{w' \Sigma\, w} \qquad \text{VaR}_{h} = S_0\left(1 - e^{\,z_{1-c}\,\sigma_p\sqrt{h}}\right) \]

El escalamiento por \(\sqrt{h}\) supone independencia serial: la varianza crece linealmente en el tiempo y la desviación estándar con su raíz.

La Tabla 3 resume el VaR paramétrico y la Tabla 4 documenta la estructura de correlaciones utilizada.

Ver código
covariance <- covariance_matrix(matrix_returns)
sigma_p <- portfolio_sigma(portfolio$weights, covariance)

param_var <- do.call(rbind, lapply(seq_len(nrow(grid)), function(i) {
  var_parametric(sigma_p, grid$confidence[i], grid$horizon[i], portfolio$value)
}))
report_table(param_var, caption = "VaR paramétrico (varianza-covarianza).", digits = 2)
Tabla 3: VaR paramétrico (varianza-covarianza).
Método Confianza Horizonte VaR (rendimiento) VaR (MXN) VaR (%)
parametric 0.95 1 -0.02 11,327 0.02
parametric 0.98 1 -0.02 13,472 0.02
parametric 0.99 1 -0.03 15,956 0.03
parametric 0.95 5 -0.04 25,032 0.04
parametric 0.98 5 -0.05 29,707 0.05
parametric 0.99 5 -0.06 35,095 0.06
parametric 0.95 10 -0.06 35,092 0.06
parametric 0.98 10 -0.07 41,577 0.07
parametric 0.99 10 -0.09 49,023 0.08
parametric 0.95 20 -0.09 49,020 0.08
parametric 0.98 20 -0.10 57,943 0.10
parametric 0.99 20 -0.12 68,137 0.11
Ver código
report_table(
  as.data.frame(round(stats::cov2cor(covariance), 3)),
  caption = "Matriz de correlaciones de los últimos seis meses."
)
Tabla 4: Matriz de correlaciones de los últimos seis meses.
ALSEA.MX BBAJIOO.MX GAPB.MX GMEXICOB.MX ORBIA.MX WALMEX.MX
ALSEA.MX 1.000 0.186 0.152 0.279 0.218 0.166
BBAJIOO.MX 0.186 1.000 0.182 0.499 0.280 0.120
GAPB.MX 0.152 0.182 1.000 0.324 0.225 0.224
GMEXICOB.MX 0.279 0.499 0.324 1.000 0.450 0.163
ORBIA.MX 0.218 0.280 0.225 0.450 1.000 0.181
WALMEX.MX 0.166 0.120 0.224 0.163 0.181 1.000

5 Backtesting

Cada día se estima el VaR con la ventana previa únicamente, de modo que la serie es lo que el modelo habría reportado ese día. Estimarlo sobre la muestra completa y contar excepciones contra esa misma muestra no es un backtest.

La prueba de cobertura incondicional de Kupiec contrasta el número observado de excepciones contra el esperado:

\[ LR = -2\ln\frac{(1-p)^{\,n-x}\,p^{\,x}}{(1-\hat\pi)^{\,n-x}\,\hat\pi^{\,x}} \;\sim\; \chi^2_{1} \]

La Tabla 5 contrasta las excedencias observadas y esperadas.

Ver código
confidence <- 0.99
rolling_hist <- rolling_var(portfolio_r, confidence, method = "historical")
rolling_param <- rolling_var(portfolio_r, confidence, method = "parametric")

backtest <- rbind(
  cbind(metodo = "Histórico", backtest_var(portfolio_r, rolling_hist, confidence)),
  cbind(metodo = "Paramétrico", backtest_var(portfolio_r, rolling_param, confidence))
)
report_table(backtest, caption = "Backtesting del VaR a un día al 99%.")
Tabla 5: Backtesting del VaR a un día al 99%.
Método Confianza Observaciones Excedencias esperadas Excedencias observadas Tasa de excedencias Estadístico LR Valor p Rechaza el modelo
Histórico 0.99 2,066 20.66 35 0.0169 8.321 0.0039 TRUE
Paramétrico 0.99 2,066 20.66 38 0.0184 11.781 0.0006 TRUE
ImportantePendiente de redacción

Comparación de excepciones observadas contra esperadas y evaluación del desempeño. Demasiadas pocas excepciones reprueban la prueba igual que demasiadas: un modelo que nunca se excede no es conservador, está mal calibrado y es caro.

6 Optimización de la cartera

Ver código
optimal <- optimise_min_var(matrix_returns)

comparison <- compare_allocations(
  matrix_returns,
  list(
    "Pesos iguales" = portfolio$weights,
    "VaR mínimo" = optimal$weights
  ),
  value = portfolio$value
)
report_table(comparison, caption = "Cartera equiponderada frente a la de VaR mínimo.", digits = 2)
ImportantePendiente de redacción

Comparación de composiciones y análisis del cambio en riesgo y diversificación. Activar el chunk cuando nloptr esté instalado (renv::restore()).

7 VaR individual y Expected Shortfall

La Tabla 6 presenta ambas medidas para la posición individual.

Ver código
position <- params()$var$position_mxn
subject_returns <- returns[returns$ticker == subject(), "log_return"]

individual <- var_monte_carlo(
  subject_returns,
  confidence = params()$var$monte_carlo$confidence,
  horizon = 1,
  value = position
)
report_table(
  individual,
  caption = paste0("VaR y ES al 99% por Monte Carlo sobre una posición de ", fmt_mxn(position), " en ", subject(), "."),
  digits = 2
)
Tabla 6: VaR y ES al 99% por Monte Carlo sobre una posición de $100,000 en GAPB.MX.
Método Confianza Horizonte Simulaciones VaR (rendimiento) VaR (MXN) VaR (%) ES (rendimiento) ES (MXN) ES (%)
monte carlo (bootstrap) 0.99 1 100,000 -0.05 5,106 0.05 -0.07 7,183 0.07

El Expected Shortfall es la pérdida promedio condicionada a haber rebasado el VaR:

\[ \text{ES}_c = \mathbb{E}\left[\,L \mid L \ge \text{VaR}_c\,\right] \]

Donde el VaR dice cuánto se pierde antes de entrar al peor 1%, el ES dice cuánto se pierde dentro de ese 1% — la pregunta que el VaR es estructuralmente incapaz de responder.

8 Análisis e interpretación

ImportantePendiente de redacción
  • ¿Qué diferencias hay entre el VaR paramétrico y el no paramétrico?
  • ¿Cuál captura mejor los eventos extremos?
  • ¿Cuál es más conservador?
  • ¿Qué implica elegir un modelo que subestima el riesgo?
  • ¿Qué arroja el backtesting y qué se concluye de ello?

9 Conclusiones

ImportantePendiente de redacción

Comparación de metodologías y justificación de cuál se usaría en la práctica.

10 Referencias